From ff6af6eede200503c58250821d1285f94039eef7 Mon Sep 17 00:00:00 2001 From: Ilias Apalodimas Date: Wed, 16 Mar 2022 17:13:37 +0200 Subject: efi_loader: Set variable attributes when EFI_BUFFER_TOO_SMALL is returned Starting UEFI Spec 2.8 we must fill in the variable attributes when GetVariable() returns EFI_BUFFER_TOO_SMALL and Attributes is non-NULL. This code was written with 2.7 in mind so let's move the code around a bit and fill in the attributes EFI_BUFFER_TOO_SMALL is returned Signed-off-by: Ilias Apalodimas Reviewed-by: Heinrich Schuchardt --- lib/efi_loader/efi_variable_tee.c | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) (limited to 'lib') diff --git a/lib/efi_loader/efi_variable_tee.c b/lib/efi_loader/efi_variable_tee.c index 58931c4..dfef184 100644 --- a/lib/efi_loader/efi_variable_tee.c +++ b/lib/efi_loader/efi_variable_tee.c @@ -368,7 +368,7 @@ efi_status_t efi_get_variable_int(const u16 *variable_name, efi_uintn_t name_size; efi_uintn_t tmp_dsize; u8 *comm_buf = NULL; - efi_status_t ret; + efi_status_t ret, tmp; if (!variable_name || !vendor || !data_size) { ret = EFI_INVALID_PARAMETER; @@ -407,23 +407,32 @@ efi_status_t efi_get_variable_int(const u16 *variable_name, /* Communicate */ ret = mm_communicate(comm_buf, payload_size); - if (ret == EFI_SUCCESS || ret == EFI_BUFFER_TOO_SMALL) { - /* Update with reported data size for trimmed case */ - *data_size = var_acc->data_size; - } - if (ret != EFI_SUCCESS) - goto out; - - ret = get_property_int(variable_name, name_size, vendor, &var_property); - if (ret != EFI_SUCCESS) + if (ret != EFI_SUCCESS && ret != EFI_BUFFER_TOO_SMALL) goto out; + /* Update with reported data size for trimmed case */ + *data_size = var_acc->data_size; + /* + * UEFI > 2.7 needs the attributes set even if the buffer is + * smaller + */ if (attributes) { + tmp = get_property_int(variable_name, name_size, vendor, + &var_property); + if (tmp != EFI_SUCCESS) { + ret = tmp; + goto out; + } *attributes = var_acc->attr; - if (var_property.property & VAR_CHECK_VARIABLE_PROPERTY_READ_ONLY) + if (var_property.property & + VAR_CHECK_VARIABLE_PROPERTY_READ_ONLY) *attributes |= EFI_VARIABLE_READ_ONLY; } + /* return if ret is EFI_BUFFER_TOO_SMALL */ + if (ret != EFI_SUCCESS) + goto out; + if (data) memcpy(data, (u8 *)var_acc->name + var_acc->name_size, var_acc->data_size); -- cgit v1.1 From 8399488672e8717d3aa454f9f67408f96168074f Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sat, 26 Feb 2022 12:10:10 +0100 Subject: efi_loader: export efi_dp_shorten() Rename function shorten_path() to efi_dp_shorten() and export it. Signed-off-by: Heinrich Schuchardt --- lib/efi_loader/efi_device_path.c | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) (limited to 'lib') diff --git a/lib/efi_loader/efi_device_path.c b/lib/efi_loader/efi_device_path.c index dc787b4..ddd5f13 100644 --- a/lib/efi_loader/efi_device_path.c +++ b/lib/efi_loader/efi_device_path.c @@ -122,20 +122,25 @@ int efi_dp_match(const struct efi_device_path *a, } } -/* +/** + * efi_dp_shorten() - shorten device-path + * * We can have device paths that start with a USB WWID or a USB Class node, * and a few other cases which don't encode the full device path with bus * hierarchy: * - * - MESSAGING:USB_WWID - * - MESSAGING:USB_CLASS - * - MEDIA:FILE_PATH - * - MEDIA:HARD_DRIVE - * - MESSAGING:URI + * * MESSAGING:USB_WWID + * * MESSAGING:USB_CLASS + * * MEDIA:FILE_PATH + * * MEDIA:HARD_DRIVE + * * MESSAGING:URI * * See UEFI spec (section 3.1.2, about short-form device-paths) + * + * @dp: original devie-path + * @Return: shortened device-path or NULL */ -static struct efi_device_path *shorten_path(struct efi_device_path *dp) +struct efi_device_path *efi_dp_shorten(struct efi_device_path *dp) { while (dp) { /* @@ -189,7 +194,7 @@ static struct efi_object *find_obj(struct efi_device_path *dp, bool short_path, } } - obj_dp = shorten_path(efi_dp_next(obj_dp)); + obj_dp = efi_dp_shorten(efi_dp_next(obj_dp)); } while (short_path && obj_dp); } -- cgit v1.1 From c409593d0853da646194b0a3d65c8b45fe7cb6d4 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Fri, 4 Mar 2022 08:20:00 +0100 Subject: efi_loader: fix efi_dp_find_obj() efi_dp_find_obj() should not return any handle with a partially matching device path but the handle with the maximum matching device path. Signed-off-by: Heinrich Schuchardt --- lib/efi_loader/efi_device_path.c | 110 ++++++++++++++++++++++----------------- 1 file changed, 61 insertions(+), 49 deletions(-) (limited to 'lib') diff --git a/lib/efi_loader/efi_device_path.c b/lib/efi_loader/efi_device_path.c index ddd5f13..aeb5264 100644 --- a/lib/efi_loader/efi_device_path.c +++ b/lib/efi_loader/efi_device_path.c @@ -159,69 +159,81 @@ struct efi_device_path *efi_dp_shorten(struct efi_device_path *dp) return dp; } -static struct efi_object *find_obj(struct efi_device_path *dp, bool short_path, - struct efi_device_path **rem) +/** + * find_handle() - find handle by device path + * + * If @rem is provided, the handle with the longest partial match is returned. + * + * @dp: device path to search + * @short_path: use short form device path for matching + * @rem: pointer to receive remaining device path + * Return: matching handle + */ +static efi_handle_t find_handle(struct efi_device_path *dp, bool short_path, + struct efi_device_path **rem) { - struct efi_object *efiobj; - efi_uintn_t dp_size = efi_dp_instance_size(dp); + efi_handle_t handle, best_handle = NULL; + efi_uintn_t len, best_len = 0; + + len = efi_dp_instance_size(dp); - list_for_each_entry(efiobj, &efi_obj_list, link) { + list_for_each_entry(handle, &efi_obj_list, link) { struct efi_handler *handler; - struct efi_device_path *obj_dp; + struct efi_device_path *dp_current; + efi_uintn_t len_current; efi_status_t ret; - ret = efi_search_protocol(efiobj, - &efi_guid_device_path, &handler); + ret = efi_search_protocol(handle, &efi_guid_device_path, + &handler); if (ret != EFI_SUCCESS) continue; - obj_dp = handler->protocol_interface; - - do { - if (efi_dp_match(dp, obj_dp) == 0) { - if (rem) { - /* - * Allow partial matches, but inform - * the caller. - */ - *rem = ((void *)dp) + - efi_dp_instance_size(obj_dp); - return efiobj; - } else { - /* Only return on exact matches */ - if (efi_dp_instance_size(obj_dp) == - dp_size) - return efiobj; - } - } - - obj_dp = efi_dp_shorten(efi_dp_next(obj_dp)); - } while (short_path && obj_dp); + dp_current = handler->protocol_interface; + if (short_path) { + dp_current = efi_dp_shorten(dp_current); + if (!dp_current) + continue; + } + len_current = efi_dp_instance_size(dp_current); + if (rem) { + if (len_current < len) + continue; + } else { + if (len_current != len) + continue; + } + if (memcmp(dp_current, dp, len)) + continue; + if (!rem) + return handle; + if (len_current > best_len) { + best_len = len_current; + best_handle = handle; + *rem = (void*)((u8 *)dp + len_current); + } } - - return NULL; + return best_handle; } -/* - * Find an efiobj from device-path, if 'rem' is not NULL, returns the - * remaining part of the device path after the matched object. +/** + * efi_dp_find_obj() - find handle by device path + * + * If @rem is provided, the handle with the longest partial match is returned. + * + * @dp: device path to search + * @rem: pointer to receive remaining device path + * Return: matching handle */ -struct efi_object *efi_dp_find_obj(struct efi_device_path *dp, - struct efi_device_path **rem) +efi_handle_t efi_dp_find_obj(struct efi_device_path *dp, + struct efi_device_path **rem) { - struct efi_object *efiobj; - - /* Search for an exact match first */ - efiobj = find_obj(dp, false, NULL); - - /* Then for a fuzzy match */ - if (!efiobj) - efiobj = find_obj(dp, false, rem); + efi_handle_t handle; - /* And now for a fuzzy short match */ - if (!efiobj) - efiobj = find_obj(dp, true, rem); + handle = find_handle(dp, false, rem); + if (!handle) + /* Match short form device path */ + handle = find_handle(dp, true, rem); - return efiobj; + return handle; } /* -- cgit v1.1 From e46ef1db9e2c87d5aa13a04ea2329b8bae7ea9db Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sat, 19 Mar 2022 06:35:43 +0100 Subject: efi_loader: efi_dp_find_obj() add protocol check Let function efi_dp_find_obj() additionally check if a given protocol is installed on the handle relating to the device-path. Signed-off-by: Heinrich Schuchardt --- lib/efi_loader/efi_boottime.c | 2 +- lib/efi_loader/efi_capsule.c | 2 +- lib/efi_loader/efi_device_path.c | 23 ++++++++++++++++------- lib/efi_loader/efi_disk.c | 2 +- 4 files changed, 19 insertions(+), 10 deletions(-) (limited to 'lib') diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index d0f3e05..a7bc371 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -1750,7 +1750,7 @@ efi_status_t efi_setup_loaded_image(struct efi_device_path *device_path, info->system_table = &systab; if (device_path) { - info->device_handle = efi_dp_find_obj(device_path, NULL); + info->device_handle = efi_dp_find_obj(device_path, NULL, NULL); dp = efi_dp_append(device_path, file_path); if (!dp) { diff --git a/lib/efi_loader/efi_capsule.c b/lib/efi_loader/efi_capsule.c index 613b531..011942b 100644 --- a/lib/efi_loader/efi_capsule.c +++ b/lib/efi_loader/efi_capsule.c @@ -680,7 +680,7 @@ static bool device_is_present_and_system_part(struct efi_device_path *dp) { efi_handle_t handle; - handle = efi_dp_find_obj(dp, NULL); + handle = efi_dp_find_obj(dp, NULL, NULL); if (!handle) return false; diff --git a/lib/efi_loader/efi_device_path.c b/lib/efi_loader/efi_device_path.c index aeb5264..0a88029 100644 --- a/lib/efi_loader/efi_device_path.c +++ b/lib/efi_loader/efi_device_path.c @@ -160,17 +160,19 @@ struct efi_device_path *efi_dp_shorten(struct efi_device_path *dp) } /** - * find_handle() - find handle by device path + * find_handle() - find handle by device path and installed protocol * * If @rem is provided, the handle with the longest partial match is returned. * * @dp: device path to search + * @guid: GUID of protocol that must be installed on path or NULL * @short_path: use short form device path for matching * @rem: pointer to receive remaining device path * Return: matching handle */ -static efi_handle_t find_handle(struct efi_device_path *dp, bool short_path, - struct efi_device_path **rem) +static efi_handle_t find_handle(struct efi_device_path *dp, + const efi_guid_t *guid, bool short_path, + struct efi_device_path **rem) { efi_handle_t handle, best_handle = NULL; efi_uintn_t len, best_len = 0; @@ -183,6 +185,11 @@ static efi_handle_t find_handle(struct efi_device_path *dp, bool short_path, efi_uintn_t len_current; efi_status_t ret; + if (guid) { + ret = efi_search_protocol(handle, guid, &handler); + if (ret != EFI_SUCCESS) + continue; + } ret = efi_search_protocol(handle, &efi_guid_device_path, &handler); if (ret != EFI_SUCCESS) @@ -195,13 +202,13 @@ static efi_handle_t find_handle(struct efi_device_path *dp, bool short_path, } len_current = efi_dp_instance_size(dp_current); if (rem) { - if (len_current < len) + if (len_current > len) continue; } else { if (len_current != len) continue; } - if (memcmp(dp_current, dp, len)) + if (memcmp(dp_current, dp, len_current)) continue; if (!rem) return handle; @@ -220,18 +227,20 @@ static efi_handle_t find_handle(struct efi_device_path *dp, bool short_path, * If @rem is provided, the handle with the longest partial match is returned. * * @dp: device path to search + * @guid: GUID of protocol that must be installed on path or NULL * @rem: pointer to receive remaining device path * Return: matching handle */ efi_handle_t efi_dp_find_obj(struct efi_device_path *dp, + const efi_guid_t *guid, struct efi_device_path **rem) { efi_handle_t handle; - handle = find_handle(dp, false, rem); + handle = find_handle(dp, guid, false, rem); if (!handle) /* Match short form device path */ - handle = find_handle(dp, true, rem); + handle = find_handle(dp, guid, true, rem); return handle; } diff --git a/lib/efi_loader/efi_disk.c b/lib/efi_loader/efi_disk.c index 45127d1..d36a35d 100644 --- a/lib/efi_loader/efi_disk.c +++ b/lib/efi_loader/efi_disk.c @@ -302,7 +302,7 @@ efi_fs_from_path(struct efi_device_path *full_path) efi_free_pool(file_path); /* Get the EFI object for the partition */ - efiobj = efi_dp_find_obj(device_path, NULL); + efiobj = efi_dp_find_obj(device_path, NULL, NULL); efi_free_pool(device_path); if (!efiobj) return NULL; -- cgit v1.1 From 9cdf470274ff30cda3ce89567a69106a27404c23 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sat, 26 Feb 2022 12:05:30 +0100 Subject: efi_loader: support booting via short-form device-path The boot manager must support loading from boot options using a short-form device-path, e.g. one where the first element is a hard drive media path. See '3.1.2 Load Options Processing' in UEFI specification version 2.9. Fixes: 0e074d12393b ("efi_loader: carve out efi_load_image_from_file()") Signed-off-by: Heinrich Schuchardt --- lib/efi_loader/efi_boottime.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'lib') diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index a7bc371..5bcb825 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -1940,7 +1940,7 @@ efi_status_t efi_load_image_from_path(bool boot_policy, { efi_handle_t device; efi_status_t ret; - struct efi_device_path *dp; + struct efi_device_path *dp, *rem; struct efi_load_file_protocol *load_file_protocol = NULL; efi_uintn_t buffer_size; uint64_t addr, pages; @@ -1951,18 +1951,18 @@ efi_status_t efi_load_image_from_path(bool boot_policy, *size = 0; dp = file_path; - ret = EFI_CALL(efi_locate_device_path( - &efi_simple_file_system_protocol_guid, &dp, &device)); + device = efi_dp_find_obj(dp, NULL, &rem); + ret = efi_search_protocol(device, &efi_simple_file_system_protocol_guid, + NULL); if (ret == EFI_SUCCESS) return efi_load_image_from_file(file_path, buffer, size); - ret = EFI_CALL(efi_locate_device_path( - &efi_guid_load_file_protocol, &dp, &device)); + ret = efi_search_protocol(device, &efi_guid_load_file_protocol, NULL); if (ret == EFI_SUCCESS) { guid = &efi_guid_load_file_protocol; } else if (!boot_policy) { guid = &efi_guid_load_file2_protocol; - ret = EFI_CALL(efi_locate_device_path(guid, &dp, &device)); + ret = efi_search_protocol(device, guid, NULL); } if (ret != EFI_SUCCESS) return EFI_NOT_FOUND; @@ -1971,9 +1971,9 @@ efi_status_t efi_load_image_from_path(bool boot_policy, if (ret != EFI_SUCCESS) return EFI_NOT_FOUND; buffer_size = 0; - ret = load_file_protocol->load_file(load_file_protocol, dp, - boot_policy, &buffer_size, - NULL); + ret = EFI_CALL(load_file_protocol->load_file( + load_file_protocol, rem, boot_policy, + &buffer_size, NULL)); if (ret != EFI_BUFFER_TOO_SMALL) goto out; pages = efi_size_in_pages(buffer_size); @@ -1984,7 +1984,7 @@ efi_status_t efi_load_image_from_path(bool boot_policy, goto out; } ret = EFI_CALL(load_file_protocol->load_file( - load_file_protocol, dp, boot_policy, + load_file_protocol, rem, boot_policy, &buffer_size, (void *)(uintptr_t)addr)); if (ret != EFI_SUCCESS) efi_free_pages(addr, pages); -- cgit v1.1 From b78631d54f72f063e1f2a5d14a349b1b0d8fbce7 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sat, 5 Mar 2022 00:36:50 +0100 Subject: efi_loader: remove efi_disk_is_system_part() The block IO protocol may be installed on any handle. We should make no assumption about the structure the handle points to. efi_disk_is_system_part() makes an illegal widening cast from a handle to a struct efi_disk_obj. Remove the function. Fixes: Fixes: 41fd506842c2 ("efi_loader: disk: add efi_disk_is_system_part()") Signed-off-by: Heinrich Schuchardt --- lib/efi_loader/efi_capsule.c | 11 +++++++++-- lib/efi_loader/efi_disk.c | 29 ----------------------------- 2 files changed, 9 insertions(+), 31 deletions(-) (limited to 'lib') diff --git a/lib/efi_loader/efi_capsule.c b/lib/efi_loader/efi_capsule.c index 011942b..f004401 100644 --- a/lib/efi_loader/efi_capsule.c +++ b/lib/efi_loader/efi_capsule.c @@ -669,22 +669,29 @@ static efi_status_t get_dp_device(u16 *boot_var, /** * device_is_present_and_system_part - check if a device exists - * @dp Device path * * Check if a device pointed to by the device path, @dp, exists and is * located in UEFI system partition. * + * @dp device path * Return: true - yes, false - no */ static bool device_is_present_and_system_part(struct efi_device_path *dp) { efi_handle_t handle; + struct efi_device_path *rem; + /* Check device exists */ handle = efi_dp_find_obj(dp, NULL, NULL); if (!handle) return false; - return efi_disk_is_system_part(handle); + /* Check device is on system partition */ + handle = efi_dp_find_obj(dp, &efi_system_partition_guid, &rem); + if (!handle) + return false; + + return true; } /** diff --git a/lib/efi_loader/efi_disk.c b/lib/efi_loader/efi_disk.c index d36a35d..c905c12 100644 --- a/lib/efi_loader/efi_disk.c +++ b/lib/efi_loader/efi_disk.c @@ -587,32 +587,3 @@ efi_status_t efi_disk_register(void) return EFI_SUCCESS; } - -/** - * efi_disk_is_system_part() - check if handle refers to an EFI system partition - * - * @handle: handle of partition - * - * Return: true if handle refers to an EFI system partition - */ -bool efi_disk_is_system_part(efi_handle_t handle) -{ - struct efi_handler *handler; - struct efi_disk_obj *diskobj; - struct disk_partition info; - efi_status_t ret; - int r; - - /* check if this is a block device */ - ret = efi_search_protocol(handle, &efi_block_io_guid, &handler); - if (ret != EFI_SUCCESS) - return false; - - diskobj = container_of(handle, struct efi_disk_obj, header); - - r = part_get_info(diskobj->desc, diskobj->part, &info); - if (r) - return false; - - return !!(info.bootable & PART_EFI_SYSTEM_PARTITION); -} -- cgit v1.1 From 9c045a49a9c96ffb6e6b43f7e615fc2bd0e2077d Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Thu, 3 Mar 2022 08:13:53 +0100 Subject: efi_loader: move dtbdump.c, initrddump.c to lib/efi_loader The tools dtbdump.efi and initrddump.efi are useful for Python testing even if CONFIG_EFI_SELFTEST=n. Don't clear the screen as it is incompatible with Python testing. Signed-off-by: Heinrich Schuchardt --- lib/efi_loader/Makefile | 12 + lib/efi_loader/dtbdump.c | 539 ++++++++++++++++++++++++++++++++++++++++++ lib/efi_loader/initrddump.c | 449 +++++++++++++++++++++++++++++++++++ lib/efi_selftest/Makefile | 12 - lib/efi_selftest/dtbdump.c | 539 ------------------------------------------ lib/efi_selftest/initrddump.c | 449 ----------------------------------- 6 files changed, 1000 insertions(+), 1000 deletions(-) create mode 100644 lib/efi_loader/dtbdump.c create mode 100644 lib/efi_loader/initrddump.c delete mode 100644 lib/efi_selftest/dtbdump.c delete mode 100644 lib/efi_selftest/initrddump.c (limited to 'lib') diff --git a/lib/efi_loader/Makefile b/lib/efi_loader/Makefile index b2c664d..befed71 100644 --- a/lib/efi_loader/Makefile +++ b/lib/efi_loader/Makefile @@ -14,12 +14,24 @@ CFLAGS_efi_boottime.o += \ -DFW_PATCHLEVEL="0x$(PATCHLEVEL)" CFLAGS_helloworld.o := $(CFLAGS_EFI) -Os -ffreestanding CFLAGS_REMOVE_helloworld.o := $(CFLAGS_NON_EFI) +CFLAGS_dtbdump.o := $(CFLAGS_EFI) -Os -ffreestanding +CFLAGS_REMOVE_dtbdump.o := $(CFLAGS_NON_EFI) +CFLAGS_initrddump_exit.o := $(CFLAGS_EFI) -Os -ffreestanding +CFLAGS_REMOVE_initrddump.o := $(CFLAGS_NON_EFI) ifneq ($(CONFIG_CMD_BOOTEFI_HELLO_COMPILE),) always += helloworld.efi targets += helloworld.o endif +ifeq ($(CONFIG_GENERATE_ACPI_TABLE),) +always += dtbdump.efi +endif + +ifdef CONFIG_EFI_LOAD_FILE2_INITRD +always += initrddump.efi +endif + obj-$(CONFIG_CMD_BOOTEFI_HELLO) += helloworld_efi.o obj-$(CONFIG_CMD_BOOTEFI_BOOTMGR) += efi_bootmgr.o obj-y += efi_boottime.o diff --git a/lib/efi_loader/dtbdump.c b/lib/efi_loader/dtbdump.c new file mode 100644 index 0000000..3ce2a07 --- /dev/null +++ b/lib/efi_loader/dtbdump.c @@ -0,0 +1,539 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2020, Heinrich Schuchardt + * + * dtbdump.efi saves the device tree provided as a configuration table + * to a file. + */ + +#include +#include +#include +#include +#include + +#define BUFFER_SIZE 64 +#define ESC 0x17 + +#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; +static struct efi_simple_text_input_protocol *cin; +static struct efi_boot_services *bs; +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; +static const efi_guid_t efi_system_partition_guid = PARTITION_SYSTEM_GUID; + +/** + * print() - print string + * + * @string: text + */ +static void print(u16 *string) +{ + cout->output_string(cout, string); +} + +/** + * error() - print error string + * + * @string: error text + */ +static void error(u16 *string) +{ + cout->set_attribute(cout, EFI_LIGHTRED | EFI_BACKGROUND_BLACK); + print(string); + cout->set_attribute(cout, EFI_LIGHTBLUE | EFI_BACKGROUND_BLACK); +} + +/** + * efi_input_yn() - get answer to yes/no question + * + * Return: + * y or Y + * EFI_SUCCESS + * n or N + * EFI_ACCESS_DENIED + * ESC + * EFI_ABORTED + */ +static efi_status_t efi_input_yn(void) +{ + struct efi_input_key key = {0}; + efi_uintn_t index; + efi_status_t ret; + + /* Drain the console input */ + ret = cin->reset(cin, true); + for (;;) { + ret = bs->wait_for_event(1, &cin->wait_for_key, &index); + if (ret != EFI_SUCCESS) + continue; + ret = cin->read_key_stroke(cin, &key); + if (ret != EFI_SUCCESS) + continue; + switch (key.scan_code) { + case 0x17: /* Escape */ + return EFI_ABORTED; + default: + break; + } + /* Convert to lower case */ + switch (key.unicode_char | 0x20) { + case 'y': + return EFI_SUCCESS; + case 'n': + return EFI_ACCESS_DENIED; + default: + break; + } + } +} + +/** + * efi_input() - read string from console + * + * @buffer: input buffer + * @buffer_size: buffer size + * Return: status code + */ +static efi_status_t efi_input(u16 *buffer, efi_uintn_t buffer_size) +{ + struct efi_input_key key = {0}; + efi_uintn_t index; + efi_uintn_t pos = 0; + u16 outbuf[2] = u" "; + efi_status_t ret; + + /* 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) + continue; + ret = cin->read_key_stroke(cin, &key); + if (ret != EFI_SUCCESS) + continue; + switch (key.scan_code) { + case 0x17: /* Escape */ + print(u"\r\nAborted\r\n"); + return EFI_ABORTED; + default: + break; + } + switch (key.unicode_char) { + case 0x08: /* Backspace */ + if (pos) { + buffer[pos--] = 0; + print(u"\b \b"); + } + break; + case 0x0a: /* Linefeed */ + case 0x0d: /* Carriage return */ + print(u"\r\n"); + return EFI_SUCCESS; + default: + break; + } + /* Ignore surrogate codes */ + if (key.unicode_char >= 0xD800 && key.unicode_char <= 0xDBFF) + continue; + if (key.unicode_char >= 0x20 && + pos < buffer_size - 1) { + *outbuf = key.unicode_char; + buffer[pos++] = key.unicode_char; + buffer[pos] = 0; + print(outbuf); + } + } +} + +/* + * Convert FDT value to host endianness. + * + * @val FDT value + * Return: converted value + */ +static u32 f2h(fdt32_t val) +{ + char *buf = (char *)&val; + char i; + + /* Swap the bytes */ + i = buf[0]; buf[0] = buf[3]; buf[3] = i; + i = buf[1]; buf[1] = buf[2]; buf[2] = i; + return *(u32 *)buf; +} + +/** + * get_dtb() - get device tree + * + * @systable: system table + * Return: device tree or NULL + */ +void *get_dtb(struct efi_system_table *systable) +{ + void *dtb = NULL; + efi_uintn_t i; + + for (i = 0; i < systable->nr_tables; ++i) { + if (!memcmp(&systable->tables[i].guid, &fdt_guid, + sizeof(efi_guid_t))) { + dtb = systable->tables[i].table; + break; + } + } + return dtb; +} + +/** + * skip_whitespace() - skip over leading whitespace + * + * @pos: UTF-16 string + * Return: pointer to first non-whitespace + */ +u16 *skip_whitespace(u16 *pos) +{ + 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(u"load - load device-tree from file\r\n"); + error(u"save - save device-tree to file\r\n"); + error(u"exit - exit the shell\r\n"); +} + +/** + * open_file_system() - open simple file system protocol + * + * file_system: interface of the simple file system protocol + * Return: status code + */ +static efi_status_t +open_file_system(struct efi_simple_file_system_protocol **file_system) +{ + struct efi_loaded_image *loaded_image; + efi_status_t ret; + efi_handle_t *handle_buffer = NULL; + efi_uintn_t count; + + ret = bs->open_protocol(handle, &loaded_image_guid, + (void **)&loaded_image, NULL, NULL, + EFI_OPEN_PROTOCOL_GET_PROTOCOL); + if (ret != EFI_SUCCESS) { + error(u"Loaded image protocol not found\r\n"); + return ret; + } + + /* Open the simple file system protocol on the same partition */ + 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) + return ret; + + /* Open the simple file system protocol on the UEFI system partition */ + ret = bs->locate_handle_buffer(BY_PROTOCOL, &efi_system_partition_guid, + NULL, &count, &handle_buffer); + if (ret == EFI_SUCCESS && handle_buffer) + ret = bs->open_protocol(handle_buffer[0], + &guid_simple_file_system_protocol, + (void **)file_system, NULL, NULL, + EFI_OPEN_PROTOCOL_GET_PROTOCOL); + if (ret != EFI_SUCCESS) + error(u"Failed to open simple file system protocol\r\n"); + if (handle) + bs->free_pool(handle_buffer); + + return ret; +} + +/** + * 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_simple_file_system_protocol *file_system; + 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; + + ret = bs->locate_protocol(&efi_dt_fixup_protocol_guid, NULL, + (void **)&dt_fixup_prot); + if (ret != EFI_SUCCESS) { + error(u"Device-tree fix-up protocol not found\r\n"); + return ret; + } + + filename = skip_whitespace(filename); + + ret = open_file_system(&file_system); + if (ret != EFI_SUCCESS) + goto out; + + /* Open volume */ + ret = file_system->open_volume(file_system, &root); + if (ret != EFI_SUCCESS) { + error(u"Failed to open volume\r\n"); + goto out; + } + + /* Open file */ + ret = root->open(root, &file, filename, EFI_FILE_MODE_READ, 0); + if (ret != EFI_SUCCESS) { + error(u"File not found\r\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(u"Can't get file info size\r\n"); + goto out; + } + ret = bs->allocate_pool(EFI_LOADER_DATA, buffer_size, (void **)&info); + if (ret != EFI_SUCCESS) { + error(u"Out of memory\r\n"); + goto out; + } + ret = file->getinfo(file, &efi_file_info_guid, &buffer_size, info); + if (ret != EFI_SUCCESS) { + error(u"Can't get file info\r\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(u"Can't free memory pool\r\n"); + /* Read file */ + ret = bs->allocate_pages(EFI_ALLOCATE_ANY_PAGES, + EFI_ACPI_RECLAIM_MEMORY, + pages, &addr); + if (ret != EFI_SUCCESS) { + error(u"Out of memory\r\n"); + goto out; + } + dtb = (struct fdt_header *)(uintptr_t)addr; + ret = file->read(file, &buffer_size, dtb); + if (ret != EFI_SUCCESS) { + error(u"Can't read file\r\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(u"Can't free memory pages\r\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(u"Out of memory\r\n"); + goto out; + } + dtb = (struct fdt_header *)(uintptr_t)addr; + ret = file->setpos(file, 0); + if (ret != EFI_SUCCESS) { + error(u"Can't position file\r\n"); + goto out; + } + ret = file->read(file, &buffer_size, dtb); + if (ret != EFI_SUCCESS) { + error(u"Can't read file\r\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) + print(u"device-tree installed\r\n"); + else + error(u"Device-tree fix-up failed\r\n"); +out: + if (addr) { + ret2 = bs->free_pages(addr, pages); + if (ret2 != EFI_SUCCESS) + error(u"Can't free memory pages\r\n"); + } + if (file) { + ret2 = file->close(file); + if (ret2 != EFI_SUCCESS) + error(u"Can't close file\r\n"); + } + if (root) { + ret2 = root->close(root); + if (ret2 != EFI_SUCCESS) + error(u"Can't close volume\r\n"); + } + return ret; +} + +/** + * do_save() - save current device-tree + * + * @filename: file name + * Return: status code + */ +efi_status_t do_save(u16 *filename) +{ + 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) { + error(u"DTB not found\r\n"); + return EFI_NOT_FOUND; + } + if (f2h(dtb->magic) != FDT_MAGIC) { + error(u"Wrong device tree magic\r\n"); + return EFI_NOT_FOUND; + } + dtb_size = f2h(dtb->totalsize); + + filename = skip_whitespace(filename); + + ret = open_file_system(&file_system); + if (ret != EFI_SUCCESS) + return ret; + + /* Open volume */ + ret = file_system->open_volume(file_system, &root); + if (ret != EFI_SUCCESS) { + error(u"Failed to open volume\r\n"); + return ret; + } + /* Check if file already exists */ + ret = root->open(root, &file, filename, EFI_FILE_MODE_READ, 0); + if (ret == EFI_SUCCESS) { + file->close(file); + print(u"Overwrite existing file (y/n)? "); + ret = efi_input_yn(); + print(u"\r\n"); + if (ret != EFI_SUCCESS) { + root->close(root); + error(u"Aborted by user\r\n"); + return ret; + } + } + + /* Create file */ + ret = root->open(root, &file, filename, + EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE | + EFI_FILE_MODE_CREATE, EFI_FILE_ARCHIVE); + if (ret == EFI_SUCCESS) { + /* Write file */ + ret = file->write(file, &dtb_size, dtb); + if (ret != EFI_SUCCESS) + error(u"Failed to write file\r\n"); + file->close(file); + } else { + error(u"Failed to open file\r\n"); + } + root->close(root); + + if (ret == EFI_SUCCESS) { + print(filename); + print(u" written\r\n"); + } + + 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); + print(u"DTB Dump\r\n========\r\n\r\n"); + cout->set_attribute(cout, EFI_LIGHTBLUE | EFI_BACKGROUND_BLACK); + + for (;;) { + u16 command[BUFFER_SIZE]; + u16 *pos; + efi_uintn_t ret; + + print(u"=> "); + ret = efi_input(command, sizeof(command)); + if (ret == EFI_ABORTED) + break; + pos = skip_whitespace(command); + if (starts_with(pos, u"exit")) + break; + else if (starts_with(pos, u"load ")) + do_load(pos + 5); + else if (starts_with(pos, u"save ")) + do_save(pos + 5); + else + do_help(); + } + + cout->set_attribute(cout, EFI_LIGHTGRAY | EFI_BACKGROUND_BLACK); + cout->clear_screen(cout); + return EFI_SUCCESS; +} diff --git a/lib/efi_loader/initrddump.c b/lib/efi_loader/initrddump.c new file mode 100644 index 0000000..7de43bc --- /dev/null +++ b/lib/efi_loader/initrddump.c @@ -0,0 +1,449 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2020, Heinrich Schuchardt + * + * initrddump.efi saves the initial RAM disk provided via the + * EFI_LOAD_FILE2_PROTOCOL. + */ + +#include +#include +#include + +#define BUFFER_SIZE 64 +#define ESC 0x17 + +#define efi_size_in_pages(size) (((size) + EFI_PAGE_MASK) >> EFI_PAGE_SHIFT) + +static struct efi_system_table *systable; +static struct efi_boot_services *bs; +static struct efi_simple_text_output_protocol *cerr; +static struct efi_simple_text_output_protocol *cout; +static struct efi_simple_text_input_protocol *cin; +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 const efi_guid_t load_file2_guid = EFI_LOAD_FILE2_PROTOCOL_GUID; +static efi_handle_t handle; + +/* + * Device path defined by Linux to identify the handle providing the + * EFI_LOAD_FILE2_PROTOCOL used for loading the initial ramdisk. + */ +static const struct efi_initrd_dp initrd_dp = { + .vendor = { + { + DEVICE_PATH_TYPE_MEDIA_DEVICE, + DEVICE_PATH_SUB_TYPE_VENDOR_PATH, + sizeof(initrd_dp.vendor), + }, + EFI_INITRD_MEDIA_GUID, + }, + .end = { + DEVICE_PATH_TYPE_END, + DEVICE_PATH_SUB_TYPE_END, + sizeof(initrd_dp.end), + } +}; + +/** + * print() - print string + * + * @string: text + */ +static void print(u16 *string) +{ + cout->output_string(cout, string); +} + +/** + * error() - print error string + * + * @string: error text + */ +static void error(u16 *string) +{ + cout->set_attribute(cout, EFI_LIGHTRED | EFI_BACKGROUND_BLACK); + print(string); + cout->set_attribute(cout, EFI_LIGHTBLUE | EFI_BACKGROUND_BLACK); +} + +/* + * printx() - print hexadecimal number + * + * @val: value to print; + * @prec: minimum number of digits to print + */ +static void printx(u64 val, u32 prec) +{ + int i; + u16 c; + u16 buf[16]; + u16 *pos = buf; + + for (i = 2 * sizeof(val) - 1; i >= 0; --i) { + c = (val >> (4 * i)) & 0x0f; + if (c || pos != buf || !i || i < prec) { + c += '0'; + if (c > '9') + c += 'a' - '9' - 1; + *pos++ = c; + } + } + *pos = 0; + print(buf); +} + +/** + * efi_input_yn() - get answer to yes/no question + * + * Return: + * y or Y + * EFI_SUCCESS + * n or N + * EFI_ACCESS_DENIED + * ESC + * EFI_ABORTED + */ +static efi_status_t efi_input_yn(void) +{ + struct efi_input_key key = {0}; + efi_uintn_t index; + efi_status_t ret; + + /* Drain the console input */ + ret = cin->reset(cin, true); + for (;;) { + ret = bs->wait_for_event(1, &cin->wait_for_key, &index); + if (ret != EFI_SUCCESS) + continue; + ret = cin->read_key_stroke(cin, &key); + if (ret != EFI_SUCCESS) + continue; + switch (key.scan_code) { + case 0x17: /* Escape */ + return EFI_ABORTED; + default: + break; + } + /* Convert to lower case */ + switch (key.unicode_char | 0x20) { + case 'y': + return EFI_SUCCESS; + case 'n': + return EFI_ACCESS_DENIED; + default: + break; + } + } +} + +/** + * efi_input() - read string from console + * + * @buffer: input buffer + * @buffer_size: buffer size + * Return: status code + */ +static efi_status_t efi_input(u16 *buffer, efi_uintn_t buffer_size) +{ + struct efi_input_key key = {0}; + efi_uintn_t index; + efi_uintn_t pos = 0; + u16 outbuf[2] = u" "; + efi_status_t ret; + + /* 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) + continue; + ret = cin->read_key_stroke(cin, &key); + if (ret != EFI_SUCCESS) + continue; + switch (key.scan_code) { + case 0x17: /* Escape */ + print(u"\r\nAborted\r\n"); + return EFI_ABORTED; + default: + break; + } + switch (key.unicode_char) { + case 0x08: /* Backspace */ + if (pos) { + buffer[pos--] = 0; + print(u"\b \b"); + } + break; + case 0x0a: /* Linefeed */ + case 0x0d: /* Carriage return */ + print(u"\r\n"); + return EFI_SUCCESS; + default: + break; + } + /* Ignore surrogate codes */ + if (key.unicode_char >= 0xD800 && key.unicode_char <= 0xDBFF) + continue; + if (key.unicode_char >= 0x20 && + pos < buffer_size - 1) { + *outbuf = key.unicode_char; + buffer[pos++] = key.unicode_char; + buffer[pos] = 0; + print(outbuf); + } + } +} + +/** + * skip_whitespace() - skip over leading whitespace + * + * @pos: UTF-16 string + * Return: pointer to first non-whitespace + */ +static u16 *skip_whitespace(u16 *pos) +{ + 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 + */ +static bool starts_with(u16 *string, u16 *keyword) +{ + for (; *keyword; ++string, ++keyword) { + if (*string != *keyword) + return false; + } + return true; +} + +/** + * do_help() - print help + */ +static void do_help(void) +{ + error(u"load - show length and CRC32 of initial RAM disk\r\n"); + error(u"save - save initial RAM disk to file\r\n"); + error(u"exit - exit the shell\r\n"); +} + +/** + * get_initrd() - read initial RAM disk via EFI_LOAD_FILE2_PROTOCOL + * + * @initrd: on return buffer with initial RAM disk + * @initrd_size: size of initial RAM disk + * Return: status code + */ +static efi_status_t get_initrd(void **initrd, efi_uintn_t *initrd_size) +{ + struct efi_device_path *dp = (struct efi_device_path *)&initrd_dp; + struct efi_load_file_protocol *load_file2_prot; + u64 buffer; + efi_handle_t handle; + efi_status_t ret; + + *initrd = NULL; + *initrd_size = 0; + ret = bs->locate_device_path(&load_file2_guid, &dp, &handle); + if (ret != EFI_SUCCESS) { + error(u"Load File2 protocol not found\r\n"); + return ret; + } + ret = bs->handle_protocol(handle, &load_file2_guid, + (void **)&load_file2_prot); + ret = load_file2_prot->load_file(load_file2_prot, dp, false, + initrd_size, NULL); + if (ret != EFI_BUFFER_TOO_SMALL) { + error(u"Load File2 protocol does not provide file length\r\n"); + return EFI_LOAD_ERROR; + } + ret = bs->allocate_pages(EFI_ALLOCATE_ANY_PAGES, EFI_LOADER_DATA, + efi_size_in_pages(*initrd_size), &buffer); + if (ret != EFI_SUCCESS) { + error(u"Out of memory\r\n"); + return ret; + } + *initrd = (void *)(uintptr_t)buffer; + ret = load_file2_prot->load_file(load_file2_prot, dp, false, + initrd_size, *initrd); + if (ret != EFI_SUCCESS) { + error(u"Load File2 protocol failed to provide file\r\n"); + bs->free_pages(buffer, efi_size_in_pages(*initrd_size)); + return EFI_LOAD_ERROR; + } + return ret; +} + +/** + * do_load() - load initial RAM disk and display CRC32 and length + * + * @filename: file name + * Return: status code + */ +static efi_status_t do_load(void) +{ + void *initrd; + efi_uintn_t initrd_size; + u32 crc32; + efi_uintn_t ret; + + ret = get_initrd(&initrd, &initrd_size); + if (ret != EFI_SUCCESS) + return ret; + print(u"length: 0x"); + printx(initrd_size, 1); + print(u"\r\n"); + + ret = bs->calculate_crc32(initrd, initrd_size, &crc32); + if (ret != EFI_SUCCESS) { + error(u"Calculating CRC32 failed\r\n"); + return EFI_LOAD_ERROR; + } + print(u"crc32: 0x"); + printx(crc32, 8); + print(u"\r\n"); + + return EFI_SUCCESS; +} + +/** + * do_save() - save initial RAM disk + * + * @filename: file name + * Return: status code + */ +static efi_status_t do_save(u16 *filename) +{ + struct efi_loaded_image *loaded_image; + struct efi_simple_file_system_protocol *file_system; + struct efi_file_handle *root, *file; + void *initrd; + efi_uintn_t initrd_size; + efi_uintn_t ret; + + ret = get_initrd(&initrd, &initrd_size); + if (ret != EFI_SUCCESS) + return ret; + + 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(u"Loaded image protocol not found\r\n"); + goto out; + } + + /* 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(u"Failed to open simple file system protocol\r\n"); + goto out; + } + + /* Open volume */ + ret = file_system->open_volume(file_system, &root); + if (ret != EFI_SUCCESS) { + error(u"Failed to open volume\r\n"); + goto out; + } + /* Check if file already exists */ + ret = root->open(root, &file, filename, EFI_FILE_MODE_READ, 0); + if (ret == EFI_SUCCESS) { + file->close(file); + print(u"Overwrite existing file (y/n)? "); + ret = efi_input_yn(); + print(u"\r\n"); + if (ret != EFI_SUCCESS) { + root->close(root); + error(u"Aborted by user\r\n"); + goto out; + } + } + + /* Create file */ + ret = root->open(root, &file, filename, + EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE | + EFI_FILE_MODE_CREATE, EFI_FILE_ARCHIVE); + if (ret == EFI_SUCCESS) { + /* Write file */ + ret = file->write(file, &initrd_size, initrd); + if (ret != EFI_SUCCESS) { + error(u"Failed to write file\r\n"); + } else { + print(filename); + print(u" written\r\n"); + } + file->close(file); + } else { + error(u"Failed to open file\r\n"); + } + root->close(root); + +out: + if (initrd) + bs->free_pages((uintptr_t)initrd, + efi_size_in_pages(initrd_size)); + 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); + print(u"INITRD Dump\r\n===========\r\n\r\n"); + cout->set_attribute(cout, EFI_LIGHTBLUE | EFI_BACKGROUND_BLACK); + + for (;;) { + u16 command[BUFFER_SIZE]; + u16 *pos; + efi_uintn_t ret; + + print(u"=> "); + ret = efi_input(command, sizeof(command)); + if (ret == EFI_ABORTED) + break; + pos = skip_whitespace(command); + if (starts_with(pos, u"exit")) + break; + else if (starts_with(pos, u"load")) + do_load(); + else if (starts_with(pos, u"save ")) + do_save(pos + 5); + else + do_help(); + } + + cout->set_attribute(cout, EFI_LIGHTGRAY | EFI_BACKGROUND_BLACK); + cout->clear_screen(cout); + return EFI_SUCCESS; +} diff --git a/lib/efi_selftest/Makefile b/lib/efi_selftest/Makefile index 9ff6e17..be8040d 100644 --- a/lib/efi_selftest/Makefile +++ b/lib/efi_selftest/Makefile @@ -8,16 +8,12 @@ asflags-y += -DHOST_ARCH="$(HOST_ARCH)" ccflags-y += -DHOST_ARCH="$(HOST_ARCH)" -CFLAGS_dtbdump.o := $(CFLAGS_EFI) -Os -ffreestanding -CFLAGS_REMOVE_dtbdump.o := $(CFLAGS_NON_EFI) CFLAGS_efi_selftest_miniapp_exception.o := $(CFLAGS_EFI) -Os -ffreestanding CFLAGS_REMOVE_efi_selftest_miniapp_exception.o := $(CFLAGS_NON_EFI) CFLAGS_efi_selftest_miniapp_exit.o := $(CFLAGS_EFI) -Os -ffreestanding CFLAGS_REMOVE_efi_selftest_miniapp_exit.o := $(CFLAGS_NON_EFI) CFLAGS_efi_selftest_miniapp_return.o := $(CFLAGS_EFI) -Os -ffreestanding CFLAGS_REMOVE_efi_selftest_miniapp_return.o := $(CFLAGS_NON_EFI) -CFLAGS_initrddump_exit.o := $(CFLAGS_EFI) -Os -ffreestanding -CFLAGS_REMOVE_initrddump.o := $(CFLAGS_NON_EFI) obj-y += \ efi_selftest.o \ @@ -83,14 +79,6 @@ efi_selftest_miniapp_exception.efi \ efi_selftest_miniapp_exit.efi \ efi_selftest_miniapp_return.efi -ifeq ($(CONFIG_GENERATE_ACPI_TABLE),) -always += dtbdump.efi -endif - -ifdef CONFIG_EFI_LOAD_FILE2_INITRD -always += initrddump.efi -endif - $(obj)/efi_miniapp_file_image_exception.h: $(obj)/efi_selftest_miniapp_exception.efi $(obj)/../../tools/file2include $(obj)/efi_selftest_miniapp_exception.efi > \ $(obj)/efi_miniapp_file_image_exception.h diff --git a/lib/efi_selftest/dtbdump.c b/lib/efi_selftest/dtbdump.c deleted file mode 100644 index 3ce2a07..0000000 --- a/lib/efi_selftest/dtbdump.c +++ /dev/null @@ -1,539 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Copyright 2020, Heinrich Schuchardt - * - * dtbdump.efi saves the device tree provided as a configuration table - * to a file. - */ - -#include -#include -#include -#include -#include - -#define BUFFER_SIZE 64 -#define ESC 0x17 - -#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; -static struct efi_simple_text_input_protocol *cin; -static struct efi_boot_services *bs; -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; -static const efi_guid_t efi_system_partition_guid = PARTITION_SYSTEM_GUID; - -/** - * print() - print string - * - * @string: text - */ -static void print(u16 *string) -{ - cout->output_string(cout, string); -} - -/** - * error() - print error string - * - * @string: error text - */ -static void error(u16 *string) -{ - cout->set_attribute(cout, EFI_LIGHTRED | EFI_BACKGROUND_BLACK); - print(string); - cout->set_attribute(cout, EFI_LIGHTBLUE | EFI_BACKGROUND_BLACK); -} - -/** - * efi_input_yn() - get answer to yes/no question - * - * Return: - * y or Y - * EFI_SUCCESS - * n or N - * EFI_ACCESS_DENIED - * ESC - * EFI_ABORTED - */ -static efi_status_t efi_input_yn(void) -{ - struct efi_input_key key = {0}; - efi_uintn_t index; - efi_status_t ret; - - /* Drain the console input */ - ret = cin->reset(cin, true); - for (;;) { - ret = bs->wait_for_event(1, &cin->wait_for_key, &index); - if (ret != EFI_SUCCESS) - continue; - ret = cin->read_key_stroke(cin, &key); - if (ret != EFI_SUCCESS) - continue; - switch (key.scan_code) { - case 0x17: /* Escape */ - return EFI_ABORTED; - default: - break; - } - /* Convert to lower case */ - switch (key.unicode_char | 0x20) { - case 'y': - return EFI_SUCCESS; - case 'n': - return EFI_ACCESS_DENIED; - default: - break; - } - } -} - -/** - * efi_input() - read string from console - * - * @buffer: input buffer - * @buffer_size: buffer size - * Return: status code - */ -static efi_status_t efi_input(u16 *buffer, efi_uintn_t buffer_size) -{ - struct efi_input_key key = {0}; - efi_uintn_t index; - efi_uintn_t pos = 0; - u16 outbuf[2] = u" "; - efi_status_t ret; - - /* 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) - continue; - ret = cin->read_key_stroke(cin, &key); - if (ret != EFI_SUCCESS) - continue; - switch (key.scan_code) { - case 0x17: /* Escape */ - print(u"\r\nAborted\r\n"); - return EFI_ABORTED; - default: - break; - } - switch (key.unicode_char) { - case 0x08: /* Backspace */ - if (pos) { - buffer[pos--] = 0; - print(u"\b \b"); - } - break; - case 0x0a: /* Linefeed */ - case 0x0d: /* Carriage return */ - print(u"\r\n"); - return EFI_SUCCESS; - default: - break; - } - /* Ignore surrogate codes */ - if (key.unicode_char >= 0xD800 && key.unicode_char <= 0xDBFF) - continue; - if (key.unicode_char >= 0x20 && - pos < buffer_size - 1) { - *outbuf = key.unicode_char; - buffer[pos++] = key.unicode_char; - buffer[pos] = 0; - print(outbuf); - } - } -} - -/* - * Convert FDT value to host endianness. - * - * @val FDT value - * Return: converted value - */ -static u32 f2h(fdt32_t val) -{ - char *buf = (char *)&val; - char i; - - /* Swap the bytes */ - i = buf[0]; buf[0] = buf[3]; buf[3] = i; - i = buf[1]; buf[1] = buf[2]; buf[2] = i; - return *(u32 *)buf; -} - -/** - * get_dtb() - get device tree - * - * @systable: system table - * Return: device tree or NULL - */ -void *get_dtb(struct efi_system_table *systable) -{ - void *dtb = NULL; - efi_uintn_t i; - - for (i = 0; i < systable->nr_tables; ++i) { - if (!memcmp(&systable->tables[i].guid, &fdt_guid, - sizeof(efi_guid_t))) { - dtb = systable->tables[i].table; - break; - } - } - return dtb; -} - -/** - * skip_whitespace() - skip over leading whitespace - * - * @pos: UTF-16 string - * Return: pointer to first non-whitespace - */ -u16 *skip_whitespace(u16 *pos) -{ - 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(u"load - load device-tree from file\r\n"); - error(u"save - save device-tree to file\r\n"); - error(u"exit - exit the shell\r\n"); -} - -/** - * open_file_system() - open simple file system protocol - * - * file_system: interface of the simple file system protocol - * Return: status code - */ -static efi_status_t -open_file_system(struct efi_simple_file_system_protocol **file_system) -{ - struct efi_loaded_image *loaded_image; - efi_status_t ret; - efi_handle_t *handle_buffer = NULL; - efi_uintn_t count; - - ret = bs->open_protocol(handle, &loaded_image_guid, - (void **)&loaded_image, NULL, NULL, - EFI_OPEN_PROTOCOL_GET_PROTOCOL); - if (ret != EFI_SUCCESS) { - error(u"Loaded image protocol not found\r\n"); - return ret; - } - - /* Open the simple file system protocol on the same partition */ - 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) - return ret; - - /* Open the simple file system protocol on the UEFI system partition */ - ret = bs->locate_handle_buffer(BY_PROTOCOL, &efi_system_partition_guid, - NULL, &count, &handle_buffer); - if (ret == EFI_SUCCESS && handle_buffer) - ret = bs->open_protocol(handle_buffer[0], - &guid_simple_file_system_protocol, - (void **)file_system, NULL, NULL, - EFI_OPEN_PROTOCOL_GET_PROTOCOL); - if (ret != EFI_SUCCESS) - error(u"Failed to open simple file system protocol\r\n"); - if (handle) - bs->free_pool(handle_buffer); - - return ret; -} - -/** - * 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_simple_file_system_protocol *file_system; - 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; - - ret = bs->locate_protocol(&efi_dt_fixup_protocol_guid, NULL, - (void **)&dt_fixup_prot); - if (ret != EFI_SUCCESS) { - error(u"Device-tree fix-up protocol not found\r\n"); - return ret; - } - - filename = skip_whitespace(filename); - - ret = open_file_system(&file_system); - if (ret != EFI_SUCCESS) - goto out; - - /* Open volume */ - ret = file_system->open_volume(file_system, &root); - if (ret != EFI_SUCCESS) { - error(u"Failed to open volume\r\n"); - goto out; - } - - /* Open file */ - ret = root->open(root, &file, filename, EFI_FILE_MODE_READ, 0); - if (ret != EFI_SUCCESS) { - error(u"File not found\r\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(u"Can't get file info size\r\n"); - goto out; - } - ret = bs->allocate_pool(EFI_LOADER_DATA, buffer_size, (void **)&info); - if (ret != EFI_SUCCESS) { - error(u"Out of memory\r\n"); - goto out; - } - ret = file->getinfo(file, &efi_file_info_guid, &buffer_size, info); - if (ret != EFI_SUCCESS) { - error(u"Can't get file info\r\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(u"Can't free memory pool\r\n"); - /* Read file */ - ret = bs->allocate_pages(EFI_ALLOCATE_ANY_PAGES, - EFI_ACPI_RECLAIM_MEMORY, - pages, &addr); - if (ret != EFI_SUCCESS) { - error(u"Out of memory\r\n"); - goto out; - } - dtb = (struct fdt_header *)(uintptr_t)addr; - ret = file->read(file, &buffer_size, dtb); - if (ret != EFI_SUCCESS) { - error(u"Can't read file\r\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(u"Can't free memory pages\r\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(u"Out of memory\r\n"); - goto out; - } - dtb = (struct fdt_header *)(uintptr_t)addr; - ret = file->setpos(file, 0); - if (ret != EFI_SUCCESS) { - error(u"Can't position file\r\n"); - goto out; - } - ret = file->read(file, &buffer_size, dtb); - if (ret != EFI_SUCCESS) { - error(u"Can't read file\r\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) - print(u"device-tree installed\r\n"); - else - error(u"Device-tree fix-up failed\r\n"); -out: - if (addr) { - ret2 = bs->free_pages(addr, pages); - if (ret2 != EFI_SUCCESS) - error(u"Can't free memory pages\r\n"); - } - if (file) { - ret2 = file->close(file); - if (ret2 != EFI_SUCCESS) - error(u"Can't close file\r\n"); - } - if (root) { - ret2 = root->close(root); - if (ret2 != EFI_SUCCESS) - error(u"Can't close volume\r\n"); - } - return ret; -} - -/** - * do_save() - save current device-tree - * - * @filename: file name - * Return: status code - */ -efi_status_t do_save(u16 *filename) -{ - 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) { - error(u"DTB not found\r\n"); - return EFI_NOT_FOUND; - } - if (f2h(dtb->magic) != FDT_MAGIC) { - error(u"Wrong device tree magic\r\n"); - return EFI_NOT_FOUND; - } - dtb_size = f2h(dtb->totalsize); - - filename = skip_whitespace(filename); - - ret = open_file_system(&file_system); - if (ret != EFI_SUCCESS) - return ret; - - /* Open volume */ - ret = file_system->open_volume(file_system, &root); - if (ret != EFI_SUCCESS) { - error(u"Failed to open volume\r\n"); - return ret; - } - /* Check if file already exists */ - ret = root->open(root, &file, filename, EFI_FILE_MODE_READ, 0); - if (ret == EFI_SUCCESS) { - file->close(file); - print(u"Overwrite existing file (y/n)? "); - ret = efi_input_yn(); - print(u"\r\n"); - if (ret != EFI_SUCCESS) { - root->close(root); - error(u"Aborted by user\r\n"); - return ret; - } - } - - /* Create file */ - ret = root->open(root, &file, filename, - EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE | - EFI_FILE_MODE_CREATE, EFI_FILE_ARCHIVE); - if (ret == EFI_SUCCESS) { - /* Write file */ - ret = file->write(file, &dtb_size, dtb); - if (ret != EFI_SUCCESS) - error(u"Failed to write file\r\n"); - file->close(file); - } else { - error(u"Failed to open file\r\n"); - } - root->close(root); - - if (ret == EFI_SUCCESS) { - print(filename); - print(u" written\r\n"); - } - - 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); - print(u"DTB Dump\r\n========\r\n\r\n"); - cout->set_attribute(cout, EFI_LIGHTBLUE | EFI_BACKGROUND_BLACK); - - for (;;) { - u16 command[BUFFER_SIZE]; - u16 *pos; - efi_uintn_t ret; - - print(u"=> "); - ret = efi_input(command, sizeof(command)); - if (ret == EFI_ABORTED) - break; - pos = skip_whitespace(command); - if (starts_with(pos, u"exit")) - break; - else if (starts_with(pos, u"load ")) - do_load(pos + 5); - else if (starts_with(pos, u"save ")) - do_save(pos + 5); - else - do_help(); - } - - cout->set_attribute(cout, EFI_LIGHTGRAY | EFI_BACKGROUND_BLACK); - cout->clear_screen(cout); - return EFI_SUCCESS; -} diff --git a/lib/efi_selftest/initrddump.c b/lib/efi_selftest/initrddump.c deleted file mode 100644 index 4648d54..0000000 --- a/lib/efi_selftest/initrddump.c +++ /dev/null @@ -1,449 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Copyright 2020, Heinrich Schuchardt - * - * initrddump.efi saves the initial RAM disk provided via the - * EFI_LOAD_FILE2_PROTOCOL. - */ - -#include -#include -#include - -#define BUFFER_SIZE 64 -#define ESC 0x17 - -#define efi_size_in_pages(size) (((size) + EFI_PAGE_MASK) >> EFI_PAGE_SHIFT) - -static struct efi_system_table *systable; -static struct efi_boot_services *bs; -static struct efi_simple_text_output_protocol *cerr; -static struct efi_simple_text_output_protocol *cout; -static struct efi_simple_text_input_protocol *cin; -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 const efi_guid_t load_file2_guid = EFI_LOAD_FILE2_PROTOCOL_GUID; -static efi_handle_t handle; - -/* - * Device path defined by Linux to identify the handle providing the - * EFI_LOAD_FILE2_PROTOCOL used for loading the initial ramdisk. - */ -static const struct efi_initrd_dp initrd_dp = { - .vendor = { - { - DEVICE_PATH_TYPE_MEDIA_DEVICE, - DEVICE_PATH_SUB_TYPE_VENDOR_PATH, - sizeof(initrd_dp.vendor), - }, - EFI_INITRD_MEDIA_GUID, - }, - .end = { - DEVICE_PATH_TYPE_END, - DEVICE_PATH_SUB_TYPE_END, - sizeof(initrd_dp.end), - } -}; - -/** - * print() - print string - * - * @string: text - */ -static void print(u16 *string) -{ - cout->output_string(cout, string); -} - -/** - * error() - print error string - * - * @string: error text - */ -static void error(u16 *string) -{ - cout->set_attribute(cout, EFI_LIGHTRED | EFI_BACKGROUND_BLACK); - print(string); - cout->set_attribute(cout, EFI_LIGHTBLUE | EFI_BACKGROUND_BLACK); -} - -/* - * printx() - print hexadecimal number - * - * @val: value to print; - * @prec: minimum number of digits to print - */ -static void printx(u64 val, u32 prec) -{ - int i; - u16 c; - u16 buf[16]; - u16 *pos = buf; - - for (i = 2 * sizeof(val) - 1; i >= 0; --i) { - c = (val >> (4 * i)) & 0x0f; - if (c || pos != buf || !i || i < prec) { - c += '0'; - if (c > '9') - c += 'a' - '9' - 1; - *pos++ = c; - } - } - *pos = 0; - print(buf); -} - -/** - * efi_input_yn() - get answer to yes/no question - * - * Return: - * y or Y - * EFI_SUCCESS - * n or N - * EFI_ACCESS_DENIED - * ESC - * EFI_ABORTED - */ -static efi_status_t efi_input_yn(void) -{ - struct efi_input_key key = {0}; - efi_uintn_t index; - efi_status_t ret; - - /* Drain the console input */ - ret = cin->reset(cin, true); - for (;;) { - ret = bs->wait_for_event(1, &cin->wait_for_key, &index); - if (ret != EFI_SUCCESS) - continue; - ret = cin->read_key_stroke(cin, &key); - if (ret != EFI_SUCCESS) - continue; - switch (key.scan_code) { - case 0x17: /* Escape */ - return EFI_ABORTED; - default: - break; - } - /* Convert to lower case */ - switch (key.unicode_char | 0x20) { - case 'y': - return EFI_SUCCESS; - case 'n': - return EFI_ACCESS_DENIED; - default: - break; - } - } -} - -/** - * efi_input() - read string from console - * - * @buffer: input buffer - * @buffer_size: buffer size - * Return: status code - */ -static efi_status_t efi_input(u16 *buffer, efi_uintn_t buffer_size) -{ - struct efi_input_key key = {0}; - efi_uintn_t index; - efi_uintn_t pos = 0; - u16 outbuf[2] = u" "; - efi_status_t ret; - - /* 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) - continue; - ret = cin->read_key_stroke(cin, &key); - if (ret != EFI_SUCCESS) - continue; - switch (key.scan_code) { - case 0x17: /* Escape */ - print(u"\r\nAborted\r\n"); - return EFI_ABORTED; - default: - break; - } - switch (key.unicode_char) { - case 0x08: /* Backspace */ - if (pos) { - buffer[pos--] = 0; - print(u"\b \b"); - } - break; - case 0x0a: /* Linefeed */ - case 0x0d: /* Carriage return */ - print(u"\r\n"); - return EFI_SUCCESS; - default: - break; - } - /* Ignore surrogate codes */ - if (key.unicode_char >= 0xD800 && key.unicode_char <= 0xDBFF) - continue; - if (key.unicode_char >= 0x20 && - pos < buffer_size - 1) { - *outbuf = key.unicode_char; - buffer[pos++] = key.unicode_char; - buffer[pos] = 0; - print(outbuf); - } - } -} - -/** - * skip_whitespace() - skip over leading whitespace - * - * @pos: UTF-16 string - * Return: pointer to first non-whitespace - */ -static u16 *skip_whitespace(u16 *pos) -{ - 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 - */ -static bool starts_with(u16 *string, u16 *keyword) -{ - for (; *keyword; ++string, ++keyword) { - if (*string != *keyword) - return false; - } - return true; -} - -/** - * do_help() - print help - */ -static void do_help(void) -{ - error(u"load - show length and CRC32 of initial RAM disk\r\n"); - error(u"save - save initial RAM disk to file\r\n"); - error(u"exit - exit the shell\r\n"); -} - -/** - * get_initrd() - read initial RAM disk via EFI_LOAD_FILE2_PROTOCOL - * - * @initrd: on return buffer with initial RAM disk - * @initrd_size: size of initial RAM disk - * Return: status code - */ -static efi_status_t get_initrd(void **initrd, efi_uintn_t *initrd_size) -{ - struct efi_device_path *dp = (struct efi_device_path *)&initrd_dp; - struct efi_load_file_protocol *load_file2_prot; - u64 buffer; - efi_handle_t handle; - efi_status_t ret; - - *initrd = NULL; - *initrd_size = 0; - ret = bs->locate_device_path(&load_file2_guid, &dp, &handle); - if (ret != EFI_SUCCESS) { - error(u"Load File2 protocol not found\r\n"); - return ret; - } - ret = bs->handle_protocol(handle, &load_file2_guid, - (void **)&load_file2_prot); - ret = load_file2_prot->load_file(load_file2_prot, dp, false, - initrd_size, NULL); - if (ret != EFI_BUFFER_TOO_SMALL) { - error(u"Load File2 protocol does not provide file length\r\n"); - return EFI_LOAD_ERROR; - } - ret = bs->allocate_pages(EFI_ALLOCATE_ANY_PAGES, EFI_LOADER_DATA, - efi_size_in_pages(*initrd_size), &buffer); - if (ret != EFI_SUCCESS) { - error(u"Out of memory\r\n"); - return ret; - } - *initrd = (void *)(uintptr_t)buffer; - ret = load_file2_prot->load_file(load_file2_prot, dp, false, - initrd_size, *initrd); - if (ret != EFI_SUCCESS) { - error(u"Load File2 protocol failed to provide file\r\n"); - bs->free_pages(buffer, efi_size_in_pages(*initrd_size)); - return EFI_LOAD_ERROR; - } - return ret; -} - -/** - * do_load() - load initial RAM disk and display CRC32 and length - * - * @filename: file name - * Return: status code - */ -static efi_status_t do_load(void) -{ - void *initrd; - efi_uintn_t initrd_size; - u32 crc32; - efi_uintn_t ret; - - ret = get_initrd(&initrd, &initrd_size); - if (ret != EFI_SUCCESS) - return ret; - print(u"length: 0x"); - printx(initrd_size, 1); - print(u"\r\n"); - - ret = bs->calculate_crc32(initrd, initrd_size, &crc32); - if (ret != EFI_SUCCESS) { - error(u"Calculating CRC32 failed\r\n"); - return EFI_LOAD_ERROR; - } - print(u"crc32: 0x"); - printx(crc32, 8); - print(u"\r\n"); - - return EFI_SUCCESS; -} - -/** - * do_save() - save initial RAM disk - * - * @filename: file name - * Return: status code - */ -static efi_status_t do_save(u16 *filename) -{ - struct efi_loaded_image *loaded_image; - struct efi_simple_file_system_protocol *file_system; - struct efi_file_handle *root, *file; - void *initrd; - efi_uintn_t initrd_size; - efi_uintn_t ret; - - ret = get_initrd(&initrd, &initrd_size); - if (ret != EFI_SUCCESS) - return ret; - - 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(u"Loaded image protocol not found\r\n"); - goto out; - } - - /* 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(u"Failed to open simple file system protocol\r\n"); - goto out; - } - - /* Open volume */ - ret = file_system->open_volume(file_system, &root); - if (ret != EFI_SUCCESS) { - error(u"Failed to open volume\r\n"); - goto out; - } - /* Check if file already exists */ - ret = root->open(root, &file, filename, EFI_FILE_MODE_READ, 0); - if (ret == EFI_SUCCESS) { - file->close(file); - print(u"Overwrite existing file (y/n)? "); - ret = efi_input_yn(); - print(u"\r\n"); - if (ret != EFI_SUCCESS) { - root->close(root); - error(u"Aborted by user\r\n"); - goto out; - } - } - - /* Create file */ - ret = root->open(root, &file, filename, - EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE | - EFI_FILE_MODE_CREATE, EFI_FILE_ARCHIVE); - if (ret == EFI_SUCCESS) { - /* Write file */ - ret = file->write(file, &initrd_size, initrd); - if (ret != EFI_SUCCESS) { - error(u"Failed to write file\r\n"); - } else { - print(filename); - print(u" written\r\n"); - } - file->close(file); - } else { - error(u"Failed to open file\r\n"); - } - root->close(root); - -out: - if (initrd) - bs->free_pages((uintptr_t)initrd, - efi_size_in_pages(initrd_size)); - 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); - print(u"INITRD Dump\r\n========\r\n\r\n"); - cout->set_attribute(cout, EFI_LIGHTBLUE | EFI_BACKGROUND_BLACK); - - for (;;) { - u16 command[BUFFER_SIZE]; - u16 *pos; - efi_uintn_t ret; - - print(u"=> "); - ret = efi_input(command, sizeof(command)); - if (ret == EFI_ABORTED) - break; - pos = skip_whitespace(command); - if (starts_with(pos, u"exit")) - break; - else if (starts_with(pos, u"load")) - do_load(); - else if (starts_with(pos, u"save ")) - do_save(pos + 5); - else - do_help(); - } - - cout->set_attribute(cout, EFI_LIGHTGRAY | EFI_BACKGROUND_BLACK); - cout->clear_screen(cout); - return EFI_SUCCESS; -} -- cgit v1.1 From df96deeed6a9b34e76544ebe9b0fdd2e44ceb6cd Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Mon, 21 Mar 2022 08:26:48 +0100 Subject: efi_loader: typo devie-path %s/devie-path/device-path/ Reported-by: Ilias Apalodimas Signed-off-by: Heinrich Schuchardt --- lib/efi_loader/efi_device_path.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/efi_loader/efi_device_path.c b/lib/efi_loader/efi_device_path.c index 0a88029..0542aaa 100644 --- a/lib/efi_loader/efi_device_path.c +++ b/lib/efi_loader/efi_device_path.c @@ -137,7 +137,7 @@ int efi_dp_match(const struct efi_device_path *a, * * See UEFI spec (section 3.1.2, about short-form device-paths) * - * @dp: original devie-path + * @dp: original device-path * @Return: shortened device-path or NULL */ struct efi_device_path *efi_dp_shorten(struct efi_device_path *dp) -- cgit v1.1 From ae794fae091759aa53fd2f88b09a8e5f31688167 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sun, 20 Mar 2022 09:21:57 +0100 Subject: efi_loader: nocolor command line attr for initrddump.efi initrddump.efi uses colored output and clear the screen. This is not helpful for integration into Python tests. Allow specifying 'nocolor' in the load option data to suppress color output and clearing the screen. Signed-off-by: Heinrich Schuchardt --- lib/efi_loader/initrddump.c | 77 +++++++++++++++++++++++++++++++++++++++------ 1 file changed, 68 insertions(+), 9 deletions(-) (limited to 'lib') diff --git a/lib/efi_loader/initrddump.c b/lib/efi_loader/initrddump.c index 7de43bc..6f8dd09 100644 --- a/lib/efi_loader/initrddump.c +++ b/lib/efi_loader/initrddump.c @@ -4,6 +4,9 @@ * * initrddump.efi saves the initial RAM disk provided via the * EFI_LOAD_FILE2_PROTOCOL. + * + * Specifying 'nocolor' as load option data suppresses colored output and + * clearing of the screen. */ #include @@ -25,6 +28,7 @@ static const efi_guid_t guid_simple_file_system_protocol = EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID; static const efi_guid_t load_file2_guid = EFI_LOAD_FILE2_PROTOCOL_GUID; static efi_handle_t handle; +static bool nocolor; /* * Device path defined by Linux to identify the handle providing the @@ -47,6 +51,17 @@ static const struct efi_initrd_dp initrd_dp = { }; /** + * color() - set foreground color + * + * @color: foreground color + */ +static void color(u8 color) +{ + if (!nocolor) + cout->set_attribute(cout, color | EFI_BACKGROUND_BLACK); +} + +/** * print() - print string * * @string: text @@ -57,15 +72,26 @@ static void print(u16 *string) } /** + * cls() - clear screen + */ +static void cls(void) +{ + if (nocolor) + print(u"\r\n"); + else + cout->clear_screen(cout); +} + +/** * error() - print error string * * @string: error text */ static void error(u16 *string) { - cout->set_attribute(cout, EFI_LIGHTRED | EFI_BACKGROUND_BLACK); + color(EFI_LIGHTRED); print(string); - cout->set_attribute(cout, EFI_LIGHTBLUE | EFI_BACKGROUND_BLACK); + color(EFI_LIGHTBLUE); } /* @@ -215,10 +241,13 @@ static u16 *skip_whitespace(u16 *pos) * * @string: string to search for keyword * @keyword: keyword to be searched - * Return: true fi @string starts with the keyword + * Return: true if @string starts with the keyword */ static bool starts_with(u16 *string, u16 *keyword) { + if (!string || !keyword) + return false; + for (; *keyword; ++string, ++keyword) { if (*string != *keyword) return false; @@ -401,6 +430,30 @@ out: } /** + * get_load_options() - get load options + * + * Return: load options or NULL + */ +u16 *get_load_options(void) +{ + efi_status_t ret; + struct efi_loaded_image *loaded_image; + + ret = bs->open_protocol(handle, &loaded_image_guid, + (void **)&loaded_image, NULL, NULL, + EFI_OPEN_PROTOCOL_GET_PROTOCOL); + if (ret != EFI_SUCCESS) { + error(u"Loaded image protocol not found\r\n"); + return NULL; + } + + if (!loaded_image->load_options_size || !loaded_image->load_options) + return NULL; + + return loaded_image->load_options; +} + +/** * efi_main() - entry point of the EFI application. * * @handle: handle of the loaded image @@ -410,18 +463,23 @@ out: efi_status_t EFIAPI efi_main(efi_handle_t image_handle, struct efi_system_table *systab) { + u16 *load_options; + handle = image_handle; systable = systab; cerr = systable->std_err; cout = systable->con_out; cin = systable->con_in; bs = systable->boottime; + load_options = get_load_options(); - cout->set_attribute(cout, EFI_LIGHTBLUE | EFI_BACKGROUND_BLACK); - cout->clear_screen(cout); - cout->set_attribute(cout, EFI_WHITE | EFI_BACKGROUND_BLACK); + if (starts_with(load_options, u"nocolor")) + nocolor = true; + + color(EFI_WHITE); + cls(); print(u"INITRD Dump\r\n===========\r\n\r\n"); - cout->set_attribute(cout, EFI_LIGHTBLUE | EFI_BACKGROUND_BLACK); + color(EFI_LIGHTBLUE); for (;;) { u16 command[BUFFER_SIZE]; @@ -443,7 +501,8 @@ efi_status_t EFIAPI efi_main(efi_handle_t image_handle, do_help(); } - cout->set_attribute(cout, EFI_LIGHTGRAY | EFI_BACKGROUND_BLACK); - cout->clear_screen(cout); + color(EFI_LIGHTGRAY); + cls(); + return EFI_SUCCESS; } -- cgit v1.1 From 24cf707ed71534f6c4dc9a4a04f9a7b4c93d286b Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Tue, 22 Mar 2022 18:20:07 +0100 Subject: efi_loader: initrddump: drain input before prompt Up to now the initrddump.efi application has drained the input after showing the prompt. This works for humans but leads to problems when automating testing. If the input is drained, this should be done before showing the prompt. Signed-off-by: Heinrich Schuchardt Acked-by: Ilias Apalodimas --- lib/efi_loader/initrddump.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) (limited to 'lib') diff --git a/lib/efi_loader/initrddump.c b/lib/efi_loader/initrddump.c index 6f8dd09..9872106 100644 --- a/lib/efi_loader/initrddump.c +++ b/lib/efi_loader/initrddump.c @@ -121,6 +121,14 @@ static void printx(u64 val, u32 prec) } /** + * efi_drain_input() - drain console input + */ +static void efi_drain_input(void) +{ + cin->reset(cin, true); +} + +/** * efi_input_yn() - get answer to yes/no question * * Return: @@ -137,8 +145,6 @@ static efi_status_t efi_input_yn(void) efi_uintn_t index; efi_status_t ret; - /* Drain the console input */ - ret = cin->reset(cin, true); for (;;) { ret = bs->wait_for_event(1, &cin->wait_for_key, &index); if (ret != EFI_SUCCESS) @@ -179,8 +185,6 @@ static efi_status_t efi_input(u16 *buffer, efi_uintn_t buffer_size) u16 outbuf[2] = u" "; efi_status_t ret; - /* Drain the console input */ - ret = cin->reset(cin, true); *buffer = 0; for (;;) { ret = bs->wait_for_event(1, &cin->wait_for_key, &index); @@ -393,6 +397,7 @@ static efi_status_t do_save(u16 *filename) ret = root->open(root, &file, filename, EFI_FILE_MODE_READ, 0); if (ret == EFI_SUCCESS) { file->close(file); + efi_drain_input(); print(u"Overwrite existing file (y/n)? "); ret = efi_input_yn(); print(u"\r\n"); @@ -486,6 +491,7 @@ efi_status_t EFIAPI efi_main(efi_handle_t image_handle, u16 *pos; efi_uintn_t ret; + efi_drain_input(); print(u"=> "); ret = efi_input(command, sizeof(command)); if (ret == EFI_ABORTED) -- cgit v1.1