[RFC,4/4] qemu: arm64: Add support for efi firmware management protocol routines

Message ID 20200323071201.5992-5-sughosh.ganu@linaro.org
State New
Headers show
Series
  • qemu: arm64: Add support for uefi firmware management protocol routines
Related show

Commit Message

Sughosh Ganu March 23, 2020, 7:12 a.m.
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 at 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

Comments

AKASHI Takahiro March 31, 2020, 5:50 a.m. | #1
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 at 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
>

Patch

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;
+}