From 5e04435970b13c512cf05147a8b3ea6625458645 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Wed, 16 Feb 2022 07:59:31 +0100 Subject: [PATCH 1/9] doc: describe generation of SMBIOS table SMBIOS is not x86 specific. So we should have an architecture independent page describing it. Signed-off-by: Heinrich Schuchardt --- doc/arch/x86.rst | 2 +- doc/develop/index.rst | 1 + doc/develop/smbios.rst | 22 ++++++++++++++++++++++ 3 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 doc/develop/smbios.rst diff --git a/doc/arch/x86.rst b/doc/arch/x86.rst index 5494155a519..634387ac095 100644 --- a/doc/arch/x86.rst +++ b/doc/arch/x86.rst @@ -732,7 +732,7 @@ SMBIOS tables To generate SMBIOS tables in U-Boot, for use by the OS, enable the CONFIG_GENERATE_SMBIOS_TABLE option. The easiest way to provide the values to use is via the device tree. For details see -device-tree-bindings/sysinfo/smbios.txt +:download:`smbios.txt <../device-tree-bindings/sysinfo/smbios.txt>`. TODO List --------- diff --git a/doc/develop/index.rst b/doc/develop/index.rst index 97148875ef4..93ebfa485f5 100644 --- a/doc/develop/index.rst +++ b/doc/develop/index.rst @@ -22,6 +22,7 @@ Implementation makefiles menus printf + smbios uefi/index version diff --git a/doc/develop/smbios.rst b/doc/develop/smbios.rst new file mode 100644 index 00000000000..a4efb0a0a38 --- /dev/null +++ b/doc/develop/smbios.rst @@ -0,0 +1,22 @@ +.. SPDX-License-Identifier: GPL-2.0+ + +SMBIOS tables +============= + +The System Management BIOS (SMBIOS) table is used to deliver management +information from the firmware to the operating system. The content is +standardized in [1]_. + +In Linux you can use the dmidecode command to view the contents of the SMBIOS +table. + +When booting via UEFI the SMBIOS table is transferred as an UEFI configuration +table to the operating system. + +To generate SMBIOS tables in U-Boot, the CONFIG_GENERATE_SMBIOS_TABLE option +must be enabled. The easiest way to provide the values to use is via the device +tree. For details see +:download:`smbios.txt <../device-tree-bindings/sysinfo/smbios.txt>`. + +.. [1] `System Management BIOS (SMBIOS) Reference, version 3.5 + `_ From 57b98efa16cd660ef3a964979dbb15fd5b21a0b6 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Fri, 18 Feb 2022 19:33:09 +0100 Subject: [PATCH 2/9] doc: describe fatload command Man-page for fatload command. Signed-off-by: Heinrich Schuchardt --- doc/usage/fatload.rst | 80 +++++++++++++++++++++++++++++++++++++++++++ doc/usage/index.rst | 1 + 2 files changed, 81 insertions(+) create mode 100644 doc/usage/fatload.rst diff --git a/doc/usage/fatload.rst b/doc/usage/fatload.rst new file mode 100644 index 00000000000..93acb27a536 --- /dev/null +++ b/doc/usage/fatload.rst @@ -0,0 +1,80 @@ +.. SPDX-License-Identifier: GPL-2.0+: + +fatload command +=============== + +Synopsis +-------- + +:: + + fatload [ [ [ [bytes [pos]]]]] + +Description +----------- + +The fatload command is used to read a file from a FAT filesystem into memory. +You can always use the :doc:`load command ` instead. + +The number of transferred bytes is saved in the environment variable filesize. +The load address is saved in the environment variable fileaddr. + +interface + interface for accessing the block device (mmc, sata, scsi, usb, ....) + +dev + device number + +part + partition number, defaults to 0 (whole device) + +addr + load address, defaults to environment variable loadaddr or if loadaddr is + not set to configuration variable CONFIG_SYS_LOAD_ADDR + +filename + path to file, defaults to environment variable bootfile + +bytes + maximum number of bytes to load + +pos + number of bytes to skip + +addr, bytes, pos are hexadecimal numbers. + +If either 'pos' or 'bytes' are not aligned according to the minimum alignment +requirement for DMA transfer (ARCH_DMA_MINALIGN) additional buffering will be +used, a misaligned buffer warning will be printed, and performance will suffer +for the load. + +Example +------- + +:: + + => fatload mmc 0:1 ${kernel_addr_r} snp.efi + 149280 bytes read in 11 ms (12.9 MiB/s) + => + => fatload mmc 0:1 ${kernel_addr_r} snp.efi 1000000 + 149280 bytes read in 9 ms (15.8 MiB/s) + => + => fatload mmc 0:1 ${kernel_addr_r} snp.efi 1000000 100 + 149024 bytes read in 10 ms (14.2 MiB/s) + => + => fatload mmc 0:1 ${kernel_addr_r} snp.efi 10 + 16 bytes read in 1 ms (15.6 KiB/s) + => + +Configuration +------------- + +The fatload command is only available if CONFIG_CMD_FAT=y. + +Return value +------------ + +The return value $? is set to 0 (true) if the file was successfully loaded +even if the number of bytes is less then the specified length. + +If an error occurs, the return value $? is set to 1 (false). diff --git a/doc/usage/index.rst b/doc/usage/index.rst index 964d761e13e..0aacf531b22 100644 --- a/doc/usage/index.rst +++ b/doc/usage/index.rst @@ -34,6 +34,7 @@ Shell commands exit false fatinfo + fatload for load loady From 625d933eddc9aa3137ab6cc31f54a2f3404d937a Mon Sep 17 00:00:00 2001 From: AKASHI Takahiro Date: Wed, 16 Feb 2022 10:49:51 +0900 Subject: [PATCH 3/9] tools: mkeficapsule: remove duplicated code That code is mistakenly duplicated due to copy-and-paste error. Just remove it. Fixes: CID 348360 Signed-off-by: AKASHI Takahiro Reviewed-by: Heinrich Schuchardt --- tools/mkeficapsule.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/tools/mkeficapsule.c b/tools/mkeficapsule.c index f7590e482f1..c118335b93b 100644 --- a/tools/mkeficapsule.c +++ b/tools/mkeficapsule.c @@ -210,8 +210,6 @@ static int create_auth_data(struct auth_context *ctx) cert.size = file_size; ret = read_bin_file(ctx->key_file, &key.data, &file_size); - if (ret < 0) - return -1; if (ret < 0) return -1; if (file_size > UINT_MAX) From bdcc0a9594d4d3ae02f9f013e105e9a7430b3266 Mon Sep 17 00:00:00 2001 From: Ilias Apalodimas Date: Mon, 14 Feb 2022 11:14:22 +0200 Subject: [PATCH 4/9] efi_loader: fix uefi secure boot with intermediate certs The general rule of accepting or rejecting an image is 1. Is the sha256 of the image in dbx 2. Is the image signed with a certificate that's found in db and not in dbx 3. The image carries a cert which is signed by a cert in db (and not in dbx) and the image can be verified against the former 4. Is the sha256 of the image in db For example SHIM is signed by "CN=Microsoft Windows UEFI Driver Publisher", which is issued by "CN=Microsoft Corporation UEFI CA 2011", which in it's turn is issued by "CN=Microsoft Corporation Third Party Marketplace Root". The latter is a self-signed CA certificate and with our current implementation allows shim to execute if we insert it in db. However it's the CA cert in the middle of the chain which usually ends up in the system's db. pkcs7_verify_one() might or might not return the root certificate for a given chain. But when verifying executables in UEFI, the trust anchor can be in the middle of the chain, as long as that certificate is present in db. Currently we only allow this check on self-signed certificates, so let's remove that check and allow all certs to try a match an entry in db. Open questions: - Does this break any aspect of variable authentication since efi_signature_verify() is used on those as well? Signed-off-by: Ilias Apalodimas --- lib/efi_loader/efi_signature.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/lib/efi_loader/efi_signature.c b/lib/efi_loader/efi_signature.c index 1bd1fdc95fc..79ed077ae7d 100644 --- a/lib/efi_loader/efi_signature.c +++ b/lib/efi_loader/efi_signature.c @@ -518,12 +518,11 @@ bool efi_signature_verify(struct efi_image_regions *regs, goto out; EFI_PRINT("Verifying last certificate in chain\n"); - if (signer->self_signed) { - if (efi_lookup_certificate(signer, db)) - if (efi_signature_check_revocation(sinfo, - signer, dbx)) - break; - } else if (efi_verify_certificate(signer, db, &root)) { + if (efi_lookup_certificate(signer, db)) + if (efi_signature_check_revocation(sinfo, signer, dbx)) + break; + if (!signer->self_signed && + efi_verify_certificate(signer, db, &root)) { bool check; check = efi_signature_check_revocation(sinfo, root, From a6aafce494ab3d963485e6425f232a9224359556 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Wed, 16 Feb 2022 15:15:42 +0900 Subject: [PATCH 5/9] efi_loader: use efi_update_capsule_firmware() for capsule on disk Since the efi_update_capsule() represents the UpdateCapsule() runtime service, it has to handle the capsule flags and update ESRT. However the capsule-on-disk doesn't need to care about such things. Thus, the capsule-on-disk should use the efi_capsule_update_firmware() directly instead of calling efi_update_capsule(). This means the roles of the efi_update_capsule() and capsule-on-disk are different. We have to keep the efi_update_capsule() for providing runtime service API at boot time. Suggested-by: AKASHI Takahiro Signed-off-by: Masami Hiramatsu Reviewed-by: Heinrich Schuchardt --- lib/efi_loader/efi_capsule.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/efi_loader/efi_capsule.c b/lib/efi_loader/efi_capsule.c index f4519c73178..d8141176dfd 100644 --- a/lib/efi_loader/efi_capsule.c +++ b/lib/efi_loader/efi_capsule.c @@ -1118,7 +1118,7 @@ efi_status_t efi_launch_capsules(void) index = 0; ret = efi_capsule_read_file(files[i], &capsule); if (ret == EFI_SUCCESS) { - ret = EFI_CALL(efi_update_capsule(&capsule, 1, 0)); + ret = efi_capsule_update_firmware(capsule); if (ret != EFI_SUCCESS) log_err("Applying capsule %ls failed\n", files[i]); From 06396e2e663809ae6b5da013d5ad165ba16445a6 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Wed, 16 Feb 2022 15:15:52 +0900 Subject: [PATCH 6/9] test/py: Handle expected reset by command Add wait_for_reboot optional argument to ConsoleBase::run_command() so that it can handle an expected reset by command execution. This is useful if a command will reset the sandbox while testing such commands, e.g. run_command("reset", wait_for_reboot = True) Signed-off-by: Masami Hiramatsu Reviewed-by: Heinrich Schuchardt --- test/py/u_boot_console_base.py | 99 ++++++++++++++++++++-------------- 1 file changed, 58 insertions(+), 41 deletions(-) diff --git a/test/py/u_boot_console_base.py b/test/py/u_boot_console_base.py index 384fd53c653..afae07d9ccf 100644 --- a/test/py/u_boot_console_base.py +++ b/test/py/u_boot_console_base.py @@ -139,8 +139,53 @@ class ConsoleBase(object): self.p.close() self.logstream.close() + def wait_for_boot_prompt(self): + """Wait for the boot up until command prompt. This is for internal use only. + """ + try: + bcfg = self.config.buildconfig + config_spl = bcfg.get('config_spl', 'n') == 'y' + config_spl_serial = bcfg.get('config_spl_serial', 'n') == 'y' + env_spl_skipped = self.config.env.get('env__spl_skipped', False) + env_spl2_skipped = self.config.env.get('env__spl2_skipped', True) + + if config_spl and config_spl_serial and not env_spl_skipped: + m = self.p.expect([pattern_u_boot_spl_signon] + + self.bad_patterns) + if m != 0: + raise Exception('Bad pattern found on SPL console: ' + + self.bad_pattern_ids[m - 1]) + if not env_spl2_skipped: + m = self.p.expect([pattern_u_boot_spl2_signon] + + self.bad_patterns) + if m != 0: + raise Exception('Bad pattern found on SPL2 console: ' + + self.bad_pattern_ids[m - 1]) + m = self.p.expect([pattern_u_boot_main_signon] + self.bad_patterns) + if m != 0: + raise Exception('Bad pattern found on console: ' + + self.bad_pattern_ids[m - 1]) + self.u_boot_version_string = self.p.after + while True: + m = self.p.expect([self.prompt_compiled, + pattern_stop_autoboot_prompt] + self.bad_patterns) + if m == 0: + break + if m == 1: + self.p.send(' ') + continue + raise Exception('Bad pattern found on console: ' + + self.bad_pattern_ids[m - 2]) + + except Exception as ex: + self.log.error(str(ex)) + self.cleanup_spawn() + raise + finally: + self.log.timestamp() + def run_command(self, cmd, wait_for_echo=True, send_nl=True, - wait_for_prompt=True): + wait_for_prompt=True, wait_for_reboot=False): """Execute a command via the U-Boot console. The command is always sent to U-Boot. @@ -168,6 +213,9 @@ class ConsoleBase(object): wait_for_prompt: Boolean indicating whether to wait for the command prompt to be sent by U-Boot. This typically occurs immediately after the command has been executed. + wait_for_reboot: Boolean indication whether to wait for the + reboot U-Boot. If this sets True, wait_for_prompt must also + be True. Returns: If wait_for_prompt == False: @@ -202,11 +250,14 @@ class ConsoleBase(object): self.bad_pattern_ids[m - 1]) if not wait_for_prompt: return - m = self.p.expect([self.prompt_compiled] + self.bad_patterns) - if m != 0: - self.at_prompt = False - raise Exception('Bad pattern found on console: ' + - self.bad_pattern_ids[m - 1]) + if wait_for_reboot: + self.wait_for_boot_prompt() + else: + m = self.p.expect([self.prompt_compiled] + self.bad_patterns) + if m != 0: + self.at_prompt = False + raise Exception('Bad pattern found on console: ' + + self.bad_pattern_ids[m - 1]) self.at_prompt = True self.at_prompt_logevt = self.logstream.logfile.cur_evt # Only strip \r\n; space/TAB might be significant if testing @@ -349,41 +400,7 @@ class ConsoleBase(object): if not self.config.gdbserver: self.p.timeout = 30000 self.p.logfile_read = self.logstream - bcfg = self.config.buildconfig - config_spl = bcfg.get('config_spl', 'n') == 'y' - config_spl_serial = bcfg.get('config_spl_serial', - 'n') == 'y' - env_spl_skipped = self.config.env.get('env__spl_skipped', - False) - env_spl2_skipped = self.config.env.get('env__spl2_skipped', - True) - if config_spl and config_spl_serial and not env_spl_skipped: - m = self.p.expect([pattern_u_boot_spl_signon] + - self.bad_patterns) - if m != 0: - raise Exception('Bad pattern found on SPL console: ' + - self.bad_pattern_ids[m - 1]) - if not env_spl2_skipped: - m = self.p.expect([pattern_u_boot_spl2_signon] + - self.bad_patterns) - if m != 0: - raise Exception('Bad pattern found on SPL2 console: ' + - self.bad_pattern_ids[m - 1]) - m = self.p.expect([pattern_u_boot_main_signon] + self.bad_patterns) - if m != 0: - raise Exception('Bad pattern found on console: ' + - self.bad_pattern_ids[m - 1]) - self.u_boot_version_string = self.p.after - while True: - m = self.p.expect([self.prompt_compiled, - pattern_stop_autoboot_prompt] + self.bad_patterns) - if m == 0: - break - if m == 1: - self.p.send(' ') - continue - raise Exception('Bad pattern found on console: ' + - self.bad_pattern_ids[m - 2]) + self.wait_for_boot_prompt() self.at_prompt = True self.at_prompt_logevt = self.logstream.logfile.cur_evt except Exception as ex: From e7233c9c93ae0f59d00063eaf4d7e4f9bd01d63d Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Wed, 16 Feb 2022 15:16:02 +0900 Subject: [PATCH 7/9] test/py: Handle expected reboot while booting sandbox Add expected_reset optional argument to ConsoleBase::ensure_spawned(), ConsoleBase::restart_uboot() and ConsoleSandbox::restart_uboot_with_flags() so that it can handle a reset while the 1st boot process after main boot logo before prompt correctly. Signed-off-by: Masami Hiramatsu Reviewed-by: Heinrich Schuchardt --- test/py/u_boot_console_base.py | 48 ++++++++++++++++++------------- test/py/u_boot_console_sandbox.py | 7 +++-- 2 files changed, 33 insertions(+), 22 deletions(-) diff --git a/test/py/u_boot_console_base.py b/test/py/u_boot_console_base.py index afae07d9ccf..3938ec13024 100644 --- a/test/py/u_boot_console_base.py +++ b/test/py/u_boot_console_base.py @@ -139,7 +139,7 @@ class ConsoleBase(object): self.p.close() self.logstream.close() - def wait_for_boot_prompt(self): + def wait_for_boot_prompt(self, loop_num = 1): """Wait for the boot up until command prompt. This is for internal use only. """ try: @@ -149,22 +149,24 @@ class ConsoleBase(object): env_spl_skipped = self.config.env.get('env__spl_skipped', False) env_spl2_skipped = self.config.env.get('env__spl2_skipped', True) - if config_spl and config_spl_serial and not env_spl_skipped: - m = self.p.expect([pattern_u_boot_spl_signon] + - self.bad_patterns) + while loop_num > 0: + loop_num -= 1 + if config_spl and config_spl_serial and not env_spl_skipped: + m = self.p.expect([pattern_u_boot_spl_signon] + + self.bad_patterns) + if m != 0: + raise Exception('Bad pattern found on SPL console: ' + + self.bad_pattern_ids[m - 1]) + if not env_spl2_skipped: + m = self.p.expect([pattern_u_boot_spl2_signon] + + self.bad_patterns) + if m != 0: + raise Exception('Bad pattern found on SPL2 console: ' + + self.bad_pattern_ids[m - 1]) + m = self.p.expect([pattern_u_boot_main_signon] + self.bad_patterns) if m != 0: - raise Exception('Bad pattern found on SPL console: ' + + raise Exception('Bad pattern found on console: ' + self.bad_pattern_ids[m - 1]) - if not env_spl2_skipped: - m = self.p.expect([pattern_u_boot_spl2_signon] + - self.bad_patterns) - if m != 0: - raise Exception('Bad pattern found on SPL2 console: ' + - self.bad_pattern_ids[m - 1]) - m = self.p.expect([pattern_u_boot_main_signon] + self.bad_patterns) - if m != 0: - raise Exception('Bad pattern found on console: ' + - self.bad_pattern_ids[m - 1]) self.u_boot_version_string = self.p.after while True: m = self.p.expect([self.prompt_compiled, @@ -372,7 +374,7 @@ class ConsoleBase(object): finally: self.p.timeout = orig_timeout - def ensure_spawned(self): + def ensure_spawned(self, expect_reset=False): """Ensure a connection to a correctly running U-Boot instance. This may require spawning a new Sandbox process or resetting target @@ -381,7 +383,9 @@ class ConsoleBase(object): This is an internal function and should not be called directly. Args: - None. + expect_reset: Boolean indication whether this boot is expected + to be reset while the 1st boot process after main boot before + prompt. False by default. Returns: Nothing. @@ -400,7 +404,11 @@ class ConsoleBase(object): if not self.config.gdbserver: self.p.timeout = 30000 self.p.logfile_read = self.logstream - self.wait_for_boot_prompt() + if expect_reset: + loop_num = 2 + else: + loop_num = 1 + self.wait_for_boot_prompt(loop_num = loop_num) self.at_prompt = True self.at_prompt_logevt = self.logstream.logfile.cur_evt except Exception as ex: @@ -433,10 +441,10 @@ class ConsoleBase(object): pass self.p = None - def restart_uboot(self): + def restart_uboot(self, expect_reset=False): """Shut down and restart U-Boot.""" self.cleanup_spawn() - self.ensure_spawned() + self.ensure_spawned(expect_reset) def get_spawn_output(self): """Return the start-up output from U-Boot diff --git a/test/py/u_boot_console_sandbox.py b/test/py/u_boot_console_sandbox.py index 7e1eb0e0b45..ce4ca7e55ef 100644 --- a/test/py/u_boot_console_sandbox.py +++ b/test/py/u_boot_console_sandbox.py @@ -57,11 +57,14 @@ class ConsoleSandbox(ConsoleBase): cmd += self.sandbox_flags return Spawn(cmd, cwd=self.config.source_dir) - def restart_uboot_with_flags(self, flags): + def restart_uboot_with_flags(self, flags, expect_reset=False): """Run U-Boot with the given command-line flags Args: flags: List of flags to pass, each a string + expect_reset: Boolean indication whether this boot is expected + to be reset while the 1st boot process after main boot before + prompt. False by default. Returns: A u_boot_spawn.Spawn object that is attached to U-Boot. @@ -69,7 +72,7 @@ class ConsoleSandbox(ConsoleBase): try: self.sandbox_flags = flags - return self.restart_uboot() + return self.restart_uboot(expect_reset) finally: self.sandbox_flags = [] From 3e6f81000672e6d0d88fde9c7ad5b01414eccf86 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Wed, 16 Feb 2022 15:16:12 +0900 Subject: [PATCH 8/9] efi_loader: test/py: Reset system after capsule update on disk Add a cold reset soon after processing capsule update on disk. This is required in UEFI specification 2.9 Section 8.5.5 "Delivery of Capsules via file on Mass Storage device" as; In all cases that a capsule is identified for processing the system is restarted after capsule processing is completed. This also reports the result of each capsule update so that the user can notice that the capsule update has been succeeded or not from console log. Signed-off-by: Masami Hiramatsu Reviewed-by: Heinrich Schuchardt --- lib/efi_loader/efi_capsule.c | 18 ++++++++- .../test_efi_capsule/test_capsule_firmware.py | 37 ++++++++++++------- 2 files changed, 39 insertions(+), 16 deletions(-) diff --git a/lib/efi_loader/efi_capsule.c b/lib/efi_loader/efi_capsule.c index d8141176dfd..613b531b825 100644 --- a/lib/efi_loader/efi_capsule.c +++ b/lib/efi_loader/efi_capsule.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -1120,8 +1121,11 @@ efi_status_t efi_launch_capsules(void) if (ret == EFI_SUCCESS) { ret = efi_capsule_update_firmware(capsule); if (ret != EFI_SUCCESS) - log_err("Applying capsule %ls failed\n", + log_err("Applying capsule %ls failed.\n", files[i]); + else + log_info("Applying capsule %ls succeeded.\n", + files[i]); /* create CapsuleXXXX */ set_capsule_result(index, capsule, ret); @@ -1142,6 +1146,16 @@ efi_status_t efi_launch_capsules(void) free(files[i]); free(files); - return ret; + /* + * UEFI spec requires to reset system after complete processing capsule + * update on the storage. + */ + log_info("Reboot after firmware update"); + /* Cold reset is required for loading the new firmware. */ + do_reset(NULL, 0, 0, NULL); + hang(); + /* not reach here */ + + return 0; } #endif /* CONFIG_EFI_CAPSULE_ON_DISK */ diff --git a/test/py/tests/test_efi_capsule/test_capsule_firmware.py b/test/py/tests/test_efi_capsule/test_capsule_firmware.py index 6e803f699f2..1dcf1c70f48 100644 --- a/test/py/tests/test_efi_capsule/test_capsule_firmware.py +++ b/test/py/tests/test_efi_capsule/test_capsule_firmware.py @@ -143,13 +143,14 @@ class TestEfiCapsuleFirmwareFit(object): 'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR]) assert 'Test01' in ''.join(output) - # reboot - u_boot_console.restart_uboot() - capsule_early = u_boot_config.buildconfig.get( 'config_efi_capsule_on_disk_early') capsule_auth = u_boot_config.buildconfig.get( 'config_efi_capsule_authenticate') + + # reboot + u_boot_console.restart_uboot(expect_reset = capsule_early) + with u_boot_console.log.section('Test Case 2-b, after reboot'): if not capsule_early: # make sure that dfu_alt_info exists even persistent variables @@ -162,7 +163,7 @@ class TestEfiCapsuleFirmwareFit(object): # need to run uefi command to initiate capsule handling output = u_boot_console.run_command( - 'env print -e Capsule0000') + 'env print -e Capsule0000', wait_for_reboot = True) output = u_boot_console.run_command_list([ 'host bind 0 %s' % disk_img, @@ -218,13 +219,14 @@ class TestEfiCapsuleFirmwareFit(object): 'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR]) assert 'Test02' in ''.join(output) - # reboot - u_boot_console.restart_uboot() - capsule_early = u_boot_config.buildconfig.get( 'config_efi_capsule_on_disk_early') capsule_auth = u_boot_config.buildconfig.get( 'config_efi_capsule_authenticate') + + # reboot + u_boot_console.restart_uboot(expect_reset = capsule_early) + with u_boot_console.log.section('Test Case 3-b, after reboot'): if not capsule_early: # make sure that dfu_alt_info exists even persistent variables @@ -237,9 +239,12 @@ class TestEfiCapsuleFirmwareFit(object): # need to run uefi command to initiate capsule handling output = u_boot_console.run_command( - 'env print -e Capsule0000') + 'env print -e Capsule0000', wait_for_reboot = True) - output = u_boot_console.run_command_list(['efidebug capsule esrt']) + # make sure the dfu_alt_info exists because it is required for making ESRT. + output = u_boot_console.run_command_list([ + 'env set dfu_alt_info "sf 0:0=u-boot-bin raw 0x100000 0x50000;u-boot-env raw 0x150000 0x200000"', + 'efidebug capsule esrt']) # ensure that EFI_FIRMWARE_IMAGE_TYPE_UBOOT_FIT_GUID is in the ESRT. assert 'AE13FF2D-9AD4-4E25-9AC8-6D80B3B22147' in ''.join(output) @@ -293,13 +298,14 @@ class TestEfiCapsuleFirmwareFit(object): 'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR]) assert 'Test03' in ''.join(output) - # reboot - u_boot_console.restart_uboot() - capsule_early = u_boot_config.buildconfig.get( 'config_efi_capsule_on_disk_early') capsule_auth = u_boot_config.buildconfig.get( 'config_efi_capsule_authenticate') + + # reboot + u_boot_console.restart_uboot(expect_reset = capsule_early) + with u_boot_console.log.section('Test Case 4-b, after reboot'): if not capsule_early: # make sure that dfu_alt_info exists even persistent variables @@ -312,9 +318,12 @@ class TestEfiCapsuleFirmwareFit(object): # need to run uefi command to initiate capsule handling output = u_boot_console.run_command( - 'env print -e Capsule0000') + 'env print -e Capsule0000', wait_for_reboot = True) - output = u_boot_console.run_command_list(['efidebug capsule esrt']) + # make sure the dfu_alt_info exists because it is required for making ESRT. + output = u_boot_console.run_command_list([ + 'env set dfu_alt_info "sf 0:0=u-boot-bin raw 0x100000 0x50000;u-boot-env raw 0x150000 0x200000"', + 'efidebug capsule esrt']) # ensure that EFI_FIRMWARE_IMAGE_TYPE_UBOOT_RAW_GUID is in the ESRT. assert 'E2BB9C06-70E9-4B14-97A3-5A7913176E3F' in ''.join(output) From 3fa9ed9ae3b30dd6e7f5e887c76d183ad72a44a2 Mon Sep 17 00:00:00 2001 From: Masahisa Kojima Date: Tue, 22 Feb 2022 09:58:30 +0900 Subject: [PATCH 9/9] efi_loader: update the timing of enabling and disabling EFI watchdog UEFI specification requires that 5 minutes watchdog timer is armed before the firmware's boot manager invokes an EFI boot option. This watchdog timer is updated as follows, according to the UEFI specification. 1) The EFI Image may reset or disable the watchdog timer as needed. 2) If control is returned to the firmware's boot manager, the watchdog timer must be disabled. 3) On successful completion of EFI_BOOT_SERVICES.ExitBootServices() the watchdog timer is disabled. 1) is up to the EFI image, and 3) is already implemented in U-Boot. This patch implements 2), the watchdog is disabled when control is returned to U-Boot. In addition, current implementation arms the EFI watchdog at only the first "bootefi" invocation. The EFI watchdog must be armed in every EFI boot option invocation. Signed-off-by: Masahisa Kojima Reviewed-by: Heinrich Schuchardt --- cmd/bootefi.c | 17 +++++++++++++++++ lib/efi_loader/efi_watchdog.c | 13 +------------ 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/cmd/bootefi.c b/cmd/bootefi.c index 94d18ca73fa..46eebd5ee22 100644 --- a/cmd/bootefi.c +++ b/cmd/bootefi.c @@ -353,6 +353,19 @@ static efi_status_t do_bootefi_exec(efi_handle_t handle, void *load_options) /* On ARM switch from EL3 or secure mode to EL2 or non-secure mode */ switch_to_non_secure_mode(); + /* + * The UEFI standard requires that the watchdog timer is set to five + * minutes when invoking an EFI boot option. + * + * Unified Extensible Firmware Interface (UEFI), version 2.7 Errata A + * 7.5. Miscellaneous Boot Services - EFI_BOOT_SERVICES.SetWatchdogTimer + */ + ret = efi_set_watchdog(300); + if (ret != EFI_SUCCESS) { + log_err("ERROR: Failed to set watchdog timer\n"); + goto out; + } + /* Call our payload! */ ret = EFI_CALL(efi_start_image(handle, &exit_data_size, &exit_data)); if (ret != EFI_SUCCESS) { @@ -366,11 +379,15 @@ static efi_status_t do_bootefi_exec(efi_handle_t handle, void *load_options) efi_restore_gd(); +out: free(load_options); if (IS_ENABLED(CONFIG_EFI_LOAD_FILE2_INITRD)) efi_initrd_deregister(); + /* Control is returned to U-Boot, disable EFI watchdog */ + efi_set_watchdog(0); + return ret; } diff --git a/lib/efi_loader/efi_watchdog.c b/lib/efi_loader/efi_watchdog.c index 87ca6c5b0b7..d741076dcdd 100644 --- a/lib/efi_loader/efi_watchdog.c +++ b/lib/efi_loader/efi_watchdog.c @@ -75,17 +75,6 @@ efi_status_t efi_watchdog_register(void) printf("ERROR: Failed to register watchdog event\n"); return r; } - /* - * The UEFI standard requires that the watchdog timer is set to five - * minutes when invoking an EFI boot option. - * - * Unified Extensible Firmware Interface (UEFI), version 2.7 Errata A - * 7.5. Miscellaneous Boot Services - EFI_BOOT_SERVICES.SetWatchdogTimer - */ - r = efi_set_watchdog(300); - if (r != EFI_SUCCESS) { - printf("ERROR: Failed to set watchdog timer\n"); - return r; - } + return EFI_SUCCESS; }