diff options
author | Tom Rini <trini@konsulko.com> | 2022-06-04 09:38:56 -0400 |
---|---|---|
committer | Tom Rini <trini@konsulko.com> | 2022-06-04 09:38:56 -0400 |
commit | 0f259fe79d7f0dca716bc954c211b0e6bc606d72 (patch) | |
tree | 8b9b58da4604290897131f8cb7fd70ab0ded7b3a | |
parent | 90189ecd59cdf14afbe6014be5c068e599b65a72 (diff) | |
parent | 8645aefc8b699f900d97cbadd55b7f492099c61e (diff) | |
download | u-boot-0f259fe79d7f0dca716bc954c211b0e6bc606d72.tar.gz |
Merge tag 'efi-2022-07-rc4-4' of https://source.denx.de/u-boot/custodians/u-boot-efiWIP/04Jun2022
Pull request for efi-2022-07-rc4-4
UEFI:
* Fix the implementation of the firmware management protocol
* Fix the unit tests for signed update capsules
-rw-r--r-- | doc/develop/uefi/uefi.rst | 2 | ||||
-rw-r--r-- | doc/usage/environment.rst | 2 | ||||
-rw-r--r-- | lib/efi_loader/efi_firmware.c | 120 | ||||
-rw-r--r-- | test/py/tests/test_efi_capsule/conftest.py | 25 | ||||
-rw-r--r-- | test/py/tests/test_efi_capsule/test_capsule_firmware_signed_fit.py | 257 | ||||
-rw-r--r-- | test/py/tests/test_efi_capsule/test_capsule_firmware_signed_raw.py (renamed from test/py/tests/test_efi_capsule/test_capsule_firmware_signed.py) | 14 |
6 files changed, 362 insertions, 58 deletions
diff --git a/doc/develop/uefi/uefi.rst b/doc/develop/uefi/uefi.rst index 753a4e5e292..941e427093f 100644 --- a/doc/develop/uefi/uefi.rst +++ b/doc/develop/uefi/uefi.rst @@ -326,7 +326,7 @@ bit in OsIndications variable with .. code-block:: console - => setenv -e -nv -bs -rt -v OsIndications =0x04 + => setenv -e -nv -bs -rt -v OsIndications =0x0000000000000004 Since U-boot doesn't currently support SetVariable at runtime, its value won't be taken over across the reboot. If this is the case, you can skip diff --git a/doc/usage/environment.rst b/doc/usage/environment.rst index dc617039446..28a8952b752 100644 --- a/doc/usage/environment.rst +++ b/doc/usage/environment.rst @@ -347,7 +347,7 @@ bootpretryperiod Unsigned value, in milliseconds. If not set, the period will be either the default (28000), or a value based on CONFIG_NET_RETRY_COUNT, if defined. This value has - precedence over the valu based on CONFIG_NET_RETRY_COUNT. + precedence over the value based on CONFIG_NET_RETRY_COUNT. memmatches Number of matches found by the last 'ms' command, in hex diff --git a/lib/efi_loader/efi_firmware.c b/lib/efi_loader/efi_firmware.c index fe4e084106d..0ce6c1e34f0 100644 --- a/lib/efi_loader/efi_firmware.c +++ b/lib/efi_loader/efi_firmware.c @@ -130,9 +130,6 @@ static efi_status_t efi_fill_image_desc_array( struct efi_fw_image *fw_array; int i; - fw_array = update_info.images; - *descriptor_count = num_image_type_guids; - total_size = sizeof(*image_info) * num_image_type_guids; if (*image_info_size < total_size) { @@ -142,6 +139,8 @@ static efi_status_t efi_fill_image_desc_array( } *image_info_size = total_size; + fw_array = update_info.images; + *descriptor_count = num_image_type_guids; *descriptor_version = EFI_FIRMWARE_IMAGE_DESCRIPTOR_VERSION; *descriptor_size = sizeof(*image_info); *package_version = 0xffffffff; /* not supported */ @@ -178,6 +177,70 @@ static efi_status_t efi_fill_image_desc_array( return EFI_SUCCESS; } +/** + * efi_firmware_capsule_authenticate - authenticate the capsule if enabled + * @p_image: Pointer to new image + * @p_image_size: Pointer to size of new image + * + * Authenticate the capsule if authentication is enabled. + * The image pointer and the image size are updated in case of success. + * + * Return: status code + */ +static +efi_status_t efi_firmware_capsule_authenticate(const void **p_image, + efi_uintn_t *p_image_size) +{ + const void *image = *p_image; + efi_uintn_t image_size = *p_image_size; + u32 fmp_hdr_signature; + struct fmp_payload_header *header; + void *capsule_payload; + efi_status_t status; + efi_uintn_t capsule_payload_size; + + if (IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)) { + capsule_payload = NULL; + capsule_payload_size = 0; + status = efi_capsule_authenticate(image, image_size, + &capsule_payload, + &capsule_payload_size); + + if (status == EFI_SECURITY_VIOLATION) { + printf("Capsule authentication check failed. Aborting update\n"); + return status; + } else if (status != EFI_SUCCESS) { + return status; + } + + debug("Capsule authentication successful\n"); + image = capsule_payload; + image_size = capsule_payload_size; + } else { + debug("Capsule authentication disabled. "); + debug("Updating capsule without authenticating.\n"); + } + + fmp_hdr_signature = FMP_PAYLOAD_HDR_SIGNATURE; + header = (void *)image; + + if (!memcmp(&header->signature, &fmp_hdr_signature, + sizeof(fmp_hdr_signature))) { + /* + * When building the capsule with the scripts in + * edk2, a FMP header is inserted above the capsule + * payload. Compensate for this header to get the + * actual payload that is to be updated. + */ + image += header->header_size; + image_size -= header->header_size; + } + + *p_image = image; + *p_image_size = image_size; + return EFI_SUCCESS; +} + #ifdef CONFIG_EFI_CAPSULE_FIRMWARE_FIT /* * This FIRMWARE_MANAGEMENT_PROTOCOL driver provides a firmware update @@ -266,12 +329,18 @@ efi_status_t EFIAPI efi_firmware_fit_set_image( efi_status_t (*progress)(efi_uintn_t completion), u16 **abort_reason) { + efi_status_t status; + EFI_ENTRY("%p %d %p %zu %p %p %p\n", this, image_index, image, image_size, vendor_code, progress, abort_reason); if (!image || image_index != 1) return EFI_EXIT(EFI_INVALID_PARAMETER); + status = efi_firmware_capsule_authenticate(&image, &image_size); + if (status != EFI_SUCCESS) + return EFI_EXIT(status); + if (fit_update(image)) return EFI_EXIT(EFI_DEVICE_ERROR); @@ -372,11 +441,7 @@ efi_status_t EFIAPI efi_firmware_raw_set_image( efi_status_t (*progress)(efi_uintn_t completion), u16 **abort_reason) { - u32 fmp_hdr_signature; - struct fmp_payload_header *header; - void *capsule_payload; efi_status_t status; - efi_uintn_t capsule_payload_size; EFI_ENTRY("%p %d %p %zu %p %p %p\n", this, image_index, image, image_size, vendor_code, progress, abort_reason); @@ -384,44 +449,9 @@ efi_status_t EFIAPI efi_firmware_raw_set_image( if (!image) return EFI_EXIT(EFI_INVALID_PARAMETER); - /* Authenticate the capsule if authentication enabled */ - if (IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)) { - capsule_payload = NULL; - capsule_payload_size = 0; - status = efi_capsule_authenticate(image, image_size, - &capsule_payload, - &capsule_payload_size); - - if (status == EFI_SECURITY_VIOLATION) { - printf("Capsule authentication check failed. Aborting update\n"); - return EFI_EXIT(status); - } else if (status != EFI_SUCCESS) { - return EFI_EXIT(status); - } - - debug("Capsule authentication successfull\n"); - image = capsule_payload; - image_size = capsule_payload_size; - } else { - debug("Capsule authentication disabled. "); - debug("Updating capsule without authenticating.\n"); - } - - fmp_hdr_signature = FMP_PAYLOAD_HDR_SIGNATURE; - header = (void *)image; - - if (!memcmp(&header->signature, &fmp_hdr_signature, - sizeof(fmp_hdr_signature))) { - /* - * When building the capsule with the scripts in - * edk2, a FMP header is inserted above the capsule - * payload. Compensate for this header to get the - * actual payload that is to be updated. - */ - image += header->header_size; - image_size -= header->header_size; - - } + status = efi_firmware_capsule_authenticate(&image, &image_size); + if (status != EFI_SUCCESS) + return EFI_EXIT(status); if (dfu_write_by_alt(image_index - 1, (void *)image, image_size, NULL, NULL)) diff --git a/test/py/tests/test_efi_capsule/conftest.py b/test/py/tests/test_efi_capsule/conftest.py index d757415c881..4879f2b5c24 100644 --- a/test/py/tests/test_efi_capsule/conftest.py +++ b/test/py/tests/test_efi_capsule/conftest.py @@ -97,23 +97,40 @@ def efi_capsule_data(request, u_boot_config): shell=True) if capsule_auth_enabled: - # firmware signed with proper key + # raw firmware signed with proper key check_call('cd %s; ' '%s/tools/mkeficapsule --index 1 --monotonic-count 1 ' '--private-key SIGNER.key --certificate SIGNER.crt ' - '--guid 09D7DF52-0720-4710-91D1-08469B7FE9C8 ' + '--guid 09D7CF52-0720-4710-91D1-08469B7FE9C8 ' 'u-boot.bin.new Test11' % (data_dir, u_boot_config.build_dir), shell=True) - # firmware signed with *mal* key + # raw firmware signed with *mal* key check_call('cd %s; ' '%s/tools/mkeficapsule --index 1 --monotonic-count 1 ' '--private-key SIGNER2.key ' '--certificate SIGNER2.crt ' - '--guid 09D7DF52-0720-4710-91D1-08469B7FE9C8 ' + '--guid 09D7CF52-0720-4710-91D1-08469B7FE9C8 ' 'u-boot.bin.new Test12' % (data_dir, u_boot_config.build_dir), shell=True) + # FIT firmware signed with proper key + check_call('cd %s; ' + '%s/tools/mkeficapsule --index 1 --monotonic-count 1 ' + '--private-key SIGNER.key --certificate SIGNER.crt ' + '--guid 3673B45D-6A7C-46F3-9E60-ADABB03F7937 ' + 'uboot_bin_env.itb Test13' + % (data_dir, u_boot_config.build_dir), + shell=True) + # FIT firmware signed with *mal* key + check_call('cd %s; ' + '%s/tools/mkeficapsule --index 1 --monotonic-count 1 ' + '--private-key SIGNER2.key ' + '--certificate SIGNER2.crt ' + '--guid 3673B45D-6A7C-46F3-9E60-ADABB03F7937 ' + 'uboot_bin_env.itb Test14' + % (data_dir, u_boot_config.build_dir), + shell=True) # Create a disk image with EFI system partition check_call('virt-make-fs --partition=gpt --size=+1M --type=vfat %s %s' % diff --git a/test/py/tests/test_efi_capsule/test_capsule_firmware_signed_fit.py b/test/py/tests/test_efi_capsule/test_capsule_firmware_signed_fit.py new file mode 100644 index 00000000000..4400b8f1368 --- /dev/null +++ b/test/py/tests/test_efi_capsule/test_capsule_firmware_signed_fit.py @@ -0,0 +1,257 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2021, Linaro Limited +# Copyright (c) 2022, Arm Limited +# Author: AKASHI Takahiro <takahiro.akashi@linaro.org>, +# adapted to FIT images by Vincent Stehlé <vincent.stehle@arm.com> +# +# U-Boot UEFI: Firmware Update (Signed capsule with FIT images) Test + +""" +This test verifies capsule-on-disk firmware update +with signed capsule files containing FIT images +""" + +import pytest +from capsule_defs import CAPSULE_DATA_DIR, CAPSULE_INSTALL_DIR + +@pytest.mark.boardspec('sandbox64') +@pytest.mark.boardspec('sandbox_flattree') +@pytest.mark.buildconfigspec('efi_capsule_firmware_fit') +@pytest.mark.buildconfigspec('efi_capsule_authenticate') +@pytest.mark.buildconfigspec('dfu') +@pytest.mark.buildconfigspec('dfu_sf') +@pytest.mark.buildconfigspec('cmd_efidebug') +@pytest.mark.buildconfigspec('cmd_fat') +@pytest.mark.buildconfigspec('cmd_memory') +@pytest.mark.buildconfigspec('cmd_nvedit_efi') +@pytest.mark.buildconfigspec('cmd_sf') +@pytest.mark.slow +class TestEfiCapsuleFirmwareSignedFit(object): + def test_efi_capsule_auth1( + self, u_boot_config, u_boot_console, efi_capsule_data): + """ + Test Case 1 - Update U-Boot on SPI Flash, FIT image format + 0x100000-0x150000: U-Boot binary (but dummy) + + If the capsule is properly signed, the authentication + should pass and the firmware be updated. + """ + disk_img = efi_capsule_data + with u_boot_console.log.section('Test Case 1-a, before reboot'): + output = u_boot_console.run_command_list([ + 'host bind 0 %s' % disk_img, + 'efidebug boot add -b 1 TEST host 0:1 /helloworld.efi', + 'efidebug boot order 1', + 'env set -e -nv -bs -rt OsIndications =0x0000000000000004', + 'env set dfu_alt_info ' + '"sf 0:0=u-boot-bin raw 0x100000 ' + '0x50000;u-boot-env raw 0x150000 0x200000"', + 'env save']) + + # initialize content + output = u_boot_console.run_command_list([ + 'sf probe 0:0', + 'fatload host 0:1 4000000 %s/u-boot.bin.old' + % CAPSULE_DATA_DIR, + 'sf write 4000000 100000 10', + 'sf read 5000000 100000 10', + 'md.b 5000000 10']) + assert 'Old' in ''.join(output) + + # place a capsule file + output = u_boot_console.run_command_list([ + 'fatload host 0:1 4000000 %s/Test13' % CAPSULE_DATA_DIR, + 'fatwrite host 0:1 4000000 %s/Test13 $filesize' + % CAPSULE_INSTALL_DIR, + 'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR]) + assert 'Test13' in ''.join(output) + + # reboot + mnt_point = u_boot_config.persistent_data_dir + '/test_efi_capsule' + u_boot_console.config.dtb = mnt_point + CAPSULE_DATA_DIR \ + + '/test_sig.dtb' + u_boot_console.restart_uboot() + + capsule_early = u_boot_config.buildconfig.get( + 'config_efi_capsule_on_disk_early') + with u_boot_console.log.section('Test Case 1-b, after reboot'): + if not capsule_early: + # make sure that dfu_alt_info exists even persistent variables + # are not available. + 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"', + 'host bind 0 %s' % disk_img, + 'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR]) + assert 'Test13' in ''.join(output) + + # need to run uefi command to initiate capsule handling + output = u_boot_console.run_command( + 'env print -e Capsule0000', wait_for_reboot = True) + + output = u_boot_console.run_command_list([ + 'host bind 0 %s' % disk_img, + 'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR]) + assert 'Test13' not in ''.join(output) + + output = u_boot_console.run_command_list([ + 'sf probe 0:0', + 'sf read 4000000 100000 10', + 'md.b 4000000 10']) + assert 'u-boot:New' in ''.join(output) + + def test_efi_capsule_auth2( + self, u_boot_config, u_boot_console, efi_capsule_data): + """ + Test Case 2 - Update U-Boot on SPI Flash, FIT image format + 0x100000-0x150000: U-Boot binary (but dummy) + + If the capsule is signed but with an invalid key, + the authentication should fail and the firmware + not be updated. + """ + disk_img = efi_capsule_data + with u_boot_console.log.section('Test Case 2-a, before reboot'): + output = u_boot_console.run_command_list([ + 'host bind 0 %s' % disk_img, + 'efidebug boot add -b 1 TEST host 0:1 /helloworld.efi', + 'efidebug boot order 1', + 'env set -e -nv -bs -rt OsIndications =0x0000000000000004', + 'env set dfu_alt_info ' + '"sf 0:0=u-boot-bin raw 0x100000 ' + '0x50000;u-boot-env raw 0x150000 0x200000"', + 'env save']) + + # initialize content + output = u_boot_console.run_command_list([ + 'sf probe 0:0', + 'fatload host 0:1 4000000 %s/u-boot.bin.old' + % CAPSULE_DATA_DIR, + 'sf write 4000000 100000 10', + 'sf read 5000000 100000 10', + 'md.b 5000000 10']) + assert 'Old' in ''.join(output) + + # place a capsule file + output = u_boot_console.run_command_list([ + 'fatload host 0:1 4000000 %s/Test14' % CAPSULE_DATA_DIR, + 'fatwrite host 0:1 4000000 %s/Test14 $filesize' + % CAPSULE_INSTALL_DIR, + 'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR]) + assert 'Test14' in ''.join(output) + + # reboot + mnt_point = u_boot_config.persistent_data_dir + '/test_efi_capsule' + u_boot_console.config.dtb = mnt_point + CAPSULE_DATA_DIR \ + + '/test_sig.dtb' + u_boot_console.restart_uboot() + + capsule_early = u_boot_config.buildconfig.get( + 'config_efi_capsule_on_disk_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 + # are not available. + 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"', + 'host bind 0 %s' % disk_img, + 'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR]) + assert 'Test14' in ''.join(output) + + # need to run uefi command to initiate capsule handling + output = u_boot_console.run_command( + 'env print -e Capsule0000', wait_for_reboot = True) + + # deleted any way + output = u_boot_console.run_command_list([ + 'host bind 0 %s' % disk_img, + 'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR]) + assert 'Test14' not in ''.join(output) + + # TODO: check CapsuleStatus in CapsuleXXXX + + output = u_boot_console.run_command_list([ + 'sf probe 0:0', + 'sf read 4000000 100000 10', + 'md.b 4000000 10']) + assert 'u-boot:Old' in ''.join(output) + + def test_efi_capsule_auth3( + self, u_boot_config, u_boot_console, efi_capsule_data): + """ + Test Case 3 - Update U-Boot on SPI Flash, FIT image format + 0x100000-0x150000: U-Boot binary (but dummy) + + If the capsule is not signed, the authentication + should fail and the firmware not be updated. + """ + disk_img = efi_capsule_data + with u_boot_console.log.section('Test Case 3-a, before reboot'): + output = u_boot_console.run_command_list([ + 'host bind 0 %s' % disk_img, + 'efidebug boot add -b 1 TEST host 0:1 /helloworld.efi', + 'efidebug boot order 1', + 'env set -e -nv -bs -rt OsIndications =0x0000000000000004', + 'env set dfu_alt_info ' + '"sf 0:0=u-boot-bin raw 0x100000 ' + '0x50000;u-boot-env raw 0x150000 0x200000"', + 'env save']) + + # initialize content + output = u_boot_console.run_command_list([ + 'sf probe 0:0', + 'fatload host 0:1 4000000 %s/u-boot.bin.old' + % CAPSULE_DATA_DIR, + 'sf write 4000000 100000 10', + 'sf read 5000000 100000 10', + 'md.b 5000000 10']) + assert 'Old' in ''.join(output) + + # place a capsule file + output = u_boot_console.run_command_list([ + 'fatload host 0:1 4000000 %s/Test02' % CAPSULE_DATA_DIR, + 'fatwrite host 0:1 4000000 %s/Test02 $filesize' + % CAPSULE_INSTALL_DIR, + 'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR]) + assert 'Test02' in ''.join(output) + + # reboot + mnt_point = u_boot_config.persistent_data_dir + '/test_efi_capsule' + u_boot_console.config.dtb = mnt_point + CAPSULE_DATA_DIR \ + + '/test_sig.dtb' + u_boot_console.restart_uboot() + + capsule_early = u_boot_config.buildconfig.get( + 'config_efi_capsule_on_disk_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 + # are not available. + 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"', + 'host bind 0 %s' % disk_img, + 'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR]) + assert 'Test02' in ''.join(output) + + # need to run uefi command to initiate capsule handling + output = u_boot_console.run_command( + 'env print -e Capsule0000', wait_for_reboot = True) + + # deleted any way + output = u_boot_console.run_command_list([ + 'host bind 0 %s' % disk_img, + 'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR]) + assert 'Test02' not in ''.join(output) + + # TODO: check CapsuleStatus in CapsuleXXXX + + output = u_boot_console.run_command_list([ + 'sf probe 0:0', + 'sf read 4000000 100000 10', + 'md.b 4000000 10']) + assert 'u-boot:Old' in ''.join(output) diff --git a/test/py/tests/test_efi_capsule/test_capsule_firmware_signed.py b/test/py/tests/test_efi_capsule/test_capsule_firmware_signed_raw.py index 593b032e901..1b5a1bb42eb 100644 --- a/test/py/tests/test_efi_capsule/test_capsule_firmware_signed.py +++ b/test/py/tests/test_efi_capsule/test_capsule_firmware_signed_raw.py @@ -2,11 +2,11 @@ # Copyright (c) 2021, Linaro Limited # Author: AKASHI Takahiro <takahiro.akashi@linaro.org> # -# U-Boot UEFI: Firmware Update (Signed capsule) Test +# U-Boot UEFI: Firmware Update (Signed capsule with raw images) Test """ This test verifies capsule-on-disk firmware update -with signed capsule files +with signed capsule files containing raw images """ import pytest @@ -23,7 +23,7 @@ from capsule_defs import CAPSULE_DATA_DIR, CAPSULE_INSTALL_DIR @pytest.mark.buildconfigspec('cmd_nvedit_efi') @pytest.mark.buildconfigspec('cmd_sf') @pytest.mark.slow -class TestEfiCapsuleFirmwareSigned(object): +class TestEfiCapsuleFirmwareSignedRaw(object): def test_efi_capsule_auth1( self, u_boot_config, u_boot_console, efi_capsule_data): """ @@ -85,7 +85,7 @@ class TestEfiCapsuleFirmwareSigned(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, @@ -160,7 +160,7 @@ class TestEfiCapsuleFirmwareSigned(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) # deleted any way output = u_boot_console.run_command_list([ @@ -237,9 +237,9 @@ class TestEfiCapsuleFirmwareSigned(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) - # deleted any way + # deleted anyway output = u_boot_console.run_command_list([ 'host bind 0 %s' % disk_img, 'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR]) |