core/firmware/firmware_component.c (552 lines of code) (raw):

// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. #include <stddef.h> #include <stdlib.h> #include <string.h> #include "firmware_component.h" #include "platform_api.h" #include "common/buffer_util.h" #include "flash/flash_util.h" #pragma pack(push,1) /** * Format 0 of the firmware component header. */ struct firmware_component_header_format0 { uint32_t length; /**< Length of the component image data. */ uint16_t sig_length; /**< Length of the image signature. */ }; /** * Format 1 of the firmware component header. */ struct firmware_component_header_format1 { uint8_t sig_digest_type; /**< Identifier for the hash algorithm used for the signature. */ uint64_t load_addr; /**< Destination address for the firmware component. */ uint8_t build_version[FW_COMPONENT_BUILD_VERSION_LENGTH]; /**< Version number of the component. */ }; /** * Parser for the firmware component header. */ struct firmware_component_header { struct firmware_component_header_format0 format0; /**< Header format 0 fields. */ struct firmware_component_header_format1 format1; /**< Header format 1 fields. */ }; #pragma pack(pop) /** * Accessor for component header data. */ #define FW_COMPONENT_HDR(img, x) \ ((struct firmware_component_header*) (img->header.data))->format##x /** * The expected length for a format 0 firmware component header. */ #define FW_COMPONENT_HDR_LENGTH_V0 \ ((sizeof (struct firmware_component_header_format0)) + (sizeof (struct image_header_info))) /** * The expected length for a format 1 firmware component header. */ #define FW_COMPONENT_HDR_LENGTH_V1 \ (FW_COMPONENT_HDR_LENGTH_V0 + sizeof (struct firmware_component_header_format1)) /** * Minimum length for a component header with an unknown format. It must be at least as long as the * known formats. */ #define FW_COMPONENT_HDR_MIN_LENGTH \ ((sizeof (struct firmware_component_header)) + (sizeof (struct image_header_info))) /** * Maximum allowed length for a component header. */ #define FW_COMPONENT_HDR_MAX_LENGTH 1024 /** * Initialize access to a component of firmware. * * @param image The image to initialize. * @param flash The flash that contains the firmware component. * @param start_addr Base address on flash of the component image. * @param marker Image marker for the component. * * @return 0 if the component was successfully initialize or an error code. */ int firmware_component_init (struct firmware_component *image, const struct flash *flash, uint32_t start_addr, uint32_t marker) { int status; if ((image == NULL) || (flash == NULL)) { return FIRMWARE_COMPONENT_INVALID_ARGUMENT; } memset (image, 0, sizeof (struct firmware_component)); status = image_header_init (&image->header, flash, start_addr, marker, FW_COMPONENT_HDR_MAX_LENGTH); if (status != 0) { return status; } switch (image->header.info.format) { case 0: if (image->header.info.length != FW_COMPONENT_HDR_LENGTH_V0) { return FIRMWARE_COMPONENT_BAD_HEADER; } break; case 1: if (image->header.info.length != FW_COMPONENT_HDR_LENGTH_V1) { return FIRMWARE_COMPONENT_BAD_HEADER; } break; default: if (image->header.info.length < FW_COMPONENT_HDR_MIN_LENGTH) { return FIRMWARE_COMPONENT_BAD_HEADER; } } status = image_header_load_data (&image->header, flash, start_addr); if (status != 0) { return status; } image->flash = flash; image->start_addr = start_addr; return 0; } /** * Initialize access to a component of firmware. There is extra header information prepended to the * component image that is included in the image signature. * * @param image The image to initialize. * @param flash The flash that contains the firmware component. * @param start_addr Base address on flash of the component image. * @param marker Image marker for the component. * @param header_length Length of the additional header data. * * @return 0 if the component was successfully initialized or an error code. */ int firmware_component_init_with_header (struct firmware_component *image, const struct flash *flash, uint32_t start_addr, uint32_t marker, size_t header_length) { int status; status = firmware_component_init (image, flash, start_addr + header_length, marker); if (status != 0) { return status; } image->start_addr = start_addr; image->offset = header_length; return 0; } /** * Release the resources for a firmware component interface. * * @param image The image to release. */ void firmware_component_release (struct firmware_component *image) { if (image) { image_header_release (&image->header); } } /** * Get the length of the component image including the header data. * * @param image The image to query. * * @return The size of the component image. */ static size_t firmware_component_get_image_length (const struct firmware_component *image) { return image->header.info.length + FW_COMPONENT_HDR (image, 0).length + image->offset; } /** * Allocate a buffer and read the image signature from flash. * * @param image The image to query. * @param signature Output for the signature buffer. * @param sig_length The size of the signature. * * @return 0 if the signature was read or an error code. */ static int firmware_component_read_signature_data (const struct firmware_component *image, uint8_t **signature, size_t *sig_length) { int status; *sig_length = FW_COMPONENT_HDR (image, 0).sig_length; *signature = platform_malloc (*sig_length); if (signature == NULL) { return FIRMWARE_COMPONENT_NO_MEMORY; } status = firmware_component_get_signature (image, *signature, *sig_length); if (ROT_IS_ERROR (status)) { platform_free (*signature); *signature = NULL; } else { status = 0; } return status; } /** * Check the component version and get the signature information necessary to verify the component. * * @param image The image being verified. * @param expected_version The version to use for component verification. This can be null. * @param hash_out Optional buffer that will be used to store the hash of the component. * @param hash_length Size of the buffer that will be used to store the image hash. * @param digest_type Output for the type of digest that will be generated for verification. * @param signature Output for the signature of the component. This will be dynamically allocated * and must be freed by the caller. * @param sig_length Output for the length of the component signature. * @param img_length Optional output for the total length of the component, excluding the signature. * * @return 0 if validation can proceed on the component or an error code. */ static int firmware_component_prepare_for_verification (const struct firmware_component *image, const uint8_t expected_version[FW_COMPONENT_BUILD_VERSION_LENGTH], uint8_t *hash_out, size_t hash_length, enum hash_type *digest_type, uint8_t **signature, size_t *sig_length, size_t *img_length) { size_t digest_length; if ((expected_version != NULL) && (buffer_compare (expected_version, firmware_component_get_build_version (image), FW_COMPONENT_BUILD_VERSION_LENGTH) != 0)) { return FIRMWARE_COMPONENT_WRONG_VERSION; } *digest_type = firmware_component_get_hash_type (image); digest_length = hash_get_hash_length (*digest_type); if (digest_length == HASH_ENGINE_UNKNOWN_HASH) { return HASH_ENGINE_UNKNOWN_HASH; } if (hash_out && (hash_length < digest_length)) { return FIRMWARE_COMPONENT_HASH_BUFFER_TOO_SMALL; } if (img_length) { *img_length = firmware_component_get_image_length (image); } return firmware_component_read_signature_data (image, signature, sig_length); } /** * Start a hash context for calculating the digest for a component. In addition to starting the * hash context, the hash will be updated with the component header data. * * On success, the hash context will remain active for additional data to be hashed. On failure, * the hash context will not be active, i.e. it will have been canceled. * * @param image The image being hashed. * @param hash The hash engine to use for image hashing. * @param digest_type The hash algorithm that should be used for the component. * @param header Additional header data to include in the component hash. If there is no data in * memory, this can be null. If additional header data is needed, it will be read from flash. * * @return 0 if the component hash was successfully started or an error code. */ static int firmware_component_start_component_hash (const struct firmware_component *image, const struct hash_engine *hash, enum hash_type digest_type, const struct image_header *header) { int status; status = hash_start_new_hash (hash, digest_type); if (status != 0) { return status; } if (header) { status = image_header_hash_update_header (header, hash); } else if (image->offset) { status = flash_hash_update_contents (image->flash, image->start_addr, image->offset, hash); } if (status != 0) { goto exit; } status = hash->update (hash, (uint8_t*) &image->header.info, sizeof (struct image_header_info)); if (status != 0) { goto exit; } status = hash->update (hash, image->header.data, image->header.info.length - sizeof (struct image_header_info)); exit: if (status != 0) { hash->cancel (hash); } return status; } /** * Get the component digest and verify it against the component signature. * * Upon completion of this call, the hash context will be finished or canceled and the signature * will freed. * * @param image The image being verified. * @param hash The hash engine used to generate the component digest. * @param verification Context to use for signature verification. * @param digest_type The type of digest generated for the component. * @param signature The signature for the component. * @param sig_length Length of the component signature. * @param hash_out Buffer to hold the component digest. * @param hash_length Size of the digest buffer. * @param hash_type Optional output for the type digest generated. * @param load_length Optional output for the length of the component image, excluding all headers * and signature. * * @return 0 if the component verification completed successfully or an error code. */ static int firmware_component_finish_verification (const struct firmware_component *image, const struct hash_engine *hash, const struct signature_verification *verification, enum hash_type digest_type, uint8_t *signature, size_t sig_length, uint8_t *hash_out, size_t hash_length, enum hash_type *hash_type, size_t *load_length) { int status; if (hash_type) { *hash_type = digest_type; } status = signature_verification_verify_hash_and_finish_save_digest (verification, hash, NULL, 0, signature, sig_length, hash_out, hash_length, NULL); if (status != 0) { goto exit; } if (load_length != NULL) { *load_length = FW_COMPONENT_HDR (image, 0).length; } exit: platform_free (signature); return status; } /** * Verify the integrity of the component image. * * @param image The image to verify. * @param hash The hash engine to use for image validation. * @param verification Context to use for signature verification. * @param expected_version Specify a build version number for the component for verification to * succeed. If there is no version requirement, set this to null. Components that do not report a * build version in the header will always fail verification if there is an expected version * specified. * @param hash_out Optional output buffer for the hash of the image. The hash algorithm used is * determined by the component header. * @param hash_length Size of the output buffer. * @param hash_type Optional output for the type of hash used to verify the component. * * @return 0 if the component image is valid or an error code. */ int firmware_component_verification (const struct firmware_component *image, const struct hash_engine *hash, const struct signature_verification *verification, const uint8_t expected_version[FW_COMPONENT_BUILD_VERSION_LENGTH], uint8_t *hash_out, size_t hash_length, enum hash_type *hash_type) { uint8_t *signature; size_t sig_length; size_t img_length; enum hash_type digest_type; int status; if ((image == NULL) || (hash == NULL) || (verification == NULL)) { return FIRMWARE_COMPONENT_INVALID_ARGUMENT; } status = firmware_component_prepare_for_verification (image, expected_version, hash_out, hash_length, &digest_type, &signature, &sig_length, &img_length); if (status != 0) { return status; } status = flash_contents_verification (image->flash, image->start_addr, img_length, hash, digest_type, verification, signature, sig_length, hash_out, hash_length); if (hash_type) { *hash_type = digest_type; } platform_free (signature); return status; } /** * Load a firmware component from flash into memory. No validation will be done against the loaded * image. * * Any load address specified in the component header will be ignored. * * @param image The image to load. * @param load_addr The memory location where the image should be loaded. * @param max_length The largest firmware component that can be loaded. * @param load_length Optional output parameter that will contain the amount of data loaded to the * destination address. * * @return 0 if the component was loaded into memory or an error code. */ int firmware_component_load (const struct firmware_component *image, uint8_t *load_addr, size_t max_length, size_t *load_length) { int status; if ((image == NULL) || (load_addr == NULL)) { return FIRMWARE_COMPONENT_INVALID_ARGUMENT; } if (FW_COMPONENT_HDR (image, 0).length > max_length) { return FIRMWARE_COMPONENT_TOO_LARGE; } status = image->flash->read (image->flash, image->start_addr + image->offset + image->header.info.length, load_addr, FW_COMPONENT_HDR (image, 0).length); if (status != 0) { return status; } if (load_length != NULL) { *load_length = FW_COMPONENT_HDR (image, 0).length; } return 0; } /** * Load the component from flash into memory. Verify the integrity of the image in memory. * * Any load address specified in the component header will be ignored. * * Any extra header data on the component will be read from flash for verification purposes, then * discarded. * * @param image The image to load. * @param load_addr The memory location where the image should be loaded. * @param max_length The largest firmware component that can be loaded. * @param hash The hash engine to use for image validation. * @param verification Context to use for signature verification. * @param expected_version Specify a build version number for the component for verification to * succeed. If there is no version requirement, set this to null. Components that do not report a * build version in the header will always fail verification if there is an expected version * specified. If the version does not match, nothing will be loaded into memory. * @param hash_out Optional output parameter that will contain the calculated hash of the component * image. This can be null if the image hash does not need to be returned. * @param hash_length The length of the hash output buffer. * @param hash_type Optional output for the type of hash used to verify the component. * @param load_length Optional output parameter that will contain the amount of data loaded to the * destination address. * * @return 0 if the component was loaded to memory and verified as good or an error code. */ int firmware_component_load_and_verify (const struct firmware_component *image, uint8_t *load_addr, size_t max_length, const struct hash_engine *hash, const struct signature_verification *verification, const uint8_t expected_version[FW_COMPONENT_BUILD_VERSION_LENGTH], uint8_t *hash_out, size_t hash_length, enum hash_type *hash_type, size_t *load_length) { return firmware_component_load_and_verify_with_header (image, load_addr, max_length, NULL, hash, verification, expected_version, hash_out, hash_length, hash_type, load_length); } /** * Load the component from flash into memory. Verify the integrity of the image in memory. * * The component includes additional header data that must be included as part of component * verification. This additional header can be preloaded into memory. If the header is already in * memory, it is not required for the component instance to have been initialized with * firmware_component_init_with_header for the header to be included as part of verification. * * Any load address specified in the component header will be ignored. * * @param image The image to load. * @param load_addr The memory location where the image should be loaded. * @param max_length The largest firmware component that can be loaded. * @param header Additional header data that must be included as part of the component verification. * If this is null, additional header data will be read from flash for verification and discarded. * @param hash The hash engine to use for image validation. * @param verification Context to use for signature verification. * @param expected_version Specify a build version number for the component for verification to * succeed. If there is no version requirement, set this to null. Components that do not report a * build version in the header will always fail verification if there is an expected version * specified. If the version does not match, nothing will be loaded into memory. * @param hash_out Optional output parameter that will contain the calculated hash of the component * image. This can be null if the image hash does not need to be returned. * @param hash_length The length of the hash output buffer. * @param hash_type Optional output for the type of hash used to verify the component. * @param load_length Optional output parameter that will contain the amount of data loaded to the * destination address. * * @return 0 if the component was loaded to memory and verified as good or an error code. */ int firmware_component_load_and_verify_with_header (const struct firmware_component *image, uint8_t *load_addr, size_t max_length, const struct image_header *header, const struct hash_engine *hash, const struct signature_verification *verification, const uint8_t expected_version[FW_COMPONENT_BUILD_VERSION_LENGTH], uint8_t *hash_out, size_t hash_length, enum hash_type *hash_type, size_t *load_length) { uint8_t img_hash[HASH_MAX_HASH_LEN] = {0}; uint8_t *signature; size_t sig_length; enum hash_type digest_type; int status; if ((image == NULL) || (load_addr == NULL) || (hash == NULL) || (verification == NULL)) { return FIRMWARE_COMPONENT_INVALID_ARGUMENT; } if (hash_out == NULL) { hash_out = img_hash; hash_length = sizeof (img_hash); } status = firmware_component_prepare_for_verification (image, expected_version, hash_out, hash_length, &digest_type, &signature, &sig_length, NULL); if (status != 0) { return status; } status = firmware_component_start_component_hash (image, hash, digest_type, header); if (status != 0) { goto error_exit; } status = firmware_component_load (image, load_addr, max_length, NULL); if (status != 0) { goto hash_fail; } status = hash->update (hash, load_addr, FW_COMPONENT_HDR (image, 0).length); if (status != 0) { goto hash_fail; } status = firmware_component_finish_verification (image, hash, verification, digest_type, signature, sig_length, hash_out, hash_length, hash_type, load_length); buffer_zeroize (img_hash, sizeof (img_hash)); return status; hash_fail: hash->cancel (hash); error_exit: platform_free (signature); return status; } /** * Load a firmware component from flash into memory. No validation will be done against the loaded * image. * * The address where the component will be loaded will be determined from the component header. If * the component header does not provide a valid load address, the operation will fail. * * If the image on flash is encrypted, the data loaded into memory will be decrypted. * * @param image The image to load. * @param loader The handler for loading the image data into memory. * @param iv The IV to use for decrypting an encrypted image. If the image is not encrypted, * this should be null. * @param iv_length Length of the IV data. This will be ignored if the IV is null. * @param load_length Optional output parameter that will contain the amount of data loaded to the * destination address. * * @return 0 if the component was loaded into memory or an error code. */ int firmware_component_load_to_memory (const struct firmware_component *image, const struct firmware_loader *loader, const uint8_t *iv, size_t iv_length, size_t *load_length) { uint64_t phy_addr; uint8_t *load_addr; int status; if ((image == NULL) || (loader == NULL)) { return FIRMWARE_COMPONENT_INVALID_ARGUMENT; } phy_addr = firmware_component_get_load_address (image); if (phy_addr == 0) { return FIRMWARE_COMPONENT_NO_LOAD_ADDRESS; } status = loader->map_address (loader, phy_addr, FW_COMPONENT_HDR (image, 0).length, (void**) &load_addr); if (status != 0) { return status; } status = loader->load_image (loader, image->flash, image->start_addr + image->offset + image->header.info.length, FW_COMPONENT_HDR (image, 0).length, load_addr, iv, iv_length, NULL, (enum hash_type) 0, NULL, 0); loader->unmap_address (loader, load_addr); if (status != 0) { return status; } if (load_length != NULL) { *load_length = FW_COMPONENT_HDR (image, 0).length; } return 0; } /** * Load the component from flash into memory. Verify the integrity of the image in memory. * * The address where the component will be loaded will be determined from the component header. If * the component header does not provide a valid load address, the operation will fail. * * If the image on flash is encrypted, the data loaded into memory will be decrypted. * * Any extra header data on the component will be read from flash for verification purposes, then * discarded. * * @param image The image to load. * @param loader The handler for loading the image data into memory. * @param iv The IV to use for decrypting an encrypted image. If the image is not encrypted, * this should be null. * @param iv_length Length of the IV data. This will be ignored if the IV is null. * @param hash The hash engine to use for image validation. * @param verification Context to use for signature verification. * @param expected_version Specify a build version number for the component for verification to * succeed. If there is no version requirement, set this to null. Components that do not report a * build version in the header will always fail verification if there is an expected version * specified. If the version does not match, nothing will be loaded into memory. * @param hash_out Optional output parameter that will contain the calculated hash of the component * image. This can be null if the image hash does not need to be returned. * @param hash_length The length of the hash output buffer. * @param hash_type Optional output for the type of hash used to verify the component. * @param load_length Optional output parameter that will contain the amount of data loaded to the * destination address. * * @return 0 if the component was loaded to memory and verified as good or an error code. */ int firmware_component_load_to_memory_and_verify (const struct firmware_component *image, const struct firmware_loader *loader, const uint8_t *iv, size_t iv_length, const struct hash_engine *hash, const struct signature_verification *verification, const uint8_t expected_version[FW_COMPONENT_BUILD_VERSION_LENGTH], uint8_t *hash_out, size_t hash_length, enum hash_type *hash_type, size_t *load_length) { return firmware_component_load_to_memory_and_verify_with_header (image, loader, iv, iv_length, NULL, hash, verification, expected_version, hash_out, hash_length, hash_type, load_length); } /** * Load the component from flash into memory. Verify the integrity of the image in memory. * * The component includes additional header data that must be included as part of component * verification. This additional header can be preloaded into memory. If the header is already in * memory, it is not required for the component instance to have been initialized with * firmware_component_init_with_header for the header to be included as part of verification. * * The address where the component will be loaded will be determined from the component header. If * the component header does not provide a valid load address, the operation will fail. * * If the image on flash is encrypted, the data loaded into memory will be decrypted. * * @param image The image to load. * @param loader The handler for loading the image data into memory. * @param iv The IV to use for decrypting an encrypted image. If the image is not encrypted, * this should be null. * @param iv_length Length of the IV data. This will be ignored if the IV is null. * @param header Additional header data that must be included as part of the component verification. * If this is null, additional header data will be read from flash for verification and discarded. * @param hash The hash engine to use for image validation. * @param verification Context to use for signature verification. * @param expected_version Specify a build version number for the component for verification to * succeed. If there is no version requirement, set this to null. Components that do not report a * build version in the header will always fail verification if there is an expected version * specified. If the version does not match, nothing will be loaded into memory. * @param hash_out Optional output parameter that will contain the calculated hash of the component * image. This can be null if the image hash does not need to be returned. * @param hash_length The length of the hash output buffer. * @param hash_type Optional output for the type of hash used to verify the component. * @param load_length Optional output parameter that will contain the amount of data loaded to the * destination address. * * @return 0 if the component was loaded to memory and verified as good or an error code. */ int firmware_component_load_to_memory_and_verify_with_header ( const struct firmware_component *image, const struct firmware_loader *loader, const uint8_t *iv, size_t iv_length, const struct image_header *header, const struct hash_engine *hash, const struct signature_verification *verification, const uint8_t expected_version[FW_COMPONENT_BUILD_VERSION_LENGTH], uint8_t *hash_out, size_t hash_length, enum hash_type *hash_type, size_t *load_length) { uint64_t phy_addr; uint8_t *load_addr; uint8_t img_hash[HASH_MAX_HASH_LEN]; uint8_t *signature; size_t sig_length; enum hash_type digest_type; int status; if ((image == NULL) || (loader == NULL) || (hash == NULL) || (verification == NULL)) { return FIRMWARE_COMPONENT_INVALID_ARGUMENT; } phy_addr = firmware_component_get_load_address (image); if (phy_addr == 0) { return FIRMWARE_COMPONENT_NO_LOAD_ADDRESS; } if (hash_out == NULL) { hash_out = img_hash; hash_length = sizeof (img_hash); } status = firmware_component_prepare_for_verification (image, expected_version, hash_out, hash_length, &digest_type, &signature, &sig_length, NULL); if (status != 0) { return status; } status = firmware_component_start_component_hash (image, hash, digest_type, header); if (status != 0) { goto error_exit; } status = loader->map_address (loader, phy_addr, FW_COMPONENT_HDR (image, 0).length, (void**) &load_addr); if (status != 0) { goto hash_fail; } status = loader->load_image_update_digest (loader, image->flash, image->start_addr + image->offset + image->header.info.length, FW_COMPONENT_HDR (image, 0).length, load_addr, iv, iv_length, hash); loader->unmap_address (loader, load_addr); if (status != 0) { goto hash_fail; } status = firmware_component_finish_verification (image, hash, verification, digest_type, signature, sig_length, hash_out, hash_length, hash_type, load_length); buffer_zeroize (img_hash, sizeof (img_hash)); return status; hash_fail: hash->cancel (hash); error_exit: platform_free (signature); return status; } /** * Copy a firmware component to flash. Nothing will be done if the component data is already on the * flash, but the copy can be optionally forced. * * Before copying the component data, the flash is erased up to the maximum size. * * @param image The image to copy. * @param flash The flash copy the component to. * @param dest_addr Destination address in flash for the component data. * @param max_length The largest firmware component that can be copied. * @param copy_length Optional output parameter that will contain the amount of data copy to the * destination flash. * @param force_copy Copy the component data to flash regardless of the current flash contents. * * @return 0 if the component data is contained on the destination flash or an error code. */ static int firmware_component_copy_to_flash (const struct firmware_component *image, const struct flash *flash, uint32_t dest_addr, size_t max_length, size_t *copy_length, bool force_copy) { int status; if ((image == NULL) || (flash == NULL)) { return FIRMWARE_COMPONENT_INVALID_ARGUMENT; } if (FW_COMPONENT_HDR (image, 0).length > max_length) { return FIRMWARE_COMPONENT_TOO_LARGE; } if (!force_copy) { status = flash_verify_copy_ext (image->flash, image->start_addr + image->offset + image->header.info.length, flash, dest_addr, FW_COMPONENT_HDR (image, 0).length); if (status == 0) { /* Component data is already on the destination flash. */ goto exit; } else if (status != FLASH_UTIL_DATA_MISMATCH) { return status; } } status = flash_erase_region (flash, dest_addr, max_length); if (status != 0) { return status; } status = flash_copy_ext_to_blank_and_verify (flash, dest_addr, image->flash, image->start_addr + image->offset + image->header.info.length, FW_COMPONENT_HDR (image, 0).length); if (status != 0) { return status; } exit: if (copy_length != NULL) { *copy_length = FW_COMPONENT_HDR (image, 0).length; } return 0; } /** * Copy a firmware component to flash. Only the component data will be copied. This excludes * header information on the component. * * Before copying the component data, the flash is erased up to the maximum size. * * @param image The image to copy. * @param flash The flash copy the component to. * @param dest_addr Destination address in flash for the component data. * @param max_length The largest firmware component that can be copied. * @param copy_length Optional output parameter that will contain the amount of data copy to the * destination flash. * * @return 0 if the component was successfully copied to flash or an error code. */ int firmware_component_copy (const struct firmware_component *image, const struct flash *flash, uint32_t dest_addr, size_t max_length, size_t *copy_length) { return firmware_component_copy_to_flash (image, flash, dest_addr, max_length, copy_length, true); } /** * Copy a firmware component to flash if the flash doesn't already contain the component data. Only * the component data will be copied. This excludes header information on the component. * * Before copying the component data, the flash is erased up to the maximum size. * * @param image The image to copy. * @param flash The flash copy the component to. * @param dest_addr Destination address in flash for the component data. * @param max_length The largest firmware component that can be copied. * @param copy_length Optional output parameter that will contain the amount of data copy to the * destination flash. * * @return 0 if the component data is contained on the destination flash or an error code. */ int firmware_component_compare_and_copy (const struct firmware_component *image, const struct flash *flash, uint32_t dest_addr, size_t max_length, size_t *copy_length) { return firmware_component_copy_to_flash (image, flash, dest_addr, max_length, copy_length, false); } /** * Get the length of the signature on the component image. * * @param image The image to query. * * @return The length of the image signature. */ size_t firmware_component_get_signature_length (const struct firmware_component *image) { if (image) { return FW_COMPONENT_HDR (image, 0).sig_length; } else { return 0; } } /** * Get the signature of the component image. * * @param image The image to query. * @param sig_out Output for the component signature. * @param sig_length Size of the output buffer. * * @return The length of the signature in the buffer or an error code. Use ROT_IS_ERROR to check * the return for an error. */ int firmware_component_get_signature (const struct firmware_component *image, uint8_t *sig_out, size_t sig_length) { size_t length; size_t offset; int status; if ((image == NULL) || (sig_out == NULL)) { return FIRMWARE_COMPONENT_INVALID_ARGUMENT; } length = FW_COMPONENT_HDR (image, 0).sig_length; offset = firmware_component_get_image_length (image); if (sig_length < length) { return FIRMWARE_COMPONENT_SIG_BUFFER_TOO_SMALL; } status = image->flash->read (image->flash, image->start_addr + offset, sig_out, length); if (status != 0) { return status; } return length; } /** * Get the type of hash used for the signature of the component. * * @param image The image to query. * * @return The type of hash to use for component verification. */ enum hash_type firmware_component_get_hash_type (const struct firmware_component *image) { if (image && (image->header.info.format > 0)) { return (enum hash_type) FW_COMPONENT_HDR (image, 1).sig_digest_type; } else { return HASH_TYPE_SHA256; } } /** * Calculate the hash for the component image. The hash that is calculated can be used for * signature verification. * * @param image The image to hash. * @param hash The hash engine to use to generate the hash. * @param hash_out Output for the calculated hash. * @param hash_length Size of the output buffer. * @param hash_type Optional output for the algorithm used to generate the hash. This can be null * if it is not needed. * * @return Length of the calculated hash or an error code. Use ROT_IS_ERROR to check the return for * an error. */ int firmware_component_get_hash (const struct firmware_component *image, const struct hash_engine *hash, uint8_t *hash_out, size_t hash_length, enum hash_type *hash_type) { size_t length; enum hash_type digest_type; size_t digest_length; int status; if ((image == NULL) || (hash == NULL) || (hash_out == NULL)) { return FIRMWARE_COMPONENT_INVALID_ARGUMENT; } digest_type = firmware_component_get_hash_type (image); digest_length = hash_get_hash_length (digest_type); if (digest_length == HASH_ENGINE_UNKNOWN_HASH) { return HASH_ENGINE_UNKNOWN_HASH; } if (hash_length < digest_length) { return FIRMWARE_COMPONENT_HASH_BUFFER_TOO_SMALL; } length = firmware_component_get_image_length (image); status = flash_hash_contents (image->flash, image->start_addr, length, hash, digest_type, hash_out, hash_length); if (status != 0) { return status; } if (hash_type) { *hash_type = digest_type; } return digest_length; } /** * Get the destination address for the component data. * * @param image The image to query. * * @return The target address for the image. This will be 0 if no address is specified. */ uint64_t firmware_component_get_load_address (const struct firmware_component *image) { if (image && (image->header.info.format > 0)) { return FW_COMPONENT_HDR (image, 1).load_addr; } else { return 0; } } /** * Get the build version number for the component. * * @param image The image to query. * * @return Reference to the opaque version number in the component header or null if there is no * version available. The build version is an array of FW_COMPONENT_BUILD_VERSION_LENGTH bytes. */ const uint8_t* firmware_component_get_build_version (const struct firmware_component *image) { if (image && (image->header.info.format > 0)) { return FW_COMPONENT_HDR (image, 1).build_version; } else { return NULL; } } /** * Get the address for the first byte of firmware data in the component image. * * @param image The image to query. * * @return The flash address for the start of firmware data. */ uint32_t firmware_component_get_data_addr (const struct firmware_component *image) { if (image) { return image->start_addr + image->offset + image->header.info.length; } else { return 0; } } /** * Get the length of the component. This is just the length of the component image data and does * not include any header or signature bytes. * * @param image The image to query. * * @return The size of the component image. */ size_t firmware_component_get_length (const struct firmware_component *image) { if (image) { return FW_COMPONENT_HDR (image, 0).length; } else { return 0; } } /** * Get the total length of the component. This length includes everything that makes up the * component, including the image data, header, signature, and any additional data prepended to the * component. This represents the total size of the component as it would exist in storage. * * @param image The image to query. * * @return The total size of the component. */ size_t firmware_component_get_total_length (const struct firmware_component *image) { if (image) { return firmware_component_get_image_length (image) + FW_COMPONENT_HDR (image, 0).sig_length; } else { return 0; } } /** * Get the address that marks the end of the component image. This will be the address immediately * following the last byte of the component image, including all image metadata like headers, * footers, and signatures. * * @param image The image to query. * * @return The address at the end of the image. */ uint32_t firmware_component_get_image_end (const struct firmware_component *image) { if (image) { return image->start_addr + firmware_component_get_total_length (image); } else { return 0; } }