From db12f518edb05cbb4ea2b4f2d9771dda2341cbe7 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Fri, 1 Jan 2021 08:39:43 +0100 Subject: [PATCH 01/16] efi_loader: implement non-blocking file services Implement services OpenEx(), ReadEx(), WriteEx(), FlushEx() of the EFI_FILE_PROTOCOL. Signed-off-by: Heinrich Schuchardt --- include/efi_api.h | 28 ++-- lib/efi_loader/efi_file.c | 319 +++++++++++++++++++++++++++++++------- 2 files changed, 281 insertions(+), 66 deletions(-) diff --git a/include/efi_api.h b/include/efi_api.h index ecb43a0607..2b54ee02a2 100644 --- a/include/efi_api.h +++ b/include/efi_api.h @@ -1589,35 +1589,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/lib/efi_loader/efi_file.c b/lib/efi_loader/efi_file.c index 72b7ec1e63..8ece8e71ee 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; } @@ -291,6 +289,75 @@ static efi_status_t EFIAPI efi_file_open(struct efi_file_handle *file, } else { 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 EFI_EXIT(ret); + return ret; } /** - * efi_file_write() - write to file + * efi_file_read() - read file * - * This function implements the Write() service of the EFI_FILE_PROTOCOL. + * This function implements the Read() 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 + * @buffer_size: number of bytes to read + * @buffer: read buffer * 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(struct efi_file_handle *this, + efi_uintn_t *buffer_size, void *buffer) { - struct file_handle *fh = to_fh(file); + 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_read_ex() - read file asynchonously + * + * This function implements the ReadEx() 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_read_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_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; } @@ -520,6 +626,67 @@ static efi_status_t EFIAPI efi_file_write(struct efi_file_handle *file, *buffer_size = actwrite; 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); + + if (!this) + return EFI_INVALID_PARAMETER; + + 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_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) +/** + * 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_read_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; -static efi_status_t EFIAPI efi_file_write_ex(struct efi_file_handle *file, - struct efi_file_io_token *token) -{ - return EFI_UNSUPPORTED; -} + EFI_ENTRY("%p, %p", this, token); -static efi_status_t EFIAPI efi_file_flush_ex(struct efi_file_handle *file, - struct efi_file_io_token *token) -{ - return EFI_UNSUPPORTED; + 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 = { From 4f6ec7754b1342ae9f9df4524fa9fa45299f3d68 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sat, 9 Jan 2021 01:52:30 +0100 Subject: [PATCH 02/16] tools: efivar.py without arguments When tools: efivar.py is called without arguments an error occurs: Traceback (most recent call last): File "tools/efivar.py", line 380, in main() File "tools/efivar.py", line 360, in main args.func(args) AttributeError: 'Namespace' object has no attribute 'func' Show the online help if the arguments do not specify a function. Signed-off-by: Heinrich Schuchardt Reviewed-by: Paulo Alcantara (SUSE) --- tools/efivar.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tools/efivar.py b/tools/efivar.py index ebfcab2f0a..c40a0fa6c7 100755 --- a/tools/efivar.py +++ b/tools/efivar.py @@ -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: From 45c0792c0208f2874a36b8730401ddbbb607cb75 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sat, 9 Jan 2021 02:02:24 +0100 Subject: [PATCH 03/16] tools: efivar.py: incorrect indentation According to https://pep8.org/#indentation we should use 4 spaces per indentation level. Signed-off-by: Heinrich Schuchardt Reviewed-by: Paulo Alcantara (SUSE) --- tools/efivar.py | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/tools/efivar.py b/tools/efivar.py index c40a0fa6c7..a02b09d46a 100755 --- a/tools/efivar.py +++ b/tools/efivar.py @@ -51,21 +51,21 @@ var_guids = { } class EfiStruct: - # struct efi_var_file - var_file_fmt = ' Date: Sat, 9 Jan 2021 02:09:44 +0100 Subject: [PATCH 04/16] tools: efivar.py should check GUID when deleting When deleting a variable we must check that the GUID provided by the user matches the GUID of the variable. Signed-off-by: Heinrich Schuchardt Reviewed-by: Paulo Alcantara (SUSE) --- tools/efivar.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/efivar.py b/tools/efivar.py index a02b09d46a..29eb90a235 100755 --- a/tools/efivar.py +++ b/tools/efivar.py @@ -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) From 4961ceefbf4e7c572537a752c6d36103520d5f19 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sat, 9 Jan 2021 02:20:45 +0100 Subject: [PATCH 05/16] tools: efivar.py unused variable Unused variables should be called '_'. Signed-off-by: Heinrich Schuchardt Reviewed-by: Paulo Alcantara (SUSE) --- tools/efivar.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/efivar.py b/tools/efivar.py index 29eb90a235..67729fa850 100755 --- a/tools/efivar.py +++ b/tools/efivar.py @@ -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() From bb33c79e47e6ae4b538702b8f3d9a8ffc4b637ea Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Tue, 12 Jan 2021 17:44:08 +0100 Subject: [PATCH 06/16] efi_loader: simplify running helloworld.efi Currently when executing 'bootefi hello' we copy helloworld.efi to the address identified by environment variable loadaddr. This is unexected behavior for a user. There is no need to copy helloworld.efi before executing it after relocation. Remove the copy action. Signed-off-by: Heinrich Schuchardt --- cmd/bootefi.c | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/cmd/bootefi.c b/cmd/bootefi.c index fdf909f8da..c82a5bacf6 100644 --- a/cmd/bootefi.c +++ b/cmd/bootefi.c @@ -403,19 +403,9 @@ static int do_bootefi_image(const char *image_opt) #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; From dd860b90ff41e064e35a5e5cc213669c9d0868fb Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Tue, 12 Jan 2021 12:40:11 +0100 Subject: [PATCH 07/16] efi_loader: print boot device and file path in helloworld Let helloworld.efi print the device path of the boot device and the file path as provided by the loaded image protocol. Signed-off-by: Heinrich Schuchardt --- lib/efi_loader/helloworld.c | 167 +++++++++++++++++++++++++++++------- 1 file changed, 137 insertions(+), 30 deletions(-) diff --git a/lib/efi_loader/helloworld.c b/lib/efi_loader/helloworld.c index 9ae2ee3389..5c8b7a96f9 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 + * Copyright 2020, Heinrich Schuchardt * - * 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 #include 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""); 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"\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); From 5dad05a0e61d759af2df8cf900d044a8485bd747 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Tue, 12 Jan 2021 12:40:32 +0100 Subject: [PATCH 08/16] efi_loader: carve out efi_check_pe() Carve out a function to check that a buffer contains a PE-COFF image. Signed-off-by: Heinrich Schuchardt --- include/efi_loader.h | 2 + lib/efi_loader/efi_image_loader.c | 80 ++++++++++++++++++------------- 2 files changed, 48 insertions(+), 34 deletions(-) diff --git a/include/efi_loader.h b/include/efi_loader.h index 4719fa93f0..df29d45a34 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -460,6 +460,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, diff --git a/lib/efi_loader/efi_image_loader.c b/lib/efi_loader/efi_image_loader.c index 94f76ef6b8..d4dd9e9433 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 */ From ea1a9ec5f430359720d9a0621ed1acfbba6a142a Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Wed, 13 Jan 2021 02:09:12 +0100 Subject: [PATCH 09/16] image-fit: fit_check_format check for valid FDT fit_check_format() must check that the buffer contains a flattened device tree before calling any device tree library functions. Failure to do may cause segmentation faults. Signed-off-by: Heinrich Schuchardt --- common/image-fit.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/common/image-fit.c b/common/image-fit.c index 6a8787ca0a..21c44bdf69 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"); From 5f59518a7b1aef9ad3a91defa06cff82dd01cdc5 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Tue, 12 Jan 2021 12:46:24 +0100 Subject: [PATCH 10/16] efi_loader: setting boot device Up to now the bootefi command used the last file loaded to determine the boot partition. This has led to errors when the fdt had been loaded from another partition after the EFI binary. Before setting the boot device from a loaded file check if it is a PE-COFF image or a FIT image. For a PE-COFF image remember address and size, boot device and path. For a FIT image remember boot device and path. If the PE-COFF image is overwritten by loading another file, forget it. Do not allow to start an image via bootefi which is not the last loaded PE-COFF image. Signed-off-by: Heinrich Schuchardt --- cmd/bootefi.c | 142 ++++++++++++++++++++++++++----------------- doc/uefi/uefi.rst | 11 ++-- fs/fs.c | 3 +- include/efi_loader.h | 6 +- net/tftp.c | 9 ++- 5 files changed, 103 insertions(+), 68 deletions(-) diff --git a/cmd/bootefi.c b/cmd/bootefi.c index c82a5bacf6..8fa4a1c287 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 @@ -398,33 +474,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")) { image_buf = __efi_helloworld_begin; size = __efi_helloworld_end - __efi_helloworld_begin; - - 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); @@ -557,11 +628,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; } @@ -681,39 +749,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/doc/uefi/uefi.rst b/doc/uefi/uefi.rst index dc930d9240..5a67737c15 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 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/fs/fs.c b/fs/fs.c index 5e80648b5b..68a15553cc 100644 --- a/fs/fs.c +++ b/fs/fs.c @@ -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_loader.h b/include/efi_loader.h index df29d45a34..2a69ef844b 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -474,7 +474,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 */ @@ -873,7 +874,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/net/tftp.c b/net/tftp.c index 6fdb1a821a..2cfa0b1486 100644 --- a/net/tftp.c +++ b/net/tftp.c @@ -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); From 85fc2ad4d13e1311652049c7322f5d36aacfc572 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Tue, 5 Jan 2021 07:52:48 +0100 Subject: [PATCH 11/16] efi_loader: move efi_(u)intn_t to efi.h Move efi_intn_t and efi_uintn_t to include/efi.h to allow usage without efi_api.h Signed-off-by: Heinrich Schuchardt --- include/efi.h | 5 +++++ include/efi_api.h | 2 -- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/include/efi.h b/include/efi.h index 5695273ce9..503fbf060b 100644 --- a/include/efi.h +++ b/include/efi.h @@ -20,6 +20,11 @@ #include #include +/* 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 2b54ee02a2..c635abe9a9 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; From 7913c7dc57eb1ce6a145c36fab7918a0c8c6860d Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Tue, 5 Jan 2021 07:50:09 +0100 Subject: [PATCH 12/16] efi_loader: typedef efi_string_t text output protocol We do not want to use typedefs in U-Boot. Do not use efi_string_t in the EFI_TEXT_OUTPUT_PROTOCOL. Signed-off-by: Heinrich Schuchardt --- include/efi_api.h | 4 ++-- lib/efi_loader/efi_console.c | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/include/efi_api.h b/include/efi_api.h index c635abe9a9..df9bee2ae4 100644 --- a/include/efi_api.h +++ b/include/efi_api.h @@ -701,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, diff --git a/lib/efi_loader/efi_console.c b/lib/efi_loader/efi_console.c index 705109596e..edcfce7bec 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); From db0f298416fb76f939fbe3071cba73db4606b017 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Tue, 5 Jan 2021 08:22:51 +0100 Subject: [PATCH 13/16] efi_loader: remove outdated TODO in efi_memory.c In efi_mem_sort() adjacent memory regions of same type are coalesced. Remove the remark "Merging of adjacent free regions is missing". Signed-off-by: Heinrich Schuchardt --- lib/efi_loader/efi_memory.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/efi_loader/efi_memory.c b/lib/efi_loader/efi_memory.c index 11e755363e..a3106aba7f 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; From 48618e9b8b2a09486983a60efc7402f5d136f992 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sun, 27 Dec 2020 00:12:28 +0100 Subject: [PATCH 14/16] cmd: conitrace: increase wait for next key At 9600 baud or less a 1 ms wait is too short to detect the end of an escape sequence. Increase the wait duration to 10 ms which will work down to 1200 baud. Signed-off-by: Heinrich Schuchardt --- cmd/conitrace.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/conitrace.c b/cmd/conitrace.c index 811f5c68a9..d50f3bf3cc 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; From 94686f60a2b9fd87842f473a5cdca316668765c3 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sun, 13 Dec 2020 10:30:24 +0100 Subject: [PATCH 15/16] efi_loader: implement EFI_DT_FIXUP_PROTOCOL A boot manager like GRUB can use the protocol to * apply U-Boot's fix-ups to the a device-tree * let U-Boot make memory reservations according to the device-tree * install the device-tree as a configuration table Signed-off-by: Heinrich Schuchardt --- cmd/bootefi.c | 80 ----------------- cmd/efidebug.c | 5 ++ include/efi_dt_fixup.h | 39 ++++++++ include/efi_loader.h | 2 + lib/efi_loader/Makefile | 3 + lib/efi_loader/efi_dt_fixup.c | 160 +++++++++++++++++++++++++++++++++ lib/efi_loader/efi_root_node.c | 6 ++ 7 files changed, 215 insertions(+), 80 deletions(-) create mode 100644 include/efi_dt_fixup.h create mode 100644 lib/efi_loader/efi_dt_fixup.c diff --git a/cmd/bootefi.c b/cmd/bootefi.c index 8fa4a1c287..fe70eec625 100644 --- a/cmd/bootefi.c +++ b/cmd/bootefi.c @@ -211,86 +211,6 @@ done: return ret; } -/** - * 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 * diff --git a/cmd/efidebug.c b/cmd/efidebug.c index 5fb7b1e3c6..6de81cab00 100644 --- a/cmd/efidebug.c +++ b/cmd/efidebug.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -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/include/efi_dt_fixup.h b/include/efi_dt_fixup.h new file mode 100644 index 0000000000..9066e8dd8e --- /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 + +#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 2a69ef844b..e53d286b9d 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 */ diff --git a/lib/efi_loader/Makefile b/lib/efi_loader/Makefile index 462d4d9ac4..412fa88245 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_dt_fixup.c b/lib/efi_loader/efi_dt_fixup.c new file mode 100644 index 0000000000..5f0ae5c338 --- /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 +#include +#include +#include + +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_root_node.c b/lib/efi_loader/efi_root_node.c index b17db312f7..b411a12cf6 100644 --- a/lib/efi_loader/efi_root_node.c +++ b/lib/efi_loader/efi_root_node.c @@ -7,6 +7,7 @@ #include #include +#include #include 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 */ From 8e70f1cb3f2c18d574b087d4fc1d79e68ce98fa9 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sun, 13 Dec 2020 19:13:57 +0100 Subject: [PATCH 16/16] efi_selftest: dtbdump support EFI_DT_FIXUP_PROTOCOL The dtbdump.efi binary can already be used to dump the configuration table with the device-tree to a file. With this patch a device-tree file can be loaded. The EFI_DT_FIXUP_PROTOCOL is called to * apply U-Boot's fix-ups * let U-Boot make memory reservations as required by the device-tree * install the new device-tree as configuration table In a next step this configuration table can be dumped. A dtbdump.efi session would look like: DTB Dump ======== => load test.dtb device-tree installed => save fixed-up.dtb fixed-up.dtb written => exit Signed-off-by: Heinrich Schuchardt --- lib/efi_selftest/dtbdump.c | 328 +++++++++++++++++++++++++++++++------ 1 file changed, 282 insertions(+), 46 deletions(-) diff --git a/lib/efi_selftest/dtbdump.c b/lib/efi_selftest/dtbdump.c index d90e3eb768..efef759863 100644 --- a/lib/efi_selftest/dtbdump.c +++ b/lib/efi_selftest/dtbdump.c @@ -8,10 +8,12 @@ #include #include +#include #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 - load device-tree from file\n"); + error(L"save - 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; - - 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); - - dtb = get_dtb(systable); - if (!dtb) { - cerr->output_string(cout, 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"); - 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) + 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; - 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; + } + /* 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) { + error(L"DTB not found\n"); + return EFI_NOT_FOUND; + } + if (f2h(dtb->magic) != FDT_MAGIC) { + error(L"Wrong device tree magic\n"); + return EFI_NOT_FOUND; + } + dtb_size = f2h(dtb->totalsize); + + 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; } @@ -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; +}