in core/manifest/pfm/pfm_flash.c [1487:1656]
static int pfm_flash_get_firmware_images_v2 (const struct pfm_flash *pfm, const char *fw,
const char *version, struct pfm_image_list *img_list)
{
union {
struct pfm_firmware_element fw_element;
struct pfm_firmware_version_element ver_element;
} buffer;
struct pfm_fw_version_element_image *img;
struct pfm_flash_region *img_regions;
struct pfm_image_hash *images;
struct flash_region *region_list;
uint8_t *element;
uint8_t entry;
size_t element_len;
int version_pad;
uint32_t offset;
uint32_t buf_offset;
size_t img_len;
size_t i;
size_t j;
int status;
if ((pfm->state->flash_dev_format < 0) || (pfm->state->flash_dev.fw_count == 0)) {
return PFM_UNKNOWN_FIRMWARE;
}
status = pfm_flash_find_firmware_element_v2 (pfm, fw, &buffer.fw_element, &entry);
if (status != 0) {
return status;
}
status = pfm_flash_find_firmware_version_element_v2 (pfm, version, &entry, &buffer.ver_element,
&element_len);
if (status != 0) {
return status;
}
img_list->count = buffer.ver_element.img_count;
img_list->images_sig = NULL;
img_list->images_hash = platform_calloc (img_list->count, sizeof (struct pfm_image_hash));
if (img_list->images_hash == NULL) {
return PFM_NO_MEMORY;
}
version_pad = buffer.ver_element.version_length % 4;
if (version_pad != 0) {
version_pad = 4 - version_pad;
}
offset = (sizeof (buffer.ver_element) - sizeof (buffer.ver_element.version)) +
buffer.ver_element.version_length + version_pad +
(sizeof (struct pfm_fw_version_element_rw_region) * buffer.ver_element.rw_count);
buf_offset = offset - (sizeof (buffer.ver_element) - sizeof (buffer.ver_element.version));
element_len -= (sizeof (buffer.ver_element) - sizeof (buffer.ver_element.version));
for (i = 0; i < img_list->count; i++) {
img = (struct pfm_fw_version_element_image*) &buffer.ver_element.version[buf_offset];
if (buf_offset >= sizeof (buffer.ver_element.version)) {
img = NULL;
}
else if ((buf_offset + pfm_flash_get_image_length_v2 (img)) >
sizeof (buffer.ver_element.version)) {
img = NULL;
}
if (img == NULL) {
/* The complete image definition does not reside in memory. We may have part of the
* image information or none of it. Either way, use the version buffer to read the
* element again starting at the beginning of the image information. */
element = buffer.ver_element.version;
status = manifest_flash_read_element_data (&pfm->base_flash, pfm->base_flash.hash,
PFM_FIRMWARE_VERSION, entry, PFM_FIRMWARE, offset, NULL, NULL, NULL, &element,
sizeof (buffer.ver_element.version));
if (ROT_IS_ERROR (status)) {
goto error;
}
element_len = status;
buf_offset = 0;
img = (struct pfm_fw_version_element_image*) buffer.ver_element.version;
}
if ((element_len - buf_offset) < sizeof (struct pfm_fw_version_element_image)) {
status = PFM_MALFORMED_FW_VER_ELEMENT;
goto error;
}
if (img->region_count == 0) {
status = PFM_FW_IMAGE_UNSUPPORTED;
goto error;
}
img_len = pfm_flash_get_image_length_v2 (img);
if (img_len > sizeof (buffer.ver_element.version)) {
/* We cannot read the entire image information in a single read, so we cannot support
* this image. Handling larger image definitions would greatly complicate processing
* for very little gain. An image that needs to be defined with that many regions would
* be an extremely rare case. If it does happen, it can be worked around by adding more
* image definitions with separate hashes instead of using many regions and a single
* hash. */
status = PFM_FW_IMAGE_UNSUPPORTED;
goto error;
}
if ((element_len - buf_offset) < img_len) {
status = PFM_MALFORMED_FW_VER_ELEMENT;
goto error;
}
buf_offset += sizeof (struct pfm_fw_version_element_image);
images = (struct pfm_image_hash*) img_list->images_hash;
images[i].count = img->region_count;
images[i].regions = platform_calloc (images[i].count, sizeof (struct flash_region));
if (images[i].regions == NULL) {
status = PFM_NO_MEMORY;
goto error;
}
region_list = (struct flash_region*) images[i].regions;
switch (img->hash_type) {
case MANIFEST_HASH_SHA256:
images[i].hash_type = HASH_TYPE_SHA256;
images[i].hash_length = SHA256_HASH_LENGTH;
break;
case MANIFEST_HASH_SHA384:
images[i].hash_type = HASH_TYPE_SHA384;
images[i].hash_length = SHA384_HASH_LENGTH;
break;
case MANIFEST_HASH_SHA512:
images[i].hash_type = HASH_TYPE_SHA512;
images[i].hash_length = SHA512_HASH_LENGTH;
break;
default:
status = PFM_UNKNOWN_HASH_TYPE;
goto error;
}
memcpy (images[i].hash, &buffer.ver_element.version[buf_offset], images[i].hash_length);
images[i].always_validate = img->flags & PFM_IMAGE_MUST_VALIDATE;
buf_offset += images[i].hash_length;
img_regions = (struct pfm_flash_region*) &buffer.ver_element.version[buf_offset];
for (j = 0; j < images[i].count; j++) {
if (img_regions[j].end_addr <= img_regions[j].start_addr) {
status = PFM_MALFORMED_FW_VER_ELEMENT;
goto error;
}
region_list[j].start_addr = img_regions[j].start_addr;
region_list[j].length = (img_regions[j].end_addr - img_regions[j].start_addr) + 1;
}
buf_offset += (sizeof (struct pfm_flash_region) * images[i].count);
offset += img_len;
}
return 0;
error:
pfm_flash_free_firmware_images (&pfm->base, img_list);
return status;
}