123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477 |
- // 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 <signatures.h>
- #include <linux/list.h>
- #define FMP_PAYLOAD_HDR_SIGNATURE SIGNATURE_32('M', 'S', 'S', '1')
- /**
- * struct fmp_payload_header - EDK2 header for the FMP payload
- *
- * This structure describes the header which is preprended to the
- * FMP payload by the edk2 capsule generation scripts.
- *
- * @signature: Header signature used to identify the header
- * @header_size: Size of the structure
- * @fw_version: Firmware versions used
- * @lowest_supported_version: Lowest supported version
- */
- struct fmp_payload_header {
- u32 signature;
- u32 header_size;
- u32 fw_version;
- u32 lowest_supported_version;
- };
- /* 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
- * @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) {
- log_warning("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;
- *descriptor_version = EFI_FIRMWARE_IMAGE_DESCRIPTOR_VERSION;
- *descriptor_count = dfu_num;
- *descriptor_size = sizeof(*image_info);
- *package_version = 0xffffffff; /* not supported */
- *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 =
- IMAGE_ATTRIBUTE_IMAGE_UPDATABLE |
- IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED;
- image_info[i].attributes_setting =
- IMAGE_ATTRIBUTE_IMAGE_UPDATABLE;
- /* Check if the capsule authentication is enabled */
- if (IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE))
- image_info[0].attributes_setting |=
- IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED;
- 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;
- }
- #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
- * @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)
- return EFI_EXIT(EFI_INVALID_PARAMETER);
- if (*image_info_size &&
- (!image_info || !descriptor_version || !descriptor_count ||
- !descriptor_size || !package_version || !package_version_name))
- 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);
- }
- /**
- * 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 %zd %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);
- }
- 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 */
- #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;
- /**
- * 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_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)
- {
- efi_status_t ret = EFI_SUCCESS;
- 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)
- return EFI_EXIT(EFI_INVALID_PARAMETER);
- if (*image_info_size &&
- (!image_info || !descriptor_version || !descriptor_count ||
- !descriptor_size || !package_version || !package_version_name))
- 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);
- }
- /**
- * 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_raw_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)
- {
- u32 fmp_hdr_signature;
- struct fmp_payload_header *header;
- void *capsule_payload;
- efi_status_t status;
- efi_uintn_t capsule_payload_size;
- EFI_ENTRY("%p %d %p %zd %p %p %p\n", this, image_index, image,
- image_size, vendor_code, progress, abort_reason);
- if (!image)
- return EFI_EXIT(EFI_INVALID_PARAMETER);
- /* Authenticate the capsule if authentication enabled */
- if (IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)) {
- capsule_payload = NULL;
- capsule_payload_size = 0;
- status = efi_capsule_authenticate(image, image_size,
- &capsule_payload,
- &capsule_payload_size);
- if (status == EFI_SECURITY_VIOLATION) {
- printf("Capsule authentication check failed. Aborting update\n");
- return EFI_EXIT(status);
- } else if (status != EFI_SUCCESS) {
- return EFI_EXIT(status);
- }
- debug("Capsule authentication successfull\n");
- image = capsule_payload;
- image_size = capsule_payload_size;
- } else {
- debug("Capsule authentication disabled. ");
- debug("Updating capsule without authenticating.\n");
- }
- fmp_hdr_signature = FMP_PAYLOAD_HDR_SIGNATURE;
- header = (void *)image;
- if (!memcmp(&header->signature, &fmp_hdr_signature,
- sizeof(fmp_hdr_signature))) {
- /*
- * When building the capsule with the scripts in
- * edk2, a FMP header is inserted above the capsule
- * payload. Compensate for this header to get the
- * actual payload that is to be updated.
- */
- image += header->header_size;
- image_size -= header->header_size;
- }
- if (dfu_write_by_alt(image_index - 1, (void *)image, image_size,
- NULL, NULL))
- return EFI_EXIT(EFI_DEVICE_ERROR);
- return EFI_EXIT(EFI_SUCCESS);
- }
- 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_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 */
|