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. Support has been
added for updating the u-boot image on platforms booting with arm
trusted firmware(tf-a), where the u-boot image gets booted as the BL33
payload(bl33.bin).

The feature can be enabled by the following config options

CONFIG_EFI_CAPSULE_ON_DISK=y
CONFIG_EFI_FIRMWARE_MANAGEMENT_PROTOCOL=y

Signed-off-by: Sughosh Ganu <sughosh.g...@linaro.org>
---
 board/emulation/qemu-arm/Kconfig        |  12 ++
 board/emulation/qemu-arm/Makefile       |   1 +
 board/emulation/qemu-arm/qemu_efi_fmp.c | 210 ++++++++++++++++++++++++
 3 files changed, 223 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..f1b2de8e41 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 && TFABOOT
+
+config EFI_FIRMWARE_MANAGEMENT_PROTOCOL
+       bool "EFI Firmware Management protocol for Qemu arm64 platform"
+       depends on EFI_CAPSULE_ON_DISK
+       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..9baea94e6c
--- /dev/null
+++ b/board/emulation/qemu-arm/qemu_efi_fmp.c
@@ -0,0 +1,210 @@
+// 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"
+
+#define EFI_FMP_QEMU_ARM64_UBOOT_CAPSULE_ID_GUID \
+       EFI_GUID(0xfb90808a, 0xba9a, 0x4d42, 0xb9, 0xa2, \
+                0xa7, 0xa9, 0x37, 0x14, 0x4a, 0xee)
+
+/**
+ * qemu_arm64_fmp_get_image_info() - Implement get_image_info
+ *
+ * @this:                      instance of the efi_firmware_management_protocol
+ * @image_info_size:           pointer to the size of image_info buffer
+ * @image_info:                        pointer to buffer which contains info 
on the
+ *                             image
+ * @desc_version:              pointer to version number of the
+ *                             efi_firmware_image_descriptor
+ * @desc_count:                        pointer to the number of descriptors of 
the
+ *                             firmware images on the device
+ * @desc_size:                 pointer to the size of an individual descriptor
+ * @package_version:           version for all firmware images on the device
+ * @package_version_name:      string representing the package version name
+ *
+ * Implement the get_image_info function of the
+ * efi_firmware_management_protocol for the platform.
+ *
+ * Return: status code
+ */
+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_FMP_QEMU_ARM64_UBOOT_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 < 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);
+}
+
+/**
+ * qemu_arm64_fmp_set_image() - Implement set_image
+ *
+ * @this:                      instance of the efi_firmware_management_protocol
+ * @image_index:               a unique number identifying the firmware image
+ * @image:                     pointer to the buffer containing the firmware
+ *                             image to be updated
+ * @image_size:                        pointer to size of the firmware image
+ * @vendor_code:               pointer to the vendor specific update policy
+ * @completion:                        function pointer that can be invoked to 
report
+ *                             firmware update progress
+ * @abort_reason:              string providing details if update is aborted
+ *
+ * Implement the set_image function of the efi_firmware_management_protocol
+ * for the platform. This updates the firmware image and authenticates the
+ * capsule, if authentication is enabled
+ *
+ * Return: status code
+ */
+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("Unable to open the firmware image for writing\n");
+               status = EFI_DEVICE_ERROR;
+               goto back;
+       }
+
+       ret = smh_write(fd, (void *)image, image_size);
+       if (ret == -1) {
+               printf("Error writing to the firmware image!");
+               smh_close(fd);
+               status = EFI_DEVICE_ERROR;
+               goto back;
+       }
+
+       printf("Capsule Update Complete\n");
+       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

Reply via email to