test/py: efi_capsule: test for FIT image capsule
The test can run on sandbox build and it attempts to execute a firmware update via a capsule-on-disk, using a FIT image capsule, CONFIG_EFI_CAPSULE_FIT. To run this test successfully, you need configure U-Boot specifically; See test_capsule_firmware.py for requirements, and hence it won't run on Travis CI, at least, for now. Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
This commit is contained in:
		
							parent
							
								
									fab430be2f
								
							
						
					
					
						commit
						450596f2ac
					
				|  | @ -0,0 +1,5 @@ | |||
| # SPDX-License-Identifier:      GPL-2.0+ | ||||
| 
 | ||||
| # Directories | ||||
| CAPSULE_DATA_DIR = '/EFI/CapsuleTestData' | ||||
| CAPSULE_INSTALL_DIR = '/EFI/UpdateCapsule' | ||||
|  | @ -0,0 +1,71 @@ | |||
| # SPDX-License-Identifier:      GPL-2.0+ | ||||
| # Copyright (c) 2020, Linaro Limited | ||||
| # Author: AKASHI Takahiro <takahiro.akashi@linaro.org> | ||||
| 
 | ||||
| import os | ||||
| import os.path | ||||
| import re | ||||
| from subprocess import call, check_call, check_output, CalledProcessError | ||||
| import pytest | ||||
| from capsule_defs import * | ||||
| 
 | ||||
| # | ||||
| # Fixture for UEFI secure boot test | ||||
| # | ||||
| 
 | ||||
| 
 | ||||
| @pytest.fixture(scope='session') | ||||
| def efi_capsule_data(request, u_boot_config): | ||||
|     """Set up a file system to be used in UEFI capsule test. | ||||
| 
 | ||||
|     Args: | ||||
|         request: Pytest request object. | ||||
|         u_boot_config: U-boot configuration. | ||||
| 
 | ||||
|     Return: | ||||
|         A path to disk image to be used for testing | ||||
|     """ | ||||
|     global CAPSULE_DATA_DIR, CAPSULE_INSTALL_DIR | ||||
| 
 | ||||
|     mnt_point = u_boot_config.persistent_data_dir + '/test_efi_capsule' | ||||
|     data_dir = mnt_point + CAPSULE_DATA_DIR | ||||
|     install_dir = mnt_point + CAPSULE_INSTALL_DIR | ||||
|     image_path = u_boot_config.persistent_data_dir + '/test_efi_capsule.img' | ||||
| 
 | ||||
|     try: | ||||
|         # Create a target device | ||||
|         check_call('dd if=/dev/zero of=./spi.bin bs=1MiB count=16', shell=True) | ||||
| 
 | ||||
|         check_call('rm -rf %s' % mnt_point, shell=True) | ||||
|         check_call('mkdir -p %s' % data_dir, shell=True) | ||||
|         check_call('mkdir -p %s' % install_dir, shell=True) | ||||
| 
 | ||||
|         # Create capsule files | ||||
|         # two regions: one for u-boot.bin and the other for u-boot.env | ||||
|         check_call('cd %s; echo -n u-boot:Old > u-boot.bin.old; echo -n u-boot:New > u-boot.bin.new; echo -n u-boot-env:Old -> u-boot.env.old; echo -n u-boot-env:New > u-boot.env.new' % data_dir, | ||||
|                    shell=True) | ||||
|         check_call('sed -e \"s?BINFILE1?u-boot.bin.new?\" -e \"s?BINFILE2?u-boot.env.new?\" %s/test/py/tests/test_efi_capsule/uboot_bin_env.its > %s/uboot_bin_env.its' % | ||||
|                    (u_boot_config.source_dir, data_dir), | ||||
|                    shell=True) | ||||
|         check_call('cd %s; %s/tools/mkimage -f uboot_bin_env.its uboot_bin_env.itb' % | ||||
|                    (data_dir, u_boot_config.build_dir), | ||||
|                    shell=True) | ||||
|         check_call('cd %s; %s/tools/mkeficapsule --fit uboot_bin_env.itb --index 1 Test01' % | ||||
|                    (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' % | ||||
|                    (mnt_point, image_path), shell=True) | ||||
|         check_call('sgdisk %s -A 1:set:0 -t 1:C12A7328-F81F-11D2-BA4B-00A0C93EC93B' % | ||||
|                    image_path, shell=True) | ||||
| 
 | ||||
|     except CalledProcessError as exception: | ||||
|         pytest.skip('Setup failed: %s' % exception.cmd) | ||||
|         return | ||||
|     else: | ||||
|         yield image_path | ||||
|     finally: | ||||
|         call('rm -rf %s' % mnt_point, shell=True) | ||||
|         call('rm -f %s' % image_path, shell=True) | ||||
|         call('rm -f ./spi.bin', shell=True) | ||||
|  | @ -0,0 +1,178 @@ | |||
| # SPDX-License-Identifier:      GPL-2.0+ | ||||
| # Copyright (c) 2020, Linaro Limited | ||||
| # Author: AKASHI Takahiro <takahiro.akashi@linaro.org> | ||||
| # | ||||
| # U-Boot UEFI: Firmware Update Test | ||||
| 
 | ||||
| """ | ||||
| This test verifies capsule-on-disk firmware update | ||||
| """ | ||||
| 
 | ||||
| from subprocess import check_call, check_output, CalledProcessError | ||||
| import pytest | ||||
| from capsule_defs import * | ||||
| 
 | ||||
| 
 | ||||
| @pytest.mark.boardspec('sandbox') | ||||
| @pytest.mark.buildconfigspec('efi_capsule_firmware_fit') | ||||
| @pytest.mark.buildconfigspec('efi_capsule_on_disk') | ||||
| @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 TestEfiCapsuleFirmwareFit(object): | ||||
|     def test_efi_capsule_fw1( | ||||
|             self, u_boot_config, u_boot_console, efi_capsule_data): | ||||
|         """ | ||||
|         Test Case 1 - Update U-Boot and U-Boot environment on SPI Flash | ||||
|                       but with OsIndications unset | ||||
|                       No update should happen | ||||
|                       0x100000-0x150000: U-Boot binary (but dummy) | ||||
|                       0x150000-0x200000: U-Boot environment (but dummy) | ||||
|         """ | ||||
|         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 1 TEST host 0:1 /helloworld.efi ""', | ||||
|                 'efidebug boot order 1', | ||||
|                 'env set -e OsIndications', | ||||
|                 'env set dfu_alt_info "sf 0:0=u-boot-bin raw 0x100000 0x50000;u-boot-env raw 0x150000 0x200000"', | ||||
|                 'env save']) | ||||
| 
 | ||||
|             # initialize contents | ||||
|             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) | ||||
|             output = u_boot_console.run_command_list([ | ||||
|                 'sf probe 0:0', | ||||
|                 'fatload host 0:1 4000000 %s/u-boot.env.old' % CAPSULE_DATA_DIR, | ||||
|                 'sf write 4000000 150000 10', | ||||
|                 'sf read 5000000 150000 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/Test01' % CAPSULE_DATA_DIR, | ||||
|                 'fatwrite host 0:1 4000000 %s/Test01 $filesize' % CAPSULE_INSTALL_DIR, | ||||
|                 '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') | ||||
|         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 'Test01' in ''.join(output) | ||||
| 
 | ||||
|                 # need to run uefi command to initiate capsule handling | ||||
|                 output = u_boot_console.run_command( | ||||
|                     'env print -e -all Capsule0000') | ||||
| 
 | ||||
|             output = u_boot_console.run_command_list([ | ||||
|                 'host bind 0 %s' % disk_img, | ||||
|                 'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR]) | ||||
|             assert 'Test01' 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:Old' in ''.join(output) | ||||
| 
 | ||||
|             output = u_boot_console.run_command_list([ | ||||
|                 'sf read 4000000 150000 10', | ||||
|                 'md.b 4000000 10']) | ||||
|             assert 'u-boot-env:Old' in ''.join(output) | ||||
| 
 | ||||
|     def test_efi_capsule_fw2( | ||||
|             self, u_boot_config, u_boot_console, efi_capsule_data): | ||||
|         """ | ||||
|         Test Case 2 - Update U-Boot and U-Boot environment on SPI Flash | ||||
|                       0x100000-0x150000: U-Boot binary (but dummy) | ||||
|                       0x150000-0x200000: U-Boot environment (but dummy) | ||||
|         """ | ||||
|         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 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 contents | ||||
|             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) | ||||
|             output = u_boot_console.run_command_list([ | ||||
|                 'sf probe 0:0', | ||||
|                 'fatload host 0:1 4000000 %s/u-boot.env.old' % CAPSULE_DATA_DIR, | ||||
|                 'sf write 4000000 150000 10', | ||||
|                 'sf read 5000000 150000 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/Test01' % CAPSULE_DATA_DIR, | ||||
|                 'fatwrite host 0:1 4000000 %s/Test01 $filesize' % CAPSULE_INSTALL_DIR, | ||||
|                 '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') | ||||
|         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 'Test01' in ''.join(output) | ||||
| 
 | ||||
|                 # need to run uefi command to initiate capsule handling | ||||
|                 output = u_boot_console.run_command( | ||||
|                     'env print -e -all Capsule0000') | ||||
| 
 | ||||
|             output = u_boot_console.run_command_list([ | ||||
|                 'host bind 0 %s' % disk_img, | ||||
|                 'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR]) | ||||
|             assert 'Test01' 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) | ||||
| 
 | ||||
|             output = u_boot_console.run_command_list([ | ||||
|                 'sf read 4000000 150000 10', | ||||
|                 'md.b 4000000 10']) | ||||
|             assert 'u-boot-env:New' in ''.join(output) | ||||
|  | @ -0,0 +1,36 @@ | |||
| /* | ||||
|  * Automatic software update for U-Boot | ||||
|  * Make sure the flashing addresses ('load' prop) is correct for your board! | ||||
|  */ | ||||
| 
 | ||||
| /dts-v1/; | ||||
| 
 | ||||
| / { | ||||
| 	description = "Automatic U-Boot environment update"; | ||||
| 	#address-cells = <2>; | ||||
| 
 | ||||
| 	images { | ||||
| 		u-boot-bin@100000 { | ||||
| 			description = "U-Boot binary on SPI Flash"; | ||||
| 			data = /incbin/("BINFILE1"); | ||||
| 			compression = "none"; | ||||
| 			type = "firmware"; | ||||
| 			arch = "sandbox"; | ||||
| 			load = <0>; | ||||
| 			hash-1 { | ||||
| 				algo = "sha1"; | ||||
| 			}; | ||||
| 		}; | ||||
| 		u-boot-env@150000 { | ||||
| 			description = "U-Boot environment on SPI Flash"; | ||||
| 			data = /incbin/("BINFILE2"); | ||||
| 			compression = "none"; | ||||
| 			type = "firmware"; | ||||
| 			arch = "sandbox"; | ||||
| 			load = <0>; | ||||
| 			hash-1 { | ||||
| 				algo = "sha1"; | ||||
| 			}; | ||||
| 		}; | ||||
| 	}; | ||||
| }; | ||||
|  | @ -98,7 +98,8 @@ static int create_fwbin(char *path, char *bin, efi_guid_t *guid, | |||
| 	} | ||||
| 	header.capsule_guid = efi_guid_fm_capsule; | ||||
| 	header.header_size = sizeof(header); | ||||
| 	header.flags = CAPSULE_FLAGS_PERSIST_ACROSS_RESET; /* TODO */ | ||||
| 	/* TODO: The current implementation ignores flags */ | ||||
| 	header.flags = CAPSULE_FLAGS_PERSIST_ACROSS_RESET; | ||||
| 	header.capsule_image_size = sizeof(header) | ||||
| 					+ sizeof(capsule) + sizeof(u64) | ||||
| 					+ sizeof(image) | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue