[RFC PATCH 0/4] qemu: arm64: Add support for uefi firmware management protocol routines

This series is based on the capsule update rfc series sent by Takahiro Akashi[1].
These routines have been tested on the qemu arm64 platform for updating the u-boot firmware through the capsule-on-disk functionality of the uefi spec. A sub-command has been added under the 'efidebug capsule' category of commands for initiating the firmware update.
The capsule file is placed on the efi system partition(esp), under the EFI/UpdateCapsule directory. The BootNext and BootXXXX variables are set accordingly by using the 'efidebug boot add' command.
The capsule update can then be initiated by the following command 'efidebug capsule disk-update'.
The feature can be enabled through the following config options CONFIG_EFI_CAPSULE_UPDATE=y CONFIG_EFI_CAPSULE_ON_DISK=y CONFIG_EFI_CAPSULE_UPDATE_FIRMWARE=y CONFIG_EFI_FIRMWARE_MANAGEMENT_PROTOCOL=y CONFIG_CMD_EFIDEBUG=y
The GenerateCapsule.py script in edk2 has been used to generate the capsule file.
Todo * Capsule authentication support. * Implement firmware version support. * Invoke capsule update functionality during platform boot. This might be taken up by Takahiro.
[1] - https://lists.denx.de/pipermail/u-boot/2020-March/403038.html
Sughosh Ganu (4): efidebug: capsule: Add a command to update capsule on disk semihosting: Change semihosting file operation functions into global symbols semihosting: Add support for writing to a file qemu: arm64: Add support for efi firmware management protocol routines
arch/arm/lib/semihosting.c | 48 ++++++- board/emulation/qemu-arm/Kconfig | 12 ++ board/emulation/qemu-arm/Makefile | 1 + board/emulation/qemu-arm/qemu_efi_fmp.c | 173 ++++++++++++++++++++++++ cmd/efidebug.c | 14 ++ include/semihosting.h | 14 ++ 6 files changed, 257 insertions(+), 5 deletions(-) create mode 100644 board/emulation/qemu-arm/qemu_efi_fmp.c create mode 100644 include/semihosting.h

Add a efidebug subcommand to initiate a firmware update using the efi firmware management protocol(fmp) set_image routine.
The firmware update can be initiated through
'efidebug capsule disk-update'
This would locate the efi capsule file on the efi system partition, and call the platform's set_image fmp routine to initiate the firmware update.
Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org --- cmd/efidebug.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+)
diff --git a/cmd/efidebug.c b/cmd/efidebug.c index 4a7661d0ac..fd8366dc90 100644 --- a/cmd/efidebug.c +++ b/cmd/efidebug.c @@ -77,6 +77,16 @@ static int do_efi_capsule_update(cmd_tbl_t *cmdtp, int flag, return CMD_RET_SUCCESS; }
+static int do_efi_capsule_on_disk_update(cmd_tbl_t *cmdtp, int flag, + int argc, char * const argv[]) +{ + efi_status_t ret; + + ret = efi_launch_capsules(); + + return ret == EFI_SUCCESS ? CMD_RET_SUCCESS : CMD_RET_FAILURE; +} + /** * do_efi_capsule_show() - show capsule information * @@ -205,6 +215,8 @@ static cmd_tbl_t cmd_efidebug_capsule_sub[] = { "", ""), U_BOOT_CMD_MKENT(show, CONFIG_SYS_MAXARGS, 1, do_efi_capsule_show, "", ""), + U_BOOT_CMD_MKENT(disk-update, 0, 0, do_efi_capsule_on_disk_update, + "", ""), U_BOOT_CMD_MKENT(result, CONFIG_SYS_MAXARGS, 1, do_efi_capsule_res, "", ""), }; @@ -1387,6 +1399,8 @@ static char efidebug_help_text[] = #ifdef CONFIG_EFI_CAPSULE_UPDATE "efidebug capsule update [-v] <capsule address>\n" " - process a capsule\n" + "efidebug capsule disk-update\n" + " - update a capsule from disk\n" "efidebug capsule show <capsule address>\n" " - show capsule information\n" "efidebug capsule result [<capsule result var>]\n"

On 3/23/20 8:11 AM, Sughosh Ganu wrote:
Add a efidebug subcommand to initiate a firmware update using the efi firmware management protocol(fmp) set_image routine.
The firmware update can be initiated through
'efidebug capsule disk-update'
This would locate the efi capsule file on the efi system partition, and call the platform's set_image fmp routine to initiate the firmware update.
Hello Sughosh,
why do we need this command? Shouldn't a simple reset do the job?
See chapter 8.5.5 "Delivery of Capsules via file on Mass Storage device" of UEFI spec 2.8.
What might be of interest is a command to start a capsule from a memory location. This would allow to load capsules from other locations than directory \EFI\UpdateCapsule.
Best regards
Heinrich
Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org
cmd/efidebug.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+)
diff --git a/cmd/efidebug.c b/cmd/efidebug.c index 4a7661d0ac..fd8366dc90 100644 --- a/cmd/efidebug.c +++ b/cmd/efidebug.c @@ -77,6 +77,16 @@ static int do_efi_capsule_update(cmd_tbl_t *cmdtp, int flag, return CMD_RET_SUCCESS; }
+static int do_efi_capsule_on_disk_update(cmd_tbl_t *cmdtp, int flag,
int argc, char * const argv[])
+{
- efi_status_t ret;
- ret = efi_launch_capsules();
- return ret == EFI_SUCCESS ? CMD_RET_SUCCESS : CMD_RET_FAILURE;
+}
/**
- do_efi_capsule_show() - show capsule information
@@ -205,6 +215,8 @@ static cmd_tbl_t cmd_efidebug_capsule_sub[] = { "", ""), U_BOOT_CMD_MKENT(show, CONFIG_SYS_MAXARGS, 1, do_efi_capsule_show, "", ""),
- U_BOOT_CMD_MKENT(disk-update, 0, 0, do_efi_capsule_on_disk_update,
U_BOOT_CMD_MKENT(result, CONFIG_SYS_MAXARGS, 1, do_efi_capsule_res, "", ""),"", ""),
}; @@ -1387,6 +1399,8 @@ static char efidebug_help_text[] = #ifdef CONFIG_EFI_CAPSULE_UPDATE "efidebug capsule update [-v] <capsule address>\n" " - process a capsule\n"
- "efidebug capsule disk-update\n"
- " - update a capsule from disk\n" "efidebug capsule show <capsule address>\n" " - show capsule information\n" "efidebug capsule result [<capsule result var>]\n"

On Mon, 23 Mar 2020 at 17:20, Heinrich Schuchardt xypron.glpk@gmx.de wrote:
On 3/23/20 8:11 AM, Sughosh Ganu wrote:
Add a efidebug subcommand to initiate a firmware update using the efi firmware management protocol(fmp) set_image routine.
The firmware update can be initiated through
'efidebug capsule disk-update'
This would locate the efi capsule file on the efi system partition, and call the platform's set_image fmp routine to initiate the firmware update.
Hello Sughosh,
why do we need this command? Shouldn't a simple reset do the job?
See chapter 8.5.5 "Delivery of Capsules via file on Mass Storage device" of UEFI spec 2.8.
Like i have mentioned in the cover letter, we need to invoke capsule-on-disk update during the platform boot -- that is on the Todo list. The spec requires setting the EFI_OS_INDICATIONS_FILE_CAPSULE_DELIVERY_SUPPORTED flag in the OsIndications variable. But we would not be supporting that, at least for now, since we do not have support of runtime variables on our platforms. The idea is to invoke a capsule-on-disk update during the platform boot. I think Takahiro will be working on this.
What might be of interest is a command to start a capsule from a memory location. This would allow to load capsules from other locations than directory \EFI\UpdateCapsule.
Takahiro has indeed added this command which updates a capsule which has been loaded into the memory. I thought it might be easier to invoke a command where the user is not required to load the capsule into memory first, but that is taken care of directly by the capsule update logic. That said, I can drop the patch if you prefer just having the version which updates the capsule from memory.
-sughosh
Best regards
Heinrich
Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org
cmd/efidebug.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+)
diff --git a/cmd/efidebug.c b/cmd/efidebug.c index 4a7661d0ac..fd8366dc90 100644 --- a/cmd/efidebug.c +++ b/cmd/efidebug.c @@ -77,6 +77,16 @@ static int do_efi_capsule_update(cmd_tbl_t *cmdtp,
int flag,
return CMD_RET_SUCCESS;
}
+static int do_efi_capsule_on_disk_update(cmd_tbl_t *cmdtp, int flag,
int argc, char * const argv[])
+{
efi_status_t ret;
ret = efi_launch_capsules();
return ret == EFI_SUCCESS ? CMD_RET_SUCCESS : CMD_RET_FAILURE;
+}
/**
- do_efi_capsule_show() - show capsule information
@@ -205,6 +215,8 @@ static cmd_tbl_t cmd_efidebug_capsule_sub[] = { "", ""), U_BOOT_CMD_MKENT(show, CONFIG_SYS_MAXARGS, 1, do_efi_capsule_show, "", ""),
U_BOOT_CMD_MKENT(disk-update, 0, 0, do_efi_capsule_on_disk_update,
"", ""), U_BOOT_CMD_MKENT(result, CONFIG_SYS_MAXARGS, 1, do_efi_capsule_res, "", ""),
}; @@ -1387,6 +1399,8 @@ static char efidebug_help_text[] = #ifdef CONFIG_EFI_CAPSULE_UPDATE "efidebug capsule update [-v] <capsule address>\n" " - process a capsule\n"
"efidebug capsule disk-update\n"
" - update a capsule from disk\n" "efidebug capsule show <capsule address>\n" " - show capsule information\n" "efidebug capsule result [<capsule result var>]\n"

On Tue, Mar 24, 2020 at 09:19:55AM +0530, Sughosh Ganu wrote:
On Mon, 23 Mar 2020 at 17:20, Heinrich Schuchardt xypron.glpk@gmx.de wrote:
On 3/23/20 8:11 AM, Sughosh Ganu wrote:
Add a efidebug subcommand to initiate a firmware update using the efi firmware management protocol(fmp) set_image routine.
The firmware update can be initiated through
'efidebug capsule disk-update'
This would locate the efi capsule file on the efi system partition, and call the platform's set_image fmp routine to initiate the firmware update.
Hello Sughosh,
why do we need this command? Shouldn't a simple reset do the job?
See chapter 8.5.5 "Delivery of Capsules via file on Mass Storage device" of UEFI spec 2.8.
Like i have mentioned in the cover letter, we need to invoke capsule-on-disk update during the platform boot -- that is on the Todo list. The spec requires setting the EFI_OS_INDICATIONS_FILE_CAPSULE_DELIVERY_SUPPORTED flag in the OsIndications variable. But we would not be supporting that, at least for now, since we do not have support of runtime variables on our platforms. The idea is to invoke a capsule-on-disk update during the platform boot. I think Takahiro will be working on this.
To be strict, under my current implementation, all the capsule files under \EFI\UpdateCapsule will be applied only at the first time when any of UEFI-related commands is invoked. This is the way how the UEFI subsystem is initialized on U-Boot.
I mentioned this in my cover letter, and how we should comply with UEFI specification and meet users' expectation is a point of discussion as RFC.
UpdateCapsule API is provided, but it is more or less an internal function in the current form so as to directly initiate capsule handling. It currently ignores CAPSULE_FLAGS_PERSIST_ACROSS_RESET and CAPSULE_FLAGS_INITIATE_RESET in a capsule. (I mentioned this in TODO list.)
What might be of interest is a command to start a capsule from a memory location. This would allow to load capsules from other locations than directory \EFI\UpdateCapsule.
Takahiro has indeed added this command which updates a capsule which has been loaded into the memory. I thought it might be easier to invoke a command where the user is not required to load the capsule into memory first, but that is taken care of directly by the capsule update logic. That said, I can drop the patch if you prefer just having the version which updates the capsule from memory.
"efidebug capsule" command, like other subcommands of efidebug, is a tool mainly for test/debug purpose. Keep the functionality in the command won't bother anyone in that sense.
-Takahiro Akashi
-sughosh
Best regards
Heinrich
Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org
cmd/efidebug.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+)
diff --git a/cmd/efidebug.c b/cmd/efidebug.c index 4a7661d0ac..fd8366dc90 100644 --- a/cmd/efidebug.c +++ b/cmd/efidebug.c @@ -77,6 +77,16 @@ static int do_efi_capsule_update(cmd_tbl_t *cmdtp,
int flag,
return CMD_RET_SUCCESS;
}
+static int do_efi_capsule_on_disk_update(cmd_tbl_t *cmdtp, int flag,
int argc, char * const argv[])
+{
efi_status_t ret;
ret = efi_launch_capsules();
return ret == EFI_SUCCESS ? CMD_RET_SUCCESS : CMD_RET_FAILURE;
+}
/**
- do_efi_capsule_show() - show capsule information
@@ -205,6 +215,8 @@ static cmd_tbl_t cmd_efidebug_capsule_sub[] = { "", ""), U_BOOT_CMD_MKENT(show, CONFIG_SYS_MAXARGS, 1, do_efi_capsule_show, "", ""),
U_BOOT_CMD_MKENT(disk-update, 0, 0, do_efi_capsule_on_disk_update,
"", ""), U_BOOT_CMD_MKENT(result, CONFIG_SYS_MAXARGS, 1, do_efi_capsule_res, "", ""),
}; @@ -1387,6 +1399,8 @@ static char efidebug_help_text[] = #ifdef CONFIG_EFI_CAPSULE_UPDATE "efidebug capsule update [-v] <capsule address>\n" " - process a capsule\n"
"efidebug capsule disk-update\n"
" - update a capsule from disk\n" "efidebug capsule show <capsule address>\n" " - show capsule information\n" "efidebug capsule result [<capsule result var>]\n"

Change the semihosting file operation functions into external symbols so that they can be called from outside the file. These functions would be required to be called for implementing firmware update functionality for the qemu arm64 platform.
Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org --- arch/arm/lib/semihosting.c | 7 ++++--- include/semihosting.h | 13 +++++++++++++ 2 files changed, 17 insertions(+), 3 deletions(-) create mode 100644 include/semihosting.h
diff --git a/arch/arm/lib/semihosting.c b/arch/arm/lib/semihosting.c index 2658026cf4..3aeda1303a 100644 --- a/arch/arm/lib/semihosting.c +++ b/arch/arm/lib/semihosting.c @@ -13,6 +13,7 @@ */ #include <common.h> #include <command.h> +#include <semihosting.h>
#define SYSOPEN 0x01 #define SYSCLOSE 0x02 @@ -43,7 +44,7 @@ static noinline long smh_trap(unsigned int sysnum, void *addr) * Open a file on the host. Mode is "r" or "rb" currently. Returns a file * descriptor or -1 on error. */ -static long smh_open(const char *fname, char *modestr) +long smh_open(const char *fname, char *modestr) { long fd; unsigned long mode; @@ -82,7 +83,7 @@ static long smh_open(const char *fname, char *modestr) /* * Read 'len' bytes of file into 'memp'. Returns 0 on success, else failure */ -static long smh_read(long fd, void *memp, size_t len) +long smh_read(long fd, void *memp, size_t len) { long ret; struct smh_read_s { @@ -116,7 +117,7 @@ static long smh_read(long fd, void *memp, size_t len) /* * Close the file using the file descriptor */ -static long smh_close(long fd) +long smh_close(long fd) { long ret;
diff --git a/include/semihosting.h b/include/semihosting.h new file mode 100644 index 0000000000..f1bf419275 --- /dev/null +++ b/include/semihosting.h @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2020, Linaro Limited + */ + +#if !defined _SEMIHOSTING_H_ +#define _SEMIHOSTING_H_ + +long smh_open(const char *fname, char *modestr); +long smh_read(long fd, void *memp, size_t len); +long smh_close(long fd); + +#endif /* _SEMIHOSTING_H_ */

Add a function to enable writing to a file. Currently, support is added for writing to a binary file. This would be used for implementing the firmware update functionality for the qemu arm64 platform.
Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org --- arch/arm/lib/semihosting.c | 41 ++++++++++++++++++++++++++++++++++++-- include/semihosting.h | 1 + 2 files changed, 40 insertions(+), 2 deletions(-)
diff --git a/arch/arm/lib/semihosting.c b/arch/arm/lib/semihosting.c index 3aeda1303a..08181132d1 100644 --- a/arch/arm/lib/semihosting.c +++ b/arch/arm/lib/semihosting.c @@ -17,11 +17,18 @@
#define SYSOPEN 0x01 #define SYSCLOSE 0x02 +#define SYSWRITE 0x5 #define SYSREAD 0x06 #define SYSFLEN 0x0C
-#define MODE_READ 0x0 -#define MODE_READBIN 0x1 +#define MODE_READ 0x0 +#define MODE_READBIN 0x1 +#define MODE_READPLUS 0x2 +#define MODE_READPLUSBIN 0x3 +#define MODE_WRITE 0x4 +#define MODE_WRITEBIN 0x5 +#define MODE_WRITEPLUS 0x6 +#define MODE_WRITEPLUSBIN 0x7
/* * Call the handler @@ -61,6 +68,8 @@ long smh_open(const char *fname, char *modestr) mode = MODE_READ; } else if (!(strcmp(modestr, "rb"))) { mode = MODE_READBIN; + } else if (!strcmp(modestr, "w+b")) { + mode = MODE_WRITEPLUSBIN; } else { printf("%s: ERROR mode '%s' not supported\n", __func__, modestr); @@ -114,6 +123,34 @@ long smh_read(long fd, void *memp, size_t len) return 0; }
+/* + * Write 'len' bytes into the file referenced by the fd. Returns 0 on success, else + * a negavite value for failure + */ +long smh_write(long fd, void *memp, size_t len) +{ + long ret; + struct smh_write_s { + long fd; + void *memp; + size_t len; + } write; + + debug("%s: fd %ld, memp %p, len %zu\n", __func__, fd, memp, len); + + write.fd = fd; + write.memp = memp; + write.len = len; + + ret = smh_trap(SYSWRITE, &write); + + if (ret > 0) + printf("%s: ERROR ret %ld, fd %ld, len %zu, memp %p\n", + __func__, ret, fd, len, memp); + + return ret == 0 ? 0 : -1; +} + /* * Close the file using the file descriptor */ diff --git a/include/semihosting.h b/include/semihosting.h index f1bf419275..fa5cecddf2 100644 --- a/include/semihosting.h +++ b/include/semihosting.h @@ -8,6 +8,7 @@
long smh_open(const char *fname, char *modestr); long smh_read(long fd, void *memp, size_t len); +long smh_write(long fd, void *memp, size_t len); long smh_close(long fd);
#endif /* _SEMIHOSTING_H_ */

Add support for the get_image_info and set_image routines, which are part of the efi firmware management protocol.
The current implementation uses the set_image routine for updating the u-boot binary image for the qemu arm64 platform. This is supported using the capsule-on-disk feature of the uefi specification, wherein the firmware image to be updated is placed on the efi system partition as a efi capsule under EFI/UpdateCapsule/ directory.
Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org --- board/emulation/qemu-arm/Kconfig | 12 ++ board/emulation/qemu-arm/Makefile | 1 + board/emulation/qemu-arm/qemu_efi_fmp.c | 173 ++++++++++++++++++++++++ 3 files changed, 186 insertions(+) create mode 100644 board/emulation/qemu-arm/qemu_efi_fmp.c
diff --git a/board/emulation/qemu-arm/Kconfig b/board/emulation/qemu-arm/Kconfig index 02ae4d9884..1ef2a27539 100644 --- a/board/emulation/qemu-arm/Kconfig +++ b/board/emulation/qemu-arm/Kconfig @@ -11,3 +11,15 @@ config BOARD_SPECIFIC_OPTIONS # dummy imply VIRTIO_BLK
endif + +if TARGET_QEMU_ARM_64BIT + +config EFI_FIRMWARE_MANAGEMENT_PROTOCOL + bool "EFI Firmware Management protocol for Qemu arm64 platform" + depends on EFI_CAPSULE_UPDATE && EFI_CAPSULE_UPDATE_FIRMWARE + default n + help + Select this option for enabling firmware management protocol + for qemu arm64 platform + +endif diff --git a/board/emulation/qemu-arm/Makefile b/board/emulation/qemu-arm/Makefile index a22d1237ff..c95ac6d233 100644 --- a/board/emulation/qemu-arm/Makefile +++ b/board/emulation/qemu-arm/Makefile @@ -1,3 +1,4 @@ # SPDX-License-Identifier: GPL-2.0+
obj-y += qemu-arm.o +obj-$(CONFIG_EFI_FIRMWARE_MANAGEMENT_PROTOCOL) += qemu_efi_fmp.o diff --git a/board/emulation/qemu-arm/qemu_efi_fmp.c b/board/emulation/qemu-arm/qemu_efi_fmp.c new file mode 100644 index 0000000000..17caa59786 --- /dev/null +++ b/board/emulation/qemu-arm/qemu_efi_fmp.c @@ -0,0 +1,173 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2020, Linaro Limited + */ + +#include <common.h> +#include <charset.h> +#include <efi_api.h> +#include <efi_loader.h> +#include <malloc.h> +#include <semihosting.h> + +#define QEMU_UBOOT_IMAGE_INDEX 0x1 +#define QEMU_UBOOT_IMAGE 0x1 + +#define UBOOT_FILE "bl33.bin" + +static efi_status_t EFIAPI qemu_arm64_fmp_get_image_info( + struct efi_firmware_management_protocol *this, + efi_uintn_t *image_info_size, + struct efi_firmware_image_descriptor *image_info, + u32 *desc_version, u8 *desc_count, + efi_uintn_t *desc_size, u32 *package_version, + u16 **package_version_name) +{ + efi_status_t status = EFI_SUCCESS; + u16 *image_id_name; + const char *image_name = "Qemu Aarch64 U-Boot"; + const efi_guid_t image_guid = EFI_FIRMWARE_MANAGEMENT_CAPSULE_ID_GUID; + + EFI_ENTRY("%p %p %p %p %p %p %p %p\n", this, image_info_size, + image_info, desc_version, desc_count, desc_size, + package_version, package_version_name); + + /* Sanity checks */ + if (*image_info_size && !image_info) { + status = EFI_INVALID_PARAMETER; + goto back; + } + + if (*image_info_size && + (!desc_version || !desc_count || !desc_size)) { + status = EFI_INVALID_PARAMETER; + goto back; + } + + if (*image_info_size && (!package_version || !package_version_name)) { + status = EFI_INVALID_PARAMETER; + goto back; + } + + if (*image_info_size < sizeof(*image_info)) { + *image_info_size = sizeof(*image_info); + status = EFI_BUFFER_TOO_SMALL; + goto back; + } + + if (desc_version) + *desc_version = EFI_FIRMWARE_IMAGE_DESCRIPTOR_VERSION; + + *desc_count = 0x1; + *desc_size = sizeof(*image_info); + + if (package_version) + *package_version = 0xffffffff; + + if (package_version_name) + *package_version_name = NULL; + + image_info[0].image_type_id = image_guid; + image_info[0].image_id = QEMU_UBOOT_IMAGE; + + image_id_name = malloc(40); + utf8_utf16_strcpy(&image_id_name, image_name); + image_info[0].image_id_name = image_id_name; + + /* Todo: Get a mechanism to store version information */ + image_info[0]. version = 0x1; + image_info[0].version_name = NULL; + + /* Todo: Need to find a mechanism to get the image size */ + image_info[0].size = 0; + + image_info[0].attributes_supported = + EFI_IMAGE_ATTRIBUTE_IMAGE_UPDATABLE; + image_info[0].attributes_setting = EFI_IMAGE_ATTRIBUTE_IMAGE_UPDATABLE; + + image_info[0].lowest_supported_image_version = 1; + image_info[0].last_attempt_version = 0; + image_info[0].last_attempt_status = LAST_ATTEMPT_STATUS_SUCCESS; + image_info[0].hardware_instance = 0; + +back: + return EFI_EXIT(status); +} + +static efi_status_t EFIAPI qemu_arm64_fmp_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) +{ + long fd, ret; + efi_status_t status = EFI_SUCCESS; + char *mode = "w+b"; + + EFI_ENTRY("%p %d %p %ld %p %p %p\n", this, image_index, image, + image_size, vendor_code, progress, abort_reason); + + /* + * Put a hack here to offset the size of + * the FMP_PAYLOAD_HEADER that gets added + * by the GenerateCapsule script in edk2. + */ + image += 0x10; + image_size -= 0x10; + + /* Do all the sanity checks first */ + if (!image) { + status = EFI_INVALID_PARAMETER; + goto back; + } + + if (image_size == 0) { + status = EFI_INVALID_PARAMETER; + goto back; + } + + if (image_index != QEMU_UBOOT_IMAGE_INDEX) { + status = EFI_INVALID_PARAMETER; + goto back; + } + + /* Do the update */ + fd = smh_open(UBOOT_FILE, mode); + if (fd == -1) { + printf("%s: Unable to open the firmware image for writing\n", + __func__); + status = EFI_DEVICE_ERROR; + goto back; + } + + ret = smh_write(fd, (void *)image, image_size); + if (ret == -1) { + printf("%s: Error writing to the firmware image!", __func__); + smh_close(fd); + status = EFI_DEVICE_ERROR; + goto back; + } + + printf("%s: Done writing the firmware image file\n", __func__); + smh_close(fd); +back: + return EFI_EXIT(status); +} + +const struct efi_firmware_management_protocol efi_qemu_arm64_fmp = { + .get_image_info = qemu_arm64_fmp_get_image_info, + .set_image = qemu_arm64_fmp_set_image, +}; + +efi_status_t arch_efi_load_capsule_drivers(void) +{ + efi_status_t ret; + + ret = EFI_CALL(efi_install_multiple_protocol_interfaces(&efi_root, + &efi_guid_firmware_management_protocol, + &efi_qemu_arm64_fmp, + NULL)); + + return ret; +}

Sughosh,
On Mon, Mar 23, 2020 at 12:42:01PM +0530, Sughosh Ganu wrote:
Add support for the get_image_info and set_image routines, which are part of the efi firmware management protocol.
The current implementation uses the set_image routine for updating the u-boot binary image for the qemu arm64 platform. This is supported using the capsule-on-disk feature of the uefi specification, wherein the firmware image to be updated is placed on the efi system partition as a efi capsule under EFI/UpdateCapsule/ directory.
Signed-off-by: Sughosh Ganu sughosh.ganu@linaro.org
board/emulation/qemu-arm/Kconfig | 12 ++ board/emulation/qemu-arm/Makefile | 1 + board/emulation/qemu-arm/qemu_efi_fmp.c | 173 ++++++++++++++++++++++++ 3 files changed, 186 insertions(+) create mode 100644 board/emulation/qemu-arm/qemu_efi_fmp.c
diff --git a/board/emulation/qemu-arm/Kconfig b/board/emulation/qemu-arm/Kconfig index 02ae4d9884..1ef2a27539 100644 --- a/board/emulation/qemu-arm/Kconfig +++ b/board/emulation/qemu-arm/Kconfig @@ -11,3 +11,15 @@ config BOARD_SPECIFIC_OPTIONS # dummy imply VIRTIO_BLK
endif
+if TARGET_QEMU_ARM_64BIT
+config EFI_FIRMWARE_MANAGEMENT_PROTOCOL
I think that we should give it a qemu-specific configuration name as there can co-exist multiple drivers of FMP at the same time.
- bool "EFI Firmware Management protocol for Qemu arm64 platform"
- depends on EFI_CAPSULE_UPDATE && EFI_CAPSULE_UPDATE_FIRMWARE
Please add a dependency on SEMIHOSTING with some description.
- default n
- help
Select this option for enabling firmware management protocol
for qemu arm64 platform
+endif diff --git a/board/emulation/qemu-arm/Makefile b/board/emulation/qemu-arm/Makefile index a22d1237ff..c95ac6d233 100644 --- a/board/emulation/qemu-arm/Makefile +++ b/board/emulation/qemu-arm/Makefile @@ -1,3 +1,4 @@ # SPDX-License-Identifier: GPL-2.0+
obj-y += qemu-arm.o +obj-$(CONFIG_EFI_FIRMWARE_MANAGEMENT_PROTOCOL) += qemu_efi_fmp.o diff --git a/board/emulation/qemu-arm/qemu_efi_fmp.c b/board/emulation/qemu-arm/qemu_efi_fmp.c new file mode 100644 index 0000000000..17caa59786 --- /dev/null +++ b/board/emulation/qemu-arm/qemu_efi_fmp.c @@ -0,0 +1,173 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/*
- Copyright (c) 2020, Linaro Limited
- */
+#include <common.h> +#include <charset.h> +#include <efi_api.h> +#include <efi_loader.h> +#include <malloc.h> +#include <semihosting.h>
+#define QEMU_UBOOT_IMAGE_INDEX 0x1 +#define QEMU_UBOOT_IMAGE 0x1
+#define UBOOT_FILE "bl33.bin"
It would be better to parameterize this file name as a configuration option so as to make this solution more generic for TFA-based and non-TFA-based system.
-Takahiro Akashi
+static efi_status_t EFIAPI qemu_arm64_fmp_get_image_info(
- struct efi_firmware_management_protocol *this,
- efi_uintn_t *image_info_size,
- struct efi_firmware_image_descriptor *image_info,
- u32 *desc_version, u8 *desc_count,
- efi_uintn_t *desc_size, u32 *package_version,
- u16 **package_version_name)
+{
- efi_status_t status = EFI_SUCCESS;
- u16 *image_id_name;
- const char *image_name = "Qemu Aarch64 U-Boot";
- const efi_guid_t image_guid = EFI_FIRMWARE_MANAGEMENT_CAPSULE_ID_GUID;
- EFI_ENTRY("%p %p %p %p %p %p %p %p\n", this, image_info_size,
image_info, desc_version, desc_count, desc_size,
package_version, package_version_name);
- /* Sanity checks */
- if (*image_info_size && !image_info) {
status = EFI_INVALID_PARAMETER;
goto back;
- }
- if (*image_info_size &&
(!desc_version || !desc_count || !desc_size)) {
status = EFI_INVALID_PARAMETER;
goto back;
- }
- if (*image_info_size && (!package_version || !package_version_name)) {
status = EFI_INVALID_PARAMETER;
goto back;
- }
- if (*image_info_size < sizeof(*image_info)) {
*image_info_size = sizeof(*image_info);
status = EFI_BUFFER_TOO_SMALL;
goto back;
- }
- if (desc_version)
*desc_version = EFI_FIRMWARE_IMAGE_DESCRIPTOR_VERSION;
- *desc_count = 0x1;
- *desc_size = sizeof(*image_info);
- if (package_version)
*package_version = 0xffffffff;
- if (package_version_name)
*package_version_name = NULL;
- image_info[0].image_type_id = image_guid;
- image_info[0].image_id = QEMU_UBOOT_IMAGE;
- image_id_name = malloc(40);
- utf8_utf16_strcpy(&image_id_name, image_name);
- image_info[0].image_id_name = image_id_name;
- /* Todo: Get a mechanism to store version information */
- image_info[0]. version = 0x1;
- image_info[0].version_name = NULL;
- /* Todo: Need to find a mechanism to get the image size */
- image_info[0].size = 0;
- image_info[0].attributes_supported =
EFI_IMAGE_ATTRIBUTE_IMAGE_UPDATABLE;
- image_info[0].attributes_setting = EFI_IMAGE_ATTRIBUTE_IMAGE_UPDATABLE;
- image_info[0].lowest_supported_image_version = 1;
- image_info[0].last_attempt_version = 0;
- image_info[0].last_attempt_status = LAST_ATTEMPT_STATUS_SUCCESS;
- image_info[0].hardware_instance = 0;
+back:
- return EFI_EXIT(status);
+}
+static efi_status_t EFIAPI qemu_arm64_fmp_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)
+{
- long fd, ret;
- efi_status_t status = EFI_SUCCESS;
- char *mode = "w+b";
- EFI_ENTRY("%p %d %p %ld %p %p %p\n", this, image_index, image,
image_size, vendor_code, progress, abort_reason);
- /*
* Put a hack here to offset the size of
* the FMP_PAYLOAD_HEADER that gets added
* by the GenerateCapsule script in edk2.
*/
- image += 0x10;
- image_size -= 0x10;
- /* Do all the sanity checks first */
- if (!image) {
status = EFI_INVALID_PARAMETER;
goto back;
- }
- if (image_size == 0) {
status = EFI_INVALID_PARAMETER;
goto back;
- }
- if (image_index != QEMU_UBOOT_IMAGE_INDEX) {
status = EFI_INVALID_PARAMETER;
goto back;
- }
- /* Do the update */
- fd = smh_open(UBOOT_FILE, mode);
- if (fd == -1) {
printf("%s: Unable to open the firmware image for writing\n",
__func__);
status = EFI_DEVICE_ERROR;
goto back;
- }
- ret = smh_write(fd, (void *)image, image_size);
- if (ret == -1) {
printf("%s: Error writing to the firmware image!", __func__);
smh_close(fd);
status = EFI_DEVICE_ERROR;
goto back;
- }
- printf("%s: Done writing the firmware image file\n", __func__);
- smh_close(fd);
+back:
- return EFI_EXIT(status);
+}
+const struct efi_firmware_management_protocol efi_qemu_arm64_fmp = {
- .get_image_info = qemu_arm64_fmp_get_image_info,
- .set_image = qemu_arm64_fmp_set_image,
+};
+efi_status_t arch_efi_load_capsule_drivers(void) +{
- efi_status_t ret;
- ret = EFI_CALL(efi_install_multiple_protocol_interfaces(&efi_root,
&efi_guid_firmware_management_protocol,
&efi_qemu_arm64_fmp,
NULL));
- return ret;
+}
2.17.1

On 3/23/20 8:11 AM, Sughosh Ganu wrote:
This series is based on the capsule update rfc series sent by Takahiro Akashi[1].
These routines have been tested on the qemu arm64 platform for updating the u-boot firmware through the capsule-on-disk functionality of the uefi spec. A sub-command has been added under the 'efidebug capsule' category of commands for initiating the firmware update.
The capsule file is placed on the efi system partition(esp), under the EFI/UpdateCapsule directory. The BootNext and BootXXXX variables are set accordingly by using the 'efidebug boot add' command.
The capsule update can then be initiated by the following command 'efidebug capsule disk-update'.
Thanks you for your work on capsule updates.
I am missing here an overview description of the system setup.
I saw a filename bl33.bin. So assume I that you want to use ATF? In how far is ATF needed for testing the capsule update?
Please, mention the use of semihosting in the overview.
Best regards
Heinrich
The feature can be enabled through the following config options CONFIG_EFI_CAPSULE_UPDATE=y CONFIG_EFI_CAPSULE_ON_DISK=y CONFIG_EFI_CAPSULE_UPDATE_FIRMWARE=y CONFIG_EFI_FIRMWARE_MANAGEMENT_PROTOCOL=y CONFIG_CMD_EFIDEBUG=y
The GenerateCapsule.py script in edk2 has been used to generate the capsule file.
Todo
- Capsule authentication support.
- Implement firmware version support.
- Invoke capsule update functionality during platform boot. This might be taken up by Takahiro.
[1] - https://lists.denx.de/pipermail/u-boot/2020-March/403038.html
Sughosh Ganu (4): efidebug: capsule: Add a command to update capsule on disk semihosting: Change semihosting file operation functions into global symbols semihosting: Add support for writing to a file qemu: arm64: Add support for efi firmware management protocol routines
arch/arm/lib/semihosting.c | 48 ++++++- board/emulation/qemu-arm/Kconfig | 12 ++ board/emulation/qemu-arm/Makefile | 1 + board/emulation/qemu-arm/qemu_efi_fmp.c | 173 ++++++++++++++++++++++++ cmd/efidebug.c | 14 ++ include/semihosting.h | 14 ++ 6 files changed, 257 insertions(+), 5 deletions(-) create mode 100644 board/emulation/qemu-arm/qemu_efi_fmp.c create mode 100644 include/semihosting.h

On Mon, 23 Mar 2020 at 17:41, Heinrich Schuchardt xypron.glpk@gmx.de wrote:
On 3/23/20 8:11 AM, Sughosh Ganu wrote:
This series is based on the capsule update rfc series sent by Takahiro Akashi[1].
These routines have been tested on the qemu arm64 platform for updating the u-boot firmware through the capsule-on-disk functionality of the uefi spec. A sub-command has been added under the 'efidebug capsule' category of commands for initiating the firmware update.
The capsule file is placed on the efi system partition(esp), under the EFI/UpdateCapsule directory. The BootNext and BootXXXX variables are set accordingly by using the 'efidebug boot add' command.
The capsule update can then be initiated by the following command 'efidebug capsule disk-update'.
Thanks you for your work on capsule updates.
I am missing here an overview description of the system setup.
I saw a filename bl33.bin. So assume I that you want to use ATF? In how far is ATF needed for testing the capsule update?
Please, mention the use of semihosting in the overview.
Thanks for looking into this. I will add description of my system setup in the next version. I think i will wait for Takahiro to send his next version of capsule update changes. Will base my next version on top of that. Meanwhile, it would be great if you can review all the patches in the series, so that I can incorporate any review comments in my next version.
-sughosh
Best regards
Heinrich
The feature can be enabled through the following config options CONFIG_EFI_CAPSULE_UPDATE=y CONFIG_EFI_CAPSULE_ON_DISK=y CONFIG_EFI_CAPSULE_UPDATE_FIRMWARE=y CONFIG_EFI_FIRMWARE_MANAGEMENT_PROTOCOL=y CONFIG_CMD_EFIDEBUG=y
The GenerateCapsule.py script in edk2 has been used to generate the capsule file.
Todo
- Capsule authentication support.
- Implement firmware version support.
- Invoke capsule update functionality during platform boot. This might be taken up by Takahiro.
[1] - https://lists.denx.de/pipermail/u-boot/2020-March/403038.html
Sughosh Ganu (4): efidebug: capsule: Add a command to update capsule on disk semihosting: Change semihosting file operation functions into global symbols semihosting: Add support for writing to a file qemu: arm64: Add support for efi firmware management protocol routines
arch/arm/lib/semihosting.c | 48 ++++++- board/emulation/qemu-arm/Kconfig | 12 ++ board/emulation/qemu-arm/Makefile | 1 + board/emulation/qemu-arm/qemu_efi_fmp.c | 173 ++++++++++++++++++++++++ cmd/efidebug.c | 14 ++ include/semihosting.h | 14 ++ 6 files changed, 257 insertions(+), 5 deletions(-) create mode 100644 board/emulation/qemu-arm/qemu_efi_fmp.c create mode 100644 include/semihosting.h
participants (3)
-
Heinrich Schuchardt
-
Sughosh Ganu
-
Takahiro Akashi