static int pfm_flash_get_firmware_images_v2()

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