[PATCH v3 00/17] efi_loader: add capsule update support

Summary ======= 'UpdateCapsule' is one of runtime services defined in UEFI specification and its aim is to allow a caller (OS) to pass information to the firmware, i.e. U-Boot. This is mostly used to update firmware binary on devices by instructions from OS.
While 'UpdateCapsule' is a runtime services function, it is, at least initially, supported only before exiting boot services alike other runtime functions, [Get/]SetVariable. This is because modifying storage which may be shared with OS must be carefully designed and there is no general assumption that we can do it.
Therefore, we practically support only "capsule on disk"; any capsule can be handed over to UEFI subsystem as a file on a specific file system.
In this patch series, all the related definitions and structures are given as UEFI specification describes, and basic framework for capsule support is provided. Currently supported is * firmware update (Firmware Management Protocol or simply FMP)
Most of functionality of firmware update is provided by FMP driver and it can be, by nature, system/platform-specific. So you can and should implement your own FMP driver(s) based on your system requirements. Under the current implementation, we provide two basic but generic drivers with two formats: * FIT image format (as used in TFTP update and dfu) * raw image format
It's totally up to users which one, or both, should be used on users' system depending on user requirements.
Quick usage =========== 1. You can create a capsule file with the following host command:
$ mkeficapsule [--fit <fit image> | --raw <raw image>] <output file>
2. Put the file under:
/EFI/UpdateCapsule of UEFI system partition
3. Specify firmware storage to be updated in "dfu_alt_info" variable (Please follow README.dfu for details.)
==> env set dfu_alt_info '...'
4. After setting up UEFI's OsIndications variable, reboot U-Boot:
OsIndications <= EFI_OS_INDICATIONS_FILE_CAPSULE_DELIVERY_SUPPORTED
Patch structure =============== Patch#1-#5,#12: preparatory patches Patch#6-#11,#13: main part of implementation Patch#14-#15: utilities Patch#16-#17: pytests
[1] https://git.linaro.org/people/takahiro.akashi/u-boot.git efi/capsule
Prerequisite patches ==================== (None as all have been merged as of v2020.07-rc4)
Test ==== * passed all the pytests which are included in this patch series on sandbox build. * passed Travis CI, except "buildman samsumg softfpga". (I don't know what this build failed yet.)
Please note that, while Travic CI passed, the capsule pytest itself won't be run in the CI because some specific configuration for sandbox build is required. See test_efi_capsule_firmware.py.
Issues ====== * Timing of executing capsules-on-disk Currently, processing a capsule is triggered only as part of UEFI subsystem initialization. This means that, for example, firmware update, may not take place at system booting time and will potentially be delayed until a first call of any UEFI functions. => See patch#6 for my proposal * A bunch of warnings like WARNING: Use 'if (IS_ENABLED(CONFIG...))' instead of '#if or #ifdef' where possible I don't think that fixing those improves anything. * Add a document in uefi.rst
TODO's ====== (Won't be addressed in this series.) * capsule authentication * capsule dependency (dependency expression instruction set) * loading drivers in a capsule * handling RESET flag in a capsule and QeuryCapsuleCaps * full semantics of ESRT (EFI System Resource Table) * enabling capsule API at runtime * json capsule * recovery from update failure
Changes ======= v3 (July 10, 2020) * rebased to Heinrich's current efi-2020-10-rc1 * refactor efi_firmware_[fit|raw]_get_image_info() (patch#11,#13)
v2 (June 17, 2020) * rebased to v2020.07-rc4 * add preparatory patches for dfu (Patch#1-#5, #12) * rework FIT capsule driver to utilize dfu_alt_info instead of CONFIG_xxx (patch#11) * extend get_image_info() to correspond to dfu_alt_info (patch#11) * add a 'raw binary' capsule support (patch#13, #17) * allow multiple capsule formats (with different GUIDs) to be installed (patch#11, #13) * extend mkeficapsule command to accept additional parameters, like version/index/hardware instance for a capsule header info. (patch#15) * mkeficapsule can now also generate raw-binary capsule (patch#16) * add function descriptions * apply autopep8 to pytests and fix more against pylint
v1 (April 27, 2020) * rebased to v2020.07-rc * removed already-merged patches (RFC's #1 to #4) * dropped 'variable update' capsule support (RFC's patch#10) * dropped 'variable configuration table' support (RFC's patch#11) (Those two should be discussed separately.) * add preparatory patches (patch#1/#2) * fix several build errors * rename some Kconfig options to be aligned with UEFI specification's terms (patch#3,4,6,7) * enforce UpdateCapsule API to be disabled after ExitBootServices (patch#3) * use config table, runtime_services_supported, instead of variable (patch#3) * make EFI_CAPSULE_ON_DISK buildable even if UpdateCapsule API is disabled (patch4) * support OsIndications, invoking capsule-on-disk only if the variable indicates so (patch#4) * introduced EFI_CAPSULE_ON_DISK_EARLY to invoke capsule-on-disk in U-Boot initialization (patch#4) * detect capsule files only if they are on EFI system partition (patch#4) * use printf, rather than EFI_PRINT, in error cases (patch#4) * use 'header_size' field to retrieve capsule data, adding sanity checks against capsule size (patch#6) * call fmpt driver interfaces with EFI_CALL (patch#6) * remove 'variable update capsule'-related code form mkeficapsule (patch#9) * add a test case of OsIndications not being set properly (patch#10) * adjust test scenario for EFI_CAPSULE_ON_DISK_EARLY (patch#10) * revise pytest scripts (patch#10)
Initial release as RFC (March 17, 2020)
AKASHI Takahiro (17): common: update_tftp: remove unnecessary build check dfu: add a hidden reverse-dependency on UPDATE_TFTP dfu: rename dfu_tftp_write() to dfu_write_by_name() common: update: add a generic interface for FIT image dfu: export dfu_list efi_loader: add option to initialise EFI subsystem early efi_loader: define UpdateCapsule api efi_loader: capsule: add capsule_on_disk support efi_loader: capsule: add memory range capsule definitions efi_loader: capsule: support firmware update efi_loader: add firmware management protocol for FIT image dfu: add dfu_write_by_alt() efi_loader: add firmware management protocol for raw image cmd: add "efidebug capsule" command tools: add mkeficapsule command for UEFI capsule update test/py: add a test for uefi firmware update capsule of FIT image test/py: add a test for uefi firmware update capsule of raw image
cmd/efidebug.c | 235 +++++ common/Kconfig | 16 + common/Makefile | 2 +- common/board_r.c | 6 + common/main.c | 4 + common/update.c | 83 +- drivers/dfu/Kconfig | 6 + drivers/dfu/Makefile | 2 +- drivers/dfu/dfu.c | 2 +- drivers/dfu/dfu_alt.c | 125 +++ drivers/dfu/dfu_tftp.c | 65 -- include/dfu.h | 57 +- include/efi_api.h | 160 +++ include/efi_loader.h | 34 + include/image.h | 12 + lib/efi_loader/Kconfig | 72 ++ lib/efi_loader/Makefile | 2 + lib/efi_loader/efi_capsule.c | 925 ++++++++++++++++++ lib/efi_loader/efi_firmware.c | 398 ++++++++ lib/efi_loader/efi_runtime.c | 104 +- lib/efi_loader/efi_setup.c | 43 +- test/py/tests/test_efi_capsule/conftest.py | 102 ++ test/py/tests/test_efi_capsule/defs.py | 24 + .../test_efi_capsule/test_capsule_firmware.py | 383 ++++++++ .../tests/test_efi_capsule/uboot_bin_env.its | 36 + test/py/tests/test_efi_capsule/uboot_env.its | 25 + tools/Makefile | 3 + tools/mkeficapsule.c | 237 +++++ 28 files changed, 3022 insertions(+), 141 deletions(-) create mode 100644 drivers/dfu/dfu_alt.c delete mode 100644 drivers/dfu/dfu_tftp.c create mode 100644 lib/efi_loader/efi_capsule.c create mode 100644 lib/efi_loader/efi_firmware.c create mode 100644 test/py/tests/test_efi_capsule/conftest.py create mode 100644 test/py/tests/test_efi_capsule/defs.py create mode 100644 test/py/tests/test_efi_capsule/test_capsule_firmware.py create mode 100644 test/py/tests/test_efi_capsule/uboot_bin_env.its create mode 100644 test/py/tests/test_efi_capsule/uboot_env.its create mode 100644 tools/mkeficapsule.c

Logically, the current update_tftp() should and does compile and work correctly even without satisfying the following condition:
#if defined(CONFIG_UPDATE_TFTP) && !defined(CONFIG_MTD_NOR_FLASH) #error "CONFIG_UPDATE_TFTP and !CONFIG_MTD_NOR_FLASH needed for legacy behaviour" #endif
It would be better to just drop it so that this function will be used on wider range of platforms.
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org --- common/update.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-)
diff --git a/common/update.c b/common/update.c index c8dd346a0956..3547dc68bf7c 100644 --- a/common/update.c +++ b/common/update.c @@ -14,10 +14,6 @@ #error "CONFIG_FIT and CONFIG_OF_LIBFDT are required for auto-update feature" #endif
-#if defined(CONFIG_UPDATE_TFTP) && !defined(CONFIG_MTD_NOR_FLASH) -#error "CONFIG_UPDATE_TFTP and !CONFIG_MTD_NOR_FLASH needed for legacy behaviour" -#endif - #include <command.h> #include <env.h> #include <flash.h> @@ -210,8 +206,10 @@ static int update_flash(ulong addr_source, ulong addr_first, ulong size) printf("Error: could not protect flash sectors\n"); return 1; } -#endif return 0; +#else + return 1; +#endif }
static int update_fit_getparams(const void *fit, int noffset, ulong *addr,

On 7/10/20 3:25 AM, AKASHI Takahiro wrote:
Logically, the current update_tftp() should and does compile and work correctly even without satisfying the following condition:
#if defined(CONFIG_UPDATE_TFTP) && !defined(CONFIG_MTD_NOR_FLASH) #error "CONFIG_UPDATE_TFTP and !CONFIG_MTD_NOR_FLASH needed for legacy behaviour" #endif
The file update.c is for the auto update feature. This should be completely orthogonal to UEFI capsule updates.
We need UEFI capsule updates to work without network. It should not depend on UPDATE_TFTP and not on NET.
Best regards
Heinrich
It would be better to just drop it so that this function will be used on wider range of platforms.
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org
common/update.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-)
diff --git a/common/update.c b/common/update.c index c8dd346a0956..3547dc68bf7c 100644 --- a/common/update.c +++ b/common/update.c @@ -14,10 +14,6 @@ #error "CONFIG_FIT and CONFIG_OF_LIBFDT are required for auto-update feature" #endif
-#if defined(CONFIG_UPDATE_TFTP) && !defined(CONFIG_MTD_NOR_FLASH) -#error "CONFIG_UPDATE_TFTP and !CONFIG_MTD_NOR_FLASH needed for legacy behaviour" -#endif
#include <command.h> #include <env.h> #include <flash.h> @@ -210,8 +206,10 @@ static int update_flash(ulong addr_source, ulong addr_first, ulong size) printf("Error: could not protect flash sectors\n"); return 1; } -#endif return 0; +#else
- return 1;
+#endif }
static int update_fit_getparams(const void *fit, int noffset, ulong *addr,

DFU_OVER_TFTP support on "dfu" command relies on update_tftp() being available. Just explicitly add this dependency.
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org --- drivers/dfu/Kconfig | 1 + 1 file changed, 1 insertion(+)
diff --git a/drivers/dfu/Kconfig b/drivers/dfu/Kconfig index 5d45d7d7c2d7..cafb6a34090e 100644 --- a/drivers/dfu/Kconfig +++ b/drivers/dfu/Kconfig @@ -12,6 +12,7 @@ config DFU_OVER_USB config DFU_OVER_TFTP bool depends on NET + select UPDATE_TFTP
if DFU config DFU_TFTP

On 7/10/20 3:25 AM, AKASHI Takahiro wrote:
DFU_OVER_TFTP support on "dfu" command relies on update_tftp() being available. Just explicitly add this dependency.
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org
drivers/dfu/Kconfig | 1 + 1 file changed, 1 insertion(+)
diff --git a/drivers/dfu/Kconfig b/drivers/dfu/Kconfig index 5d45d7d7c2d7..cafb6a34090e 100644 --- a/drivers/dfu/Kconfig +++ b/drivers/dfu/Kconfig @@ -12,6 +12,7 @@ config DFU_OVER_USB config DFU_OVER_TFTP bool depends on NET
- select UPDATE_TFTP
UPDATE_TFTP depends on FIT while DFU_OVER_TFTP does not depend on FIT.
So selecting UPDATE_TFTP without FIT creates an invalid configuration:
Symbol: UPDATE_TFTP [=y] Type : bool Prompt: Auto-update using fitImage via TFTP Location: (1) -> Update support Defined at common/Kconfig:1017 Depends on: FIT [=n] Selected by [y]: - DFU_OVER_TFTP [=y] && NET [=y]
WARNING: unmet direct dependencies detected for UPDATE_TFTP Depends on [n]: FIT [=n] Selected by [y]: - DFU_OVER_TFTP [=y] && NET [=y]
common/update.c: In function ‘update_fit_getparams’: common/update.c:220:6: error: implicit declaration of function ‘fit_image_get_data’; did you mean ‘image_get_data’? [-Werror=implicit-function-declaration] 220 | if (fit_image_get_data(fit, noffset, &data, (size_t *)size))
The whole UEFI capsule update development should not depend on NET and hence not on UPDATE_TFTP.
Best regards
Heinrich
if DFU config DFU_TFTP

Heinrich,
On Fri, Jul 10, 2020 at 07:21:14AM +0200, Heinrich Schuchardt wrote:
On 7/10/20 3:25 AM, AKASHI Takahiro wrote:
DFU_OVER_TFTP support on "dfu" command relies on update_tftp() being available. Just explicitly add this dependency.
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org
drivers/dfu/Kconfig | 1 + 1 file changed, 1 insertion(+)
diff --git a/drivers/dfu/Kconfig b/drivers/dfu/Kconfig index 5d45d7d7c2d7..cafb6a34090e 100644 --- a/drivers/dfu/Kconfig +++ b/drivers/dfu/Kconfig @@ -12,6 +12,7 @@ config DFU_OVER_USB config DFU_OVER_TFTP bool depends on NET
- select UPDATE_TFTP
UPDATE_TFTP depends on FIT while DFU_OVER_TFTP does not depend on FIT.
So selecting UPDATE_TFTP without FIT creates an invalid configuration:
Please read the code in common/update.c carefully. Whether my patch is applied or not, update_tftp() relies on FIT (and helper functions.)
So 'selecting UPDATE_TFTP without FIT' is just invalid.
Symbol: UPDATE_TFTP [=y] Type : bool Prompt: Auto-update using fitImage via TFTP Location: (1) -> Update support Defined at common/Kconfig:1017 Depends on: FIT [=n] Selected by [y]:
- DFU_OVER_TFTP [=y] && NET [=y]
WARNING: unmet direct dependencies detected for UPDATE_TFTP Depends on [n]: FIT [=n] Selected by [y]:
- DFU_OVER_TFTP [=y] && NET [=y]
common/update.c: In function ‘update_fit_getparams’: common/update.c:220:6: error: implicit declaration of function ‘fit_image_get_data’; did you mean ‘image_get_data’? [-Werror=implicit-function-declaration] 220 | if (fit_image_get_data(fit, noffset, &data, (size_t *)size))
The whole UEFI capsule update development should not depend on NET and hence not on UPDATE_TFTP.
Please read my succeeding patches carefully. With all the patches applied, I guarantee that your assertion above is valid.
-Takahiro Akashi
Best regards
Heinrich
if DFU config DFU_TFTP

This function is essentially independent from tffp, and will also be utilised in implementing UEFI capsule update in a later commit. So just give it a more generic name. In addition, a new configuration option, CONFIG_DFU_ALT, was introduced so that the file will be compiled with different options, particularly one added in a later commit.
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org --- drivers/dfu/Kconfig | 5 +++++ drivers/dfu/Makefile | 2 +- drivers/dfu/{dfu_tftp.c => dfu_alt.c} | 17 ++++++++++++-- include/dfu.h | 32 +++++++++++++-------------- 4 files changed, 37 insertions(+), 19 deletions(-) rename drivers/dfu/{dfu_tftp.c => dfu_alt.c} (67%)
diff --git a/drivers/dfu/Kconfig b/drivers/dfu/Kconfig index cafb6a34090e..1bddaef45532 100644 --- a/drivers/dfu/Kconfig +++ b/drivers/dfu/Kconfig @@ -15,8 +15,13 @@ config DFU_OVER_TFTP select UPDATE_TFTP
if DFU +config DFU_ALT + bool + default n + config DFU_TFTP bool "DFU via TFTP" + select DFU_ALT select DFU_OVER_TFTP help This option allows performing update of DFU-managed medium with data diff --git a/drivers/dfu/Makefile b/drivers/dfu/Makefile index 0d7925c083ef..cc7de1d3ed9b 100644 --- a/drivers/dfu/Makefile +++ b/drivers/dfu/Makefile @@ -9,5 +9,5 @@ obj-$(CONFIG_$(SPL_)DFU_MTD) += dfu_mtd.o obj-$(CONFIG_$(SPL_)DFU_NAND) += dfu_nand.o obj-$(CONFIG_$(SPL_)DFU_RAM) += dfu_ram.o obj-$(CONFIG_$(SPL_)DFU_SF) += dfu_sf.o -obj-$(CONFIG_$(SPL_)DFU_TFTP) += dfu_tftp.o +obj-$(CONFIG_$(SPL_)DFU_ALT) += dfu_alt.o obj-$(CONFIG_$(SPL_)DFU_VIRT) += dfu_virt.o diff --git a/drivers/dfu/dfu_tftp.c b/drivers/dfu/dfu_alt.c similarity index 67% rename from drivers/dfu/dfu_tftp.c rename to drivers/dfu/dfu_alt.c index ffae4bb54f80..5b1b13d7170d 100644 --- a/drivers/dfu/dfu_tftp.c +++ b/drivers/dfu/dfu_alt.c @@ -10,8 +10,21 @@ #include <errno.h> #include <dfu.h>
-int dfu_tftp_write(char *dfu_entity_name, unsigned int addr, unsigned int len, - char *interface, char *devstring) +/** + * dfu_write_by_name() - write data to DFU medium + * @dfu_entity_name: Name of DFU entity to write + * @addr: Address of data buffer to write + * @len: Number of bytes + * @interface: Destination DFU medium (e.g. "mmc") + * @devstring: Instance number of destination DFU medium (e.g. "1") + * + * This function is storing data received on DFU supported medium which + * is specified by @dfu_entity_name. + * + * Return: 0 - on success, error code - otherwise + */ +int dfu_write_by_name(char *dfu_entity_name, unsigned int addr, + unsigned int len, char *interface, char *devstring) { char *s, *sb; int alt_setting_num, ret; diff --git a/include/dfu.h b/include/dfu.h index 6fa450593605..94b0a9e68317 100644 --- a/include/dfu.h +++ b/include/dfu.h @@ -494,27 +494,27 @@ static inline int dfu_fill_entity_virt(struct dfu_entity *dfu, char *devstr, #endif
/** - * dfu_tftp_write() - write TFTP data to DFU medium + * dfu_write_by_name() - write data to DFU medium + * @dfu_entity_name: Name of DFU entity to write + * @addr: Address of data buffer to write + * @len: Number of bytes + * @interface: Destination DFU medium (e.g. "mmc") + * @devstring: Instance number of destination DFU medium (e.g. "1") * - * This function is storing data received via TFTP on DFU supported medium. + * This function is storing data received on DFU supported medium which + * is specified by @dfu_entity_name. * - * @dfu_entity_name: name of DFU entity to write - * @addr: address of data buffer to write - * @len: number of bytes - * @interface: destination DFU medium (e.g. "mmc") - * @devstring: instance number of destination DFU medium (e.g. "1") - * - * Return: 0 on success, otherwise error code + * Return: 0 - on success, error code - otherwise */ -#if CONFIG_IS_ENABLED(DFU_TFTP) -int dfu_tftp_write(char *dfu_entity_name, unsigned int addr, unsigned int len, - char *interface, char *devstring); +#if CONFIG_IS_ENABLED(DFU_ALT) +int dfu_write_by_name(char *dfu_entity_name, unsigned int addr, + unsigned int len, char *interface, char *devstring); #else -static inline int dfu_tftp_write(char *dfu_entity_name, unsigned int addr, - unsigned int len, char *interface, - char *devstring) +static inline int dfu_write_by_name(char *dfu_entity_name, unsigned int addr, + unsigned int len, char *interface, + char *devstring) { - puts("TFTP write support for DFU not available!\n"); + puts("write support for DFU not available!\n"); return -ENOSYS; } #endif

On 7/10/20 3:25 AM, AKASHI Takahiro wrote:
This function is essentially independent from tffp, and will also be utilised in implementing UEFI capsule update in a later commit. So just give it a more generic name. In addition, a new configuration option, CONFIG_DFU_ALT, was introduced
_ALT does not convey any meaning to me. Why don't you simply move the function to drivers/dfu/dfu.c? If it is not needed the linker will drop it.
dfu_write_buffer() might be a good name for the function.
Best regards
Heinrich
so that the file will be compiled with different options, particularly one added in a later commit.
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org
drivers/dfu/Kconfig | 5 +++++ drivers/dfu/Makefile | 2 +- drivers/dfu/{dfu_tftp.c => dfu_alt.c} | 17 ++++++++++++-- include/dfu.h | 32 +++++++++++++-------------- 4 files changed, 37 insertions(+), 19 deletions(-) rename drivers/dfu/{dfu_tftp.c => dfu_alt.c} (67%)
diff --git a/drivers/dfu/Kconfig b/drivers/dfu/Kconfig index cafb6a34090e..1bddaef45532 100644 --- a/drivers/dfu/Kconfig +++ b/drivers/dfu/Kconfig @@ -15,8 +15,13 @@ config DFU_OVER_TFTP select UPDATE_TFTP
if DFU +config DFU_ALT
- bool
- default n
config DFU_TFTP bool "DFU via TFTP"
- select DFU_ALT select DFU_OVER_TFTP help This option allows performing update of DFU-managed medium with data
diff --git a/drivers/dfu/Makefile b/drivers/dfu/Makefile index 0d7925c083ef..cc7de1d3ed9b 100644 --- a/drivers/dfu/Makefile +++ b/drivers/dfu/Makefile @@ -9,5 +9,5 @@ obj-$(CONFIG_$(SPL_)DFU_MTD) += dfu_mtd.o obj-$(CONFIG_$(SPL_)DFU_NAND) += dfu_nand.o obj-$(CONFIG_$(SPL_)DFU_RAM) += dfu_ram.o obj-$(CONFIG_$(SPL_)DFU_SF) += dfu_sf.o -obj-$(CONFIG_$(SPL_)DFU_TFTP) += dfu_tftp.o +obj-$(CONFIG_$(SPL_)DFU_ALT) += dfu_alt.o obj-$(CONFIG_$(SPL_)DFU_VIRT) += dfu_virt.o diff --git a/drivers/dfu/dfu_tftp.c b/drivers/dfu/dfu_alt.c similarity index 67% rename from drivers/dfu/dfu_tftp.c rename to drivers/dfu/dfu_alt.c index ffae4bb54f80..5b1b13d7170d 100644 --- a/drivers/dfu/dfu_tftp.c +++ b/drivers/dfu/dfu_alt.c @@ -10,8 +10,21 @@ #include <errno.h> #include <dfu.h>
-int dfu_tftp_write(char *dfu_entity_name, unsigned int addr, unsigned int len,
char *interface, char *devstring)
+/**
- dfu_write_by_name() - write data to DFU medium
- @dfu_entity_name: Name of DFU entity to write
- @addr: Address of data buffer to write
- @len: Number of bytes
- @interface: Destination DFU medium (e.g. "mmc")
- @devstring: Instance number of destination DFU medium (e.g. "1")
- This function is storing data received on DFU supported medium which
- is specified by @dfu_entity_name.
- Return: 0 - on success, error code - otherwise
- */
+int dfu_write_by_name(char *dfu_entity_name, unsigned int addr,
unsigned int len, char *interface, char *devstring)
{ char *s, *sb; int alt_setting_num, ret; diff --git a/include/dfu.h b/include/dfu.h index 6fa450593605..94b0a9e68317 100644 --- a/include/dfu.h +++ b/include/dfu.h @@ -494,27 +494,27 @@ static inline int dfu_fill_entity_virt(struct dfu_entity *dfu, char *devstr, #endif
/**
- dfu_tftp_write() - write TFTP data to DFU medium
- dfu_write_by_name() - write data to DFU medium
- @dfu_entity_name: Name of DFU entity to write
- @addr: Address of data buffer to write
- @len: Number of bytes
- @interface: Destination DFU medium (e.g. "mmc")
- @devstring: Instance number of destination DFU medium (e.g. "1")
- This function is storing data received via TFTP on DFU supported medium.
- This function is storing data received on DFU supported medium which
- is specified by @dfu_entity_name.
- @dfu_entity_name: name of DFU entity to write
- @addr: address of data buffer to write
- @len: number of bytes
- @interface: destination DFU medium (e.g. "mmc")
- @devstring: instance number of destination DFU medium (e.g. "1")
- Return: 0 on success, otherwise error code
*/
- Return: 0 - on success, error code - otherwise
-#if CONFIG_IS_ENABLED(DFU_TFTP) -int dfu_tftp_write(char *dfu_entity_name, unsigned int addr, unsigned int len,
char *interface, char *devstring);
+#if CONFIG_IS_ENABLED(DFU_ALT) +int dfu_write_by_name(char *dfu_entity_name, unsigned int addr,
unsigned int len, char *interface, char *devstring);
#else -static inline int dfu_tftp_write(char *dfu_entity_name, unsigned int addr,
unsigned int len, char *interface,
char *devstring)
+static inline int dfu_write_by_name(char *dfu_entity_name, unsigned int addr,
unsigned int len, char *interface,
char *devstring)
{
- puts("TFTP write support for DFU not available!\n");
- puts("write support for DFU not available!\n"); return -ENOSYS;
} #endif

Heinrich,
On Fri, Jul 10, 2020 at 07:45:30AM +0200, Heinrich Schuchardt wrote:
On 7/10/20 3:25 AM, AKASHI Takahiro wrote:
This function is essentially independent from tffp, and will also be utilised in implementing UEFI capsule update in a later commit. So just give it a more generic name. In addition, a new configuration option, CONFIG_DFU_ALT, was introduced
_ALT does not convey any meaning to me. Why don't you simply move the function to drivers/dfu/dfu.c?
Please read my succeeding patches carefully. I added a variant function later.
If it is not needed the linker will drop it.
CONFIG_DFU_ALT is NOT a user-selectable configuration. So unless it is really necessary, it won't compile 'automatically'. We don't have to rely on linkers here.
dfu_write_buffer() might be a good name for the function.
See my comment above. There is a good reason for the name.
-Takahiro Akashi
Best regards
Heinrich
so that the file will be compiled with different options, particularly one added in a later commit.
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org
drivers/dfu/Kconfig | 5 +++++ drivers/dfu/Makefile | 2 +- drivers/dfu/{dfu_tftp.c => dfu_alt.c} | 17 ++++++++++++-- include/dfu.h | 32 +++++++++++++-------------- 4 files changed, 37 insertions(+), 19 deletions(-) rename drivers/dfu/{dfu_tftp.c => dfu_alt.c} (67%)
diff --git a/drivers/dfu/Kconfig b/drivers/dfu/Kconfig index cafb6a34090e..1bddaef45532 100644 --- a/drivers/dfu/Kconfig +++ b/drivers/dfu/Kconfig @@ -15,8 +15,13 @@ config DFU_OVER_TFTP select UPDATE_TFTP
if DFU +config DFU_ALT
- bool
- default n
config DFU_TFTP bool "DFU via TFTP"
- select DFU_ALT select DFU_OVER_TFTP help This option allows performing update of DFU-managed medium with data
diff --git a/drivers/dfu/Makefile b/drivers/dfu/Makefile index 0d7925c083ef..cc7de1d3ed9b 100644 --- a/drivers/dfu/Makefile +++ b/drivers/dfu/Makefile @@ -9,5 +9,5 @@ obj-$(CONFIG_$(SPL_)DFU_MTD) += dfu_mtd.o obj-$(CONFIG_$(SPL_)DFU_NAND) += dfu_nand.o obj-$(CONFIG_$(SPL_)DFU_RAM) += dfu_ram.o obj-$(CONFIG_$(SPL_)DFU_SF) += dfu_sf.o -obj-$(CONFIG_$(SPL_)DFU_TFTP) += dfu_tftp.o +obj-$(CONFIG_$(SPL_)DFU_ALT) += dfu_alt.o obj-$(CONFIG_$(SPL_)DFU_VIRT) += dfu_virt.o diff --git a/drivers/dfu/dfu_tftp.c b/drivers/dfu/dfu_alt.c similarity index 67% rename from drivers/dfu/dfu_tftp.c rename to drivers/dfu/dfu_alt.c index ffae4bb54f80..5b1b13d7170d 100644 --- a/drivers/dfu/dfu_tftp.c +++ b/drivers/dfu/dfu_alt.c @@ -10,8 +10,21 @@ #include <errno.h> #include <dfu.h>
-int dfu_tftp_write(char *dfu_entity_name, unsigned int addr, unsigned int len,
char *interface, char *devstring)
+/**
- dfu_write_by_name() - write data to DFU medium
- @dfu_entity_name: Name of DFU entity to write
- @addr: Address of data buffer to write
- @len: Number of bytes
- @interface: Destination DFU medium (e.g. "mmc")
- @devstring: Instance number of destination DFU medium (e.g. "1")
- This function is storing data received on DFU supported medium which
- is specified by @dfu_entity_name.
- Return: 0 - on success, error code - otherwise
- */
+int dfu_write_by_name(char *dfu_entity_name, unsigned int addr,
unsigned int len, char *interface, char *devstring)
{ char *s, *sb; int alt_setting_num, ret; diff --git a/include/dfu.h b/include/dfu.h index 6fa450593605..94b0a9e68317 100644 --- a/include/dfu.h +++ b/include/dfu.h @@ -494,27 +494,27 @@ static inline int dfu_fill_entity_virt(struct dfu_entity *dfu, char *devstr, #endif
/**
- dfu_tftp_write() - write TFTP data to DFU medium
- dfu_write_by_name() - write data to DFU medium
- @dfu_entity_name: Name of DFU entity to write
- @addr: Address of data buffer to write
- @len: Number of bytes
- @interface: Destination DFU medium (e.g. "mmc")
- @devstring: Instance number of destination DFU medium (e.g. "1")
- This function is storing data received via TFTP on DFU supported medium.
- This function is storing data received on DFU supported medium which
- is specified by @dfu_entity_name.
- @dfu_entity_name: name of DFU entity to write
- @addr: address of data buffer to write
- @len: number of bytes
- @interface: destination DFU medium (e.g. "mmc")
- @devstring: instance number of destination DFU medium (e.g. "1")
- Return: 0 on success, otherwise error code
*/
- Return: 0 - on success, error code - otherwise
-#if CONFIG_IS_ENABLED(DFU_TFTP) -int dfu_tftp_write(char *dfu_entity_name, unsigned int addr, unsigned int len,
char *interface, char *devstring);
+#if CONFIG_IS_ENABLED(DFU_ALT) +int dfu_write_by_name(char *dfu_entity_name, unsigned int addr,
unsigned int len, char *interface, char *devstring);
#else -static inline int dfu_tftp_write(char *dfu_entity_name, unsigned int addr,
unsigned int len, char *interface,
char *devstring)
+static inline int dfu_write_by_name(char *dfu_entity_name, unsigned int addr,
unsigned int len, char *interface,
char *devstring)
{
- puts("TFTP write support for DFU not available!\n");
- puts("write support for DFU not available!\n"); return -ENOSYS;
} #endif

The main purpose of this patch is to separate a generic interface for updating firmware using DFU drivers from "auto-update" via tftp.
This function will also be used in implementing UEFI capsule update in a later commit.
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org --- common/Kconfig | 16 +++++++++++ common/Makefile | 2 +- common/update.c | 75 +++++++++++++++++++++++++++++++++++++++++++++++-- include/image.h | 12 ++++++++ 4 files changed, 102 insertions(+), 3 deletions(-)
diff --git a/common/Kconfig b/common/Kconfig index 2d86dd7e63c6..a51d2a7b9d2a 100644 --- a/common/Kconfig +++ b/common/Kconfig @@ -985,9 +985,16 @@ endmenu
menu "Update support"
+config UPDATE_COMMON + bool + default n + select DFU_ALT + config UPDATE_TFTP bool "Auto-update using fitImage via TFTP" depends on FIT + depends on DFU + select UPDATE_COMMON help This option allows performing update of NOR with data in fitImage sent via TFTP boot. @@ -1002,6 +1009,15 @@ config UPDATE_TFTP_MSEC_MAX default 100 depends on UPDATE_TFTP
+config UPDATE_FIT + bool "Firmware update using fitImage" + depends on FIT + depends on DFU + select UPDATE_COMMON + help + This option allows performing update of DFU-capable storage with + data in fitImage. + config ANDROID_AB bool "Android A/B updates" default n diff --git a/common/Makefile b/common/Makefile index 2e7a090588d9..c238db8108e7 100644 --- a/common/Makefile +++ b/common/Makefile @@ -53,7 +53,7 @@ obj-$(CONFIG_LCD_ROTATION) += lcd_console_rotation.o obj-$(CONFIG_LCD_DT_SIMPLEFB) += lcd_simplefb.o obj-$(CONFIG_LYNXKDI) += lynxkdi.o obj-$(CONFIG_MENU) += menu.o -obj-$(CONFIG_UPDATE_TFTP) += update.o +obj-$(CONFIG_UPDATE_COMMON) += update.o obj-$(CONFIG_DFU_TFTP) += update.o obj-$(CONFIG_USB_KEYBOARD) += usb_kbd.o obj-$(CONFIG_CMDLINE) += cli_readline.o cli_simple.o diff --git a/common/update.c b/common/update.c index 3547dc68bf7c..a8151383fae7 100644 --- a/common/update.c +++ b/common/update.c @@ -24,6 +24,7 @@ #include <errno.h> #include <mtd/cfi_flash.h>
+#ifdef CONFIG_UPDATE_TFTP /* env variable holding the location of the update file */ #define UPDATE_FILE_ENV "updatefile"
@@ -211,6 +212,7 @@ static int update_flash(ulong addr_source, ulong addr_first, ulong size) return 1; #endif } +#endif /* CONFIG_UPDATE_TFTP */
static int update_fit_getparams(const void *fit, int noffset, ulong *addr, ulong *fladdr, ulong *size) @@ -228,6 +230,7 @@ static int update_fit_getparams(const void *fit, int noffset, ulong *addr, return 0; }
+#ifdef CONFIG_UPDATE_TFTP int update_tftp(ulong addr, char *interface, char *devstring) { char *filename, *env_addr, *fit_image_name; @@ -322,8 +325,9 @@ got_update_file: } } else if (fit_image_check_type(fit, noffset, IH_TYPE_FIRMWARE)) { - ret = dfu_tftp_write(fit_image_name, update_addr, - update_size, interface, devstring); + ret = dfu_write_by_name(fit_image_name, update_addr, + update_size, interface, + devstring); if (ret) return ret; } @@ -333,3 +337,70 @@ next_node:
return ret; } +#endif + +#ifdef CONFIG_UPDATE_FIT +/** + * fit_update - update storage with FIT image + * @fit: Pointer to FIT image + * + * Update firmware on storage using FIT image as input. + * The storage area to be update will be identified by the name + * in FIT and matching it to "dfu_alt_info" variable. + * + * Return: 0 - on success, non-zero - otherwise + */ +int fit_update(const void *fit) +{ + char *fit_image_name; + ulong update_addr, update_fladdr, update_size; + int images_noffset, ndepth, noffset; + int ret = 0; + + if (!fit) + return -EINVAL; + + if (!fit_check_format((void *)fit)) { + printf("Bad FIT format of the update file, aborting auto-update\n"); + return -EINVAL; + } + + /* process updates */ + images_noffset = fdt_path_offset(fit, FIT_IMAGES_PATH); + + ndepth = 0; + noffset = fdt_next_node(fit, images_noffset, &ndepth); + while (noffset >= 0 && ndepth > 0) { + if (ndepth != 1) + goto next_node; + + fit_image_name = (char *)fit_get_name(fit, noffset, NULL); + printf("Processing update '%s' :", fit_image_name); + + if (!fit_image_verify(fit, noffset)) { + printf("Error: invalid update hash, aborting\n"); + ret = 1; + goto next_node; + } + + printf("\n"); + if (update_fit_getparams(fit, noffset, &update_addr, + &update_fladdr, &update_size)) { + printf("Error: can't get update parameters, aborting\n"); + ret = 1; + goto next_node; + } + + if (fit_image_check_type(fit, noffset, IH_TYPE_FIRMWARE)) { + ret = dfu_write_by_name(fit_image_name, update_addr, + update_size, NULL, NULL); + if (ret) + return ret; + } +next_node: + noffset = fdt_next_node(fit, noffset, &ndepth); + } + + return ret; +} +#endif diff --git a/include/image.h b/include/image.h index ad81dad44429..1f2c53339d51 100644 --- a/include/image.h +++ b/include/image.h @@ -1573,4 +1573,16 @@ struct fit_loadable_tbl { .handler = _handler, \ }
+/** + * fit_update - update storage with FIT image + * @fit: Pointer to FIT image + * + * Update firmware on storage using FIT image as input. + * The storage area to be update will be indentified by the name + * in FIT and matching it to "dfu_alt_info" variable. + * + * Return: 0 on success, non-zero otherwise + */ +int fit_update(const void *fit); + #endif /* __IMAGE_H__ */

On 10.07.20 03:25, AKASHI Takahiro wrote:
The main purpose of this patch is to separate a generic interface for updating firmware using DFU drivers from "auto-update" via tftp.
This function will also be used in implementing UEFI capsule update in a later commit.
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org
common/Kconfig | 16 +++++++++++ common/Makefile | 2 +- common/update.c | 75 +++++++++++++++++++++++++++++++++++++++++++++++-- include/image.h | 12 ++++++++ 4 files changed, 102 insertions(+), 3 deletions(-)
diff --git a/common/Kconfig b/common/Kconfig index 2d86dd7e63c6..a51d2a7b9d2a 100644 --- a/common/Kconfig +++ b/common/Kconfig @@ -985,9 +985,16 @@ endmenu
menu "Update support"
+config UPDATE_COMMON
- bool
- default n
- select DFU_ALT
config UPDATE_TFTP bool "Auto-update using fitImage via TFTP" depends on FIT
- depends on DFU
- select UPDATE_COMMON help This option allows performing update of NOR with data in fitImage sent via TFTP boot.
@@ -1002,6 +1009,15 @@ config UPDATE_TFTP_MSEC_MAX default 100 depends on UPDATE_TFTP
+config UPDATE_FIT
- bool "Firmware update using fitImage"
- depends on FIT
- depends on DFU
- select UPDATE_COMMON
- help
This option allows performing update of DFU-capable storage with
data in fitImage.
config ANDROID_AB bool "Android A/B updates" default n diff --git a/common/Makefile b/common/Makefile index 2e7a090588d9..c238db8108e7 100644 --- a/common/Makefile +++ b/common/Makefile @@ -53,7 +53,7 @@ obj-$(CONFIG_LCD_ROTATION) += lcd_console_rotation.o obj-$(CONFIG_LCD_DT_SIMPLEFB) += lcd_simplefb.o obj-$(CONFIG_LYNXKDI) += lynxkdi.o obj-$(CONFIG_MENU) += menu.o -obj-$(CONFIG_UPDATE_TFTP) += update.o +obj-$(CONFIG_UPDATE_COMMON) += update.o obj-$(CONFIG_DFU_TFTP) += update.o obj-$(CONFIG_USB_KEYBOARD) += usb_kbd.o obj-$(CONFIG_CMDLINE) += cli_readline.o cli_simple.o diff --git a/common/update.c b/common/update.c index 3547dc68bf7c..a8151383fae7 100644 --- a/common/update.c +++ b/common/update.c @@ -24,6 +24,7 @@ #include <errno.h> #include <mtd/cfi_flash.h>
+#ifdef CONFIG_UPDATE_TFTP /* env variable holding the location of the update file */ #define UPDATE_FILE_ENV "updatefile"
@@ -211,6 +212,7 @@ static int update_flash(ulong addr_source, ulong addr_first, ulong size) return 1; #endif } +#endif /* CONFIG_UPDATE_TFTP */
static int update_fit_getparams(const void *fit, int noffset, ulong *addr, ulong *fladdr, ulong *size) @@ -228,6 +230,7 @@ static int update_fit_getparams(const void *fit, int noffset, ulong *addr, return 0; }
+#ifdef CONFIG_UPDATE_TFTP int update_tftp(ulong addr, char *interface, char *devstring) { char *filename, *env_addr, *fit_image_name; @@ -322,8 +325,9 @@ got_update_file: } } else if (fit_image_check_type(fit, noffset, IH_TYPE_FIRMWARE)) {
ret = dfu_tftp_write(fit_image_name, update_addr,
update_size, interface, devstring);
ret = dfu_write_by_name(fit_image_name, update_addr,
update_size, interface,
}devstring); if (ret) return ret;
@@ -333,3 +337,70 @@ next_node:
return ret; } +#endif
+#ifdef CONFIG_UPDATE_FIT +/**
- fit_update - update storage with FIT image
- @fit: Pointer to FIT image
- Update firmware on storage using FIT image as input.
- The storage area to be update will be identified by the name
- in FIT and matching it to "dfu_alt_info" variable.
- Return: 0 - on success, non-zero - otherwise
- */
+int fit_update(const void *fit)
With patch
cmd: drop fitupd command https://patchwork.ozlabs.org/project/uboot/patch/20200617090903.9268-1-xypro...
the fitupd command is removed.
Lukasz has to put the patch into a pull request, yet
Once that patch has been inserted only the automatic fit updates will use code in common/update.c. This code is not called by the 'dfu tftp' command.
The function fit_update() that you create should be considered a part of the dfu framework and should be placed in drivers/dfu/dfu.c. Please, give it a name starting with dfu_.
This way there is no need to change the Kconfig for common/update.c as update.c remains dedicated to automatic updates via tFTP.
Best regards
Heinrich
+{
- char *fit_image_name;
- ulong update_addr, update_fladdr, update_size;
- int images_noffset, ndepth, noffset;
- int ret = 0;
- if (!fit)
return -EINVAL;
- if (!fit_check_format((void *)fit)) {
printf("Bad FIT format of the update file, aborting auto-update\n");
return -EINVAL;
- }
- /* process updates */
- images_noffset = fdt_path_offset(fit, FIT_IMAGES_PATH);
- ndepth = 0;
- noffset = fdt_next_node(fit, images_noffset, &ndepth);
- while (noffset >= 0 && ndepth > 0) {
if (ndepth != 1)
goto next_node;
fit_image_name = (char *)fit_get_name(fit, noffset, NULL);
printf("Processing update '%s' :", fit_image_name);
if (!fit_image_verify(fit, noffset)) {
printf("Error: invalid update hash, aborting\n");
ret = 1;
goto next_node;
}
printf("\n");
if (update_fit_getparams(fit, noffset, &update_addr,
&update_fladdr, &update_size)) {
printf("Error: can't get update parameters, aborting\n");
ret = 1;
goto next_node;
}
if (fit_image_check_type(fit, noffset, IH_TYPE_FIRMWARE)) {
ret = dfu_write_by_name(fit_image_name, update_addr,
update_size, NULL, NULL);
if (ret)
return ret;
}
+next_node:
noffset = fdt_next_node(fit, noffset, &ndepth);
- }
- return ret;
+} +#endif diff --git a/include/image.h b/include/image.h index ad81dad44429..1f2c53339d51 100644 --- a/include/image.h +++ b/include/image.h @@ -1573,4 +1573,16 @@ struct fit_loadable_tbl { .handler = _handler, \ }
+/**
- fit_update - update storage with FIT image
- @fit: Pointer to FIT image
- Update firmware on storage using FIT image as input.
- The storage area to be update will be indentified by the name
- in FIT and matching it to "dfu_alt_info" variable.
- Return: 0 on success, non-zero otherwise
- */
+int fit_update(const void *fit);
#endif /* __IMAGE_H__ */

Heinrich,
On Fri, Jul 10, 2020 at 06:24:45PM +0200, Heinrich Schuchardt wrote:
On 10.07.20 03:25, AKASHI Takahiro wrote:
The main purpose of this patch is to separate a generic interface for updating firmware using DFU drivers from "auto-update" via tftp.
This function will also be used in implementing UEFI capsule update in a later commit.
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org
common/Kconfig | 16 +++++++++++ common/Makefile | 2 +- common/update.c | 75 +++++++++++++++++++++++++++++++++++++++++++++++-- include/image.h | 12 ++++++++ 4 files changed, 102 insertions(+), 3 deletions(-)
diff --git a/common/Kconfig b/common/Kconfig index 2d86dd7e63c6..a51d2a7b9d2a 100644 --- a/common/Kconfig +++ b/common/Kconfig @@ -985,9 +985,16 @@ endmenu
menu "Update support"
+config UPDATE_COMMON
- bool
- default n
- select DFU_ALT
config UPDATE_TFTP bool "Auto-update using fitImage via TFTP" depends on FIT
- depends on DFU
- select UPDATE_COMMON help This option allows performing update of NOR with data in fitImage sent via TFTP boot.
@@ -1002,6 +1009,15 @@ config UPDATE_TFTP_MSEC_MAX default 100 depends on UPDATE_TFTP
+config UPDATE_FIT
- bool "Firmware update using fitImage"
- depends on FIT
- depends on DFU
- select UPDATE_COMMON
- help
This option allows performing update of DFU-capable storage with
data in fitImage.
config ANDROID_AB bool "Android A/B updates" default n diff --git a/common/Makefile b/common/Makefile index 2e7a090588d9..c238db8108e7 100644 --- a/common/Makefile +++ b/common/Makefile @@ -53,7 +53,7 @@ obj-$(CONFIG_LCD_ROTATION) += lcd_console_rotation.o obj-$(CONFIG_LCD_DT_SIMPLEFB) += lcd_simplefb.o obj-$(CONFIG_LYNXKDI) += lynxkdi.o obj-$(CONFIG_MENU) += menu.o -obj-$(CONFIG_UPDATE_TFTP) += update.o +obj-$(CONFIG_UPDATE_COMMON) += update.o obj-$(CONFIG_DFU_TFTP) += update.o obj-$(CONFIG_USB_KEYBOARD) += usb_kbd.o obj-$(CONFIG_CMDLINE) += cli_readline.o cli_simple.o diff --git a/common/update.c b/common/update.c index 3547dc68bf7c..a8151383fae7 100644 --- a/common/update.c +++ b/common/update.c @@ -24,6 +24,7 @@ #include <errno.h> #include <mtd/cfi_flash.h>
+#ifdef CONFIG_UPDATE_TFTP /* env variable holding the location of the update file */ #define UPDATE_FILE_ENV "updatefile"
@@ -211,6 +212,7 @@ static int update_flash(ulong addr_source, ulong addr_first, ulong size) return 1; #endif } +#endif /* CONFIG_UPDATE_TFTP */
static int update_fit_getparams(const void *fit, int noffset, ulong *addr, ulong *fladdr, ulong *size) @@ -228,6 +230,7 @@ static int update_fit_getparams(const void *fit, int noffset, ulong *addr, return 0; }
+#ifdef CONFIG_UPDATE_TFTP int update_tftp(ulong addr, char *interface, char *devstring) { char *filename, *env_addr, *fit_image_name; @@ -322,8 +325,9 @@ got_update_file: } } else if (fit_image_check_type(fit, noffset, IH_TYPE_FIRMWARE)) {
ret = dfu_tftp_write(fit_image_name, update_addr,
update_size, interface, devstring);
ret = dfu_write_by_name(fit_image_name, update_addr,
update_size, interface,
}devstring); if (ret) return ret;
@@ -333,3 +337,70 @@ next_node:
return ret; } +#endif
+#ifdef CONFIG_UPDATE_FIT +/**
- fit_update - update storage with FIT image
- @fit: Pointer to FIT image
- Update firmware on storage using FIT image as input.
- The storage area to be update will be identified by the name
- in FIT and matching it to "dfu_alt_info" variable.
- Return: 0 - on success, non-zero - otherwise
- */
+int fit_update(const void *fit)
With patch
cmd: drop fitupd command https://patchwork.ozlabs.org/project/uboot/patch/20200617090903.9268-1-xypro...
the fitupd command is removed.
Lukasz has to put the patch into a pull request, yet
Once that patch has been inserted only the automatic fit updates will use code in common/update.c. This code is not called by the 'dfu tftp' command.
? In cmd/dfu.c, ===8<=== #ifdef CONFIG_DFU_OVER_TFTP if (!strcmp(argv[1], "tftp")) return update_tftp(value, interface, devstring); #endif ===>8===
The function fit_update() that you create should be considered a part of the dfu framework and should be placed in drivers/dfu/dfu.c. Please, give it a name starting with dfu_.
Please read my code carefully. There are some helper functions common to update_tftp() and fit_update(). There is a good reason why I put this code in this file.
-Takahiro Akashi
This way there is no need to change the Kconfig for common/update.c as update.c remains dedicated to automatic updates via tFTP.
Best regards
Heinrich
+{
- char *fit_image_name;
- ulong update_addr, update_fladdr, update_size;
- int images_noffset, ndepth, noffset;
- int ret = 0;
- if (!fit)
return -EINVAL;
- if (!fit_check_format((void *)fit)) {
printf("Bad FIT format of the update file, aborting auto-update\n");
return -EINVAL;
- }
- /* process updates */
- images_noffset = fdt_path_offset(fit, FIT_IMAGES_PATH);
- ndepth = 0;
- noffset = fdt_next_node(fit, images_noffset, &ndepth);
- while (noffset >= 0 && ndepth > 0) {
if (ndepth != 1)
goto next_node;
fit_image_name = (char *)fit_get_name(fit, noffset, NULL);
printf("Processing update '%s' :", fit_image_name);
if (!fit_image_verify(fit, noffset)) {
printf("Error: invalid update hash, aborting\n");
ret = 1;
goto next_node;
}
printf("\n");
if (update_fit_getparams(fit, noffset, &update_addr,
&update_fladdr, &update_size)) {
printf("Error: can't get update parameters, aborting\n");
ret = 1;
goto next_node;
}
if (fit_image_check_type(fit, noffset, IH_TYPE_FIRMWARE)) {
ret = dfu_write_by_name(fit_image_name, update_addr,
update_size, NULL, NULL);
if (ret)
return ret;
}
+next_node:
noffset = fdt_next_node(fit, noffset, &ndepth);
- }
- return ret;
+} +#endif diff --git a/include/image.h b/include/image.h index ad81dad44429..1f2c53339d51 100644 --- a/include/image.h +++ b/include/image.h @@ -1573,4 +1573,16 @@ struct fit_loadable_tbl { .handler = _handler, \ }
+/**
- fit_update - update storage with FIT image
- @fit: Pointer to FIT image
- Update firmware on storage using FIT image as input.
- The storage area to be update will be indentified by the name
- in FIT and matching it to "dfu_alt_info" variable.
- Return: 0 on success, non-zero otherwise
- */
+int fit_update(const void *fit);
#endif /* __IMAGE_H__ */

On 15.07.20 06:28, AKASHI Takahiro wrote:
Heinrich,
On Fri, Jul 10, 2020 at 06:24:45PM +0200, Heinrich Schuchardt wrote:
On 10.07.20 03:25, AKASHI Takahiro wrote:
The main purpose of this patch is to separate a generic interface for updating firmware using DFU drivers from "auto-update" via tftp.
This function will also be used in implementing UEFI capsule update in a later commit.
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org
common/Kconfig | 16 +++++++++++ common/Makefile | 2 +- common/update.c | 75 +++++++++++++++++++++++++++++++++++++++++++++++-- include/image.h | 12 ++++++++ 4 files changed, 102 insertions(+), 3 deletions(-)
diff --git a/common/Kconfig b/common/Kconfig index 2d86dd7e63c6..a51d2a7b9d2a 100644 --- a/common/Kconfig +++ b/common/Kconfig @@ -985,9 +985,16 @@ endmenu
menu "Update support"
+config UPDATE_COMMON
- bool
- default n
- select DFU_ALT
config UPDATE_TFTP bool "Auto-update using fitImage via TFTP" depends on FIT
- depends on DFU
- select UPDATE_COMMON help This option allows performing update of NOR with data in fitImage sent via TFTP boot.
@@ -1002,6 +1009,15 @@ config UPDATE_TFTP_MSEC_MAX default 100 depends on UPDATE_TFTP
+config UPDATE_FIT
- bool "Firmware update using fitImage"
- depends on FIT
- depends on DFU
- select UPDATE_COMMON
- help
This option allows performing update of DFU-capable storage with
data in fitImage.
config ANDROID_AB bool "Android A/B updates" default n diff --git a/common/Makefile b/common/Makefile index 2e7a090588d9..c238db8108e7 100644 --- a/common/Makefile +++ b/common/Makefile @@ -53,7 +53,7 @@ obj-$(CONFIG_LCD_ROTATION) += lcd_console_rotation.o obj-$(CONFIG_LCD_DT_SIMPLEFB) += lcd_simplefb.o obj-$(CONFIG_LYNXKDI) += lynxkdi.o obj-$(CONFIG_MENU) += menu.o -obj-$(CONFIG_UPDATE_TFTP) += update.o +obj-$(CONFIG_UPDATE_COMMON) += update.o obj-$(CONFIG_DFU_TFTP) += update.o obj-$(CONFIG_USB_KEYBOARD) += usb_kbd.o obj-$(CONFIG_CMDLINE) += cli_readline.o cli_simple.o diff --git a/common/update.c b/common/update.c index 3547dc68bf7c..a8151383fae7 100644 --- a/common/update.c +++ b/common/update.c @@ -24,6 +24,7 @@ #include <errno.h> #include <mtd/cfi_flash.h>
+#ifdef CONFIG_UPDATE_TFTP /* env variable holding the location of the update file */ #define UPDATE_FILE_ENV "updatefile"
@@ -211,6 +212,7 @@ static int update_flash(ulong addr_source, ulong addr_first, ulong size) return 1; #endif } +#endif /* CONFIG_UPDATE_TFTP */
static int update_fit_getparams(const void *fit, int noffset, ulong *addr, ulong *fladdr, ulong *size) @@ -228,6 +230,7 @@ static int update_fit_getparams(const void *fit, int noffset, ulong *addr, return 0; }
+#ifdef CONFIG_UPDATE_TFTP int update_tftp(ulong addr, char *interface, char *devstring) { char *filename, *env_addr, *fit_image_name; @@ -322,8 +325,9 @@ got_update_file: } } else if (fit_image_check_type(fit, noffset, IH_TYPE_FIRMWARE)) {
ret = dfu_tftp_write(fit_image_name, update_addr,
update_size, interface, devstring);
ret = dfu_write_by_name(fit_image_name, update_addr,
update_size, interface,
}devstring); if (ret) return ret;
@@ -333,3 +337,70 @@ next_node:
return ret; } +#endif
+#ifdef CONFIG_UPDATE_FIT +/**
- fit_update - update storage with FIT image
- @fit: Pointer to FIT image
- Update firmware on storage using FIT image as input.
- The storage area to be update will be identified by the name
- in FIT and matching it to "dfu_alt_info" variable.
- Return: 0 - on success, non-zero - otherwise
- */
+int fit_update(const void *fit)
With patch
cmd: drop fitupd command https://patchwork.ozlabs.org/project/uboot/patch/20200617090903.9268-1-xypro...
the fitupd command is removed.
Lukasz has to put the patch into a pull request, yet
Once that patch has been inserted only the automatic fit updates will use code in common/update.c. This code is not called by the 'dfu tftp' command.
? In cmd/dfu.c, ===8<=== #ifdef CONFIG_DFU_OVER_TFTP if (!strcmp(argv[1], "tftp")) return update_tftp(value, interface, devstring); #endif ===>8===
The function fit_update() that you create should be considered a part of the dfu framework and should be placed in drivers/dfu/dfu.c. Please, give it a name starting with dfu_.
Please read my code carefully. There are some helper functions common to update_tftp() and fit_update(). There is a good reason why I put this code in this file.
common/update.c should be completely dropped from U-Boot because it is not used by any board and the dfu command can be used instead.
The DFU API is described in include/dfu.h. This is sufficient to implement an update. I agree that it may make sense to provide functions at a higher abstraction level. These functions should be in drivers/dfu/.
Best regards
Heinrich
-Takahiro Akashi
This way there is no need to change the Kconfig for common/update.c as update.c remains dedicated to automatic updates via tFTP.
Best regards
Heinrich

Heinrich,
On Wed, Jul 15, 2020 at 09:07:26AM +0200, Heinrich Schuchardt wrote:
On 15.07.20 06:28, AKASHI Takahiro wrote:
Heinrich,
On Fri, Jul 10, 2020 at 06:24:45PM +0200, Heinrich Schuchardt wrote:
On 10.07.20 03:25, AKASHI Takahiro wrote:
The main purpose of this patch is to separate a generic interface for updating firmware using DFU drivers from "auto-update" via tftp.
This function will also be used in implementing UEFI capsule update in a later commit.
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org
common/Kconfig | 16 +++++++++++ common/Makefile | 2 +- common/update.c | 75 +++++++++++++++++++++++++++++++++++++++++++++++-- include/image.h | 12 ++++++++ 4 files changed, 102 insertions(+), 3 deletions(-)
diff --git a/common/Kconfig b/common/Kconfig index 2d86dd7e63c6..a51d2a7b9d2a 100644 --- a/common/Kconfig +++ b/common/Kconfig @@ -985,9 +985,16 @@ endmenu
menu "Update support"
+config UPDATE_COMMON
- bool
- default n
- select DFU_ALT
config UPDATE_TFTP bool "Auto-update using fitImage via TFTP" depends on FIT
- depends on DFU
- select UPDATE_COMMON help This option allows performing update of NOR with data in fitImage sent via TFTP boot.
@@ -1002,6 +1009,15 @@ config UPDATE_TFTP_MSEC_MAX default 100 depends on UPDATE_TFTP
+config UPDATE_FIT
- bool "Firmware update using fitImage"
- depends on FIT
- depends on DFU
- select UPDATE_COMMON
- help
This option allows performing update of DFU-capable storage with
data in fitImage.
config ANDROID_AB bool "Android A/B updates" default n diff --git a/common/Makefile b/common/Makefile index 2e7a090588d9..c238db8108e7 100644 --- a/common/Makefile +++ b/common/Makefile @@ -53,7 +53,7 @@ obj-$(CONFIG_LCD_ROTATION) += lcd_console_rotation.o obj-$(CONFIG_LCD_DT_SIMPLEFB) += lcd_simplefb.o obj-$(CONFIG_LYNXKDI) += lynxkdi.o obj-$(CONFIG_MENU) += menu.o -obj-$(CONFIG_UPDATE_TFTP) += update.o +obj-$(CONFIG_UPDATE_COMMON) += update.o obj-$(CONFIG_DFU_TFTP) += update.o obj-$(CONFIG_USB_KEYBOARD) += usb_kbd.o obj-$(CONFIG_CMDLINE) += cli_readline.o cli_simple.o diff --git a/common/update.c b/common/update.c index 3547dc68bf7c..a8151383fae7 100644 --- a/common/update.c +++ b/common/update.c @@ -24,6 +24,7 @@ #include <errno.h> #include <mtd/cfi_flash.h>
+#ifdef CONFIG_UPDATE_TFTP /* env variable holding the location of the update file */ #define UPDATE_FILE_ENV "updatefile"
@@ -211,6 +212,7 @@ static int update_flash(ulong addr_source, ulong addr_first, ulong size) return 1; #endif } +#endif /* CONFIG_UPDATE_TFTP */
static int update_fit_getparams(const void *fit, int noffset, ulong *addr, ulong *fladdr, ulong *size) @@ -228,6 +230,7 @@ static int update_fit_getparams(const void *fit, int noffset, ulong *addr, return 0; }
+#ifdef CONFIG_UPDATE_TFTP int update_tftp(ulong addr, char *interface, char *devstring) { char *filename, *env_addr, *fit_image_name; @@ -322,8 +325,9 @@ got_update_file: } } else if (fit_image_check_type(fit, noffset, IH_TYPE_FIRMWARE)) {
ret = dfu_tftp_write(fit_image_name, update_addr,
update_size, interface, devstring);
ret = dfu_write_by_name(fit_image_name, update_addr,
update_size, interface,
}devstring); if (ret) return ret;
@@ -333,3 +337,70 @@ next_node:
return ret; } +#endif
+#ifdef CONFIG_UPDATE_FIT +/**
- fit_update - update storage with FIT image
- @fit: Pointer to FIT image
- Update firmware on storage using FIT image as input.
- The storage area to be update will be identified by the name
- in FIT and matching it to "dfu_alt_info" variable.
- Return: 0 - on success, non-zero - otherwise
- */
+int fit_update(const void *fit)
With patch
cmd: drop fitupd command https://patchwork.ozlabs.org/project/uboot/patch/20200617090903.9268-1-xypro...
the fitupd command is removed.
Lukasz has to put the patch into a pull request, yet
Once that patch has been inserted only the automatic fit updates will use code in common/update.c. This code is not called by the 'dfu tftp' command.
? In cmd/dfu.c, ===8<=== #ifdef CONFIG_DFU_OVER_TFTP if (!strcmp(argv[1], "tftp")) return update_tftp(value, interface, devstring); #endif ===>8===
The function fit_update() that you create should be considered a part of the dfu framework and should be placed in drivers/dfu/dfu.c. Please, give it a name starting with dfu_.
Please read my code carefully. There are some helper functions common to update_tftp() and fit_update(). There is a good reason why I put this code in this file.
common/update.c should be completely dropped from U-Boot because it is not used by any board and the dfu command can be used instead.
As I said in my previous email, update_tftp() is used in cmd/dfu.c.
The DFU API is described in include/dfu.h. This is sufficient to implement an update. I agree that it may make sense to provide functions at a higher abstraction level. These functions should be in drivers/dfu/.
Which part of my code do you think is 'functions at a higher abstraction level'?
-Takahiro Akashi
Best regards
Heinrich
-Takahiro Akashi
This way there is no need to change the Kconfig for common/update.c as update.c remains dedicated to automatic updates via tFTP.
Best regards
Heinrich

This variable will be utilized to enumerate all dfu entities for UEFI capsule firmware update in a later commit.
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org --- drivers/dfu/dfu.c | 2 +- include/dfu.h | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-)
diff --git a/drivers/dfu/dfu.c b/drivers/dfu/dfu.c index a298c2c43999..501a60b34400 100644 --- a/drivers/dfu/dfu.c +++ b/drivers/dfu/dfu.c @@ -18,7 +18,7 @@ #include <linux/list.h> #include <linux/compiler.h>
-static LIST_HEAD(dfu_list); +LIST_HEAD(dfu_list); static int dfu_alt_num; static int alt_num_cnt; static struct hash_algo *dfu_hash_algo; diff --git a/include/dfu.h b/include/dfu.h index 94b0a9e68317..e9af9503d685 100644 --- a/include/dfu.h +++ b/include/dfu.h @@ -158,6 +158,9 @@ struct dfu_entity { unsigned int inited:1; };
+struct list_head; +extern struct list_head dfu_list; + #ifdef CONFIG_SET_DFU_ALT_INFO /** * set_dfu_alt_info() - set dfu_alt_info environment variable

On 10.07.20 03:25, AKASHI Takahiro wrote:
This variable will be utilized to enumerate all dfu entities for UEFI capsule firmware update in a later commit.
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org
drivers/dfu/dfu.c | 2 +- include/dfu.h | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-)
diff --git a/drivers/dfu/dfu.c b/drivers/dfu/dfu.c index a298c2c43999..501a60b34400 100644 --- a/drivers/dfu/dfu.c +++ b/drivers/dfu/dfu.c @@ -18,7 +18,7 @@ #include <linux/list.h> #include <linux/compiler.h>
-static LIST_HEAD(dfu_list); +LIST_HEAD(dfu_list); static int dfu_alt_num; static int alt_num_cnt; static struct hash_algo *dfu_hash_algo; diff --git a/include/dfu.h b/include/dfu.h index 94b0a9e68317..e9af9503d685 100644 --- a/include/dfu.h +++ b/include/dfu.h @@ -158,6 +158,9 @@ struct dfu_entity { unsigned int inited:1; };
+struct list_head; +extern struct list_head dfu_list;
#ifdef CONFIG_SET_DFU_ALT_INFO /**
- set_dfu_alt_info() - set dfu_alt_info environment variable
Why would you need to export this list if the current implementation of the dfu command does not need to export it?
Please, use the existing DFU API functions instead of accessing a private member of the implementation.
Best regards
Heinrich

Heinrich,
On Fri, Jul 10, 2020 at 06:25:16PM +0200, Heinrich Schuchardt wrote:
On 10.07.20 03:25, AKASHI Takahiro wrote:
This variable will be utilized to enumerate all dfu entities for UEFI capsule firmware update in a later commit.
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org
drivers/dfu/dfu.c | 2 +- include/dfu.h | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-)
diff --git a/drivers/dfu/dfu.c b/drivers/dfu/dfu.c index a298c2c43999..501a60b34400 100644 --- a/drivers/dfu/dfu.c +++ b/drivers/dfu/dfu.c @@ -18,7 +18,7 @@ #include <linux/list.h> #include <linux/compiler.h>
-static LIST_HEAD(dfu_list); +LIST_HEAD(dfu_list); static int dfu_alt_num; static int alt_num_cnt; static struct hash_algo *dfu_hash_algo; diff --git a/include/dfu.h b/include/dfu.h index 94b0a9e68317..e9af9503d685 100644 --- a/include/dfu.h +++ b/include/dfu.h @@ -158,6 +158,9 @@ struct dfu_entity { unsigned int inited:1; };
+struct list_head; +extern struct list_head dfu_list;
#ifdef CONFIG_SET_DFU_ALT_INFO /**
- set_dfu_alt_info() - set dfu_alt_info environment variable
Why would you need to export this list if the current implementation of the dfu command does not need to export it?
Because there is a new 'need', that is enumerating all the definitions of dfu targets for users, i.e. UEFI subsystem.
Please, use the existing DFU API functions instead of accessing a private member of the implementation.
There is no appropriate API at this moment.
-Takahiro Akashi
Best regards
Heinrich

If this option, CONFIG_EFI_SETUP_EARLY, is enabled, the initialisation of UEFI subsystem will be done as part of U-Boot initialisation.
Please note that this option won't be enabled explicitly by users, instead, should be enabled implicitly by other configuration options.
Specifically, this feature will be utilised in implementing capsule-on-disk feature.
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org --- common/board_r.c | 6 ++++++ lib/efi_loader/Kconfig | 4 ++++ 2 files changed, 10 insertions(+)
diff --git a/common/board_r.c b/common/board_r.c index fa57fa9b6993..dcb8c6f79d2f 100644 --- a/common/board_r.c +++ b/common/board_r.c @@ -68,6 +68,9 @@ #if defined(CONFIG_GPIO_HOG) #include <asm/gpio.h> #endif +#ifdef CONFIG_EFI_SETUP_EARLY +#include <efi_loader.h> +#endif
DECLARE_GLOBAL_DATA_PTR;
@@ -858,6 +861,9 @@ static init_fnc_t init_sequence_r[] = { #endif #if defined(CONFIG_M68K) && defined(CONFIG_BLOCK_CACHE) blkcache_init, +#endif +#ifdef CONFIG_EFI_SETUP_EARLY + (init_fnc_t)efi_init_obj_list, #endif run_main_loop, }; diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig index 6c9df3a76763..f0a30a43bc48 100644 --- a/lib/efi_loader/Kconfig +++ b/lib/efi_loader/Kconfig @@ -27,6 +27,10 @@ config EFI_LOADER
if EFI_LOADER
+config EFI_SETUP_EARLY + bool + default n + config EFI_GET_TIME bool "GetTime() runtime service" depends on DM_RTC

On 10.07.20 03:25, AKASHI Takahiro wrote:
If this option, CONFIG_EFI_SETUP_EARLY, is enabled, the initialisation of UEFI subsystem will be done as part of U-Boot initialisation.
Please note that this option won't be enabled explicitly by users, instead, should be enabled implicitly by other configuration options.
Specifically, this feature will be utilised in implementing capsule-on-disk feature.
This breaks access to block devices in UEFI. You cannot do the initialization before the initialization of block devices which currently is not done automatically but requires commands like 'scsi scan' in a script or entered manually.
It might make sense to scan all block devices automatically instead of requiring command entry.
Best regards
Heinrich
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org
common/board_r.c | 6 ++++++ lib/efi_loader/Kconfig | 4 ++++ 2 files changed, 10 insertions(+)
diff --git a/common/board_r.c b/common/board_r.c index fa57fa9b6993..dcb8c6f79d2f 100644 --- a/common/board_r.c +++ b/common/board_r.c @@ -68,6 +68,9 @@ #if defined(CONFIG_GPIO_HOG) #include <asm/gpio.h> #endif +#ifdef CONFIG_EFI_SETUP_EARLY +#include <efi_loader.h> +#endif
DECLARE_GLOBAL_DATA_PTR;
@@ -858,6 +861,9 @@ static init_fnc_t init_sequence_r[] = { #endif #if defined(CONFIG_M68K) && defined(CONFIG_BLOCK_CACHE) blkcache_init, +#endif +#ifdef CONFIG_EFI_SETUP_EARLY
- (init_fnc_t)efi_init_obj_list,
#endif run_main_loop, }; diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig index 6c9df3a76763..f0a30a43bc48 100644 --- a/lib/efi_loader/Kconfig +++ b/lib/efi_loader/Kconfig @@ -27,6 +27,10 @@ config EFI_LOADER
if EFI_LOADER
+config EFI_SETUP_EARLY
- bool
- default n
config EFI_GET_TIME bool "GetTime() runtime service" depends on DM_RTC

Heinrich,
On Fri, Jul 10, 2020 at 06:25:34PM +0200, Heinrich Schuchardt wrote:
On 10.07.20 03:25, AKASHI Takahiro wrote:
If this option, CONFIG_EFI_SETUP_EARLY, is enabled, the initialisation of UEFI subsystem will be done as part of U-Boot initialisation.
Please note that this option won't be enabled explicitly by users, instead, should be enabled implicitly by other configuration options.
Specifically, this feature will be utilised in implementing capsule-on-disk feature.
This breaks access to block devices in UEFI. You cannot do the initialization before the initialization of block devices which currently is not done automatically but requires commands like 'scsi scan' in a script or entered manually.
I know that, but I'm also sure there are some devices detected before executing such kind of commands.
The reason that I added this configuration is to give users alternative choices depending on their systems and 'needs'.
It might make sense to scan all block devices automatically instead of requiring command entry.
I don't object to this idea, but it can break the compatibility with existing config_distro_bootcmd framework.
-Takahiro Akashi
Best regards
Heinrich
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org
common/board_r.c | 6 ++++++ lib/efi_loader/Kconfig | 4 ++++ 2 files changed, 10 insertions(+)
diff --git a/common/board_r.c b/common/board_r.c index fa57fa9b6993..dcb8c6f79d2f 100644 --- a/common/board_r.c +++ b/common/board_r.c @@ -68,6 +68,9 @@ #if defined(CONFIG_GPIO_HOG) #include <asm/gpio.h> #endif +#ifdef CONFIG_EFI_SETUP_EARLY +#include <efi_loader.h> +#endif
DECLARE_GLOBAL_DATA_PTR;
@@ -858,6 +861,9 @@ static init_fnc_t init_sequence_r[] = { #endif #if defined(CONFIG_M68K) && defined(CONFIG_BLOCK_CACHE) blkcache_init, +#endif +#ifdef CONFIG_EFI_SETUP_EARLY
- (init_fnc_t)efi_init_obj_list,
#endif run_main_loop, }; diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig index 6c9df3a76763..f0a30a43bc48 100644 --- a/lib/efi_loader/Kconfig +++ b/lib/efi_loader/Kconfig @@ -27,6 +27,10 @@ config EFI_LOADER
if EFI_LOADER
+config EFI_SETUP_EARLY
- bool
- default n
config EFI_GET_TIME bool "GetTime() runtime service" depends on DM_RTC

In this commit, skeleton functions for capsule-related API's are added under CONFIG_EFI_UPDATE_CAPSULE configuration. Detailed implementation for a specific capsule type will be added in the succeeding patches.
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org --- include/efi_api.h | 12 +++ include/efi_loader.h | 13 +++ lib/efi_loader/Kconfig | 11 +++ lib/efi_loader/Makefile | 1 + lib/efi_loader/efi_capsule.c | 168 +++++++++++++++++++++++++++++++++++ lib/efi_loader/efi_runtime.c | 104 +++++++++++++--------- lib/efi_loader/efi_setup.c | 31 +++++-- 7 files changed, 289 insertions(+), 51 deletions(-) create mode 100644 lib/efi_loader/efi_capsule.c
diff --git a/include/efi_api.h b/include/efi_api.h index 759d9118758e..4be01495f096 100644 --- a/include/efi_api.h +++ b/include/efi_api.h @@ -217,6 +217,10 @@ enum efi_reset_type { #define CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE 0x00020000 #define CAPSULE_FLAGS_INITIATE_RESET 0x00040000
+#define EFI_CAPSULE_REPORT_GUID \ + EFI_GUID(0x39b68c46, 0xf7fb, 0x441b, 0xb6, 0xec, \ + 0x16, 0xb0, 0xf6, 0x98, 0x21, 0xf3) + struct efi_capsule_header { efi_guid_t capsule_guid; u32 header_size; @@ -224,6 +228,14 @@ struct efi_capsule_header { u32 capsule_image_size; } __packed;
+struct efi_capsule_result_variable_header { + u32 variable_total_size; + u32 reserved; + efi_guid_t capsule_guid; + struct efi_time capsule_processed; + efi_status_t capsule_status; +} __packed; + #define EFI_RT_SUPPORTED_GET_TIME 0x0001 #define EFI_RT_SUPPORTED_SET_TIME 0x0002 #define EFI_RT_SUPPORTED_GET_WAKEUP_TIME 0x0004 diff --git a/include/efi_loader.h b/include/efi_loader.h index fc9344c7428e..ce73509200e9 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -208,6 +208,8 @@ extern const efi_guid_t efi_guid_cert_type_pkcs7;
/* GUID of RNG protocol */ extern const efi_guid_t efi_guid_rng_protocol; +/* GUID of capsule update result */ +extern const efi_guid_t efi_guid_capsule_report;
extern unsigned int __efi_runtime_start, __efi_runtime_stop; extern unsigned int __efi_runtime_rel_start, __efi_runtime_rel_stop; @@ -786,6 +788,17 @@ bool efi_secure_boot_enabled(void); bool efi_image_parse(void *efi, size_t len, struct efi_image_regions **regp, WIN_CERTIFICATE **auth, size_t *auth_len);
+/* Capsule update */ +efi_status_t EFIAPI efi_update_capsule( + struct efi_capsule_header **capsule_header_array, + efi_uintn_t capsule_count, + u64 scatter_gather_list); +efi_status_t EFIAPI efi_query_capsule_caps( + struct efi_capsule_header **capsule_header_array, + efi_uintn_t capsule_count, + u64 *maximum_capsule_size, + u32 *reset_type); + #else /* CONFIG_IS_ENABLED(EFI_LOADER) */
/* Without CONFIG_EFI_LOADER we don't have a runtime section, stub it out */ diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig index f0a30a43bc48..2434ea452380 100644 --- a/lib/efi_loader/Kconfig +++ b/lib/efi_loader/Kconfig @@ -47,6 +47,17 @@ config EFI_SET_TIME Provide the SetTime() runtime service at boottime. This service can be used by an EFI application to adjust the real time clock.
+config EFI_HAVE_CAPSULE_SUPPORT + bool + +config EFI_RUNTIME_UPDATE_CAPSULE + bool "UpdateCapsule() runtime service" + default n + select EFI_HAVE_CAPSULE_SUPPORT + help + Select this option if you want to use UpdateCapsule and + QueryCapsuleCapabilities API's. + config EFI_DEVICE_PATH_TO_TEXT bool "Device path to text protocol" default y diff --git a/lib/efi_loader/Makefile b/lib/efi_loader/Makefile index 57c7e66ea0e2..67bc5c9c0907 100644 --- a/lib/efi_loader/Makefile +++ b/lib/efi_loader/Makefile @@ -23,6 +23,7 @@ endif obj-$(CONFIG_CMD_BOOTEFI_HELLO) += helloworld_efi.o obj-y += efi_bootmgr.o obj-y += efi_boottime.o +obj-$(CONFIG_EFI_HAVE_CAPSULE_SUPPORT) += efi_capsule.o obj-y += efi_console.o obj-y += efi_device_path.o obj-$(CONFIG_EFI_DEVICE_PATH_TO_TEXT) += efi_device_path_to_text.o diff --git a/lib/efi_loader/efi_capsule.c b/lib/efi_loader/efi_capsule.c new file mode 100644 index 000000000000..cfe422bee924 --- /dev/null +++ b/lib/efi_loader/efi_capsule.c @@ -0,0 +1,168 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * EFI Capsule + * + * Copyright (c) 2018 Linaro Limited + * Author: AKASHI Takahiro + */ + +#include <common.h> +#include <efi_loader.h> +#include <fs.h> +#include <malloc.h> +#include <sort.h> + +const efi_guid_t efi_guid_capsule_report = EFI_CAPSULE_REPORT_GUID; + +/** + * get_last_capsule - get the last capsule number + * + * Retrieve the number of capsule invoked last time from "CapsuleLast" + * variable. + * + * Return: + * * > 0 - the last capsule number invoked + * * 0xffff - on error, or no capsule invoked yet + */ +static __maybe_unused int get_last_capsule(void) +{ + u16 value16[11]; /* "CapsuleXXXX": non-null-terminated */ + char value[11], *p; + efi_uintn_t size; + unsigned long num = 0xffff; + efi_status_t ret; + + size = sizeof(value16); + ret = EFI_CALL(efi_get_variable(L"CapsuleLast", + &efi_guid_capsule_report, + NULL, &size, value16)); + if (ret != EFI_SUCCESS || u16_strncmp(value16, L"Capsule", 7)) + goto err; + + p = value; + utf16_utf8_strcpy(&p, value16); + strict_strtoul(&value[7], 16, &num); +err: + return (int)num; +} + +/** + * set_capsule_result - set a result variable + * @capsule: Capsule + * @return_status: Return status + * + * Create and set a result variable, "CapsuleXXXX", for the capsule, + * @capsule. + */ +static __maybe_unused +void set_capsule_result(int num, struct efi_capsule_header *capsule, + efi_status_t return_status) +{ + char variable_name[12]; + u16 variable_name16[12], *p; + struct efi_capsule_result_variable_header result; + struct efi_time time; + efi_status_t ret; + + sprintf(variable_name, "Capsule%04X", num); + p = variable_name16; + utf8_utf16_strncpy(&p, variable_name, 11); + result.variable_total_size = sizeof(result); + result.capsule_guid = capsule->capsule_guid; + ret = EFI_CALL((*efi_runtime_services.get_time)(&time, NULL)); + if (ret == EFI_SUCCESS) + memcpy(&result.capsule_processed, &time, sizeof(time)); + else + memset(&result.capsule_processed, 0, sizeof(time)); + result.capsule_status = return_status; + ret = EFI_CALL(efi_set_variable(variable_name16, + &efi_guid_capsule_report, + EFI_VARIABLE_NON_VOLATILE | + EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_RUNTIME_ACCESS, + sizeof(result), &result)); + if (ret) + printf("EFI: creating %s failed\n", variable_name); +} + +/** + * efi_update_capsule() - process information from operating system + * @capsule_header_array: Array of virtual address pointers + * @capsule_count: Number of pointers in capsule_header_array + * @scatter_gather_list: Array of physical address pointers + * + * This function implements the UpdateCapsule() runtime service. + * + * See the Unified Extensible Firmware Interface (UEFI) specification for + * details. + * + * Return: status code + */ +efi_status_t EFIAPI efi_update_capsule( + struct efi_capsule_header **capsule_header_array, + efi_uintn_t capsule_count, + u64 scatter_gather_list) +{ + struct efi_capsule_header *capsule; + unsigned int i; + efi_status_t ret; + + EFI_ENTRY("%p, %lu, %llu\n", capsule_header_array, capsule_count, + scatter_gather_list); + + if (!capsule_count) { + ret = EFI_INVALID_PARAMETER; + goto out; + } + + ret = EFI_SUCCESS; + for (i = 0, capsule = *capsule_header_array; i < capsule_count; + i++, capsule = *(++capsule_header_array)) { + } +out: + return EFI_EXIT(ret); +} + +/** + * efi_query_capsule_caps() - check if capsule is supported + * @capsule_header_array: Array of virtual pointers + * @capsule_count: Number of pointers in capsule_header_array + * @maximum_capsule_size: Maximum capsule size + * @reset_type: Type of reset needed for capsule update + * + * This function implements the QueryCapsuleCapabilities() runtime service. + * + * See the Unified Extensible Firmware Interface (UEFI) specification for + * details. + * + * Return: status code + */ +efi_status_t EFIAPI efi_query_capsule_caps( + struct efi_capsule_header **capsule_header_array, + efi_uintn_t capsule_count, + u64 *maximum_capsule_size, + u32 *reset_type) +{ + struct efi_capsule_header *capsule __attribute__((unused)); + unsigned int i; + efi_status_t ret; + + EFI_ENTRY("%p, %lu, %p, %p\n", capsule_header_array, capsule_count, + maximum_capsule_size, reset_type); + + if (!maximum_capsule_size) { + ret = EFI_INVALID_PARAMETER; + goto out; + } + + *maximum_capsule_size = U64_MAX; + *reset_type = EFI_RESET_COLD; + + ret = EFI_SUCCESS; + for (i = 0, capsule = *capsule_header_array; i < capsule_count; + i++, capsule = *(++capsule_header_array)) { + /* TODO */ + } +out: + return EFI_EXIT(ret); +} diff --git a/lib/efi_loader/efi_runtime.c b/lib/efi_loader/efi_runtime.c index c0bd99b86746..d5a4ac1590ae 100644 --- a/lib/efi_loader/efi_runtime.c +++ b/lib/efi_loader/efi_runtime.c @@ -131,6 +131,10 @@ efi_status_t efi_init_runtime_supported(void) #ifdef CONFIG_EFI_HAVE_RUNTIME_RESET rt_table->runtime_services_supported |= EFI_RT_SUPPORTED_RESET_SYSTEM; #endif + if (IS_ENABLED(CONFIG_EFI_RUNTIME_UPDATE_CAPSULE)) + rt_table->runtime_services_supported |= + (EFI_RT_SUPPORTED_UPDATE_CAPSULE | + EFI_RT_SUPPORTED_QUERY_CAPSULE_CAPABILITIES);
ret = efi_install_configuration_table(&efi_rt_properties_table_guid, rt_table); @@ -411,6 +415,50 @@ efi_status_t __weak __efi_runtime EFIAPI efi_set_time(struct efi_time *time) return EFI_UNSUPPORTED; }
+/** + * efi_update_capsule_unsupported() - process information from operating system + * + * This function implements the UpdateCapsule() runtime service. + * + * See the Unified Extensible Firmware Interface (UEFI) specification for + * details. + * + * @capsule_header_array: pointer to array of virtual pointers + * @capsule_count: number of pointers in capsule_header_array + * @scatter_gather_list: pointer to arry of physical pointers + * Returns: status code + */ +efi_status_t __efi_runtime EFIAPI efi_update_capsule_unsupported( + struct efi_capsule_header **capsule_header_array, + efi_uintn_t capsule_count, + u64 scatter_gather_list) +{ + return EFI_UNSUPPORTED; +} + +/** + * efi_query_capsule_caps_unsupported() - check if capsule is supported + * + * This function implements the QueryCapsuleCapabilities() runtime service. + * + * See the Unified Extensible Firmware Interface (UEFI) specification for + * details. + * + * @capsule_header_array: pointer to array of virtual pointers + * @capsule_count: number of pointers in capsule_header_array + * @maximum_capsule_size: maximum capsule size + * @reset_type: type of reset needed for capsule update + * Returns: status code + */ +efi_status_t __efi_runtime EFIAPI efi_query_capsule_caps_unsupported( + struct efi_capsule_header **capsule_header_array, + efi_uintn_t capsule_count, + u64 *maximum_capsule_size, + u32 *reset_type) +{ + return EFI_UNSUPPORTED; +} + /** * efi_is_runtime_service_pointer() - check if pointer points to runtime table * @@ -434,6 +482,13 @@ void efi_runtime_detach(void) efi_runtime_services.reset_system = efi_reset_system; efi_runtime_services.get_time = efi_get_time; efi_runtime_services.set_time = efi_set_time; + if (IS_ENABLED(CONFIG_EFI_RUNTIME_UPDATE_CAPSULE)) { + /* won't support at runtime */ + efi_runtime_services.update_capsule = + efi_update_capsule_unsupported; + efi_runtime_services.query_capsule_caps = + efi_query_capsule_caps_unsupported; + }
/* Update CRC32 */ efi_update_table_header_crc32(&efi_runtime_services.hdr); @@ -837,50 +892,6 @@ static efi_status_t __efi_runtime EFIAPI efi_unimplemented(void) return EFI_UNSUPPORTED; }
-/** - * efi_update_capsule() - process information from operating system - * - * This function implements the UpdateCapsule() runtime service. - * - * See the Unified Extensible Firmware Interface (UEFI) specification for - * details. - * - * @capsule_header_array: pointer to array of virtual pointers - * @capsule_count: number of pointers in capsule_header_array - * @scatter_gather_list: pointer to arry of physical pointers - * Returns: status code - */ -efi_status_t __efi_runtime EFIAPI efi_update_capsule( - struct efi_capsule_header **capsule_header_array, - efi_uintn_t capsule_count, - u64 scatter_gather_list) -{ - return EFI_UNSUPPORTED; -} - -/** - * efi_query_capsule_caps() - check if capsule is supported - * - * This function implements the QueryCapsuleCapabilities() runtime service. - * - * See the Unified Extensible Firmware Interface (UEFI) specification for - * details. - * - * @capsule_header_array: pointer to array of virtual pointers - * @capsule_count: number of pointers in capsule_header_array - * @maximum_capsule_size: maximum capsule size - * @reset_type: type of reset needed for capsule update - * Returns: status code - */ -efi_status_t __efi_runtime EFIAPI efi_query_capsule_caps( - struct efi_capsule_header **capsule_header_array, - efi_uintn_t capsule_count, - u64 *maximum_capsule_size, - u32 *reset_type) -{ - return EFI_UNSUPPORTED; -} - struct efi_runtime_services __efi_runtime_data efi_runtime_services = { .hdr = { .signature = EFI_RUNTIME_SERVICES_SIGNATURE, @@ -898,7 +909,12 @@ struct efi_runtime_services __efi_runtime_data efi_runtime_services = { .set_variable = efi_set_variable, .get_next_high_mono_count = (void *)&efi_unimplemented, .reset_system = &efi_reset_system_boottime, +#ifdef CONFIG_EFI_RUNTIME_UPDATE_CAPSULE .update_capsule = efi_update_capsule, .query_capsule_caps = efi_query_capsule_caps, +#else + .update_capsule = efi_update_capsule_unsupported, + .query_capsule_caps = efi_query_capsule_caps_unsupported, +#endif .query_variable_info = efi_query_variable_info, }; diff --git a/lib/efi_loader/efi_setup.c b/lib/efi_loader/efi_setup.c index a3b05a4a9b18..7fafb0db9d93 100644 --- a/lib/efi_loader/efi_setup.c +++ b/lib/efi_loader/efi_setup.c @@ -115,6 +115,29 @@ static efi_status_t efi_init_secure_boot(void) } #endif /* CONFIG_EFI_SECURE_BOOT */
+/** + * efi_init_os_indications() - indicate supported features for OS requests + * + * Set the OsIndicationsSupported variable. + * + * Return: status code + */ +static efi_status_t efi_init_os_indications(void) +{ + u64 os_indications_supported = 0; + + if (IS_ENABLED(CONFIG_EFI_HAVE_CAPSULE_SUPPORT)) + os_indications_supported |= + EFI_OS_INDICATIONS_CAPSULE_RESULT_VAR_SUPPORTED; + + return EFI_CALL(efi_set_variable(L"OsIndicationsSupported", + &efi_global_variable_guid, + EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_RUNTIME_ACCESS, + sizeof(os_indications_supported), + &os_indications_supported)); +} + /** * efi_init_obj_list() - Initialize and populate EFI object list * @@ -122,7 +145,6 @@ static efi_status_t efi_init_secure_boot(void) */ efi_status_t efi_init_obj_list(void) { - u64 os_indications_supported = 0; /* None */ efi_status_t ret = EFI_SUCCESS;
/* Initialize once only */ @@ -160,12 +182,7 @@ efi_status_t efi_init_obj_list(void) goto out;
/* Indicate supported features */ - ret = EFI_CALL(efi_set_variable(L"OsIndicationsSupported", - &efi_global_variable_guid, - EFI_VARIABLE_BOOTSERVICE_ACCESS | - EFI_VARIABLE_RUNTIME_ACCESS, - sizeof(os_indications_supported), - &os_indications_supported)); + ret = efi_init_os_indications(); if (ret != EFI_SUCCESS) goto out;

Capsule data can be loaded into the system either via UpdateCapsule runtime service or files on a file system (of boot device). The latter case is called "capsules on disk", and actual updates will take place at the next boot time.
In this commit, we will support capsule on disk mechanism.
Please note that U-Boot itself has no notion of "boot device" and all the capsule files to be executed will be detected only if they are located in a specific directory, \EFI\UpdateCapsule, on a device that is identified as a boot device by "BootXXXX" variables.
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org --- common/main.c | 4 + include/efi_loader.h | 16 ++ lib/efi_loader/Kconfig | 22 ++ lib/efi_loader/efi_capsule.c | 510 +++++++++++++++++++++++++++++++++++ lib/efi_loader/efi_setup.c | 8 + 5 files changed, 560 insertions(+)
diff --git a/common/main.c b/common/main.c index 4b3cd302c3e2..ae5bcdb32f8b 100644 --- a/common/main.c +++ b/common/main.c @@ -16,6 +16,7 @@ #include <init.h> #include <net.h> #include <version.h> +#include <efi_loader.h>
static void run_preboot_environment_command(void) { @@ -53,6 +54,9 @@ void main_loop(void) if (IS_ENABLED(CONFIG_UPDATE_TFTP)) update_tftp(0UL, NULL, NULL);
+ if (IS_ENABLED(CONFIG_EFI_CAPSULE_ON_DISK_EARLY)) + efi_launch_capsules(); + s = bootdelay_process(); if (cli_process_fdt(&s)) cli_secure_boot_cmd(s); diff --git a/include/efi_loader.h b/include/efi_loader.h index ce73509200e9..ad6dd9f49546 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -799,6 +799,18 @@ efi_status_t EFIAPI efi_query_capsule_caps( u64 *maximum_capsule_size, u32 *reset_type);
+#ifdef CONFIG_EFI_CAPSULE_ON_DISK +#define EFI_CAPSULE_DIR L"\EFI\UpdateCapsule\" + +/* Hook at initialization */ +efi_status_t efi_launch_capsules(void); +#else +static inline efi_status_t efi_launch_capsules(void) +{ + return EFI_SUCCESS; +} +#endif /* CONFIG_EFI_CAPSULE_ON_DISK */ + #else /* CONFIG_IS_ENABLED(EFI_LOADER) */
/* Without CONFIG_EFI_LOADER we don't have a runtime section, stub it out */ @@ -815,6 +827,10 @@ static inline void efi_set_bootdev(const char *dev, const char *devnr, const char *path) { } 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) +{ + return EFI_SUCCESS; +}
#endif /* CONFIG_IS_ENABLED(EFI_LOADER) */
diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig index 2434ea452380..c26a0cef4957 100644 --- a/lib/efi_loader/Kconfig +++ b/lib/efi_loader/Kconfig @@ -58,6 +58,28 @@ config EFI_RUNTIME_UPDATE_CAPSULE Select this option if you want to use UpdateCapsule and QueryCapsuleCapabilities API's.
+config EFI_CAPSULE_ON_DISK + bool "Enable capsule-on-disk support" + select EFI_HAVE_CAPSULE_SUPPORT + default n + help + Select this option if you want to use capsule-on-disk feature, + that is, capsules can be fetched and executed from files + under a specific directory on UEFI system partition instead of + via UpdateCapsule API. + +config EFI_CAPSULE_ON_DISK_EARLY + bool "Initiate capsule-on-disk at U-Boot boottime" + depends on EFI_CAPSULE_ON_DISK + default y + select EFI_SETUP_EARLY + help + Normally, without this option enabled, capsules will be + executed only at the first time of invoking one of efi command. + If this option is enabled, capsules will be enforced to be + executed as part of U-Boot initialisation so that they will + surely take place whatever is set to distro_bootcmd. + config EFI_DEVICE_PATH_TO_TEXT bool "Device path to text protocol" default y diff --git a/lib/efi_loader/efi_capsule.c b/lib/efi_loader/efi_capsule.c index cfe422bee924..2a224546dd11 100644 --- a/lib/efi_loader/efi_capsule.c +++ b/lib/efi_loader/efi_capsule.c @@ -10,10 +10,16 @@ #include <efi_loader.h> #include <fs.h> #include <malloc.h> +#include <mapmem.h> #include <sort.h>
const efi_guid_t efi_guid_capsule_report = EFI_CAPSULE_REPORT_GUID;
+#ifdef CONFIG_EFI_CAPSULE_ON_DISK +/* for file system access */ +static struct efi_file_handle *bootdev_root; +#endif + /** * get_last_capsule - get the last capsule number * @@ -166,3 +172,507 @@ efi_status_t EFIAPI efi_query_capsule_caps( out: return EFI_EXIT(ret); } + +#ifdef CONFIG_EFI_CAPSULE_ON_DISK +/** + * get_dp_device - retrieve a device path from boot variable + * @boot_var: Boot variable name + * @device_dp Device path + * + * Retrieve a device patch from boot variable, @boot_var. + * + * Return: status code + */ +static efi_status_t get_dp_device(u16 *boot_var, + struct efi_device_path **device_dp) +{ + void *buf = NULL; + efi_uintn_t size; + struct efi_load_option lo; + struct efi_device_path *file_dp; + efi_status_t ret; + + size = 0; + ret = EFI_CALL(efi_get_variable(boot_var, &efi_global_variable_guid, + NULL, &size, NULL)); + if (ret == EFI_BUFFER_TOO_SMALL) { + buf = malloc(size); + if (!buf) + return EFI_OUT_OF_RESOURCES; + ret = EFI_CALL(efi_get_variable(boot_var, + &efi_global_variable_guid, + NULL, &size, buf)); + } + if (ret != EFI_SUCCESS) + return ret; + + efi_deserialize_load_option(&lo, buf, &size); + + if (lo.attributes & LOAD_OPTION_ACTIVE) { + efi_dp_split_file_path(lo.file_path, device_dp, &file_dp); + efi_free_pool(file_dp); + + ret = EFI_SUCCESS; + } else { + ret = EFI_NOT_FOUND; + } + + free(buf); + + return ret; +} + +/** + * device_is_present_and_system_part - check if a device exists + * @dp Device path + * + * Check if a device pointed to by the device path, @dp, exists and is + * located in UEFI system partition. + * + * Return: true - yes, false - no + */ +static bool device_is_present_and_system_part(struct efi_device_path *dp) +{ + efi_handle_t handle; + + handle = efi_dp_find_obj(dp, NULL); + if (!handle) + return false; + + return efi_disk_is_system_part(handle); +} + +/** + * find_boot_device - identify the boot device + * + * Identify the boot device from boot-related variables as UEFI + * specification describes and put its handle into bootdev_root. + * + * Return: status code + */ +static efi_status_t find_boot_device(void) +{ + char boot_var[9]; + u16 boot_var16[9], *p, bootnext, *boot_order = NULL; + efi_uintn_t size; + int i, num; + struct efi_simple_file_system_protocol *volume; + struct efi_device_path *boot_dev = NULL; + efi_status_t ret; + + /* find active boot device in BootNext */ + bootnext = 0; + size = sizeof(bootnext); + ret = EFI_CALL(efi_get_variable(L"BootNext", + (efi_guid_t *)&efi_global_variable_guid, + NULL, &size, &bootnext)); + if (ret == EFI_SUCCESS || ret == EFI_BUFFER_TOO_SMALL) { + /* BootNext does exist here */ + if (ret == EFI_BUFFER_TOO_SMALL || size != sizeof(u16)) { + printf("BootNext must be 16-bit integer\n"); + goto skip; + } + sprintf((char *)boot_var, "Boot%04X", bootnext); + p = boot_var16; + utf8_utf16_strcpy(&p, boot_var); + + ret = get_dp_device(boot_var16, &boot_dev); + if (ret == EFI_SUCCESS) { + if (device_is_present_and_system_part(boot_dev)) { + goto out; + } else { + efi_free_pool(boot_dev); + boot_dev = NULL; + } + } + } + +skip: + /* find active boot device in BootOrder */ + size = 0; + ret = EFI_CALL(efi_get_variable(L"BootOrder", &efi_global_variable_guid, + NULL, &size, NULL)); + if (ret == EFI_BUFFER_TOO_SMALL) { + boot_order = malloc(size); + if (!boot_order) { + ret = EFI_OUT_OF_RESOURCES; + goto out; + } + + ret = EFI_CALL(efi_get_variable( + L"BootOrder", &efi_global_variable_guid, + NULL, &size, boot_order)); + } + if (ret != EFI_SUCCESS) + goto out; + + /* check in higher order */ + num = size / sizeof(u16); + for (i = 0; i < num; i++) { + sprintf((char *)boot_var, "Boot%04X", boot_order[i]); + p = boot_var16; + utf8_utf16_strcpy(&p, boot_var); + ret = get_dp_device(boot_var16, &boot_dev); + if (ret != EFI_SUCCESS) + continue; + + if (device_is_present_and_system_part(boot_dev)) + break; + + efi_free_pool(boot_dev); + boot_dev = NULL; + } +out: + if (boot_dev) { + u16 *path_str; + + path_str = efi_dp_str(boot_dev); + EFI_PRINT("EFI Capsule: bootdev is %ls\n", path_str); + efi_free_pool(path_str); + + volume = efi_fs_from_path(boot_dev); + if (!volume) + ret = EFI_DEVICE_ERROR; + else + ret = EFI_CALL(volume->open_volume(volume, + &bootdev_root)); + efi_free_pool(boot_dev); + } else { + ret = EFI_NOT_FOUND; + } + free(boot_order); + + return ret; +} + +/** + * efi_capsule_scan_dir - traverse a capsule directory in boot device + * @files: Array of file names + * @num: Number of elements in @files + * + * Traverse a capsule directory in boot device. + * Called by initialization code, and returns an array of capsule file + * names in @files. + * + * Return: status code + */ +static efi_status_t efi_capsule_scan_dir(u16 ***files, int *num) +{ + struct efi_file_handle *dirh; + struct efi_file_info *dirent; + efi_uintn_t dirent_size, tmp_size; + int count; + u16 **tmp_files; + efi_status_t ret; + + ret = find_boot_device(); + if (ret == EFI_NOT_FOUND) { + EFI_PRINT("EFI Capsule: bootdev is not set\n"); + *num = 0; + return EFI_SUCCESS; + } else if (ret != EFI_SUCCESS) { + return EFI_DEVICE_ERROR; + } + + /* count capsule files */ + ret = EFI_CALL((*bootdev_root->open)(bootdev_root, &dirh, + EFI_CAPSULE_DIR, + EFI_FILE_MODE_READ, 0)); + if (ret != EFI_SUCCESS) { + *num = 0; + return EFI_SUCCESS; + } + + dirent_size = 256; + dirent = malloc(dirent_size); + if (!dirent) + return EFI_OUT_OF_RESOURCES; + + count = 0; + while (1) { + tmp_size = dirent_size; + ret = EFI_CALL((*dirh->read)(dirh, &tmp_size, dirent)); + if (ret == EFI_BUFFER_TOO_SMALL) { + dirent = realloc(dirent, tmp_size); + if (!dirent) { + ret = EFI_OUT_OF_RESOURCES; + goto err; + } + dirent_size = tmp_size; + ret = EFI_CALL((*dirh->read)(dirh, &tmp_size, dirent)); + } + if (ret != EFI_SUCCESS) + goto err; + if (!tmp_size) + break; + + if (!(dirent->attribute & EFI_FILE_DIRECTORY) && + u16_strcmp(dirent->file_name, L".") && + u16_strcmp(dirent->file_name, L"..")) + count++; + } + + ret = EFI_CALL((*dirh->setpos)(dirh, 0)); + if (ret != EFI_SUCCESS) + goto err; + + /* make a list */ + tmp_files = malloc(count * sizeof(*files)); + if (!tmp_files) { + ret = EFI_OUT_OF_RESOURCES; + goto err; + } + + count = 0; + while (1) { + tmp_size = dirent_size; + ret = EFI_CALL((*dirh->read)(dirh, &tmp_size, dirent)); + if (ret != EFI_SUCCESS) + goto err; + if (!tmp_size) + break; + + if (!(dirent->attribute & EFI_FILE_DIRECTORY) && + u16_strcmp(dirent->file_name, L".") && + u16_strcmp(dirent->file_name, L"..")) + tmp_files[count++] = u16_strdup(dirent->file_name); + } + /* ignore an error */ + EFI_CALL((*dirh->close)(dirh)); + + /* in ascii order */ + /* FIXME: u16 version of strcasecmp */ + qsort(tmp_files, count, sizeof(*tmp_files), + (int (*)(const void *, const void *))strcasecmp); + *files = tmp_files; + *num = count; + ret = EFI_SUCCESS; +err: + free(dirent); + + return ret; +} + +/** + * efi_capsule_read_file - read in a capsule file + * @filename: File name + * @capsule: Pointer to buffer for capsule + * + * Read a capsule file and put its content in @capsule. + * + * Return: status code + */ +static efi_status_t efi_capsule_read_file(u16 *filename, + struct efi_capsule_header **capsule) +{ + struct efi_file_handle *dirh, *fh; + struct efi_file_info *file_info = NULL; + struct efi_capsule_header *buf = NULL; + efi_uintn_t size; + efi_status_t ret; + + ret = EFI_CALL((*bootdev_root->open)(bootdev_root, &dirh, + EFI_CAPSULE_DIR, + EFI_FILE_MODE_READ, 0)); + if (ret != EFI_SUCCESS) + return ret; + ret = EFI_CALL((*dirh->open)(dirh, &fh, filename, + EFI_FILE_MODE_READ, 0)); + /* ignore an error */ + EFI_CALL((*dirh->close)(dirh)); + if (ret != EFI_SUCCESS) + return ret; + + /* file size */ + size = 0; + ret = EFI_CALL((*fh->getinfo)(fh, &efi_file_info_guid, + &size, file_info)); + if (ret == EFI_BUFFER_TOO_SMALL) { + file_info = malloc(size); + if (!file_info) { + ret = EFI_OUT_OF_RESOURCES; + goto err; + } + ret = EFI_CALL((*fh->getinfo)(fh, &efi_file_info_guid, + &size, file_info)); + } + if (ret != EFI_SUCCESS) + goto err; + size = file_info->file_size; + free(file_info); + buf = malloc(size); + if (!buf) { + ret = EFI_OUT_OF_RESOURCES; + goto err; + } + + /* fetch data */ + ret = EFI_CALL((*fh->read)(fh, &size, buf)); + if (ret == EFI_SUCCESS) { + if (size >= buf->capsule_image_size) { + *capsule = buf; + } else { + free(buf); + ret = EFI_INVALID_PARAMETER; + } + } else { + free(buf); + } +err: + EFI_CALL((*fh->close)(fh)); + + return ret; +} + +/** + * efi_capsule_delete_file - delete a capsule file + * @filename: File name + * + * Delete a capsule file from capsule directory. + * + * Return: status code + */ +static efi_status_t efi_capsule_delete_file(u16 *filename) +{ + struct efi_file_handle *dirh, *fh; + efi_status_t ret; + + ret = EFI_CALL((*bootdev_root->open)(bootdev_root, &dirh, + EFI_CAPSULE_DIR, + EFI_FILE_MODE_READ, 0)); + if (ret != EFI_SUCCESS) + return ret; + ret = EFI_CALL((*dirh->open)(dirh, &fh, filename, + EFI_FILE_MODE_READ, 0)); + /* ignore an error */ + EFI_CALL((*dirh->close)(dirh)); + + ret = EFI_CALL((*fh->delete)(fh)); + + return ret; +} + +/** + * efi_capsule_scan_done - reset a scan help function + * + * Reset a scan help function + */ +static void efi_capsule_scan_done(void) +{ + EFI_CALL((*bootdev_root->close)(bootdev_root)); + bootdev_root = NULL; +} + +/** + * arch_efi_load_capsule_drivers - initialize capsule drivers + * + * Architecture or board specific initialization routine + * + * Return: status code + */ +efi_status_t __weak arch_efi_load_capsule_drivers(void) +{ + return EFI_SUCCESS; +} + +/** + * efi_launch_capsule - launch capsules + * + * Launch all the capsules in system at boot time. + * Called by efi init code + * + * Return: status codde + */ +efi_status_t efi_launch_capsules(void) +{ + u64 os_indications; + efi_uintn_t size; + struct efi_capsule_header *capsule = NULL; + u16 **files; + int nfiles, num, i; + char variable_name[12]; + u16 variable_name16[12], *p; + efi_status_t ret; + + size = sizeof(os_indications); + ret = EFI_CALL(efi_get_variable(L"OsIndications", + &efi_global_variable_guid, + NULL, &size, &os_indications)); + if (ret != EFI_SUCCESS || + !(os_indications + & EFI_OS_INDICATIONS_FILE_CAPSULE_DELIVERY_SUPPORTED)) + return EFI_SUCCESS; + + num = get_last_capsule(); + + /* Load capsule drivers */ + ret = arch_efi_load_capsule_drivers(); + if (ret != EFI_SUCCESS) + return ret; + + /* + * Find capsules on disk. + * All the capsules are collected at the beginning because + * capsule files will be removed instantly. + */ + nfiles = 0; + files = NULL; + ret = efi_capsule_scan_dir(&files, &nfiles); + if (ret != EFI_SUCCESS) + return ret; + if (!nfiles) + return EFI_SUCCESS; + + /* Launch capsules */ + for (i = 0, ++num; i < nfiles; i++, num++) { + EFI_PRINT("capsule from %ls ...\n", files[i]); + if (num > 0xffff) + num = 0; + ret = efi_capsule_read_file(files[i], &capsule); + if (ret == EFI_SUCCESS) { + ret = EFI_CALL(efi_update_capsule(&capsule, 1, 0)); + if (ret != EFI_SUCCESS) + printf("EFI Capsule update failed at %ls\n", + files[i]); + + free(capsule); + } else { + printf("EFI: reading capsule failed: %ls\n", + files[i]); + } + /* create CapsuleXXXX */ + set_capsule_result(num, capsule, ret); + + /* delete a capsule either in case of success or failure */ + ret = efi_capsule_delete_file(files[i]); + if (ret != EFI_SUCCESS) + printf("EFI: deleting a capsule file failed: %ls\n", + files[i]); + } + efi_capsule_scan_done(); + + for (i = 0; i < nfiles; i++) + free(files[i]); + free(files); + + /* CapsuleMax */ + p = variable_name16; + utf8_utf16_strncpy(&p, "CapsuleFFFF", 11); + EFI_CALL(efi_set_variable(L"CapsuleMax", &efi_guid_capsule_report, + EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_RUNTIME_ACCESS, + 22, variable_name16)); + + /* CapsuleLast */ + sprintf(variable_name, "Capsule%04X", num - 1); + p = variable_name16; + utf8_utf16_strncpy(&p, variable_name, 11); + EFI_CALL(efi_set_variable(L"CapsuleLast", &efi_guid_capsule_report, + EFI_VARIABLE_NON_VOLATILE | + EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_RUNTIME_ACCESS, + 22, variable_name16)); + + return ret; +} +#endif /* CONFIG_EFI_CAPSULE_ON_DISK */ diff --git a/lib/efi_loader/efi_setup.c b/lib/efi_loader/efi_setup.c index 7fafb0db9d93..3a745c4f84d0 100644 --- a/lib/efi_loader/efi_setup.c +++ b/lib/efi_loader/efi_setup.c @@ -130,6 +130,10 @@ static efi_status_t efi_init_os_indications(void) os_indications_supported |= EFI_OS_INDICATIONS_CAPSULE_RESULT_VAR_SUPPORTED;
+ if (IS_ENABLED(CONFIG_EFI_CAPSULE_ON_DISK)) + os_indications_supported |= + EFI_OS_INDICATIONS_FILE_CAPSULE_DELIVERY_SUPPORTED; + return EFI_CALL(efi_set_variable(L"OsIndicationsSupported", &efi_global_variable_guid, EFI_VARIABLE_BOOTSERVICE_ACCESS | @@ -240,6 +244,10 @@ efi_status_t efi_init_obj_list(void) if (ret != EFI_SUCCESS) goto out;
+ /* Execute capsules after reboot */ + if (IS_ENABLED(CONFIG_EFI_CAPSULE_ON_DISK) && + !IS_ENABLED(CONFIG_EFI_CAPSULE_ON_DISK_EARLY)) + ret = efi_launch_capsules(); out: efi_obj_list_initialized = ret; return ret;

Memory range capsule gives us a way to notify that some memory regions should be left untouched across the next reset. See UEFI specification, section 8.5.3.
Since how we should handle this kind of capsule is totally up to the system, no implementation will be added in this commit.
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org --- include/efi_api.h | 17 +++++++++++++++++ 1 file changed, 17 insertions(+)
diff --git a/include/efi_api.h b/include/efi_api.h index 4be01495f096..eeec08efccb1 100644 --- a/include/efi_api.h +++ b/include/efi_api.h @@ -221,6 +221,10 @@ enum efi_reset_type { EFI_GUID(0x39b68c46, 0xf7fb, 0x441b, 0xb6, 0xec, \ 0x16, 0xb0, 0xf6, 0x98, 0x21, 0xf3)
+#define EFI_MEMORY_RANGE_CAPSULE_GUID \ + EFI_GUID(0xde9f0ec, 0x88b6, 0x428f, 0x97, 0x7a, \ + 0x25, 0x8f, 0x1d, 0xe, 0x5e, 0x72) + struct efi_capsule_header { efi_guid_t capsule_guid; u32 header_size; @@ -236,6 +240,19 @@ struct efi_capsule_result_variable_header { efi_status_t capsule_status; } __packed;
+struct efi_memory_range { + efi_physical_addr_t address; + u64 length; +}; + +struct efi_memory_range_capsule { + struct efi_capsule_header *header; + /* EFI_MEMORY_TYPE: 0x80000000-0xFFFFFFFF */ + enum efi_mem_type os_requested_memory_type; + u64 number_of_memory_ranges; + struct efi_memory_range memory_ranges[]; +} __packed; + #define EFI_RT_SUPPORTED_GET_TIME 0x0001 #define EFI_RT_SUPPORTED_SET_TIME 0x0002 #define EFI_RT_SUPPORTED_GET_WAKEUP_TIME 0x0004

A capsule tagged with the guid, EFI_FIRMWARE_MANAGEMENT_CAPSULE_ID_GUID, is handled as a firmware update object. What efi_update_capsule() basically does is to load any firmware management protocol (or fmp) drivers contained in a capsule, find out an appropriate fmp driver and then invoke its set_image() interface against each binary in a capsule. In this commit, however, loading drivers is not supported.
The result of applying a capsule is set to be stored in "CapsuleXXXX" variable, but its implementation is deferred to a fmp driver.
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org --- include/efi_api.h | 123 +++++++++++++++++++ include/efi_loader.h | 2 + lib/efi_loader/Kconfig | 8 ++ lib/efi_loader/efi_capsule.c | 229 +++++++++++++++++++++++++++++++++++ lib/efi_loader/efi_setup.c | 4 + 5 files changed, 366 insertions(+)
diff --git a/include/efi_api.h b/include/efi_api.h index eeec08efccb1..b062720e8220 100644 --- a/include/efi_api.h +++ b/include/efi_api.h @@ -225,6 +225,10 @@ enum efi_reset_type { EFI_GUID(0xde9f0ec, 0x88b6, 0x428f, 0x97, 0x7a, \ 0x25, 0x8f, 0x1d, 0xe, 0x5e, 0x72)
+#define EFI_FIRMWARE_MANAGEMENT_CAPSULE_ID_GUID \ + EFI_GUID(0x6dcbd5ed, 0xe82d, 0x4c44, 0xbd, 0xa1, \ + 0x71, 0x94, 0x19, 0x9a, 0xd9, 0x2a) + struct efi_capsule_header { efi_guid_t capsule_guid; u32 header_size; @@ -253,6 +257,32 @@ struct efi_memory_range_capsule { struct efi_memory_range memory_ranges[]; } __packed;
+struct efi_firmware_management_capsule_header { + u32 version; + u16 embedded_driver_count; + u16 payload_item_count; + u64 item_offset_list[]; +} __packed; + +struct efi_firmware_management_capsule_image_header { + u32 version; + efi_guid_t update_image_type_id; + u8 update_image_index; + u8 reserved[3]; + u32 update_image_size; + u32 update_vendor_code_size; + u64 update_hardware_instance; +} __packed; + +struct efi_capsule_result_variable_fmp { + u16 version; + u8 payload_index; + u8 update_image_index; + efi_guid_t update_image_type_id; + // u16 capsule_file_name[]; + // u16 capsule_target[]; +} __packed; + #define EFI_RT_SUPPORTED_GET_TIME 0x0001 #define EFI_RT_SUPPORTED_SET_TIME 0x0002 #define EFI_RT_SUPPORTED_GET_WAKEUP_TIME 0x0004 @@ -1806,4 +1836,97 @@ struct efi_signature_list { /* struct efi_signature_data signatures[...][signature_size]; */ } __attribute__((__packed__));
+/* + * Firmware management protocol + */ +#define EFI_FIRMWARE_MANAGEMENT_PROTOCOL_GUID \ + EFI_GUID(0x86c77a67, 0x0b97, 0x4633, 0xa1, 0x87, \ + 0x49, 0x10, 0x4d, 0x06, 0x85, 0xc7) + +#define EFI_IMAGE_ATTRIBUTE_IMAGE_UPDATABLE 0x1 +#define EFI_IMAGE_ATTRIBUTE_RESET_REQUIRED 0x2 +#define EFI_IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED 0x4 +#define EFI_IMAGE_ATTRIBUTE_IN_USE 0x8 +#define EFI_IMAGE_ATTRIBUTE_UEFI_IMAGE 0x10 + +#define EFI_IMAGE_COMPATIBILITY_CHECK_SUPPORTED 0x1 +#define EFI_FIRMWARE_IMAGE_DESCRIPTOR_VERSION 4 + +#define EFI_IMAGE_UPDATABLE_VALID 0x1 +#define EFI_IMAGE_UPDATABLE_INVALID 0x2 +#define EFI_IMAGE_UPDATABLE_INVALID_TYPE 0x4 +#define EFI_IMAGE_UPDATABLE_INVALID_OLLD 0x8 +#define EFI_IMAGE_UPDATABLE_VALID_WITH_VENDOR_CODE 0x10 + +#define EFI_PACKAGE_ATTRIBUTE_VERSION_UPDATABLE 0x1 +#define EFI_PACKAGE_ATTRIBUTE_RESET_REQUIRED 0x2 +#define EFI_PACKAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED 0x4 + +typedef struct efi_firmware_image_dependencies { + u8 dependencies[0]; +} efi_fmp_dep_t; + +struct efi_firmware_image_descriptor { + u8 image_index; + efi_guid_t image_type_id; + u64 image_id; + u16 *image_id_name; + u32 version; + u16 *version_name; + efi_uintn_t size; + u64 attributes_supported; + u64 attributes_setting; + u64 compatibilities; + u32 lowest_supported_image_version; + u32 last_attempt_version; + u32 last_attempt_status; + u64 hardware_instance; + efi_fmp_dep_t *dependencies; +}; + +struct efi_firmware_management_protocol { + efi_status_t (EFIAPI *get_image_info)( + struct efi_firmware_management_protocol *this, + efi_uintn_t *image_info_size, + struct efi_firmware_image_descriptor *image_info, + u32 *descriptor_version, + u8 *descriptor_count, + efi_uintn_t *descriptor_size, + u32 *package_version, + u16 **package_version_name); + efi_status_t (EFIAPI *get_image)( + struct efi_firmware_management_protocol *this, + u8 image_index, + void *image, + efi_uintn_t *image_size); + efi_status_t (EFIAPI *set_image)( + struct efi_firmware_management_protocol *this, + u8 image_index, + const void *image, + efi_uintn_t image_size, + const void *vendor_code, + efi_status_t (*progress)(efi_uintn_t completion), + u16 **abort_reason); + efi_status_t (EFIAPI *check_image)( + struct efi_firmware_management_protocol *this, + u8 image_index, + const void *image, + efi_uintn_t *image_size, + u32 *image_updatable); + efi_status_t (EFIAPI *get_package_info)( + struct efi_firmware_management_protocol *this, + u32 *package_version, + u16 **package_version_name, + u32 *package_version_name_maxlen, + u64 *attributes_supported, + u64 *attributes_setting); + efi_status_t (EFIAPI *set_package_info)( + struct efi_firmware_management_protocol *this, + const void *image, + efi_uintn_t *image_size, + const void *vendor_code, + u32 package_version, + const u16 *package_version_name); +}; + #endif diff --git a/include/efi_loader.h b/include/efi_loader.h index ad6dd9f49546..b76ea7c9e56a 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -210,6 +210,8 @@ extern const efi_guid_t efi_guid_cert_type_pkcs7; extern const efi_guid_t efi_guid_rng_protocol; /* GUID of capsule update result */ extern const efi_guid_t efi_guid_capsule_report; +/* GUID of firmware management protocol */ +extern const efi_guid_t efi_guid_firmware_management_protocol;
extern unsigned int __efi_runtime_start, __efi_runtime_stop; extern unsigned int __efi_runtime_rel_start, __efi_runtime_rel_stop; diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig index c26a0cef4957..635d4c51afe0 100644 --- a/lib/efi_loader/Kconfig +++ b/lib/efi_loader/Kconfig @@ -80,6 +80,14 @@ config EFI_CAPSULE_ON_DISK_EARLY executed as part of U-Boot initialisation so that they will surely take place whatever is set to distro_bootcmd.
+config EFI_CAPSULE_FIRMWARE_MANAGEMENT + bool "Capsule: Firmware Management Protocol" + depends on EFI_HAVE_CAPSULE_SUPPORT + default y + help + Select this option if you want to enable capsule-based + firmware update using Firmware Management Protocol. + config EFI_DEVICE_PATH_TO_TEXT bool "Device path to text protocol" default y diff --git a/lib/efi_loader/efi_capsule.c b/lib/efi_loader/efi_capsule.c index 2a224546dd11..22d15bc4d8cd 100644 --- a/lib/efi_loader/efi_capsule.c +++ b/lib/efi_loader/efi_capsule.c @@ -14,6 +14,10 @@ #include <sort.h>
const efi_guid_t efi_guid_capsule_report = EFI_CAPSULE_REPORT_GUID; +static const efi_guid_t efi_guid_firmware_management_capsule_id = + EFI_FIRMWARE_MANAGEMENT_CAPSULE_ID_GUID; +const efi_guid_t efi_guid_firmware_management_protocol = + EFI_FIRMWARE_MANAGEMENT_PROTOCOL_GUID;
#ifdef CONFIG_EFI_CAPSULE_ON_DISK /* for file system access */ @@ -91,6 +95,211 @@ void set_capsule_result(int num, struct efi_capsule_header *capsule, printf("EFI: creating %s failed\n", variable_name); }
+#ifdef CONFIG_EFI_CAPSULE_FIRMWARE_MANAGEMENT +/** + * efi_fmp_find - search for Firmware Management Protocol drivers + * @image_type: Image type guid + * @instance: Instance number + * @handles: Handles of FMP drivers + * @no_handles: Number of handles + * + * Search for Firmware Management Protocol drivers, matching the image + * type, @image_type and the machine instance, @instance, from the list, + * @handles. + * + * Return: + * * Protocol instance - on success + * * NULL - on failure + */ +static struct efi_firmware_management_protocol * +efi_fmp_find(efi_guid_t *image_type, u64 instance, efi_handle_t *handles, + efi_uintn_t no_handles) +{ + efi_handle_t *handle; + struct efi_firmware_management_protocol *fmp; + struct efi_firmware_image_descriptor *image_info, *desc; + efi_uintn_t info_size, descriptor_size; + u32 descriptor_version; + u8 descriptor_count; + bool found = false; + int i, j; + efi_status_t ret; + + for (i = 0, handle = handles; i < no_handles; i++, handle++) { + ret = EFI_CALL(efi_handle_protocol( + *handle, + &efi_guid_firmware_management_protocol, + (void **)&fmp)); + if (ret != EFI_SUCCESS) + continue; + + /* get device's image info */ + info_size = 0; + image_info = NULL; + descriptor_version = 0; + descriptor_count = 0; + descriptor_size = 0; + ret = EFI_CALL(fmp->get_image_info(fmp, &info_size, + image_info, + &descriptor_version, + &descriptor_count, + &descriptor_size, + NULL, NULL)); + if (ret != EFI_BUFFER_TOO_SMALL) + goto skip; + + image_info = malloc(info_size); + if (!image_info) + goto skip; + + ret = EFI_CALL(fmp->get_image_info(fmp, &info_size, + image_info, + &descriptor_version, + &descriptor_count, + &descriptor_size, + NULL, NULL)); + if (ret != EFI_SUCCESS) + goto skip; + + /* matching */ + for (j = 0, desc = image_info; j < descriptor_count; + j++, desc = (void *)desc + descriptor_size) { + EFI_PRINT("+++ desc[%d] index: %d, name: %ls\n", + j, desc->image_index, desc->image_id_name); + if (!guidcmp(&desc->image_type_id, image_type) && + (!instance || + !desc->hardware_instance || + (descriptor_version >= 3 && + desc->hardware_instance == instance))) + found = true; + } + +skip: + free(image_info); + EFI_CALL(efi_close_protocol( + (efi_handle_t)fmp, + &efi_guid_firmware_management_protocol, + NULL, NULL)); + if (found) + return fmp; + } + + return NULL; +} + +/** + * efi_capsule_update_firmware - update firmware from capsule + * @capsule_data: Capsule + * + * Update firmware, using a capsule, @capsule_data. Loading any FMP + * drivers embedded in a capsule is not supported. + * + * Return: status code + */ +static efi_status_t efi_capsule_update_firmware( + struct efi_capsule_header *capsule_data) +{ + struct efi_firmware_management_capsule_header *capsule; + struct efi_firmware_management_capsule_image_header *image; + size_t capsule_size; + void *image_binary, *vendor_code; + efi_handle_t *handles; + efi_uintn_t no_handles; + int item; + struct efi_firmware_management_protocol *fmp; + u16 *abort_reason; + efi_status_t ret = EFI_SUCCESS; + + /* sanity check */ + if (capsule_data->header_size < sizeof(*capsule) || + capsule_data->header_size >= capsule_data->capsule_image_size) + return EFI_INVALID_PARAMETER; + + capsule = (void *)capsule_data + capsule_data->header_size; + capsule_size = capsule_data->capsule_image_size + - capsule_data->header_size; + + if (capsule->version != 0x00000001) + return EFI_INVALID_PARAMETER; + + /* Drivers */ + /* TODO: support loading drivers */ + + handles = NULL; + ret = EFI_CALL(efi_locate_handle_buffer( + BY_PROTOCOL, + &efi_guid_firmware_management_protocol, + NULL, &no_handles, (efi_handle_t **)&handles)); + if (ret != EFI_SUCCESS) + return EFI_UNSUPPORTED; + + /* Payload */ + for (item = capsule->embedded_driver_count; + item < capsule->embedded_driver_count + + capsule->payload_item_count; item++) { + /* sanity check */ + if ((capsule->item_offset_list[item] + sizeof(*image) + >= capsule_size)) { + printf("EFI: A capsule has not enough size of data\n"); + ret = EFI_INVALID_PARAMETER; + goto out; + } + + image = (void *)capsule + capsule->item_offset_list[item]; + + if (image->version != 0x00000001 && + image->version != 0x00000002 && + image->version != 0x00000003) { + ret = EFI_INVALID_PARAMETER; + goto out; + } + + /* find a device for update firmware */ + /* TODO: should we pass index as well, or nothing but type? */ + fmp = efi_fmp_find(&image->update_image_type_id, + image->version == 0x1 ? 0 : + image->update_hardware_instance, + handles, no_handles); + if (!fmp) { + printf("EFI Capsule: driver not found for firmware type: %pUl, hardware instance: %lld\n", + &image->update_image_type_id, + image->version == 0x1 ? 0 : + image->update_hardware_instance); + ret = EFI_UNSUPPORTED; + goto out; + } + + /* do it */ + image_binary = (void *)image + sizeof(*image); + vendor_code = image_binary + image->update_image_size; + + abort_reason = NULL; + ret = EFI_CALL(fmp->set_image(fmp, image->update_image_index, + image_binary, + image->update_image_size, + vendor_code, NULL, + &abort_reason)); + if (ret != EFI_SUCCESS) { + printf("EFI Capsule: firmware update failed: %ls\n", + abort_reason); + efi_free_pool(abort_reason); + goto out; + } + } + +out: + efi_free_pool(handles); + + return ret; +} +#else +static efi_status_t efi_capsule_update_firmware( + struct efi_capsule_header *capsule_data) +{ + return EFI_UNSUPPORTED; +} +#endif /* CONFIG_EFI_CAPSULE_FIRMWARE_MANAGEMENT */ + /** * efi_update_capsule() - process information from operating system * @capsule_header_array: Array of virtual address pointers @@ -124,6 +333,26 @@ efi_status_t EFIAPI efi_update_capsule( ret = EFI_SUCCESS; for (i = 0, capsule = *capsule_header_array; i < capsule_count; i++, capsule = *(++capsule_header_array)) { + /* sanity check */ + if (capsule->header_size < sizeof(*capsule) || + capsule->capsule_image_size < sizeof(*capsule)) { + printf("EFI: A capsule has not enough size of data\n"); + continue; + } + + EFI_PRINT("Capsule[%d] (guid:%pUl)\n", + i, &capsule->capsule_guid); + if (!guidcmp(&capsule->capsule_guid, + &efi_guid_firmware_management_capsule_id)) { + ret = efi_capsule_update_firmware(capsule); + } else { + printf("EFI: not support capsule type: %pUl\n", + &capsule->capsule_guid); + ret = EFI_UNSUPPORTED; + } + + if (ret != EFI_SUCCESS) + goto out; } out: return EFI_EXIT(ret); diff --git a/lib/efi_loader/efi_setup.c b/lib/efi_loader/efi_setup.c index 3a745c4f84d0..342d83183b4b 100644 --- a/lib/efi_loader/efi_setup.c +++ b/lib/efi_loader/efi_setup.c @@ -134,6 +134,10 @@ static efi_status_t efi_init_os_indications(void) os_indications_supported |= EFI_OS_INDICATIONS_FILE_CAPSULE_DELIVERY_SUPPORTED;
+ if (IS_ENABLED(CONFIG_EFI_CAPSULE_FIRMWARE_MANAGEMENT)) + os_indications_supported |= + EFI_OS_INDICATIONS_FMP_CAPSULE_SUPPORTED; + return EFI_CALL(efi_set_variable(L"OsIndicationsSupported", &efi_global_variable_guid, EFI_VARIABLE_BOOTSERVICE_ACCESS |

In this commit, a very simple firmware management protocol driver is implemented. It will take a common FIT image firmware in a capsule file and apply the data using dfu backend storage drivers via update_fit() interface.
So "dfu_alt_info" variable should be properly set to specify a device and location to be updated. Please read README.dfu.
Fit image is a common file format for firmware update on U-Boot, and this protocol works neatly just as a wrapper for one.
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org --- include/efi_api.h | 4 + include/efi_loader.h | 2 + lib/efi_loader/Kconfig | 11 ++ lib/efi_loader/Makefile | 1 + lib/efi_loader/efi_capsule.c | 12 +- lib/efi_loader/efi_firmware.c | 291 ++++++++++++++++++++++++++++++++++ 6 files changed, 320 insertions(+), 1 deletion(-) create mode 100644 lib/efi_loader/efi_firmware.c
diff --git a/include/efi_api.h b/include/efi_api.h index b062720e8220..c3fc4edbedc4 100644 --- a/include/efi_api.h +++ b/include/efi_api.h @@ -1843,6 +1843,10 @@ struct efi_signature_list { EFI_GUID(0x86c77a67, 0x0b97, 0x4633, 0xa1, 0x87, \ 0x49, 0x10, 0x4d, 0x06, 0x85, 0xc7)
+#define EFI_FIRMWARE_IMAGE_TYPE_UBOOT_FIT_GUID \ + EFI_GUID(0xae13ff2d, 0x9ad4, 0x4e25, 0x9a, 0xc8, \ + 0x6d, 0x80, 0xb3, 0xb2, 0x21, 0x47) + #define EFI_IMAGE_ATTRIBUTE_IMAGE_UPDATABLE 0x1 #define EFI_IMAGE_ATTRIBUTE_RESET_REQUIRED 0x2 #define EFI_IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED 0x4 diff --git a/include/efi_loader.h b/include/efi_loader.h index b76ea7c9e56a..a755ccfb21f9 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -790,6 +790,8 @@ bool efi_secure_boot_enabled(void); bool efi_image_parse(void *efi, size_t len, struct efi_image_regions **regp, WIN_CERTIFICATE **auth, size_t *auth_len);
+extern const struct efi_firmware_management_protocol efi_fmp_fit; + /* Capsule update */ efi_status_t EFIAPI efi_update_capsule( struct efi_capsule_header **capsule_header_array, diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig index 635d4c51afe0..849349649f51 100644 --- a/lib/efi_loader/Kconfig +++ b/lib/efi_loader/Kconfig @@ -88,6 +88,17 @@ config EFI_CAPSULE_FIRMWARE_MANAGEMENT Select this option if you want to enable capsule-based firmware update using Firmware Management Protocol.
+config EFI_CAPSULE_FIRMWARE_FIT + bool "FMP driver for FIT image" + depends on EFI_CAPSULE_FIRMWARE_MANAGEMENT + depends on FIT + select UPDATE_FIT + select DFU + default n + help + Select this option if you want to enable firmware management protocol + driver for FIT image + config EFI_DEVICE_PATH_TO_TEXT bool "Device path to text protocol" default y diff --git a/lib/efi_loader/Makefile b/lib/efi_loader/Makefile index 67bc5c9c0907..ea1fbc4a9deb 100644 --- a/lib/efi_loader/Makefile +++ b/lib/efi_loader/Makefile @@ -24,6 +24,7 @@ obj-$(CONFIG_CMD_BOOTEFI_HELLO) += helloworld_efi.o obj-y += efi_bootmgr.o obj-y += efi_boottime.o obj-$(CONFIG_EFI_HAVE_CAPSULE_SUPPORT) += efi_capsule.o +obj-$(CONFIG_EFI_CAPSULE_FIRMWARE_FIT) += efi_firmware.o obj-y += efi_console.o obj-y += efi_device_path.o obj-$(CONFIG_EFI_DEVICE_PATH_TO_TEXT) += efi_device_path_to_text.o diff --git a/lib/efi_loader/efi_capsule.c b/lib/efi_loader/efi_capsule.c index 22d15bc4d8cd..c2cd1d23f9ce 100644 --- a/lib/efi_loader/efi_capsule.c +++ b/lib/efi_loader/efi_capsule.c @@ -801,7 +801,17 @@ static void efi_capsule_scan_done(void) */ efi_status_t __weak arch_efi_load_capsule_drivers(void) { - return EFI_SUCCESS; + __maybe_unused efi_handle_t handle; + efi_status_t ret = EFI_SUCCESS; + + if (IS_ENABLED(CONFIG_EFI_CAPSULE_FIRMWARE_FIT)) { + handle = NULL; + ret = EFI_CALL(efi_install_multiple_protocol_interfaces( + &handle, &efi_guid_firmware_management_protocol, + &efi_fmp_fit, NULL)); + } + + return ret; }
/** diff --git a/lib/efi_loader/efi_firmware.c b/lib/efi_loader/efi_firmware.c new file mode 100644 index 000000000000..ee98ba683fc5 --- /dev/null +++ b/lib/efi_loader/efi_firmware.c @@ -0,0 +1,291 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * EFI Firmware management protocol + * + * Copyright (c) 2020 Linaro Limited + * Author: AKASHI Takahiro + */ + +#include <common.h> +#include <charset.h> +#include <dfu.h> +#include <efi_loader.h> +#include <image.h> +#include <linux/list.h> + +/* + * This FIRMWARE_MANAGEMENT_PROTOCOL driver provides a firmware update + * method with existing FIT image format, and handles + * - multiple regions of firmware via DFU + * but doesn't support + * - versioning of firmware image + * - package information + */ +const efi_guid_t efi_firmware_image_type_uboot_fit = + EFI_FIRMWARE_IMAGE_TYPE_UBOOT_FIT_GUID; + +/** + * efi_get_dfu_info - return information about the current firmware image + * @this: Protocol instance + * @image_info_size: Size of @image_info + * @image_info: Image information + * @descriptor_version: Pointer to version number + * @descriptor_count: Pointer to number of descriptors + * @descriptor_size: Pointer to descriptor size + * package_version: Package version + * package_version_name: Package version's name + * image_type: Image type GUID + * + * Return information bout the current firmware image in @image_info. + * @image_info will consist of a number of descriptors. + * Each descriptor will be created based on "dfu_alt_info" variable. + * + * Return status code + */ +static efi_status_t efi_get_dfu_info( + efi_uintn_t *image_info_size, + struct efi_firmware_image_descriptor *image_info, + u32 *descriptor_version, + u8 *descriptor_count, + efi_uintn_t *descriptor_size, + u32 *package_version, + u16 **package_version_name, + const efi_guid_t *image_type) +{ + struct dfu_entity *dfu; + size_t names_len, total_size; + int dfu_num, i; + u16 *name, *next; + + dfu_init_env_entities(NULL, NULL); + + names_len = 0; + dfu_num = 0; + list_for_each_entry(dfu, &dfu_list, list) { + names_len += (utf8_utf16_strlen(dfu->name) + 1) * 2; + dfu_num++; + } + if (!dfu_num) { + EFI_PRINT("Probably dfu_alt_info not defined\n"); + *image_info_size = 0; + dfu_free_entities(); + + return EFI_SUCCESS; + } + + total_size = sizeof(*image_info) * dfu_num + names_len; + /* + * we will assume that sizeof(*image_info) * dfu_name + * is, at least, a multiple of 2. So the start address for + * image_id_name would be aligned with 2 bytes. + */ + if (*image_info_size < total_size) { + *image_info_size = total_size; + dfu_free_entities(); + + return EFI_BUFFER_TOO_SMALL; + } + *image_info_size = total_size; + + if (descriptor_version) + *descriptor_version = EFI_FIRMWARE_IMAGE_DESCRIPTOR_VERSION; + if (descriptor_count) + *descriptor_count = dfu_num; + if (descriptor_size) + *descriptor_size = sizeof(*image_info); + if (package_version) + *package_version = 0xffffffff; /* not supported */ + if (package_version_name) + *package_version_name = NULL; /* not supported */ + + /* DFU alt number should correspond to image_index */ + i = 0; + /* Name area starts just after descriptors */ + name = (u16 *)((u8 *)image_info + sizeof(*image_info) * dfu_num); + next = name; + list_for_each_entry(dfu, &dfu_list, list) { + image_info[i].image_index = dfu->alt + 1; + image_info[i].image_type_id = *image_type; + image_info[i].image_id = dfu->alt; + + /* copy the DFU entity name */ + utf8_utf16_strcpy(&next, dfu->name); + image_info[i].image_id_name = name; + name = ++next; + + image_info[i].version = 0; /* not supported */ + image_info[i].version_name = NULL; /* not supported */ + image_info[i].size = 0; + image_info[i].attributes_supported = + EFI_IMAGE_ATTRIBUTE_IMAGE_UPDATABLE; + image_info[i].attributes_setting = + EFI_IMAGE_ATTRIBUTE_IMAGE_UPDATABLE; + image_info[i].lowest_supported_image_version = 0; + image_info[i].last_attempt_version = 0; + image_info[i].last_attempt_status = LAST_ATTEMPT_STATUS_SUCCESS; + image_info[i].hardware_instance = 1; + image_info[i].dependencies = NULL; + + i++; + } + + dfu_free_entities(); + + return EFI_SUCCESS; +} + +/** + * efi_firmware_fit_get_image_info - return information about the current + * firmware image + * @this: Protocol instance + * @image_info_size: Size of @image_info + * @image_info: Image information + * @descriptor_version: Pointer to version number + * @descriptor_count: Pointer to number of descriptors + * @descriptor_size: Pointer to descriptor size + * package_version: Package version + * package_version_name: Package version's name + * + * Return information bout the current firmware image in @image_info. + * @image_info will consist of a number of descriptors. + * Each descriptor will be created based on "dfu_alt_info" variable. + * + * Return status code + */ +static +efi_status_t EFIAPI efi_firmware_fit_get_image_info( + struct efi_firmware_management_protocol *this, + efi_uintn_t *image_info_size, + struct efi_firmware_image_descriptor *image_info, + u32 *descriptor_version, + u8 *descriptor_count, + efi_uintn_t *descriptor_size, + u32 *package_version, + u16 **package_version_name) +{ + efi_status_t ret; + + EFI_ENTRY("%p %p %p %p %p %p %p %p\n", this, + image_info_size, image_info, + descriptor_version, descriptor_count, descriptor_size, + package_version, package_version_name); + + if (!image_info_size || (*image_info_size && !image_info)) + return EFI_EXIT(EFI_INVALID_PARAMETER); + + ret = efi_get_dfu_info(image_info_size, image_info, + descriptor_version, descriptor_count, + descriptor_size, + package_version, package_version_name, + &efi_firmware_image_type_uboot_fit); + + return EFI_EXIT(ret); +} + +/* Place holder; not supported */ +static +efi_status_t EFIAPI efi_firmware_get_image_unsupported( + struct efi_firmware_management_protocol *this, + u8 image_index, + void *image, + efi_uintn_t *image_size) +{ + EFI_ENTRY("%p %d %p %p\n", this, image_index, image, image_size); + + return EFI_EXIT(EFI_UNSUPPORTED); +} + +/** + * efi_firmware_fit_set_image - update the firmware image + * @this: Protocol instance + * @image_index: Image index number + * @image: New image + * @image_size: Size of new image + * @vendor_code: Vendor-specific update policy + * @progress: Function to report the progress of update + * @abort_reason: Pointer to string of abort reason + * + * Update the firmware to new image, using dfu. The new image should + * have FIT image format commonly used in U-Boot. + * @vendor_code, @progress and @abort_reason are not supported. + * + * Return: status code + */ +static +efi_status_t EFIAPI efi_firmware_fit_set_image( + struct efi_firmware_management_protocol *this, + u8 image_index, + const void *image, + efi_uintn_t image_size, + const void *vendor_code, + efi_status_t (*progress)(efi_uintn_t completion), + u16 **abort_reason) +{ + EFI_ENTRY("%p %d %p %ld %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); + + if (fit_update(image)) + return EFI_EXIT(EFI_DEVICE_ERROR); + + return EFI_EXIT(EFI_SUCCESS); +} + +/* Place holder; not supported */ +static +efi_status_t EFIAPI efi_firmware_check_image_unsupported( + struct efi_firmware_management_protocol *this, + u8 image_index, + const void *image, + efi_uintn_t *image_size, + u32 *image_updatable) +{ + EFI_ENTRY("%p %d %p %p %p\n", this, image_index, image, image_size, + image_updatable); + + return EFI_EXIT(EFI_UNSUPPORTED); +} + +/* Place holder; not supported */ +static +efi_status_t EFIAPI efi_firmware_get_package_info_unsupported( + struct efi_firmware_management_protocol *this, + u32 *package_version, + u16 **package_version_name, + u32 *package_version_name_maxlen, + u64 *attributes_supported, + u64 *attributes_setting) +{ + EFI_ENTRY("%p %p %p %p %p %p\n", this, package_version, + package_version_name, package_version_name_maxlen, + attributes_supported, attributes_setting); + + return EFI_EXIT(EFI_UNSUPPORTED); +} + +/* Place holder; not supported */ +static +efi_status_t EFIAPI efi_firmware_set_package_info_unsupported( + struct efi_firmware_management_protocol *this, + const void *image, + efi_uintn_t *image_size, + const void *vendor_code, + u32 package_version, + const u16 *package_version_name) +{ + EFI_ENTRY("%p %p %p %p %x %p\n", this, image, image_size, vendor_code, + package_version, package_version_name); + + return EFI_EXIT(EFI_UNSUPPORTED); +} + +const struct efi_firmware_management_protocol efi_fmp_fit = { + .get_image_info = efi_firmware_fit_get_image_info, + .get_image = efi_firmware_get_image_unsupported, + .set_image = efi_firmware_fit_set_image, + .check_image = efi_firmware_check_image_unsupported, + .get_package_info = efi_firmware_get_package_info_unsupported, + .set_package_info = efi_firmware_set_package_info_unsupported, +};

This function is a variant of dfu_write_by_name() and takes a DFU alt setting number for dfu configuration.
It will be utilised to implement UEFI capsule management protocol for raw image in a later commit.
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org --- drivers/dfu/dfu_alt.c | 47 +++++++++++++++++++++++++++++++++++++++++++ include/dfu.h | 26 +++++++++++++++++++++++- 2 files changed, 72 insertions(+), 1 deletion(-)
diff --git a/drivers/dfu/dfu_alt.c b/drivers/dfu/dfu_alt.c index 5b1b13d7170d..bd846bdefffd 100644 --- a/drivers/dfu/dfu_alt.c +++ b/drivers/dfu/dfu_alt.c @@ -76,3 +76,50 @@ done:
return ret; } + +/** + * dfu_write_by_alt() - write data to DFU medium + * @dfu_alt_num: DFU alt setting number + * @addr: Address of data buffer to write + * @len: Number of bytes + * @interface: Destination DFU medium (e.g. "mmc") + * @devstring: Instance number of destination DFU medium (e.g. "1") + * + * This function is storing data received on DFU supported medium which + * is specified by @dfu_alt_name. + * + * Return: 0 - on success, error code - otherwise + */ +int dfu_write_by_alt(int dfu_alt_num, unsigned int addr, unsigned int len, + char *interface, char *devstring) +{ + struct dfu_entity *dfu; + int ret; + + debug("%s: alt: %d addr: 0x%x len: %d device: %s:%s\n", __func__, + dfu_alt_num, addr, len, interface, devstring); + + ret = dfu_init_env_entities(interface, devstring); + if (ret) + goto done; + + if (dfu_alt_num < 0) { + pr_err("Invalid alt number: %d", dfu_alt_num); + ret = -ENODEV; + goto done; + } + + dfu = dfu_get_entity(dfu_alt_num); + if (!dfu) { + pr_err("DFU entity for alt: %d not found!", dfu_alt_num); + ret = -ENODEV; + goto done; + } + + ret = dfu_write_from_mem_addr(dfu, (void *)(uintptr_t)addr, len); + +done: + dfu_free_entities(); + + return ret; +} diff --git a/include/dfu.h b/include/dfu.h index e9af9503d685..36c80c28f081 100644 --- a/include/dfu.h +++ b/include/dfu.h @@ -496,6 +496,7 @@ static inline int dfu_fill_entity_virt(struct dfu_entity *dfu, char *devstr, } #endif
+#if CONFIG_IS_ENABLED(DFU_ALT) /** * dfu_write_by_name() - write data to DFU medium * @dfu_entity_name: Name of DFU entity to write @@ -509,9 +510,24 @@ static inline int dfu_fill_entity_virt(struct dfu_entity *dfu, char *devstr, * * Return: 0 - on success, error code - otherwise */ -#if CONFIG_IS_ENABLED(DFU_ALT) int dfu_write_by_name(char *dfu_entity_name, unsigned int addr, unsigned int len, char *interface, char *devstring); + +/** + * dfu_write_by_alt() - write data to DFU medium + * @dfu_alt_num: DFU alt setting number + * @addr: Address of data buffer to write + * @len: Number of bytes + * @interface: Destination DFU medium (e.g. "mmc") + * @devstring: Instance number of destination DFU medium (e.g. "1") + * + * This function is storing data received on DFU supported medium which + * is specified by @dfu_alt_name. + * + * Return: 0 - on success, error code - otherwise + */ +int dfu_write_by_alt(int dfu_alt_num, unsigned int addr, unsigned int len, + char *interface, char *devstring); #else static inline int dfu_write_by_name(char *dfu_entity_name, unsigned int addr, unsigned int len, char *interface, @@ -520,6 +536,14 @@ static inline int dfu_write_by_name(char *dfu_entity_name, unsigned int addr, puts("write support for DFU not available!\n"); return -ENOSYS; } + +static inline int dfu_write_by_alt(int dfu_alt_num, unsigned int addr, + unsigned int len, char *interface, + char *devstring) +{ + puts("write support for DFU not available!\n"); + return -ENOSYS; +} #endif
int dfu_add(struct usb_configuration *c);

In this commit, a very simple firmware management protocol driver is implemented. It will take a binary image in a capsule file and apply the data using dfu backend storage drivers via dfu_write_by_alt() interface.
So "dfu_alt_info" variable should be properly set to specify a device and location to be updated. Please read README.dfu.
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org --- include/efi_api.h | 4 + include/efi_loader.h | 1 + lib/efi_loader/Kconfig | 16 +++ lib/efi_loader/Makefile | 2 +- lib/efi_loader/efi_capsule.c | 8 ++ lib/efi_loader/efi_firmware.c | 221 +++++++++++++++++++++++++--------- 6 files changed, 194 insertions(+), 58 deletions(-)
diff --git a/include/efi_api.h b/include/efi_api.h index c3fc4edbedc4..afd7ff922ee4 100644 --- a/include/efi_api.h +++ b/include/efi_api.h @@ -1847,6 +1847,10 @@ struct efi_signature_list { EFI_GUID(0xae13ff2d, 0x9ad4, 0x4e25, 0x9a, 0xc8, \ 0x6d, 0x80, 0xb3, 0xb2, 0x21, 0x47)
+#define EFI_FIRMWARE_IMAGE_TYPE_UBOOT_RAW_GUID \ + EFI_GUID(0xe2bb9c06, 0x70e9, 0x4b14, 0x97, 0xa3, \ + 0x5a, 0x79, 0x13, 0x17, 0x6e, 0x3f) + #define EFI_IMAGE_ATTRIBUTE_IMAGE_UPDATABLE 0x1 #define EFI_IMAGE_ATTRIBUTE_RESET_REQUIRED 0x2 #define EFI_IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED 0x4 diff --git a/include/efi_loader.h b/include/efi_loader.h index a755ccfb21f9..4470613a5abc 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -791,6 +791,7 @@ bool efi_image_parse(void *efi, size_t len, struct efi_image_regions **regp, WIN_CERTIFICATE **auth, size_t *auth_len);
extern const struct efi_firmware_management_protocol efi_fmp_fit; +extern const struct efi_firmware_management_protocol efi_fmp_raw;
/* Capsule update */ efi_status_t EFIAPI efi_update_capsule( diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig index 849349649f51..1a7f3866dafc 100644 --- a/lib/efi_loader/Kconfig +++ b/lib/efi_loader/Kconfig @@ -80,6 +80,10 @@ config EFI_CAPSULE_ON_DISK_EARLY executed as part of U-Boot initialisation so that they will surely take place whatever is set to distro_bootcmd.
+config EFI_CAPSULE_FIRMWARE + bool + default n + config EFI_CAPSULE_FIRMWARE_MANAGEMENT bool "Capsule: Firmware Management Protocol" depends on EFI_HAVE_CAPSULE_SUPPORT @@ -94,11 +98,23 @@ config EFI_CAPSULE_FIRMWARE_FIT depends on FIT select UPDATE_FIT select DFU + select EFI_CAPSULE_FIRMWARE default n help Select this option if you want to enable firmware management protocol driver for FIT image
+config EFI_CAPSULE_FIRMWARE_RAW + bool "FMP driver for raw image" + depends on EFI_CAPSULE_FIRMWARE_MANAGEMENT + select DFU + select DFU_ALT + select EFI_CAPSULE_FIRMWARE + default n + help + Select this option if you want to enable firmware management protocol + driver for raw image + config EFI_DEVICE_PATH_TO_TEXT bool "Device path to text protocol" default y diff --git a/lib/efi_loader/Makefile b/lib/efi_loader/Makefile index ea1fbc4a9deb..08ce3bf56429 100644 --- a/lib/efi_loader/Makefile +++ b/lib/efi_loader/Makefile @@ -24,7 +24,7 @@ obj-$(CONFIG_CMD_BOOTEFI_HELLO) += helloworld_efi.o obj-y += efi_bootmgr.o obj-y += efi_boottime.o obj-$(CONFIG_EFI_HAVE_CAPSULE_SUPPORT) += efi_capsule.o -obj-$(CONFIG_EFI_CAPSULE_FIRMWARE_FIT) += efi_firmware.o +obj-$(CONFIG_EFI_CAPSULE_FIRMWARE) += efi_firmware.o obj-y += efi_console.o obj-y += efi_device_path.o obj-$(CONFIG_EFI_DEVICE_PATH_TO_TEXT) += efi_device_path_to_text.o diff --git a/lib/efi_loader/efi_capsule.c b/lib/efi_loader/efi_capsule.c index c2cd1d23f9ce..360b40011417 100644 --- a/lib/efi_loader/efi_capsule.c +++ b/lib/efi_loader/efi_capsule.c @@ -811,6 +811,14 @@ efi_status_t __weak arch_efi_load_capsule_drivers(void) &efi_fmp_fit, NULL)); }
+ if (IS_ENABLED(CONFIG_EFI_CAPSULE_FIRMWARE_RAW)) { + handle = NULL; + ret = EFI_CALL(efi_install_multiple_protocol_interfaces( + &efi_root, + &efi_guid_firmware_management_protocol, + &efi_fmp_raw, NULL)); + } + return ret; }
diff --git a/lib/efi_loader/efi_firmware.c b/lib/efi_loader/efi_firmware.c index ee98ba683fc5..955dc03fb76f 100644 --- a/lib/efi_loader/efi_firmware.c +++ b/lib/efi_loader/efi_firmware.c @@ -13,16 +13,66 @@ #include <image.h> #include <linux/list.h>
-/* - * This FIRMWARE_MANAGEMENT_PROTOCOL driver provides a firmware update - * method with existing FIT image format, and handles - * - multiple regions of firmware via DFU - * but doesn't support - * - versioning of firmware image - * - package information - */ -const efi_guid_t efi_firmware_image_type_uboot_fit = - EFI_FIRMWARE_IMAGE_TYPE_UBOOT_FIT_GUID; +/* Place holder; not supported */ +static +efi_status_t EFIAPI efi_firmware_get_image_unsupported( + struct efi_firmware_management_protocol *this, + u8 image_index, + void *image, + efi_uintn_t *image_size) +{ + EFI_ENTRY("%p %d %p %p\n", this, image_index, image, image_size); + + return EFI_EXIT(EFI_UNSUPPORTED); +} + +/* Place holder; not supported */ +static +efi_status_t EFIAPI efi_firmware_check_image_unsupported( + struct efi_firmware_management_protocol *this, + u8 image_index, + const void *image, + efi_uintn_t *image_size, + u32 *image_updatable) +{ + EFI_ENTRY("%p %d %p %p %p\n", this, image_index, image, image_size, + image_updatable); + + return EFI_EXIT(EFI_UNSUPPORTED); +} + +/* Place holder; not supported */ +static +efi_status_t EFIAPI efi_firmware_get_package_info_unsupported( + struct efi_firmware_management_protocol *this, + u32 *package_version, + u16 **package_version_name, + u32 *package_version_name_maxlen, + u64 *attributes_supported, + u64 *attributes_setting) +{ + EFI_ENTRY("%p %p %p %p %p %p\n", this, package_version, + package_version_name, package_version_name_maxlen, + attributes_supported, attributes_setting); + + return EFI_EXIT(EFI_UNSUPPORTED); +} + +/* Place holder; not supported */ +static +efi_status_t EFIAPI efi_firmware_set_package_info_unsupported( + struct efi_firmware_management_protocol *this, + const void *image, + efi_uintn_t *image_size, + const void *vendor_code, + u32 package_version, + const u16 *package_version_name) +{ + EFI_ENTRY("%p %p %p %p %x %p\n", this, image, image_size, vendor_code, + package_version, package_version_name); + + return EFI_EXIT(EFI_UNSUPPORTED); +}
/** * efi_get_dfu_info - return information about the current firmware image @@ -134,6 +184,18 @@ static efi_status_t efi_get_dfu_info( return EFI_SUCCESS; }
+#ifdef CONFIG_EFI_CAPSULE_FIRMWARE_FIT +/* + * This FIRMWARE_MANAGEMENT_PROTOCOL driver provides a firmware update + * method with existing FIT image format, and handles + * - multiple regions of firmware via DFU + * but doesn't support + * - versioning of firmware image + * - package information + */ +const efi_guid_t efi_firmware_image_type_uboot_fit = + EFI_FIRMWARE_IMAGE_TYPE_UBOOT_FIT_GUID; + /** * efi_firmware_fit_get_image_info - return information about the current * firmware image @@ -182,19 +244,6 @@ efi_status_t EFIAPI efi_firmware_fit_get_image_info( return EFI_EXIT(ret); }
-/* Place holder; not supported */ -static -efi_status_t EFIAPI efi_firmware_get_image_unsupported( - struct efi_firmware_management_protocol *this, - u8 image_index, - void *image, - efi_uintn_t *image_size) -{ - EFI_ENTRY("%p %d %p %p\n", this, image_index, image, image_size); - - return EFI_EXIT(EFI_UNSUPPORTED); -} - /** * efi_firmware_fit_set_image - update the firmware image * @this: Protocol instance @@ -233,59 +282,117 @@ efi_status_t EFIAPI efi_firmware_fit_set_image( return EFI_EXIT(EFI_SUCCESS); }
-/* Place holder; not supported */ -static -efi_status_t EFIAPI efi_firmware_check_image_unsupported( - struct efi_firmware_management_protocol *this, - u8 image_index, - const void *image, - efi_uintn_t *image_size, - u32 *image_updatable) -{ - EFI_ENTRY("%p %d %p %p %p\n", this, image_index, image, image_size, - image_updatable); +const struct efi_firmware_management_protocol efi_fmp_fit = { + .get_image_info = efi_firmware_fit_get_image_info, + .get_image = efi_firmware_get_image_unsupported, + .set_image = efi_firmware_fit_set_image, + .check_image = efi_firmware_check_image_unsupported, + .get_package_info = efi_firmware_get_package_info_unsupported, + .set_package_info = efi_firmware_set_package_info_unsupported, +}; +#endif /* CONFIG_EFI_CAPSULE_FIRMWARE_FIT */
- return EFI_EXIT(EFI_UNSUPPORTED); -} +#ifdef CONFIG_EFI_CAPSULE_FIRMWARE_RAW +/* + * This FIRMWARE_MANAGEMENT_PROTOCOL driver provides a firmware update + * method with raw data. + */ +const efi_guid_t efi_firmware_image_type_uboot_raw = + EFI_FIRMWARE_IMAGE_TYPE_UBOOT_RAW_GUID;
-/* Place holder; not supported */ +/** + * efi_firmware_raw_get_image_info - return information about the current + firmware image + * @this: Protocol instance + * @image_info_size: Size of @image_info + * @image_info: Image information + * @descriptor_version: Pointer to version number + * @descriptor_count: Pointer to number of descriptors + * @descriptor_size: Pointer to descriptor size + * package_version: Package version + * package_version_name: Package version's name + * + * Return information bout the current firmware image in @image_info. + * @image_info will consist of a number of descriptors. + * Each descriptor will be created based on "dfu_alt_info" variable. + * + * Return status code + */ static -efi_status_t EFIAPI efi_firmware_get_package_info_unsupported( +efi_status_t EFIAPI efi_firmware_raw_get_image_info( struct efi_firmware_management_protocol *this, + efi_uintn_t *image_info_size, + struct efi_firmware_image_descriptor *image_info, + u32 *descriptor_version, + u8 *descriptor_count, + efi_uintn_t *descriptor_size, u32 *package_version, - u16 **package_version_name, - u32 *package_version_name_maxlen, - u64 *attributes_supported, - u64 *attributes_setting) + u16 **package_version_name) { - EFI_ENTRY("%p %p %p %p %p %p\n", this, package_version, - package_version_name, package_version_name_maxlen, - attributes_supported, attributes_setting); + efi_status_t ret = EFI_SUCCESS;
- return EFI_EXIT(EFI_UNSUPPORTED); + EFI_ENTRY("%p %p %p %p %p %p %p %p\n", this, + image_info_size, image_info, + descriptor_version, descriptor_count, descriptor_size, + package_version, package_version_name); + + if (!image_info_size || (*image_info_size && !image_info)) + return EFI_EXIT(EFI_INVALID_PARAMETER); + + ret = efi_get_dfu_info(image_info_size, image_info, + descriptor_version, descriptor_count, + descriptor_size, + package_version, package_version_name, + &efi_firmware_image_type_uboot_raw); + + return EFI_EXIT(ret); }
-/* Place holder; not supported */ +/** + * efi_firmware_raw_set_image - update the firmware image + * @this: Protocol instance + * @image_index: Image index number + * @image: New image + * @image_size: Size of new image + * @vendor_code: Vendor-specific update policy + * @progress: Function to report the progress of update + * @abort_reason: Pointer to string of abort reason + * + * Update the firmware to new image, using dfu. The new image should + * be a single raw image. + * @vendor_code, @progress and @abort_reason are not supported. + * + * Return: status code + */ static -efi_status_t EFIAPI efi_firmware_set_package_info_unsupported( +efi_status_t EFIAPI efi_firmware_raw_set_image( struct efi_firmware_management_protocol *this, + u8 image_index, const void *image, - efi_uintn_t *image_size, + efi_uintn_t image_size, const void *vendor_code, - u32 package_version, - const u16 *package_version_name) + efi_status_t (*progress)(efi_uintn_t completion), + u16 **abort_reason) { - EFI_ENTRY("%p %p %p %p %x %p\n", this, image, image_size, vendor_code, - package_version, package_version_name); + EFI_ENTRY("%p %d %p %ld %p %p %p\n", this, image_index, image, + image_size, vendor_code, progress, abort_reason);
- return EFI_EXIT(EFI_UNSUPPORTED); + if (!image) + return EFI_EXIT(EFI_INVALID_PARAMETER); + + if (dfu_write_by_alt(image_index - 1, (uintptr_t)image, image_size, + NULL, NULL)) + return EFI_EXIT(EFI_DEVICE_ERROR); + + return EFI_EXIT(EFI_SUCCESS); }
-const struct efi_firmware_management_protocol efi_fmp_fit = { - .get_image_info = efi_firmware_fit_get_image_info, +const struct efi_firmware_management_protocol efi_fmp_raw = { + .get_image_info = efi_firmware_raw_get_image_info, .get_image = efi_firmware_get_image_unsupported, - .set_image = efi_firmware_fit_set_image, + .set_image = efi_firmware_raw_set_image, .check_image = efi_firmware_check_image_unsupported, .get_package_info = efi_firmware_get_package_info_unsupported, .set_package_info = efi_firmware_set_package_info_unsupported, }; +#endif /* CONFIG_EFI_CAPSULE_FIRMWARE_RAW */

"efidebug capsule" is more or less a debugging utility. efidebug capsule update: invoke UpdateCapsule against data on memory efidebug capsule show: show a capsule header efidebug capsule result: dump a capsule result variable
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org --- cmd/efidebug.c | 235 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 235 insertions(+)
diff --git a/cmd/efidebug.c b/cmd/efidebug.c index 58018f700cd4..29054d09daba 100644 --- a/cmd/efidebug.c +++ b/cmd/efidebug.c @@ -18,6 +18,228 @@ #include <linux/ctype.h>
#define BS systab.boottime +#define RT systab.runtime + +#ifdef CONFIG_EFI_HAVE_CAPSULE_SUPPORT +/** + * do_efi_capsule_update() - process a capsule update + * + * @cmdtp: Command table + * @flag: Command flag + * @argc: Number of arguments + * @argv: Argument array + * Return: CMD_RET_SUCCESS on success, CMD_RET_RET_FAILURE on failure + * + * Implement efidebug "capsule update" sub-command. + * process a capsule update. + * + * efidebug capsule update [-v] <capsule address> + */ +static int do_efi_capsule_update(struct cmd_tbl *cmdtp, int flag, + int argc, char * const argv[]) +{ + struct efi_capsule_header *capsule; + int verbose = 0; + char *endp; + efi_status_t ret; + + if (argc != 2 && argc != 3) + return CMD_RET_USAGE; + + if (argc == 3) { + if (strcmp(argv[1], "-v")) + return CMD_RET_USAGE; + + verbose = 1; + argc--; + argv++; + } + + capsule = (typeof(capsule))simple_strtoul(argv[1], &endp, 16); + if (endp == argv[1]) { + printf("Invalid address: %s", argv[1]); + return CMD_RET_FAILURE; + } + + if (verbose) { + printf("Capsule guid: %pUl\n", &capsule->capsule_guid); + printf("Capsule flags: 0x%x\n", capsule->flags); + printf("Capsule header size: 0x%x\n", capsule->header_size); + printf("Capsule image size: 0x%x\n", + capsule->capsule_image_size); + } + + ret = EFI_CALL(RT->update_capsule(&capsule, 1, (u64)NULL)); + if (ret) { + printf("Cannot handle a capsule at %p", capsule); + return CMD_RET_FAILURE; + } + + return CMD_RET_SUCCESS; +} + +/** + * do_efi_capsule_show() - show capsule information + * + * @cmdtp: Command table + * @flag: Command flag + * @argc: Number of arguments + * @argv: Argument array + * Return: CMD_RET_SUCCESS on success, CMD_RET_RET_FAILURE on failure + * + * Implement efidebug "capsule show" sub-command. + * show capsule information. + * + * efidebug capsule show <capsule address> + */ +static int do_efi_capsule_show(struct cmd_tbl *cmdtp, int flag, + int argc, char * const argv[]) +{ + struct efi_capsule_header *capsule; + char *endp; + + if (argc != 2) + return CMD_RET_USAGE; + + capsule = (typeof(capsule))simple_strtoul(argv[1], &endp, 16); + if (endp == argv[1]) { + printf("Invalid address: %s", argv[1]); + return CMD_RET_FAILURE; + } + + printf("Capsule guid: %pUl\n", &capsule->capsule_guid); + printf("Capsule flags: 0x%x\n", capsule->flags); + printf("Capsule header size: 0x%x\n", capsule->header_size); + printf("Capsule image size: 0x%x\n", + capsule->capsule_image_size); + + return CMD_RET_SUCCESS; +} + +/** + * do_efi_capsule_res() - show a capsule update result + * + * @cmdtp: Command table + * @flag: Command flag + * @argc: Number of arguments + * @argv: Argument array + * Return: CMD_RET_SUCCESS on success, CMD_RET_RET_FAILURE on failure + * + * Implement efidebug "capsule result" sub-command. + * show a capsule update result. + * If result number is not specified, CapsuleLast will be shown. + * + * efidebug capsule result [<capsule result number>] + */ +static int do_efi_capsule_res(struct cmd_tbl *cmdtp, int flag, + int argc, char * const argv[]) +{ + int capsule_id; + char *endp; + char var_name[12]; + u16 var_name16[12], *p; + efi_guid_t guid; + struct efi_capsule_result_variable_header *result = NULL; + efi_uintn_t size; + efi_status_t ret; + + if (argc != 1 && argc != 2) + return CMD_RET_USAGE; + + guid = efi_guid_capsule_report; + if (argc == 1) { + size = sizeof(var_name16); + ret = EFI_CALL(RT->get_variable(L"CapsuleLast", &guid, NULL, + &size, var_name16)); + if (ret != EFI_SUCCESS) { + if (ret == EFI_NOT_FOUND) + printf("CapsuleLast doesn't exist\n"); + else + printf("Failed to get CapsuleLast\n"); + + return CMD_RET_FAILURE; + } + printf("CapsuleLast is %ls\n", var_name16); + } else { + argc--; + argv++; + + capsule_id = simple_strtoul(argv[0], &endp, 16); + if (capsule_id < 0 || capsule_id > 0xffff) + return CMD_RET_USAGE; + + sprintf(var_name, "Capsule%04X", capsule_id); + p = var_name16; + utf8_utf16_strncpy(&p, var_name, 9); + } + + size = 0; + ret = EFI_CALL(RT->get_variable(var_name16, &guid, NULL, &size, NULL)); + if (ret == EFI_BUFFER_TOO_SMALL) { + result = malloc(size); + ret = EFI_CALL(RT->get_variable(var_name16, &guid, NULL, &size, + result)); + if (ret != EFI_SUCCESS) { + free(result); + printf("Failed to get %ls\n", var_name16); + + return CMD_RET_FAILURE; + } + } + + printf("Result total size: 0x%x\n", result->variable_total_size); + printf("Capsule guid: %pUl\n", &result->capsule_guid); + printf("Time processed: %04d-%02d-%02d %02d:%02d:%02d\n", + result->capsule_processed.year, result->capsule_processed.month, + result->capsule_processed.day, result->capsule_processed.hour, + result->capsule_processed.minute, + result->capsule_processed.second); + printf("Capsule status: 0x%lx\n", result->capsule_status); + + free(result); + + return CMD_RET_SUCCESS; +} + +static struct cmd_tbl cmd_efidebug_capsule_sub[] = { + U_BOOT_CMD_MKENT(update, CONFIG_SYS_MAXARGS, 1, do_efi_capsule_update, + "", ""), + U_BOOT_CMD_MKENT(show, CONFIG_SYS_MAXARGS, 1, do_efi_capsule_show, + "", ""), + U_BOOT_CMD_MKENT(result, CONFIG_SYS_MAXARGS, 1, do_efi_capsule_res, + "", ""), +}; + +/** + * do_efi_capsule() - manage UEFI capsules + * + * @cmdtp: Command table + * @flag: Command flag + * @argc: Number of arguments + * @argv: Argument array + * Return: CMD_RET_SUCCESS on success, + * CMD_RET_USAGE or CMD_RET_RET_FAILURE on failure + * + * Implement efidebug "capsule" sub-command. + */ +static int do_efi_capsule(struct cmd_tbl *cmdtp, int flag, + int argc, char * const argv[]) +{ + struct cmd_tbl *cp; + + if (argc < 2) + return CMD_RET_USAGE; + + argc--; argv++; + + cp = find_cmd_tbl(argv[0], cmd_efidebug_capsule_sub, + ARRAY_SIZE(cmd_efidebug_capsule_sub)); + if (!cp) + return CMD_RET_USAGE; + + return cp->cmd(cmdtp, flag, argc, argv); +} +#endif /* CONFIG_EFI_HAVE_CAPSULE_SUPPORT */
/** * efi_get_device_handle_info() - get information of UEFI device @@ -1230,6 +1452,10 @@ static int do_efi_query_info(struct cmd_tbl *cmdtp, int flag,
static struct cmd_tbl cmd_efidebug_sub[] = { U_BOOT_CMD_MKENT(boot, CONFIG_SYS_MAXARGS, 1, do_efi_boot_opt, "", ""), +#ifdef CONFIG_EFI_HAVE_CAPSULE_SUPPORT + U_BOOT_CMD_MKENT(capsule, CONFIG_SYS_MAXARGS, 1, do_efi_capsule, + "", ""), +#endif U_BOOT_CMD_MKENT(devices, CONFIG_SYS_MAXARGS, 1, do_efi_show_devices, "", ""), U_BOOT_CMD_MKENT(drivers, CONFIG_SYS_MAXARGS, 1, do_efi_show_drivers, @@ -1304,6 +1530,15 @@ static char efidebug_help_text[] = "efidebug boot order [<bootid#1> [<bootid#2> [<bootid#3> [...]]]]\n" " - set/show UEFI boot order\n" "\n" +#ifdef CONFIG_EFI_HAVE_CAPSULE_SUPPORT + "efidebug capsule update [-v] <capsule address>\n" + " - process a capsule\n" + "efidebug capsule show <capsule address>\n" + " - show capsule information\n" + "efidebug capsule result [<capsule result var>]\n" + " - show a capsule update result\n" + "\n" +#endif "efidebug devices\n" " - show UEFI devices\n" "efidebug drivers\n"

This is a utility mainly for test purpose. mkeficapsule -f: create a test capsule file for FIT image firmware
Having said that, you will be able to customize the code to fit your specific requirements for your platform.
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org --- tools/Makefile | 3 + tools/mkeficapsule.c | 237 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 240 insertions(+) create mode 100644 tools/mkeficapsule.c
diff --git a/tools/Makefile b/tools/Makefile index 879c3fd4a748..4833e6842e55 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -217,6 +217,9 @@ hostprogs-$(CONFIG_MIPS) += mips-relocs hostprogs-$(CONFIG_ASN1_COMPILER) += asn1_compiler HOSTCFLAGS_asn1_compiler.o = -idirafter $(srctree)/include
+# TODO: only build this for capsule pytest +hostprogs-$(CONFIG_EFI_HAVE_CAPSULE_SUPPORT) += mkeficapsule + # We build some files with extra pedantic flags to try to minimize things # that won't build on some weird host compiler -- though there are lots of # exceptions for files that aren't complaint. diff --git a/tools/mkeficapsule.c b/tools/mkeficapsule.c new file mode 100644 index 000000000000..93f22192fdb4 --- /dev/null +++ b/tools/mkeficapsule.c @@ -0,0 +1,237 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2018 Linaro Limited + * Author: AKASHI Takahiro + */ + +#include <getopt.h> +#include <malloc.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <linux/types.h> +#include <sys/stat.h> +#include <sys/types.h> + +typedef __u8 u8; +typedef __u16 u16; +typedef __u32 u32; +typedef __u64 u64; +typedef __s16 s16; +typedef __s32 s32; + +#define aligned_u64 __aligned_u64 + +#ifndef __packed +#define __packed __attribute__((packed)) +#endif + +#include <efi.h> +#include <efi_api.h> + +static const char *tool_name = "mkeficapsule"; + +efi_guid_t efi_guid_fm_capsule = EFI_FIRMWARE_MANAGEMENT_CAPSULE_ID_GUID; +efi_guid_t efi_guid_image_type_uboot_fit = + EFI_FIRMWARE_IMAGE_TYPE_UBOOT_FIT_GUID; +efi_guid_t efi_guid_image_type_uboot_raw = + EFI_FIRMWARE_IMAGE_TYPE_UBOOT_RAW_GUID; + +static struct option options[] = { + {"fit", required_argument, NULL, 'f'}, + {"raw", required_argument, NULL, 'r'}, + {"index", required_argument, NULL, 'i'}, + {"instance", required_argument, NULL, 'I'}, + {"version", required_argument, NULL, 'v'}, + {"help", no_argument, NULL, 'h'}, + {NULL, 0, NULL, 0}, +}; + +static void print_usage(void) +{ + printf("Usage: %s [options] <output file>\n" + "Options:\n" + "\t--fit <fit image> new FIT image file\n" + "\t--raw <raw image> new raw image file\n" + "\t--index <index> update image index\n" + "\t--instance <instance> update hardware instance\n" + "\t--version <version> firmware version\n" + "\t--help print a help message\n", + tool_name); +} + +static int create_fwbin(char *path, char *bin, efi_guid_t *guid, + unsigned long version, unsigned long index, + unsigned long instance) +{ + struct efi_capsule_header header; + struct efi_firmware_management_capsule_header capsule; + struct efi_firmware_management_capsule_image_header image; + FILE *f, *g; + struct stat bin_stat; + u8 *data; + size_t size; + +#ifdef DEBUG + printf("For output: %s\n", path); + printf("\tbin: %s\n\ttype: %pUl\n" bin, guid); + printf("\tversion: %ld\n\tindex: %ld\n\tinstance: %ld\n", + version, index, instance); +#endif + + g = fopen(bin, "r"); + if (!g) { + printf("cannot open %s\n", bin); + return -1; + } + if (stat(bin, &bin_stat) < 0) { + printf("cannot determine the size of %s\n", bin); + goto err_1; + } + data = malloc(bin_stat.st_size); + if (!data) { + printf("cannot allocate memory: %lx\n", bin_stat.st_size); + goto err_1; + } + f = fopen(path, "w"); + if (!f) { + printf("cannot open %s\n", path); + goto err_2; + } + header.capsule_guid = efi_guid_fm_capsule; + header.header_size = sizeof(header); + header.flags = CAPSULE_FLAGS_PERSIST_ACROSS_RESET; /* TODO */ + header.capsule_image_size = sizeof(header) + + sizeof(capsule) + sizeof(u64) + + sizeof(image) + + bin_stat.st_size; + + size = fwrite(&header, 1, sizeof(header), f); + if (size < sizeof(header)) { + printf("write failed (%lx)\n", size); + goto err_3; + } + + capsule.version = 0x00000001; + capsule.embedded_driver_count = 0; + capsule.payload_item_count = 1; + capsule.item_offset_list[0] = sizeof(capsule) + sizeof(u64); + size = fwrite(&capsule, 1, sizeof(capsule) + sizeof(u64), f); + if (size < (sizeof(capsule) + sizeof(u64))) { + printf("write failed (%lx)\n", size); + goto err_3; + } + + image.version = version; + memcpy(&image.update_image_type_id, guid, sizeof(*guid)); + image.update_image_index = index; + image.update_image_size = bin_stat.st_size; + image.update_vendor_code_size = 0; /* none */ + image.update_hardware_instance = instance; + + size = fwrite(&image, 1, sizeof(image), f); + if (size < sizeof(image)) { + printf("write failed (%lx)\n", size); + goto err_3; + } + size = fread(data, 1, bin_stat.st_size, g); + if (size < bin_stat.st_size) { + printf("read failed (%lx)\n", size); + goto err_3; + } + size = fwrite(data, 1, bin_stat.st_size, f); + if (size < bin_stat.st_size) { + printf("write failed (%lx)\n", size); + goto err_3; + } + + fclose(f); + fclose(g); + free(data); + + return 0; + +err_3: + fclose(f); +err_2: + free(data); +err_1: + fclose(g); + + return -1; +} + +/* + * Usage: + * $ mkeficapsule -f <firmware binary> <output file> + */ +int main(int argc, char **argv) +{ + char *file; + efi_guid_t *guid; + unsigned long index, instance, version; + int c, idx; + + file = NULL; + guid = NULL; + index = 0; + instance = 0; + version = 0; + for (;;) { + c = getopt_long(argc, argv, "f:r:i:I:v:h", options, &idx); + if (c == -1) + break; + + switch (c) { + case 'f': + if (file) { + printf("Image already specified\n"); + return -1; + } + file = optarg; + guid = &efi_guid_image_type_uboot_fit; + break; + case 'r': + if (file) { + printf("Image already specified\n"); + return -1; + } + file = optarg; + guid = &efi_guid_image_type_uboot_raw; + break; + case 'i': + index = strtoul(optarg, NULL, 0); + break; + case 'I': + instance = strtoul(optarg, NULL, 0); + break; + case 'v': + version = strtoul(optarg, NULL, 0); + break; + case 'h': + print_usage(); + return 0; + } + } + + /* need a output file */ + if (argc != optind + 1) { + print_usage(); + return -1; + } + + /* need a fit image file or raw image file */ + if (!file) { + print_usage(); + return -1; + } + + if (create_fwbin(argv[optind], file, guid, version, index, instance) + < 0) { + printf("Creating firmware capsule failed\n"); + return -1; + } + + return 0; +}

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.
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org --- test/py/tests/test_efi_capsule/conftest.py | 102 ++++++ test/py/tests/test_efi_capsule/defs.py | 24 ++ .../test_efi_capsule/test_capsule_firmware.py | 308 ++++++++++++++++++ .../tests/test_efi_capsule/uboot_bin_env.its | 36 ++ test/py/tests/test_efi_capsule/uboot_env.its | 25 ++ 5 files changed, 495 insertions(+) create mode 100644 test/py/tests/test_efi_capsule/conftest.py create mode 100644 test/py/tests/test_efi_capsule/defs.py create mode 100644 test/py/tests/test_efi_capsule/test_capsule_firmware.py create mode 100644 test/py/tests/test_efi_capsule/uboot_bin_env.its create mode 100644 test/py/tests/test_efi_capsule/uboot_env.its
diff --git a/test/py/tests/test_efi_capsule/conftest.py b/test/py/tests/test_efi_capsule/conftest.py new file mode 100644 index 000000000000..58daf63f1e52 --- /dev/null +++ b/test/py/tests/test_efi_capsule/conftest.py @@ -0,0 +1,102 @@ +# 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 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 + """ + image_path = u_boot_config.persistent_data_dir + image_path = image_path + '/' + EFI_BOOTDEV_IMAGE_NAME + + try: + # create U-Boot environment storage + check_call('dd if=/dev/zero of=./spi.bin bs=1MiB count=16', shell=True) + + # create a disk/partition + check_call('dd if=/dev/zero of=%s bs=1MiB count=%d' + % (image_path, EFI_BOOTDEV_IMAGE_SIZE), shell=True) + check_call( + 'sgdisk %s -n 1:0:+%dMiB -A 1:set:0 -t 1:C12A7328-F81F-11D2-BA4B-00A0C93EC93B' % + (image_path, EFI_BOOTDEV_PART_SIZE), shell=True) + # create a file system + check_call('dd if=/dev/zero of=%s.tmp bs=1MiB count=%d' + % (image_path, EFI_BOOTDEV_PART_SIZE), shell=True) + check_call('mkfs -t %s %s.tmp' + % (EFI_BOOTDEV_FS_TYPE, image_path), shell=True) + check_call('dd if=%s.tmp of=%s bs=1MiB seek=1 count=%d conv=notrunc' + % (image_path, image_path, 1), shell=True) + check_call('rm %s.tmp' % image_path, shell=True) + loop_dev = check_output( + 'sudo losetup -o 1MiB --sizelimit %dMiB --show -f %s | tr -d "\n"' % + (EFI_BOOTDEV_PART_SIZE, image_path), shell=True).decode() + check_call('sudo mkdir -p %s' % MNT_PNT, shell=True) + check_call('sudo mount -t %s -o umask=000 %s %s' + % (EFI_BOOTDEV_FS_TYPE, loop_dev, MNT_PNT), + shell=True) + check_call('mkdir -p %s%s' % (MNT_PNT, CAPSULE_DATA_DIR), shell=True) + check_call( + 'mkdir -p %s%s' % + (MNT_PNT, CAPSULE_INSTALL_DIR), shell=True) + + # Create its' + # one region for U-Boot env in FIT image + check_call( + 'sed -e "s?BINFILE?%s%s/%s?" %s/test/py/tests/test_efi_capsule/uboot_env.its > %s%s/uboot_env.its' % + (MNT_PNT, CAPSULE_DATA_DIR, FW_BIN, u_boot_config.source_dir, MNT_PNT, CAPSULE_DATA_DIR), shell=True) + + # two regions: one for u-boot.bin and the other for u-boot.env + check_call( + 'echo -n u-boot:Old > %s%s/u-boot.bin.old; echo -n u-boot:New > %s%s/u-boot.bin.new; echo -n u-boot-env:Old -> %s%s/u-boot.env.old; echo -n u-boot-env:New > %s%s/u-boot.env.new' % + (MNT_PNT, + CAPSULE_DATA_DIR, + MNT_PNT, + CAPSULE_DATA_DIR, + MNT_PNT, + CAPSULE_DATA_DIR, + MNT_PNT, + CAPSULE_DATA_DIR), + shell=True) + check_call( + 'sed -e "s?BINFILE1?%s%s/u-boot.bin.new?" -e "s?BINFILE2?%s%s/u-boot.env.new?" %s/test/py/tests/test_efi_capsule/uboot_bin_env.its > %s%s/uboot_bin_env.its' % + (MNT_PNT, + CAPSULE_DATA_DIR, + MNT_PNT, + CAPSULE_DATA_DIR, + u_boot_config.source_dir, + MNT_PNT, + CAPSULE_DATA_DIR), + shell=True) + + # cleanup + call('sudo umount %s' % loop_dev, shell=True) + call('sudo losetup -d %s' % loop_dev, shell=True) + + except CalledProcessError as exception: + pytest.skip('Setup failed: %s' % exception.cmd) + return + else: + yield image_path + finally: + call('sudo rm -rf %s' % MNT_PNT, shell=True) + #call('rm -f %s' % image_path, shell=True) + call('rm -f ./spi.bin', shell=True) diff --git a/test/py/tests/test_efi_capsule/defs.py b/test/py/tests/test_efi_capsule/defs.py new file mode 100644 index 000000000000..911883368b27 --- /dev/null +++ b/test/py/tests/test_efi_capsule/defs.py @@ -0,0 +1,24 @@ +# SPDX-License-Identifier: GPL-2.0+ + +# Disk image name +EFI_BOOTDEV_IMAGE_NAME = 'test_efi_capsule.img' + +# Size in MiB +EFI_BOOTDEV_IMAGE_SIZE = 16 +EFI_BOOTDEV_PART_SIZE = 8 + +# Partition file system type +EFI_BOOTDEV_FS_TYPE = 'vfat' + +# Mount Point for set-up +MNT_PNT = '/mnt/test_efi_capsule' + +# Owner guid +GUID = '11111111-2222-3333-4444-123456789abc' + +# Directories +CAPSULE_DATA_DIR = '/EFI/CapsuleTestData' +CAPSULE_INSTALL_DIR = '/EFI/UpdateCapsule' + +# +FW_BIN = 'spi_sf.bin' diff --git a/test/py/tests/test_efi_capsule/test_capsule_firmware.py b/test/py/tests/test_efi_capsule/test_capsule_firmware.py new file mode 100644 index 000000000000..490dc039d81f --- /dev/null +++ b/test/py/tests/test_efi_capsule/test_capsule_firmware.py @@ -0,0 +1,308 @@ +# 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 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('env_is_in_spi_flash') +@pytest.mark.buildconfigspec('cmd_efidebug') +@pytest.mark.buildconfigspec('cmd_fat') +@pytest.mark.buildconfigspec('cmd_nvedit_efi') +@pytest.mark.buildconfigspec('cmd_saveenv') +@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 environment on SPI Flash + """ + # "-T" (or "-D") is required to enable spi flash on sandbox + u_boot_console.restart_uboot_with_flags('-T') + + 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 -nv -bs -rt OsIndications =0x0000000000000004', + 'env set dfu_alt_info sf 0:0=u-boot-env raw 0 0x200000', + 'env set FW_STATUS This is Old environment', + 'env print FW_STATUS', + 'env save']) + assert 'Old environment' in ''.join(output) + + output = u_boot_console.run_command_list([ + 'env set FW_STATUS This is New environment', + 'env export -c 5000000', + 'fatwrite host 0:1 5000000 %s/%s $filesize' + % (CAPSULE_DATA_DIR, FW_BIN), + 'env set -e -guid 39b68c46-f7fb-441b-b6ec-16b0f69821f3 Capsule0000', + 'fatls host 0:1 %s' % CAPSULE_DATA_DIR + ]) + assert '%s' % FW_BIN in ''.join(output) + + # create a capsule file + try: + loop_dev = check_output( + 'sudo losetup -o 1MiB --sizelimit %dMiB --show -f %s | tr -d "\n"' % + (EFI_BOOTDEV_PART_SIZE, disk_img), shell=True).decode() + check_call('sudo mount -t %s -o umask=000 %s %s' + % (EFI_BOOTDEV_FS_TYPE, loop_dev, MNT_PNT), shell=True) + check_call( + '%s/tools/mkimage -f %s%s/uboot_env.its %s%s/uboot_env.itb' % + (u_boot_config.build_dir, + MNT_PNT, + CAPSULE_DATA_DIR, + MNT_PNT, + CAPSULE_DATA_DIR), + shell=True) + check_call( + '%s/tools/mkeficapsule --fit %s%s/uboot_env.itb --version 1 --index 1 %s%s/Test01' % + (u_boot_config.build_dir, + MNT_PNT, + CAPSULE_DATA_DIR, + MNT_PNT, + CAPSULE_INSTALL_DIR), + shell=True) + check_call('ls %s/%s' % (MNT_PNT, CAPSULE_INSTALL_DIR), shell=True) + check_call('sudo umount %s' % loop_dev, shell=True) + check_call('sudo losetup -d %s' % loop_dev, shell=True) + except CalledProcessError as exception: + assert 'failed to create firmware capsule: %s' % exception.cmd + + # reboot + u_boot_console.restart_uboot_with_flags('-T') + + capsule_early = u_boot_config.buildconfig.get( + 'config_efi_capsule_on_disk_early') + if not capsule_early: + with u_boot_console.log.section('Test Case 1-b, after reboot'): + output = u_boot_console.run_command_list([ + 'host bind 0 %s' % disk_img, + 'env print FW_STATUS']) + assert 'Old environment' in ''.join(output) + + output = u_boot_console.run_command('fatls host 0:1 %s' + % CAPSULE_INSTALL_DIR) + assert 'Test01' in output + + # need to run uefi command to initiate capsule handling + output = u_boot_console.run_command( + 'env print -e -all Capsule0000') + + # reboot again + u_boot_console.restart_uboot_with_flags('-T') + + with u_boot_console.log.section('Test Case 1-c, after reboot'): + output = u_boot_console.run_command_list([ + 'host bind 0 %s' % disk_img, + 'env print FW_STATUS']) + assert 'New environment' in ''.join(output) + + output = u_boot_console.run_command( + 'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR) + assert 'Test01' not in output + + def test_efi_capsule_fw2( + self, u_boot_config, u_boot_console, efi_capsule_data): + """ + Test Case 2 - Update U-Boot environment on SPI Flash + but with OsIndications unset + No update should happen + """ + # "-T" (or "-D") is required to enable spi flash on sandbox + u_boot_console.restart_uboot_with_flags('-T') + + 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 OsIndications', + 'env set dfu_alt_info sf 0:0=u-boot-env raw 0 0x200000', + 'env set FW_STATUS This is Old environment', + 'env print FW_STATUS', + 'env save']) + assert 'Old environment' in ''.join(output) + + output = u_boot_console.run_command_list([ + 'env set FW_STATUS This is New environment', + 'env export -c 5000000', + 'fatwrite host 0:1 5000000 %s/%s $filesize' + % (CAPSULE_DATA_DIR, FW_BIN), + 'env set -e -guid 39b68c46-f7fb-441b-b6ec-16b0f69821f3 Capsule0000', + 'fatls host 0:1 %s' % CAPSULE_DATA_DIR + ]) + assert '%s' % FW_BIN in ''.join(output) + + # create a capsule file + try: + loop_dev = check_output( + 'sudo losetup -o 1MiB --sizelimit %dMiB --show -f %s | tr -d "\n"' % + (EFI_BOOTDEV_PART_SIZE, disk_img), shell=True).decode() + check_call('sudo mount -t %s -o umask=000 %s %s' + % (EFI_BOOTDEV_FS_TYPE, loop_dev, MNT_PNT), shell=True) + check_call( + '%s/tools/mkimage -f %s%s/uboot_env.its %s%s/uboot_env.itb' % + (u_boot_config.build_dir, + MNT_PNT, + CAPSULE_DATA_DIR, + MNT_PNT, + CAPSULE_DATA_DIR), + shell=True) + check_call( + '%s/tools/mkeficapsule --fit %s%s/uboot_env.itb --version 1 --index 1 %s%s/Test01' % + (u_boot_config.build_dir, + MNT_PNT, + CAPSULE_DATA_DIR, + MNT_PNT, + CAPSULE_INSTALL_DIR), + shell=True) + check_call('ls %s/%s' % (MNT_PNT, CAPSULE_INSTALL_DIR), shell=True) + check_call('sudo umount %s' % loop_dev, shell=True) + check_call('sudo losetup -d %s' % loop_dev, shell=True) + except CalledProcessError as exception: + assert 'failed to create firmware capsule: %s' % exception.cmd + + # reboot + u_boot_console.restart_uboot_with_flags('-T') + + capsule_early = u_boot_config.buildconfig.get( + 'config_efi_capsule_on_disk_early') + if not capsule_early: + with u_boot_console.log.section('Test Case 2-b, after reboot'): + output = u_boot_console.run_command_list([ + 'host bind 0 %s' % disk_img, + 'env print FW_STATUS']) + assert 'Old environment' in ''.join(output) + + output = u_boot_console.run_command('fatls host 0:1 %s' + % CAPSULE_INSTALL_DIR) + assert 'Test01' in output + + # need to run uefi command to initiate capsule handling + output = u_boot_console.run_command( + 'env print -e -all Capsule0000') + + # reboot again + u_boot_console.restart_uboot_with_flags('-T') + + with u_boot_console.log.section('Test Case 2-c, after reboot'): + output = u_boot_console.run_command_list([ + 'host bind 0 %s' % disk_img, + 'env print FW_STATUS']) + assert 'Old environment' in ''.join(output) + + output = u_boot_console.run_command( + 'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR) + assert 'Test01' in output + + def test_efi_capsule_fw3( + self, u_boot_config, u_boot_console, efi_capsule_data): + """ + Test Case 3 - Update U-Boot and U-Boot environment on SPI Flash + 0x100000-0x150000: U-Boot binary (but dummy) + 0x150000-0x200000: U-Boot environment (but dummy) + """ + # "-T" (or "-D") is required to enable spi flash on sandbox + u_boot_console.restart_uboot_with_flags('-T') + + 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 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) + + # create a capsule file + try: + loop_dev = check_output( + 'sudo losetup -o 1MiB --sizelimit %dMiB --show -f %s | tr -d "\n"' % + (EFI_BOOTDEV_PART_SIZE, disk_img), shell=True).decode() + check_call('sudo mount -t %s -o umask=000 %s %s' + % (EFI_BOOTDEV_FS_TYPE, loop_dev, MNT_PNT), shell=True) + check_call( + '%s/tools/mkimage -f %s%s/uboot_bin_env.its %s%s/uboot_bin_env.itb' % + (u_boot_config.build_dir, + MNT_PNT, + CAPSULE_DATA_DIR, + MNT_PNT, + CAPSULE_DATA_DIR), + shell=True) + check_call( + '%s/tools/mkeficapsule --fit %s%s/uboot_bin_env.itb --version 1 --index 1 %s%s/Test01' % + (u_boot_config.build_dir, + MNT_PNT, + CAPSULE_DATA_DIR, + MNT_PNT, + CAPSULE_INSTALL_DIR), + shell=True) + check_call('ls -l %s/%s' % (MNT_PNT, CAPSULE_INSTALL_DIR), + shell=True) + check_call('sudo umount %s' % loop_dev, shell=True) + check_call('sudo losetup -d %s' % loop_dev, shell=True) + except CalledProcessError as exception: + assert 'failed to create firmware capsule: %s' % exception.cmd + + # reboot + u_boot_console.restart_uboot_with_flags('-T') + + 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: + 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) + + # 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) diff --git a/test/py/tests/test_efi_capsule/uboot_bin_env.its b/test/py/tests/test_efi_capsule/uboot_bin_env.its new file mode 100644 index 000000000000..31e2f8049f9a --- /dev/null +++ b/test/py/tests/test_efi_capsule/uboot_bin_env.its @@ -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"; + }; + }; + }; +}; diff --git a/test/py/tests/test_efi_capsule/uboot_env.its b/test/py/tests/test_efi_capsule/uboot_env.its new file mode 100644 index 000000000000..8e71892c0fae --- /dev/null +++ b/test/py/tests/test_efi_capsule/uboot_env.its @@ -0,0 +1,25 @@ +/* + * 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-env@0 { + description = "U-Boot environment on SPI Flash"; + data = /incbin/("BINFILE"); + compression = "none"; + type = "firmware"; + arch = "sandbox"; + load = <0>; + hash-1 { + algo = "sha1"; + }; + }; + }; +};

The test can run on sandbox build and it attempts to execute a firmware update via a capsule-on-disk, using a raw image capsule, CONFIG_EFI_CAPSULE_RAW.
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.
Signed-off-by: AKASHI Takahiro takahiro.akashi@linaro.org --- .../test_efi_capsule/test_capsule_firmware.py | 75 +++++++++++++++++++ 1 file changed, 75 insertions(+)
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 490dc039d81f..e7c88c796a65 100644 --- a/test/py/tests/test_efi_capsule/test_capsule_firmware.py +++ b/test/py/tests/test_efi_capsule/test_capsule_firmware.py @@ -306,3 +306,78 @@ class TestEfiCapsuleFirmwareFit(object): 'sf read 4000000 150000 10', 'md.b 4000000 10']) assert 'u-boot-env:New' in ''.join(output) + + def test_efi_capsule_fw4( + self, u_boot_config, u_boot_console, efi_capsule_data): + """ + Test Case 4 - Update U-Boot on SPI Flash, raw image format + 0x100000-0x150000: U-Boot binary (but dummy) + """ + # "-T" (or "-D") is required to enable spi flash on sandbox + u_boot_console.restart_uboot_with_flags('-T') + + disk_img = efi_capsule_data + with u_boot_console.log.section('Test Case 4-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 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) + + # create a capsule file + try: + loop_dev = check_output( + 'sudo losetup -o 1MiB --sizelimit %dMiB --show -f %s | tr -d "\n"' % + (EFI_BOOTDEV_PART_SIZE, disk_img), shell=True).decode() + check_call('sudo mount -t %s -o umask=000 %s %s' + % (EFI_BOOTDEV_FS_TYPE, loop_dev, MNT_PNT), shell=True) + check_call( + '%s/tools/mkeficapsule --raw %s%s/u-boot.bin.new --version 1 --index 1 %s%s/Test01' % + (u_boot_config.build_dir, + MNT_PNT, + CAPSULE_DATA_DIR, + MNT_PNT, + CAPSULE_INSTALL_DIR), + shell=True) + check_call('ls -l %s/%s' % (MNT_PNT, CAPSULE_INSTALL_DIR), + shell=True) + check_call('sudo umount %s' % loop_dev, shell=True) + check_call('sudo losetup -d %s' % loop_dev, shell=True) + except CalledProcessError as exception: + assert 'failed to create firmware capsule: %s' % exception.cmd + + # reboot + u_boot_console.restart_uboot_with_flags('-T') + + capsule_early = u_boot_config.buildconfig.get( + 'config_efi_capsule_on_disk_early') + with u_boot_console.log.section('Test Case 4-b, after reboot'): + if not capsule_early: + 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) + + # 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)
participants (2)
-
AKASHI Takahiro
-
Heinrich Schuchardt