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
This commit is contained in:
Tom Rini
2022-06-04 09:38:56 -04:00
6 changed files with 362 additions and 58 deletions

View File

@@ -326,7 +326,7 @@ bit in OsIndications variable with
.. code-block:: console .. 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 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 won't be taken over across the reboot. If this is the case, you can skip

View File

@@ -347,7 +347,7 @@ bootpretryperiod
Unsigned value, in milliseconds. If not set, the period will Unsigned value, in milliseconds. If not set, the period will
be either the default (28000), or a value based on be either the default (28000), or a value based on
CONFIG_NET_RETRY_COUNT, if defined. This value has 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 memmatches
Number of matches found by the last 'ms' command, in hex Number of matches found by the last 'ms' command, in hex

View File

@@ -130,9 +130,6 @@ static efi_status_t efi_fill_image_desc_array(
struct efi_fw_image *fw_array; struct efi_fw_image *fw_array;
int i; int i;
fw_array = update_info.images;
*descriptor_count = num_image_type_guids;
total_size = sizeof(*image_info) * num_image_type_guids; total_size = sizeof(*image_info) * num_image_type_guids;
if (*image_info_size < total_size) { if (*image_info_size < total_size) {
@@ -142,6 +139,8 @@ static efi_status_t efi_fill_image_desc_array(
} }
*image_info_size = total_size; *image_info_size = total_size;
fw_array = update_info.images;
*descriptor_count = num_image_type_guids;
*descriptor_version = EFI_FIRMWARE_IMAGE_DESCRIPTOR_VERSION; *descriptor_version = EFI_FIRMWARE_IMAGE_DESCRIPTOR_VERSION;
*descriptor_size = sizeof(*image_info); *descriptor_size = sizeof(*image_info);
*package_version = 0xffffffff; /* not supported */ *package_version = 0xffffffff; /* not supported */
@@ -178,6 +177,70 @@ static efi_status_t efi_fill_image_desc_array(
return EFI_SUCCESS; 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 #ifdef CONFIG_EFI_CAPSULE_FIRMWARE_FIT
/* /*
* This FIRMWARE_MANAGEMENT_PROTOCOL driver provides a firmware update * 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), efi_status_t (*progress)(efi_uintn_t completion),
u16 **abort_reason) u16 **abort_reason)
{ {
efi_status_t status;
EFI_ENTRY("%p %d %p %zu %p %p %p\n", this, image_index, image, EFI_ENTRY("%p %d %p %zu %p %p %p\n", this, image_index, image,
image_size, vendor_code, progress, abort_reason); image_size, vendor_code, progress, abort_reason);
if (!image || image_index != 1) if (!image || image_index != 1)
return EFI_EXIT(EFI_INVALID_PARAMETER); 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)) if (fit_update(image))
return EFI_EXIT(EFI_DEVICE_ERROR); 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), efi_status_t (*progress)(efi_uintn_t completion),
u16 **abort_reason) u16 **abort_reason)
{ {
u32 fmp_hdr_signature;
struct fmp_payload_header *header;
void *capsule_payload;
efi_status_t status; efi_status_t status;
efi_uintn_t capsule_payload_size;
EFI_ENTRY("%p %d %p %zu %p %p %p\n", this, image_index, image, EFI_ENTRY("%p %d %p %zu %p %p %p\n", this, image_index, image,
image_size, vendor_code, progress, abort_reason); image_size, vendor_code, progress, abort_reason);
@@ -384,44 +449,9 @@ efi_status_t EFIAPI efi_firmware_raw_set_image(
if (!image) if (!image)
return EFI_EXIT(EFI_INVALID_PARAMETER); return EFI_EXIT(EFI_INVALID_PARAMETER);
/* Authenticate the capsule if authentication enabled */ status = efi_firmware_capsule_authenticate(&image, &image_size);
if (IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)) { if (status != EFI_SUCCESS)
capsule_payload = NULL; return EFI_EXIT(status);
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;
}
if (dfu_write_by_alt(image_index - 1, (void *)image, image_size, if (dfu_write_by_alt(image_index - 1, (void *)image, image_size,
NULL, NULL)) NULL, NULL))

View File

@@ -97,23 +97,40 @@ def efi_capsule_data(request, u_boot_config):
shell=True) shell=True)
if capsule_auth_enabled: if capsule_auth_enabled:
# firmware signed with proper key # raw firmware signed with proper key
check_call('cd %s; ' check_call('cd %s; '
'%s/tools/mkeficapsule --index 1 --monotonic-count 1 ' '%s/tools/mkeficapsule --index 1 --monotonic-count 1 '
'--private-key SIGNER.key --certificate SIGNER.crt ' '--private-key SIGNER.key --certificate SIGNER.crt '
'--guid 09D7DF52-0720-4710-91D1-08469B7FE9C8 ' '--guid 09D7CF52-0720-4710-91D1-08469B7FE9C8 '
'u-boot.bin.new Test11' 'u-boot.bin.new Test11'
% (data_dir, u_boot_config.build_dir), % (data_dir, u_boot_config.build_dir),
shell=True) shell=True)
# firmware signed with *mal* key # raw firmware signed with *mal* key
check_call('cd %s; ' check_call('cd %s; '
'%s/tools/mkeficapsule --index 1 --monotonic-count 1 ' '%s/tools/mkeficapsule --index 1 --monotonic-count 1 '
'--private-key SIGNER2.key ' '--private-key SIGNER2.key '
'--certificate SIGNER2.crt ' '--certificate SIGNER2.crt '
'--guid 09D7DF52-0720-4710-91D1-08469B7FE9C8 ' '--guid 09D7CF52-0720-4710-91D1-08469B7FE9C8 '
'u-boot.bin.new Test12' 'u-boot.bin.new Test12'
% (data_dir, u_boot_config.build_dir), % (data_dir, u_boot_config.build_dir),
shell=True) 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 # Create a disk image with EFI system partition
check_call('virt-make-fs --partition=gpt --size=+1M --type=vfat %s %s' % check_call('virt-make-fs --partition=gpt --size=+1M --type=vfat %s %s' %

View File

@@ -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)

View File

@@ -2,11 +2,11 @@
# Copyright (c) 2021, Linaro Limited # Copyright (c) 2021, Linaro Limited
# Author: AKASHI Takahiro <takahiro.akashi@linaro.org> # 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 This test verifies capsule-on-disk firmware update
with signed capsule files with signed capsule files containing raw images
""" """
import pytest 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_nvedit_efi')
@pytest.mark.buildconfigspec('cmd_sf') @pytest.mark.buildconfigspec('cmd_sf')
@pytest.mark.slow @pytest.mark.slow
class TestEfiCapsuleFirmwareSigned(object): class TestEfiCapsuleFirmwareSignedRaw(object):
def test_efi_capsule_auth1( def test_efi_capsule_auth1(
self, u_boot_config, u_boot_console, efi_capsule_data): 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 # need to run uefi command to initiate capsule handling
output = u_boot_console.run_command( 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([ output = u_boot_console.run_command_list([
'host bind 0 %s' % disk_img, 'host bind 0 %s' % disk_img,
@@ -160,7 +160,7 @@ class TestEfiCapsuleFirmwareSigned(object):
# need to run uefi command to initiate capsule handling # need to run uefi command to initiate capsule handling
output = u_boot_console.run_command( output = u_boot_console.run_command(
'env print -e Capsule0000') 'env print -e Capsule0000', wait_for_reboot = True)
# deleted any way # deleted any way
output = u_boot_console.run_command_list([ output = u_boot_console.run_command_list([
@@ -237,9 +237,9 @@ class TestEfiCapsuleFirmwareSigned(object):
# need to run uefi command to initiate capsule handling # need to run uefi command to initiate capsule handling
output = u_boot_console.run_command( 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([ output = u_boot_console.run_command_list([
'host bind 0 %s' % disk_img, 'host bind 0 %s' % disk_img,
'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR]) 'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])