diff mbox series

[RFC,04/10] FWU: Add metadata access functions for GPT partitioned block devices

Message ID 20211125070146.2389-5-sughosh.ganu@linaro.org
State Superseded
Headers show
Series FWU: Add support for FWU Multi Bank Update feature | expand

Commit Message

Sughosh Ganu Nov. 25, 2021, 7:01 a.m. UTC
In the FWU Multi Bank Update feature, the information about the
updatable images is stored as part of the metadata, on a separate
partition. Add functions for reading from and writing to the metadata
when the updatable images and the metadata are stored on a block
device which is formated with GPT based partition scheme.

Signed-off-by: Sughosh Ganu <sughosh.ganu@linaro.org>
---
 lib/fwu_updates/fwu_metadata_gpt_blk.c | 716 +++++++++++++++++++++++++
 1 file changed, 716 insertions(+)
 create mode 100644 lib/fwu_updates/fwu_metadata_gpt_blk.c

Comments

Patrick DELAUNAY Dec. 7, 2021, 2:23 p.m. UTC | #1
Hi Sughsoh,

On 11/25/21 8:01 AM, Sughosh Ganu wrote:
> In the FWU Multi Bank Update feature, the information about the
> updatable images is stored as part of the metadata, on a separate
> partition. Add functions for reading from and writing to the metadata
> when the updatable images and the metadata are stored on a block
> device which is formated with GPT based partition scheme.
>
> Signed-off-by: Sughosh Ganu <sughosh.ganu@linaro.org>
> ---
>   lib/fwu_updates/fwu_metadata_gpt_blk.c | 716 +++++++++++++++++++++++++
>   1 file changed, 716 insertions(+)
>   create mode 100644 lib/fwu_updates/fwu_metadata_gpt_blk.c
>
> diff --git a/lib/fwu_updates/fwu_metadata_gpt_blk.c b/lib/fwu_updates/fwu_metadata_gpt_blk.c
> new file mode 100644
> index 0000000000..98cc53f706
> --- /dev/null
> +++ b/lib/fwu_updates/fwu_metadata_gpt_blk.c
> @@ -0,0 +1,716 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Copyright (c) 2021, Linaro Limited
> + */
> +
> +#include <blk.h>
> +#include <efi_loader.h>
> +#include <fwu_metadata.h>
> +#include <malloc.h>
> +#include <memalign.h>
> +#include <part.h>
> +#include <part_efi.h>
> +
> +#include <linux/errno.h>
> +#include <linux/types.h>
> +#include <u-boot/crc.h>
> +
> +#define PRIMARY_VALID		0x1
> +#define SECONDARY_VALID		0x2
> +
> +#define MDATA_READ		(u8)0x1
> +#define MDATA_WRITE		(u8)0x2
> +
> +#define IMAGE_ACCEPT_SET	(u8)0x1
> +#define IMAGE_ACCEPT_CLEAR	(u8)0x2
> +
> +static int gpt_verify_metadata(struct fwu_metadata *metadata, bool pri_part)
> +{
> +	u32 calc_crc32;
> +	void *buf;
> +
> +	buf = &metadata->version;
> +	calc_crc32 = crc32(0, buf, sizeof(*metadata) - sizeof(u32));
> +
> +	if (calc_crc32 != metadata->crc32) {
> +		log_err("crc32 check failed for %s metadata partition\n",
> +			pri_part ? "primary" : "secondary");
> +		return -1;
> +	}
> +
> +	return 0;
> +}
> +
> +static int gpt_get_metadata_partitions(struct blk_desc *desc,
> +				       u32 *primary_mpart,
> +				       u32 *secondary_mpart)
> +{
> +	int i, ret;
> +	u32 nparts, mparts;
> +	gpt_entry *gpt_pte = NULL;
> +	const efi_guid_t fwu_metadata_guid = FWU_METADATA_GUID;
> +
> +	ALLOC_CACHE_ALIGN_BUFFER_PAD(gpt_header, gpt_head, 1,
> +				     desc->blksz);
> +
> +	ret = get_gpt_hdr_parts(desc, gpt_head, &gpt_pte);
> +	if (ret < 0) {
> +		log_err("Error getting GPT header and partitions\n");
> +		ret = -EIO;
> +		goto out;
> +	}
> +
> +	nparts = gpt_head->num_partition_entries;
> +	for (i = 0, mparts = 0; i < nparts; i++) {
> +		if (!guidcmp(&fwu_metadata_guid,
> +			     &gpt_pte[i].partition_type_guid)) {
> +			++mparts;
> +			if (!*primary_mpart)
> +				*primary_mpart = i + 1;
> +			else
> +				*secondary_mpart = i + 1;
> +		}
> +	}
> +
> +	if (mparts != 2) {
> +		log_err("Expect two copies of the metadata instead of %d\n",
> +			mparts);
> +		ret = -EINVAL;
> +	} else {
> +		ret = 0;
> +	}
> +out:
> +	free(gpt_pte);
> +
> +	return ret;
> +}
> +
> +static int gpt_get_metadata_disk_part(struct blk_desc *desc,
> +				      struct disk_partition *info,
> +				      u32 part_num)
> +{
> +	int ret;
> +	char *metadata_guid_str = "8a7a84a0-8387-40f6-ab41-a8b9a5a60d23";
> +
> +	ret = part_get_info(desc, part_num, info);
> +	if (ret < 0) {
> +		log_err("Unable to get the partition info for the metadata part %d",
> +			part_num);
> +		return -1;
> +	}
> +
> +	/* Check that it is indeed the metadata partition */
> +	if (!strncmp(info->type_guid, metadata_guid_str, UUID_STR_LEN)) {
> +		/* Found the metadata partition */
> +		return 0;
> +	}
> +
> +	return -1;
> +}
> +
> +static int gpt_read_write_metadata(struct blk_desc *desc,
> +				   struct fwu_metadata *metadata,
> +				   u8 access, u32 part_num)
> +{
> +	int ret;
> +	u32 len, blk_start, blkcnt;
> +	struct disk_partition info;
> +
> +	ALLOC_CACHE_ALIGN_BUFFER_PAD(struct fwu_metadata, mdata, 1, desc->blksz);
> +
> +	ret = gpt_get_metadata_disk_part(desc, &info, part_num);
> +	if (ret < 0) {
> +		printf("Unable to get the metadata partition\n");
> +		return -ENODEV;
> +	}
> +
> +	len = sizeof(*metadata);
> +	blkcnt = BLOCK_CNT(len, desc);
> +	if (blkcnt > info.size) {
> +		log_err("Block count exceeds metadata partition size\n");
> +		return -ERANGE;
> +	}
> +
> +	blk_start = info.start;
> +	if (access == MDATA_READ) {
> +		if (blk_dread(desc, blk_start, blkcnt, mdata) != blkcnt) {
> +			log_err("Error reading metadata from the device\n");
> +			return -EIO;
> +		}
> +		memcpy(metadata, mdata, sizeof(struct fwu_metadata));
> +	} else {
> +		if (blk_dwrite(desc, blk_start, blkcnt, metadata) != blkcnt) {
> +			log_err("Error writing metadata to the device\n");
> +			return -EIO;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +static int gpt_read_metadata(struct blk_desc *desc,
> +			     struct fwu_metadata *metadata, u32 part_num)
> +{
> +	return gpt_read_write_metadata(desc, metadata, MDATA_READ, part_num);
> +}
> +
> +static int gpt_write_metadata_partition(struct blk_desc *desc,
> +					struct fwu_metadata *metadata,
> +					u32 part_num)
> +{
> +	return gpt_read_write_metadata(desc, metadata, MDATA_WRITE, part_num);
> +}
> +
> +static int gpt_update_metadata(struct fwu_metadata *metadata)
> +{
> +	int ret;
> +	struct blk_desc *desc;
> +	u32 primary_mpart, secondary_mpart;
> +
> +	ret = fwu_plat_get_blk_desc(&desc);
> +	if (ret < 0) {
> +		log_err("Block device not found\n");
> +		return -ENODEV;
> +	}
> +
> +	primary_mpart = secondary_mpart = 0;
> +	ret = gpt_get_metadata_partitions(desc, &primary_mpart,
> +					  &secondary_mpart);
> +
> +	if (ret < 0) {
> +		log_err("Error getting the metadata partitions\n");
> +		return -ENODEV;
> +	}
> +
> +	/* First write the primary partition*/
> +	ret = gpt_write_metadata_partition(desc, metadata, primary_mpart);
> +	if (ret < 0) {
> +		log_err("Updating primary metadata partition failed\n");
> +		return ret;
> +	}
> +
> +	/* And now the replica */
> +	ret = gpt_write_metadata_partition(desc, metadata, secondary_mpart);
> +	if (ret < 0) {
> +		log_err("Updating secondary metadata partition failed\n");
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static int gpt_get_valid_metadata(struct fwu_metadata **metadata)
> +{
> +	int ret;
> +	struct blk_desc *desc;
> +	u32 primary_mpart, secondary_mpart;
> +
> +	ret = fwu_plat_get_blk_desc(&desc);
> +	if (ret < 0) {
> +		log_err("Block device not found\n");
> +		return -ENODEV;
> +	}
> +
> +	primary_mpart = secondary_mpart = 0;
> +	ret = gpt_get_metadata_partitions(desc, &primary_mpart,
> +					  &secondary_mpart);
> +
> +	if (ret < 0) {
> +		log_err("Error getting the metadata partitions\n");
> +		return -ENODEV;
> +	}
> +
> +	*metadata = malloc(sizeof(struct fwu_metadata));
> +	if (!*metadata) {
> +		log_err("Unable to allocate memory for reading metadata\n");
> +		return -ENOMEM;
> +	}
> +
> +	ret = gpt_read_metadata(desc, *metadata, primary_mpart);
> +	if (ret < 0) {
> +		log_err("Failed to read the metadata from the device\n");
> +		return -EIO;
> +	}
> +
> +	ret = gpt_verify_metadata(*metadata, 1);
> +	if (!ret)
> +		return 0;
> +
> +	/*
> +	 * Verification of the primary metadata copy failed.
> +	 * Try to read the replica.
> +	 */
> +	memset(*metadata, 0, sizeof(struct fwu_metadata));
> +	ret = gpt_read_metadata(desc, *metadata, secondary_mpart);
> +	if (ret < 0) {
> +		log_err("Failed to read the metadata from the device\n");
> +		return -EIO;
> +	}
> +
> +	ret = gpt_verify_metadata(*metadata, 0);
> +	if (!ret)
> +		return 0;
> +
> +	/* Both the metadata copies are corrupted. */
> +	return -1;
> +}
> +
> +static int gpt_check_metadata_validity(void)
> +{
> +	int ret;
> +	struct blk_desc *desc;
> +	struct fwu_metadata *pri_metadata;
> +	struct fwu_metadata *secondary_metadata;
> +	u32 primary_mpart, secondary_mpart;
> +	u32 valid_partitions;
> +
> +	ret = fwu_plat_get_blk_desc(&desc);
> +	if (ret < 0) {
> +		log_err("Block device not found\n");
> +		return -ENODEV;
> +	}
> +
> +	/*
> +	 * Two metadata partitions are expected.
> +	 * If we don't have two, user needs to create
> +	 * them first
> +	 */
> +	primary_mpart = secondary_mpart = 0;
> +	valid_partitions = 0;
> +	ret = gpt_get_metadata_partitions(desc, &primary_mpart,
> +					  &secondary_mpart);
> +
> +	if (ret < 0) {
> +		log_err("Error getting the metadata partitions\n");
> +		return -ENODEV;
> +	}
> +
> +	pri_metadata = malloc(sizeof(*pri_metadata));
> +	if (!pri_metadata) {
> +		log_err("Unable to allocate memory for reading metadata\n");
> +		return -ENOMEM;
> +	}
> +
> +	secondary_metadata = malloc(sizeof(*secondary_metadata));
> +	if (!secondary_metadata) {
> +		log_err("Unable to allocate memory for reading metadata\n");
> +		return -ENOMEM;
> +	}
> +
> +	ret = gpt_read_metadata(desc, pri_metadata, primary_mpart);
> +	if (ret < 0) {
> +		log_err("Failed to read the metadata from the device\n");
> +		ret = -EIO;
> +		goto out;
> +	}
> +
> +	ret = gpt_verify_metadata(pri_metadata, 1);
> +	if (!ret)
> +		valid_partitions |= PRIMARY_VALID;
> +
> +	/* Now check the secondary partition */
> +	ret = gpt_read_metadata(desc, secondary_metadata, secondary_mpart);
> +	if (ret < 0) {
> +		log_err("Failed to read the metadata from the device\n");
> +		ret = -EIO;
> +		goto out;
> +	}
> +
> +	ret = gpt_verify_metadata(secondary_metadata, 0);
> +	if (!ret)
> +		valid_partitions |= SECONDARY_VALID;
> +
> +	if (valid_partitions == (PRIMARY_VALID | SECONDARY_VALID)) {
> +		ret = -1;
> +		/*
> +		 * Before returning, check that both the
> +		 * metadata copies are the same. If not,
> +		 * the metadata copies need to be
> +		 * re-populated.
> +		 */
> +		if (!memcmp(pri_metadata, secondary_metadata,
> +			    sizeof(*pri_metadata)))
> +			ret = 0;
> +		goto out;
> +	} else if (valid_partitions == SECONDARY_VALID) {
> +		ret = gpt_write_metadata_partition(desc, secondary_metadata,
> +						   primary_mpart);
> +		if (ret < 0) {
> +			log_err("Restoring primary metadata partition failed\n");
> +			goto out;
> +		}
> +	} else if (valid_partitions == PRIMARY_VALID) {
> +		ret = gpt_write_metadata_partition(desc, pri_metadata,
> +						   secondary_mpart);
> +		if (ret < 0) {
> +			log_err("Restoring secondary metadata partition failed\n");
> +			goto out;
> +		}
> +	} else {
> +		ret = -1;
> +	}
> +
> +out:
> +	free(pri_metadata);
> +
> +	return ret;
> +}
> +
> +static int gpt_fill_partition_guid_array(struct blk_desc *desc,
> +					 efi_guid_t **part_guid_arr,
> +					 u32 *nparts)
> +{
> +	int ret, i;
> +	u32 parts;
> +	gpt_entry *gpt_pte = NULL;
> +	const efi_guid_t null_guid = NULL_GUID;
> +
> +	ALLOC_CACHE_ALIGN_BUFFER_PAD(gpt_header, gpt_head, 1,
> +				     desc->blksz);
> +
> +	ret = get_gpt_hdr_parts(desc, gpt_head, &gpt_pte);
> +	if (ret < 0) {
> +		log_err("Error getting GPT header and partitions\n");
> +		ret = -EIO;
> +		goto out;
> +	}
> +
> +	*nparts = gpt_head->num_partition_entries;
> +
> +	/*
> +	 * There could be a scenario where the number of partition entries
> +	 * configured in the GPT header is the default value of 128. Find
> +	 * the actual number of populated partitioned entries
> +	 */
> +	for (i = 0, parts = 0; i < *nparts; i++) {
> +		if (!guidcmp(&gpt_pte[i].partition_type_guid, &null_guid))
> +			continue;
> +		++parts;
> +	}
> +
> +	*nparts = parts;
> +	*part_guid_arr = malloc(sizeof(efi_guid_t) * *nparts);
> +	if (!part_guid_arr) {
> +		log_err("Unable to allocate memory for guid array\n");
> +		ret = -ENOMEM;
> +		goto out;
> +	}
> +
> +	for (i = 0; i < *nparts; i++) {
> +		guidcpy((*part_guid_arr + i),
> +			&gpt_pte[i].partition_type_guid);
> +	}
> +
> +out:
> +	free(gpt_pte);
> +	return ret;
> +}
> +
> +int fwu_gpt_get_active_index(u32 *active_idx)
> +{
> +	int ret;
> +	struct fwu_metadata *metadata;
> +
> +	ret = gpt_get_valid_metadata(&metadata);
> +	if (ret < 0) {
> +		log_err("Unable to get valid metadata\n");
> +		goto out;
> +	}
> +
> +	/*
> +	 * Found the metadata partition, now read the active_index
> +	 * value
> +	 */
> +	*active_idx = metadata->active_index;
> +	if (*active_idx > CONFIG_FWU_NUM_BANKS - 1) {
> +		printf("Active index value read is incorrect\n");
> +		ret = -EINVAL;
> +		goto out;
> +	}
> +
> +out:
> +	free(metadata);
> +
> +	return ret;
> +}
> +
> +static int gpt_get_image_alt_num(struct blk_desc *desc,
> +				 efi_guid_t image_type_id,
> +				 u32 update_bank, int *alt_no)
> +{
> +	int ret, i;
> +	u32 nparts;
> +	gpt_entry *gpt_pte = NULL;
> +	struct fwu_metadata *metadata;
> +	struct fwu_image_entry *img_entry;
> +	struct fwu_image_bank_info *img_bank_info;
> +	efi_guid_t unique_part_guid;
> +	efi_guid_t image_guid = NULL_GUID;
> +
> +	ALLOC_CACHE_ALIGN_BUFFER_PAD(gpt_header, gpt_head, 1,
> +				     desc->blksz);
> +
> +	ret = gpt_get_valid_metadata(&metadata);
> +	if (ret < 0) {
> +		log_err("Unable to read valid metadata\n");
> +		goto out;
> +	}
> +
> +	/*
> +	 * The metadata has been read. Now get the image_uuid for the
> +	 * image with the update_bank.
> +	 */
> +	for (i = 0; i < CONFIG_FWU_NUM_IMAGES_PER_BANK; i++) {
> +		if (!guidcmp(&image_type_id,
> +			     &metadata->img_entry[i].image_type_uuid)) {
> +			img_entry = &metadata->img_entry[i];
> +			img_bank_info = &img_entry->img_bank_info[update_bank];
> +			guidcpy(&image_guid, &img_bank_info->image_uuid);
> +		}
> +	}
> +
> +	/*
> +	 * Now read the GPT Partition Table Entries to find a matching
> +	 * partition with UniquePartitionGuid value. We need to iterate
> +	 * through all the GPT partitions since they might be in any
> +	 * order
> +	 */
> +	ret = get_gpt_hdr_parts(desc, gpt_head, &gpt_pte);
> +	if (ret < 0) {
> +		log_err("Error getting GPT header and partitions\n");
> +		ret = -EIO;
> +		goto out;
> +	}
> +
> +	nparts = gpt_head->num_partition_entries;
> +
> +	for (i = 0; i < nparts; i++) {
> +		unique_part_guid = gpt_pte[i].unique_partition_guid;
> +		if (!guidcmp(&unique_part_guid, &image_guid)) {
> +			/* Found the partition */
> +			*alt_no = i + 1;

here we found the partition = <i>

but I am not sure of the hard-coded mapping here <alt> = <part_id> + 1

at least I think it should be configured for each board

for example with a weak function ?

*alt_no = board_get_dfu_alt_fwu(i);

with:

/* default => dfu alternate use the same order than parttion */
weak int board_get_dfu_alt_fwu(int part) {
	return part + 1;
)

But I think we can make something more dynamic here by parsing DFU information
to found the alternate associated to the requested partition

by example by using dfu_get_entity()
=> search on the "mmc" dev / part

int mmc_get_dfu_alt_fwu(int dev, int part) {

for (i = 0; i < nb_alt; i++) {
	dfu = dfu_get_entity(i);
	/* only SDCard / eMMC suport with GPT partitioning */
	if (dfu->dev_type != DFU_DEV_MMC)
		continue;
	if (dfu->layout = DFU_RAW_ADDR &&
             dfu->data.mmc.dev == dev &&
             dfu->data.mmc.part == part)
		return
  }

this request can support boards with several MMC instances
as the partitions are not the necessary the first one

for example trying to update partition in mmc 1
when partition on mmc 0 are the first one in dfu alternate.

> +			break;
> +		}
> +	}
> +
> +	if (i == nparts) {
> +		log_err("Partition with the image guid not found\n");
> +		ret = -EINVAL;
> +	}
> +
> +out:
> +	free(metadata);
> +	free(gpt_pte);
> +	return ret;
> +}
> +
> +int fwu_gpt_update_active_index(u32 active_idx)
> +{
> +	int ret;
> +	void *buf;
> +	u32 cur_active_index;
> +	struct fwu_metadata *metadata;
> +
> +	if (active_idx > CONFIG_FWU_NUM_BANKS) {
> +		printf("Active index value to be updated is incorrect\n");
> +		return -1;
> +	}
> +
> +	ret = gpt_get_valid_metadata(&metadata);
> +	if (ret < 0) {
> +		log_err("Unable to get valid metadata\n");
> +		goto out;
> +	}
> +
> +	/*
> +	 * Update the active index and previous_active_index fields
> +	 * in the metadata
> +	 */
> +	cur_active_index = metadata->active_index;
> +	metadata->active_index = active_idx;
> +	metadata->previous_active_index = cur_active_index;
> +
> +	/*
> +	 * Calculate the crc32 for the updated metadata
> +	 * and put the updated value in the metadata crc32
> +	 * field
> +	 */
> +	buf = &metadata->version;
> +	metadata->crc32 = crc32(0, buf, sizeof(*metadata) - sizeof(u32));
> +
> +	/*
> +	 * Now write this updated metadata to both the
> +	 * metadata partitions
> +	 */
> +	ret = gpt_update_metadata(metadata);
> +	if (ret < 0) {
> +		log_err("Failed to update metadata partitions\n");
> +		ret = -EIO;
> +	}
> +
> +out:
> +	free(metadata);
> +
> +	return ret;
> +}
> +
> +int fwu_gpt_fill_partition_guid_array(efi_guid_t **part_guid_arr, u32 *nparts)
> +{
> +	int ret;
> +	struct blk_desc *desc;
> +
> +	ret = fwu_plat_get_blk_desc(&desc);
> +	if (ret < 0) {
> +		log_err("Block device not found\n");
> +		return -ENODEV;
> +	}
> +
> +	return gpt_fill_partition_guid_array(desc, part_guid_arr, nparts);
> +}
> +
> +int fwu_gpt_get_image_alt_num(efi_guid_t image_type_id, u32 update_bank,
> +			      int *alt_no)
> +{
> +	int ret;
> +	struct blk_desc *desc;
> +
> +	ret = fwu_plat_get_blk_desc(&desc);
> +	if (ret < 0) {
> +		log_err("Block device not found\n");
> +		return -ENODEV;
> +	}
> +
> +	return gpt_get_image_alt_num(desc, image_type_id, update_bank, alt_no);
> +}
> +
> +int fwu_gpt_metadata_check(void)
> +{
> +	/*
> +	 * Check if both the copies of the metadata are valid.
> +	 * If one has gone bad, restore it from the other good
> +	 * copy.
> +	 */
> +	return gpt_check_metadata_validity();
> +}
> +
> +int fwu_gpt_get_metadata(struct fwu_metadata **metadata)
> +{
> +	return gpt_get_valid_metadata(metadata);
> +}
> +
> +int fwu_gpt_revert_boot_index(u32 *active_idx)
> +{
> +	int ret;
> +	void *buf;
> +	u32 cur_active_index;
> +	struct fwu_metadata *metadata;
> +
> +	ret = gpt_get_valid_metadata(&metadata);
> +	if (ret < 0) {
> +		log_err("Unable to get valid metadata\n");
> +		goto out;
> +	}
> +
> +	/*
> +	 * Swap the active index and previous_active_index fields
> +	 * in the metadata
> +	 */
> +	cur_active_index = metadata->active_index;
> +	metadata->active_index = metadata->previous_active_index;
> +	metadata->previous_active_index = cur_active_index;
> +	*active_idx = metadata->active_index;
> +
> +	/*
> +	 * Calculate the crc32 for the updated metadata
> +	 * and put the updated value in the metadata crc32
> +	 * field
> +	 */
> +	buf = &metadata->version;
> +	metadata->crc32 = crc32(0, buf, sizeof(*metadata) - sizeof(u32));
> +
> +	/*
> +	 * Now write this updated metadata to both the
> +	 * metadata partitions
> +	 */
> +	ret = gpt_update_metadata(metadata);
> +	if (ret < 0) {
> +		log_err("Failed to update metadata partitions\n");
> +		ret = -EIO;
> +	}
> +
> +out:
> +	free(metadata);
> +
> +	return ret;
> +}
> +
> +static int fwu_gpt_set_clear_image_accept(efi_guid_t *img_type_id,
> +					  u32 bank, u8 action)
> +{
> +	void *buf;
> +	int ret, i;
> +	u32 nimages;
> +	struct fwu_metadata *metadata;
> +	struct fwu_image_entry *img_entry;
> +	struct fwu_image_bank_info *img_bank_info;
> +
> +	ret = gpt_get_valid_metadata(&metadata);
> +	if (ret < 0) {
> +		log_err("Unable to get valid metadata\n");
> +		goto out;
> +	}
> +
> +	if (action == IMAGE_ACCEPT_SET)
> +		bank = metadata->active_index;
> +
> +	nimages = CONFIG_FWU_NUM_IMAGES_PER_BANK;
> +	img_entry = &metadata->img_entry[0];
> +	for (i = 0; i < nimages; i++) {
> +		if (!guidcmp(&img_entry[i].image_type_uuid, img_type_id)) {
> +			img_bank_info = &img_entry[i].img_bank_info[bank];
> +			if (action == IMAGE_ACCEPT_SET)
> +				img_bank_info->accepted |= FWU_IMAGE_ACCEPTED;
> +			else
> +				img_bank_info->accepted = 0;
> +
> +			buf = &metadata->version;
> +			metadata->crc32 = crc32(0, buf, sizeof(*metadata) -
> +						sizeof(u32));
> +
> +			ret = gpt_update_metadata(metadata);
> +			goto out;
> +		}
> +	}
> +
> +	/* Image not found */
> +	ret = -EINVAL;
> +
> +out:
> +	free(metadata);
> +
> +	return ret;
> +}
> +
> +int fwu_gpt_accept_image(efi_guid_t *img_type_id)
> +{
> +	return fwu_gpt_set_clear_image_accept(img_type_id, 0,
> +					      IMAGE_ACCEPT_SET);
> +}
> +
> +int fwu_gpt_clear_accept_image(efi_guid_t *img_type_id, u32 bank)
> +{
> +	return fwu_gpt_set_clear_image_accept(img_type_id, bank,
> +					      IMAGE_ACCEPT_CLEAR);
> +}
> +
> +struct fwu_metadata_ops fwu_gpt_blk_ops = {
> +	.get_active_index = fwu_gpt_get_active_index,
> +	.update_active_index = fwu_gpt_update_active_index,
> +	.fill_partition_guid_array = fwu_gpt_fill_partition_guid_array,
> +	.get_image_alt_num = fwu_gpt_get_image_alt_num,
> +	.metadata_check = fwu_gpt_metadata_check,
> +	.revert_boot_index = fwu_gpt_revert_boot_index,
> +	.set_accept_image = fwu_gpt_accept_image,
> +	.clear_accept_image = fwu_gpt_clear_accept_image,
> +	.get_metadata = fwu_gpt_get_metadata,
> +};


Regards


Patrick
Sughosh Ganu Dec. 8, 2021, 7:18 a.m. UTC | #2
hi Patrick,

On Tue, 7 Dec 2021 at 19:54, Patrick DELAUNAY <patrick.delaunay@foss.st.com>
wrote:

> Hi Sughsoh,
>
> On 11/25/21 8:01 AM, Sughosh Ganu wrote:
> > In the FWU Multi Bank Update feature, the information about the
> > updatable images is stored as part of the metadata, on a separate
> > partition. Add functions for reading from and writing to the metadata
> > when the updatable images and the metadata are stored on a block
> > device which is formated with GPT based partition scheme.
> >
> > Signed-off-by: Sughosh Ganu <sughosh.ganu@linaro.org>
> > ---
> >   lib/fwu_updates/fwu_metadata_gpt_blk.c | 716 +++++++++++++++++++++++++
> >   1 file changed, 716 insertions(+)
> >   create mode 100644 lib/fwu_updates/fwu_metadata_gpt_blk.c
> >
> > diff --git a/lib/fwu_updates/fwu_metadata_gpt_blk.c
> b/lib/fwu_updates/fwu_metadata_gpt_blk.c
> > new file mode 100644
> > index 0000000000..98cc53f706
> > --- /dev/null
> > +++ b/lib/fwu_updates/fwu_metadata_gpt_blk.c
> > @@ -0,0 +1,716 @@
> > +// SPDX-License-Identifier: GPL-2.0+
> > +/*
> > + * Copyright (c) 2021, Linaro Limited
> > + */
> > +
> > +#include <blk.h>
> > +#include <efi_loader.h>
> > +#include <fwu_metadata.h>
> > +#include <malloc.h>
> > +#include <memalign.h>
> > +#include <part.h>
> > +#include <part_efi.h>
> > +
> > +#include <linux/errno.h>
> > +#include <linux/types.h>
> > +#include <u-boot/crc.h>
> > +
> > +#define PRIMARY_VALID                0x1
> > +#define SECONDARY_VALID              0x2
> > +
> > +#define MDATA_READ           (u8)0x1
> > +#define MDATA_WRITE          (u8)0x2
> > +
> > +#define IMAGE_ACCEPT_SET     (u8)0x1
> > +#define IMAGE_ACCEPT_CLEAR   (u8)0x2
> > +
> > +static int gpt_verify_metadata(struct fwu_metadata *metadata, bool
> pri_part)
> > +{
> > +     u32 calc_crc32;
> > +     void *buf;
> > +
> > +     buf = &metadata->version;
> > +     calc_crc32 = crc32(0, buf, sizeof(*metadata) - sizeof(u32));
> > +
> > +     if (calc_crc32 != metadata->crc32) {
> > +             log_err("crc32 check failed for %s metadata partition\n",
> > +                     pri_part ? "primary" : "secondary");
> > +             return -1;
> > +     }
> > +
> > +     return 0;
> > +}
> > +
> > +static int gpt_get_metadata_partitions(struct blk_desc *desc,
> > +                                    u32 *primary_mpart,
> > +                                    u32 *secondary_mpart)
> > +{
> > +     int i, ret;
> > +     u32 nparts, mparts;
> > +     gpt_entry *gpt_pte = NULL;
> > +     const efi_guid_t fwu_metadata_guid = FWU_METADATA_GUID;
> > +
> > +     ALLOC_CACHE_ALIGN_BUFFER_PAD(gpt_header, gpt_head, 1,
> > +                                  desc->blksz);
> > +
> > +     ret = get_gpt_hdr_parts(desc, gpt_head, &gpt_pte);
> > +     if (ret < 0) {
> > +             log_err("Error getting GPT header and partitions\n");
> > +             ret = -EIO;
> > +             goto out;
> > +     }
> > +
> > +     nparts = gpt_head->num_partition_entries;
> > +     for (i = 0, mparts = 0; i < nparts; i++) {
> > +             if (!guidcmp(&fwu_metadata_guid,
> > +                          &gpt_pte[i].partition_type_guid)) {
> > +                     ++mparts;
> > +                     if (!*primary_mpart)
> > +                             *primary_mpart = i + 1;
> > +                     else
> > +                             *secondary_mpart = i + 1;
> > +             }
> > +     }
> > +
> > +     if (mparts != 2) {
> > +             log_err("Expect two copies of the metadata instead of
> %d\n",
> > +                     mparts);
> > +             ret = -EINVAL;
> > +     } else {
> > +             ret = 0;
> > +     }
> > +out:
> > +     free(gpt_pte);
> > +
> > +     return ret;
> > +}
> > +
> > +static int gpt_get_metadata_disk_part(struct blk_desc *desc,
> > +                                   struct disk_partition *info,
> > +                                   u32 part_num)
> > +{
> > +     int ret;
> > +     char *metadata_guid_str = "8a7a84a0-8387-40f6-ab41-a8b9a5a60d23";
> > +
> > +     ret = part_get_info(desc, part_num, info);
> > +     if (ret < 0) {
> > +             log_err("Unable to get the partition info for the metadata
> part %d",
> > +                     part_num);
> > +             return -1;
> > +     }
> > +
> > +     /* Check that it is indeed the metadata partition */
> > +     if (!strncmp(info->type_guid, metadata_guid_str, UUID_STR_LEN)) {
> > +             /* Found the metadata partition */
> > +             return 0;
> > +     }
> > +
> > +     return -1;
> > +}
> > +
> > +static int gpt_read_write_metadata(struct blk_desc *desc,
> > +                                struct fwu_metadata *metadata,
> > +                                u8 access, u32 part_num)
> > +{
> > +     int ret;
> > +     u32 len, blk_start, blkcnt;
> > +     struct disk_partition info;
> > +
> > +     ALLOC_CACHE_ALIGN_BUFFER_PAD(struct fwu_metadata, mdata, 1,
> desc->blksz);
> > +
> > +     ret = gpt_get_metadata_disk_part(desc, &info, part_num);
> > +     if (ret < 0) {
> > +             printf("Unable to get the metadata partition\n");
> > +             return -ENODEV;
> > +     }
> > +
> > +     len = sizeof(*metadata);
> > +     blkcnt = BLOCK_CNT(len, desc);
> > +     if (blkcnt > info.size) {
> > +             log_err("Block count exceeds metadata partition size\n");
> > +             return -ERANGE;
> > +     }
> > +
> > +     blk_start = info.start;
> > +     if (access == MDATA_READ) {
> > +             if (blk_dread(desc, blk_start, blkcnt, mdata) != blkcnt) {
> > +                     log_err("Error reading metadata from the
> device\n");
> > +                     return -EIO;
> > +             }
> > +             memcpy(metadata, mdata, sizeof(struct fwu_metadata));
> > +     } else {
> > +             if (blk_dwrite(desc, blk_start, blkcnt, metadata) !=
> blkcnt) {
> > +                     log_err("Error writing metadata to the device\n");
> > +                     return -EIO;
> > +             }
> > +     }
> > +
> > +     return 0;
> > +}
> > +
> > +static int gpt_read_metadata(struct blk_desc *desc,
> > +                          struct fwu_metadata *metadata, u32 part_num)
> > +{
> > +     return gpt_read_write_metadata(desc, metadata, MDATA_READ,
> part_num);
> > +}
> > +
> > +static int gpt_write_metadata_partition(struct blk_desc *desc,
> > +                                     struct fwu_metadata *metadata,
> > +                                     u32 part_num)
> > +{
> > +     return gpt_read_write_metadata(desc, metadata, MDATA_WRITE,
> part_num);
> > +}
> > +
> > +static int gpt_update_metadata(struct fwu_metadata *metadata)
> > +{
> > +     int ret;
> > +     struct blk_desc *desc;
> > +     u32 primary_mpart, secondary_mpart;
> > +
> > +     ret = fwu_plat_get_blk_desc(&desc);
> > +     if (ret < 0) {
> > +             log_err("Block device not found\n");
> > +             return -ENODEV;
> > +     }
> > +
> > +     primary_mpart = secondary_mpart = 0;
> > +     ret = gpt_get_metadata_partitions(desc, &primary_mpart,
> > +                                       &secondary_mpart);
> > +
> > +     if (ret < 0) {
> > +             log_err("Error getting the metadata partitions\n");
> > +             return -ENODEV;
> > +     }
> > +
> > +     /* First write the primary partition*/
> > +     ret = gpt_write_metadata_partition(desc, metadata, primary_mpart);
> > +     if (ret < 0) {
> > +             log_err("Updating primary metadata partition failed\n");
> > +             return ret;
> > +     }
> > +
> > +     /* And now the replica */
> > +     ret = gpt_write_metadata_partition(desc, metadata,
> secondary_mpart);
> > +     if (ret < 0) {
> > +             log_err("Updating secondary metadata partition failed\n");
> > +             return ret;
> > +     }
> > +
> > +     return 0;
> > +}
> > +
> > +static int gpt_get_valid_metadata(struct fwu_metadata **metadata)
> > +{
> > +     int ret;
> > +     struct blk_desc *desc;
> > +     u32 primary_mpart, secondary_mpart;
> > +
> > +     ret = fwu_plat_get_blk_desc(&desc);
> > +     if (ret < 0) {
> > +             log_err("Block device not found\n");
> > +             return -ENODEV;
> > +     }
> > +
> > +     primary_mpart = secondary_mpart = 0;
> > +     ret = gpt_get_metadata_partitions(desc, &primary_mpart,
> > +                                       &secondary_mpart);
> > +
> > +     if (ret < 0) {
> > +             log_err("Error getting the metadata partitions\n");
> > +             return -ENODEV;
> > +     }
> > +
> > +     *metadata = malloc(sizeof(struct fwu_metadata));
> > +     if (!*metadata) {
> > +             log_err("Unable to allocate memory for reading
> metadata\n");
> > +             return -ENOMEM;
> > +     }
> > +
> > +     ret = gpt_read_metadata(desc, *metadata, primary_mpart);
> > +     if (ret < 0) {
> > +             log_err("Failed to read the metadata from the device\n");
> > +             return -EIO;
> > +     }
> > +
> > +     ret = gpt_verify_metadata(*metadata, 1);
> > +     if (!ret)
> > +             return 0;
> > +
> > +     /*
> > +      * Verification of the primary metadata copy failed.
> > +      * Try to read the replica.
> > +      */
> > +     memset(*metadata, 0, sizeof(struct fwu_metadata));
> > +     ret = gpt_read_metadata(desc, *metadata, secondary_mpart);
> > +     if (ret < 0) {
> > +             log_err("Failed to read the metadata from the device\n");
> > +             return -EIO;
> > +     }
> > +
> > +     ret = gpt_verify_metadata(*metadata, 0);
> > +     if (!ret)
> > +             return 0;
> > +
> > +     /* Both the metadata copies are corrupted. */
> > +     return -1;
> > +}
> > +
> > +static int gpt_check_metadata_validity(void)
> > +{
> > +     int ret;
> > +     struct blk_desc *desc;
> > +     struct fwu_metadata *pri_metadata;
> > +     struct fwu_metadata *secondary_metadata;
> > +     u32 primary_mpart, secondary_mpart;
> > +     u32 valid_partitions;
> > +
> > +     ret = fwu_plat_get_blk_desc(&desc);
> > +     if (ret < 0) {
> > +             log_err("Block device not found\n");
> > +             return -ENODEV;
> > +     }
> > +
> > +     /*
> > +      * Two metadata partitions are expected.
> > +      * If we don't have two, user needs to create
> > +      * them first
> > +      */
> > +     primary_mpart = secondary_mpart = 0;
> > +     valid_partitions = 0;
> > +     ret = gpt_get_metadata_partitions(desc, &primary_mpart,
> > +                                       &secondary_mpart);
> > +
> > +     if (ret < 0) {
> > +             log_err("Error getting the metadata partitions\n");
> > +             return -ENODEV;
> > +     }
> > +
> > +     pri_metadata = malloc(sizeof(*pri_metadata));
> > +     if (!pri_metadata) {
> > +             log_err("Unable to allocate memory for reading
> metadata\n");
> > +             return -ENOMEM;
> > +     }
> > +
> > +     secondary_metadata = malloc(sizeof(*secondary_metadata));
> > +     if (!secondary_metadata) {
> > +             log_err("Unable to allocate memory for reading
> metadata\n");
> > +             return -ENOMEM;
> > +     }
> > +
> > +     ret = gpt_read_metadata(desc, pri_metadata, primary_mpart);
> > +     if (ret < 0) {
> > +             log_err("Failed to read the metadata from the device\n");
> > +             ret = -EIO;
> > +             goto out;
> > +     }
> > +
> > +     ret = gpt_verify_metadata(pri_metadata, 1);
> > +     if (!ret)
> > +             valid_partitions |= PRIMARY_VALID;
> > +
> > +     /* Now check the secondary partition */
> > +     ret = gpt_read_metadata(desc, secondary_metadata, secondary_mpart);
> > +     if (ret < 0) {
> > +             log_err("Failed to read the metadata from the device\n");
> > +             ret = -EIO;
> > +             goto out;
> > +     }
> > +
> > +     ret = gpt_verify_metadata(secondary_metadata, 0);
> > +     if (!ret)
> > +             valid_partitions |= SECONDARY_VALID;
> > +
> > +     if (valid_partitions == (PRIMARY_VALID | SECONDARY_VALID)) {
> > +             ret = -1;
> > +             /*
> > +              * Before returning, check that both the
> > +              * metadata copies are the same. If not,
> > +              * the metadata copies need to be
> > +              * re-populated.
> > +              */
> > +             if (!memcmp(pri_metadata, secondary_metadata,
> > +                         sizeof(*pri_metadata)))
> > +                     ret = 0;
> > +             goto out;
> > +     } else if (valid_partitions == SECONDARY_VALID) {
> > +             ret = gpt_write_metadata_partition(desc,
> secondary_metadata,
> > +                                                primary_mpart);
> > +             if (ret < 0) {
> > +                     log_err("Restoring primary metadata partition
> failed\n");
> > +                     goto out;
> > +             }
> > +     } else if (valid_partitions == PRIMARY_VALID) {
> > +             ret = gpt_write_metadata_partition(desc, pri_metadata,
> > +                                                secondary_mpart);
> > +             if (ret < 0) {
> > +                     log_err("Restoring secondary metadata partition
> failed\n");
> > +                     goto out;
> > +             }
> > +     } else {
> > +             ret = -1;
> > +     }
> > +
> > +out:
> > +     free(pri_metadata);
> > +
> > +     return ret;
> > +}
> > +
> > +static int gpt_fill_partition_guid_array(struct blk_desc *desc,
> > +                                      efi_guid_t **part_guid_arr,
> > +                                      u32 *nparts)
> > +{
> > +     int ret, i;
> > +     u32 parts;
> > +     gpt_entry *gpt_pte = NULL;
> > +     const efi_guid_t null_guid = NULL_GUID;
> > +
> > +     ALLOC_CACHE_ALIGN_BUFFER_PAD(gpt_header, gpt_head, 1,
> > +                                  desc->blksz);
> > +
> > +     ret = get_gpt_hdr_parts(desc, gpt_head, &gpt_pte);
> > +     if (ret < 0) {
> > +             log_err("Error getting GPT header and partitions\n");
> > +             ret = -EIO;
> > +             goto out;
> > +     }
> > +
> > +     *nparts = gpt_head->num_partition_entries;
> > +
> > +     /*
> > +      * There could be a scenario where the number of partition entries
> > +      * configured in the GPT header is the default value of 128. Find
> > +      * the actual number of populated partitioned entries
> > +      */
> > +     for (i = 0, parts = 0; i < *nparts; i++) {
> > +             if (!guidcmp(&gpt_pte[i].partition_type_guid, &null_guid))
> > +                     continue;
> > +             ++parts;
> > +     }
> > +
> > +     *nparts = parts;
> > +     *part_guid_arr = malloc(sizeof(efi_guid_t) * *nparts);
> > +     if (!part_guid_arr) {
> > +             log_err("Unable to allocate memory for guid array\n");
> > +             ret = -ENOMEM;
> > +             goto out;
> > +     }
> > +
> > +     for (i = 0; i < *nparts; i++) {
> > +             guidcpy((*part_guid_arr + i),
> > +                     &gpt_pte[i].partition_type_guid);
> > +     }
> > +
> > +out:
> > +     free(gpt_pte);
> > +     return ret;
> > +}
> > +
> > +int fwu_gpt_get_active_index(u32 *active_idx)
> > +{
> > +     int ret;
> > +     struct fwu_metadata *metadata;
> > +
> > +     ret = gpt_get_valid_metadata(&metadata);
> > +     if (ret < 0) {
> > +             log_err("Unable to get valid metadata\n");
> > +             goto out;
> > +     }
> > +
> > +     /*
> > +      * Found the metadata partition, now read the active_index
> > +      * value
> > +      */
> > +     *active_idx = metadata->active_index;
> > +     if (*active_idx > CONFIG_FWU_NUM_BANKS - 1) {
> > +             printf("Active index value read is incorrect\n");
> > +             ret = -EINVAL;
> > +             goto out;
> > +     }
> > +
> > +out:
> > +     free(metadata);
> > +
> > +     return ret;
> > +}
> > +
> > +static int gpt_get_image_alt_num(struct blk_desc *desc,
> > +                              efi_guid_t image_type_id,
> > +                              u32 update_bank, int *alt_no)
> > +{
> > +     int ret, i;
> > +     u32 nparts;
> > +     gpt_entry *gpt_pte = NULL;
> > +     struct fwu_metadata *metadata;
> > +     struct fwu_image_entry *img_entry;
> > +     struct fwu_image_bank_info *img_bank_info;
> > +     efi_guid_t unique_part_guid;
> > +     efi_guid_t image_guid = NULL_GUID;
> > +
> > +     ALLOC_CACHE_ALIGN_BUFFER_PAD(gpt_header, gpt_head, 1,
> > +                                  desc->blksz);
> > +
> > +     ret = gpt_get_valid_metadata(&metadata);
> > +     if (ret < 0) {
> > +             log_err("Unable to read valid metadata\n");
> > +             goto out;
> > +     }
> > +
> > +     /*
> > +      * The metadata has been read. Now get the image_uuid for the
> > +      * image with the update_bank.
> > +      */
> > +     for (i = 0; i < CONFIG_FWU_NUM_IMAGES_PER_BANK; i++) {
> > +             if (!guidcmp(&image_type_id,
> > +                          &metadata->img_entry[i].image_type_uuid)) {
> > +                     img_entry = &metadata->img_entry[i];
> > +                     img_bank_info =
> &img_entry->img_bank_info[update_bank];
> > +                     guidcpy(&image_guid, &img_bank_info->image_uuid);
> > +             }
> > +     }
> > +
> > +     /*
> > +      * Now read the GPT Partition Table Entries to find a matching
> > +      * partition with UniquePartitionGuid value. We need to iterate
> > +      * through all the GPT partitions since they might be in any
> > +      * order
> > +      */
> > +     ret = get_gpt_hdr_parts(desc, gpt_head, &gpt_pte);
> > +     if (ret < 0) {
> > +             log_err("Error getting GPT header and partitions\n");
> > +             ret = -EIO;
> > +             goto out;
> > +     }
> > +
> > +     nparts = gpt_head->num_partition_entries;
> > +
> > +     for (i = 0; i < nparts; i++) {
> > +             unique_part_guid = gpt_pte[i].unique_partition_guid;
> > +             if (!guidcmp(&unique_part_guid, &image_guid)) {
> > +                     /* Found the partition */
> > +                     *alt_no = i + 1;
>
> here we found the partition = <i>
>
> but I am not sure of the hard-coded mapping here <alt> = <part_id> + 1
>
> at least I think it should be configured for each board
>
> for example with a weak function ?
>
> *alt_no = board_get_dfu_alt_fwu(i);
>
> with:
>
> /* default => dfu alternate use the same order than parttion */
> weak int board_get_dfu_alt_fwu(int part) {
>         return part + 1;
> )
>
> But I think we can make something more dynamic here by parsing DFU
> information
> to found the alternate associated to the requested partition
>
> by example by using dfu_get_entity()
> => search on the "mmc" dev / part
>
> int mmc_get_dfu_alt_fwu(int dev, int part) {
>
> for (i = 0; i < nb_alt; i++) {
>         dfu = dfu_get_entity(i);
>         /* only SDCard / eMMC suport with GPT partitioning */
>         if (dfu->dev_type != DFU_DEV_MMC)
>                 continue;
>         if (dfu->layout = DFU_RAW_ADDR &&
>              dfu->data.mmc.dev == dev &&
>              dfu->data.mmc.part == part)
>                 return
>   }
>
> this request can support boards with several MMC instances
> as the partitions are not the necessary the first one
>
> for example trying to update partition in mmc 1
> when partition on mmc 0 are the first one in dfu alternate.
>

Thanks for this suggestion. I will try it out on my side and incorporate it
in my V2. This should also get rid of patch 2, Move the ram partitions to
the end of the dfu_alt_info variable.

-sughosh


>
> > +                     break;
> > +             }
> > +     }
> > +
> > +     if (i == nparts) {
> > +             log_err("Partition with the image guid not found\n");
> > +             ret = -EINVAL;
> > +     }
> > +
> > +out:
> > +     free(metadata);
> > +     free(gpt_pte);
> > +     return ret;
> > +}
> > +
> > +int fwu_gpt_update_active_index(u32 active_idx)
> > +{
> > +     int ret;
> > +     void *buf;
> > +     u32 cur_active_index;
> > +     struct fwu_metadata *metadata;
> > +
> > +     if (active_idx > CONFIG_FWU_NUM_BANKS) {
> > +             printf("Active index value to be updated is incorrect\n");
> > +             return -1;
> > +     }
> > +
> > +     ret = gpt_get_valid_metadata(&metadata);
> > +     if (ret < 0) {
> > +             log_err("Unable to get valid metadata\n");
> > +             goto out;
> > +     }
> > +
> > +     /*
> > +      * Update the active index and previous_active_index fields
> > +      * in the metadata
> > +      */
> > +     cur_active_index = metadata->active_index;
> > +     metadata->active_index = active_idx;
> > +     metadata->previous_active_index = cur_active_index;
> > +
> > +     /*
> > +      * Calculate the crc32 for the updated metadata
> > +      * and put the updated value in the metadata crc32
> > +      * field
> > +      */
> > +     buf = &metadata->version;
> > +     metadata->crc32 = crc32(0, buf, sizeof(*metadata) - sizeof(u32));
> > +
> > +     /*
> > +      * Now write this updated metadata to both the
> > +      * metadata partitions
> > +      */
> > +     ret = gpt_update_metadata(metadata);
> > +     if (ret < 0) {
> > +             log_err("Failed to update metadata partitions\n");
> > +             ret = -EIO;
> > +     }
> > +
> > +out:
> > +     free(metadata);
> > +
> > +     return ret;
> > +}
> > +
> > +int fwu_gpt_fill_partition_guid_array(efi_guid_t **part_guid_arr, u32
> *nparts)
> > +{
> > +     int ret;
> > +     struct blk_desc *desc;
> > +
> > +     ret = fwu_plat_get_blk_desc(&desc);
> > +     if (ret < 0) {
> > +             log_err("Block device not found\n");
> > +             return -ENODEV;
> > +     }
> > +
> > +     return gpt_fill_partition_guid_array(desc, part_guid_arr, nparts);
> > +}
> > +
> > +int fwu_gpt_get_image_alt_num(efi_guid_t image_type_id, u32 update_bank,
> > +                           int *alt_no)
> > +{
> > +     int ret;
> > +     struct blk_desc *desc;
> > +
> > +     ret = fwu_plat_get_blk_desc(&desc);
> > +     if (ret < 0) {
> > +             log_err("Block device not found\n");
> > +             return -ENODEV;
> > +     }
> > +
> > +     return gpt_get_image_alt_num(desc, image_type_id, update_bank,
> alt_no);
> > +}
> > +
> > +int fwu_gpt_metadata_check(void)
> > +{
> > +     /*
> > +      * Check if both the copies of the metadata are valid.
> > +      * If one has gone bad, restore it from the other good
> > +      * copy.
> > +      */
> > +     return gpt_check_metadata_validity();
> > +}
> > +
> > +int fwu_gpt_get_metadata(struct fwu_metadata **metadata)
> > +{
> > +     return gpt_get_valid_metadata(metadata);
> > +}
> > +
> > +int fwu_gpt_revert_boot_index(u32 *active_idx)
> > +{
> > +     int ret;
> > +     void *buf;
> > +     u32 cur_active_index;
> > +     struct fwu_metadata *metadata;
> > +
> > +     ret = gpt_get_valid_metadata(&metadata);
> > +     if (ret < 0) {
> > +             log_err("Unable to get valid metadata\n");
> > +             goto out;
> > +     }
> > +
> > +     /*
> > +      * Swap the active index and previous_active_index fields
> > +      * in the metadata
> > +      */
> > +     cur_active_index = metadata->active_index;
> > +     metadata->active_index = metadata->previous_active_index;
> > +     metadata->previous_active_index = cur_active_index;
> > +     *active_idx = metadata->active_index;
> > +
> > +     /*
> > +      * Calculate the crc32 for the updated metadata
> > +      * and put the updated value in the metadata crc32
> > +      * field
> > +      */
> > +     buf = &metadata->version;
> > +     metadata->crc32 = crc32(0, buf, sizeof(*metadata) - sizeof(u32));
> > +
> > +     /*
> > +      * Now write this updated metadata to both the
> > +      * metadata partitions
> > +      */
> > +     ret = gpt_update_metadata(metadata);
> > +     if (ret < 0) {
> > +             log_err("Failed to update metadata partitions\n");
> > +             ret = -EIO;
> > +     }
> > +
> > +out:
> > +     free(metadata);
> > +
> > +     return ret;
> > +}
> > +
> > +static int fwu_gpt_set_clear_image_accept(efi_guid_t *img_type_id,
> > +                                       u32 bank, u8 action)
> > +{
> > +     void *buf;
> > +     int ret, i;
> > +     u32 nimages;
> > +     struct fwu_metadata *metadata;
> > +     struct fwu_image_entry *img_entry;
> > +     struct fwu_image_bank_info *img_bank_info;
> > +
> > +     ret = gpt_get_valid_metadata(&metadata);
> > +     if (ret < 0) {
> > +             log_err("Unable to get valid metadata\n");
> > +             goto out;
> > +     }
> > +
> > +     if (action == IMAGE_ACCEPT_SET)
> > +             bank = metadata->active_index;
> > +
> > +     nimages = CONFIG_FWU_NUM_IMAGES_PER_BANK;
> > +     img_entry = &metadata->img_entry[0];
> > +     for (i = 0; i < nimages; i++) {
> > +             if (!guidcmp(&img_entry[i].image_type_uuid, img_type_id)) {
> > +                     img_bank_info = &img_entry[i].img_bank_info[bank];
> > +                     if (action == IMAGE_ACCEPT_SET)
> > +                             img_bank_info->accepted |=
> FWU_IMAGE_ACCEPTED;
> > +                     else
> > +                             img_bank_info->accepted = 0;
> > +
> > +                     buf = &metadata->version;
> > +                     metadata->crc32 = crc32(0, buf, sizeof(*metadata) -
> > +                                             sizeof(u32));
> > +
> > +                     ret = gpt_update_metadata(metadata);
> > +                     goto out;
> > +             }
> > +     }
> > +
> > +     /* Image not found */
> > +     ret = -EINVAL;
> > +
> > +out:
> > +     free(metadata);
> > +
> > +     return ret;
> > +}
> > +
> > +int fwu_gpt_accept_image(efi_guid_t *img_type_id)
> > +{
> > +     return fwu_gpt_set_clear_image_accept(img_type_id, 0,
> > +                                           IMAGE_ACCEPT_SET);
> > +}
> > +
> > +int fwu_gpt_clear_accept_image(efi_guid_t *img_type_id, u32 bank)
> > +{
> > +     return fwu_gpt_set_clear_image_accept(img_type_id, bank,
> > +                                           IMAGE_ACCEPT_CLEAR);
> > +}
> > +
> > +struct fwu_metadata_ops fwu_gpt_blk_ops = {
> > +     .get_active_index = fwu_gpt_get_active_index,
> > +     .update_active_index = fwu_gpt_update_active_index,
> > +     .fill_partition_guid_array = fwu_gpt_fill_partition_guid_array,
> > +     .get_image_alt_num = fwu_gpt_get_image_alt_num,
> > +     .metadata_check = fwu_gpt_metadata_check,
> > +     .revert_boot_index = fwu_gpt_revert_boot_index,
> > +     .set_accept_image = fwu_gpt_accept_image,
> > +     .clear_accept_image = fwu_gpt_clear_accept_image,
> > +     .get_metadata = fwu_gpt_get_metadata,
> > +};
>
>
> Regards
>
>
> Patrick
>
>
Simon Glass Dec. 9, 2021, 2:32 a.m. UTC | #3
Hi Sughosh,

On Thu, 25 Nov 2021 at 00:03, Sughosh Ganu <sughosh.ganu@linaro.org> wrote:
>
> In the FWU Multi Bank Update feature, the information about the
> updatable images is stored as part of the metadata, on a separate
> partition. Add functions for reading from and writing to the metadata
> when the updatable images and the metadata are stored on a block
> device which is formated with GPT based partition scheme.
>
> Signed-off-by: Sughosh Ganu <sughosh.ganu@linaro.org>
> ---
>  lib/fwu_updates/fwu_metadata_gpt_blk.c | 716 +++++++++++++++++++++++++
>  1 file changed, 716 insertions(+)
>  create mode 100644 lib/fwu_updates/fwu_metadata_gpt_blk.c

Given that you use mdata in some places, could you use it everywhere
(identifiers and filenames)? I think mdata is a much better name than
the generic word 'metadata'.

Regards,
Simon
Sughosh Ganu Dec. 9, 2021, 9:01 a.m. UTC | #4
hi Simon,

On Thu, 9 Dec 2021 at 08:02, Simon Glass <sjg@chromium.org> wrote:

> Hi Sughosh,
>
> On Thu, 25 Nov 2021 at 00:03, Sughosh Ganu <sughosh.ganu@linaro.org>
> wrote:
> >
> > In the FWU Multi Bank Update feature, the information about the
> > updatable images is stored as part of the metadata, on a separate
> > partition. Add functions for reading from and writing to the metadata
> > when the updatable images and the metadata are stored on a block
> > device which is formated with GPT based partition scheme.
> >
> > Signed-off-by: Sughosh Ganu <sughosh.ganu@linaro.org>
> > ---
> >  lib/fwu_updates/fwu_metadata_gpt_blk.c | 716 +++++++++++++++++++++++++
> >  1 file changed, 716 insertions(+)
> >  create mode 100644 lib/fwu_updates/fwu_metadata_gpt_blk.c
>
> Given that you use mdata in some places, could you use it everywhere
> (identifiers and filenames)? I think mdata is a much better name than
> the generic word 'metadata'.
>

Okay. Will make the change as per your suggestion. Thanks.

-sughosh
Jason Liu Dec. 9, 2021, 9:35 a.m. UTC | #5
> -----Original Message-----
> From: Sughosh Ganu <sughosh.ganu@linaro.org>
> Sent: 2021年11月25日 15:02
> To: u-boot@lists.denx.de
> Cc: Patrick Delaunay <patrick.delaunay@foss.st.com>; Patrice Chotard
> <patrice.chotard@foss.st.com>; Heinrich Schuchardt <xypron.glpk@gmx.de>;
> Alexander Graf <agraf@csgraf.de>; Simon Glass <sjg@chromium.org>; Bin
> Meng <bmeng.cn@gmail.com>; Peng Fan <peng.fan@nxp.com>; AKASHI
> Takahiro <takahiro.akashi@linaro.org>; Ilias Apalodimas
> <ilias.apalodimas@linaro.org>; Jose Marinho <jose.marinho@arm.com>; Grant
> Likely <grant.likely@arm.com>; Jason Liu <jason.hui.liu@nxp.com>; Sughosh
> Ganu <sughosh.ganu@linaro.org>
> Subject: [RFC PATCH 04/10] FWU: Add metadata access functions for GPT
> partitioned block devices
> 
> In the FWU Multi Bank Update feature, the information about the updatable
> images is stored as part of the metadata, on a separate partition. Add
> functions for reading from and writing to the metadata when the updatable
> images and the metadata are stored on a block device which is formated
with
> GPT based partition scheme.
> 
> Signed-off-by: Sughosh Ganu <sughosh.ganu@linaro.org>
> ---
>  lib/fwu_updates/fwu_metadata_gpt_blk.c | 716
> +++++++++++++++++++++++++
>  1 file changed, 716 insertions(+)
>  create mode 100644 lib/fwu_updates/fwu_metadata_gpt_blk.c
> 
> diff --git a/lib/fwu_updates/fwu_metadata_gpt_blk.c
> b/lib/fwu_updates/fwu_metadata_gpt_blk.c
> new file mode 100644
> index 0000000000..98cc53f706
> --- /dev/null
> +++ b/lib/fwu_updates/fwu_metadata_gpt_blk.c
> @@ -0,0 +1,716 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Copyright (c) 2021, Linaro Limited
> + */
> +
> +#include <blk.h>
> +#include <efi_loader.h>
> +#include <fwu_metadata.h>
> +#include <malloc.h>
> +#include <memalign.h>
> +#include <part.h>
> +#include <part_efi.h>
> +
> +#include <linux/errno.h>
> +#include <linux/types.h>
> +#include <u-boot/crc.h>
> +
> +#define PRIMARY_VALID		0x1
> +#define SECONDARY_VALID		0x2
> +
> +#define MDATA_READ		(u8)0x1
> +#define MDATA_WRITE		(u8)0x2
> +
> +#define IMAGE_ACCEPT_SET	(u8)0x1
> +#define IMAGE_ACCEPT_CLEAR	(u8)0x2

Better to define as the followings:

#define MDATA_READ		((u8)0x1)
#define MDATA_WRITE		((u8)0x2)

#define IMAGE_ACCEPT_SET	((u8)0x1)
#define IMAGE_ACCEPT_CLEAR	((u8)0x2)

> +
> +static int gpt_verify_metadata(struct fwu_metadata *metadata, bool
> +pri_part) {
> +	u32 calc_crc32;
> +	void *buf;
> +
> +	buf = &metadata->version;
> +	calc_crc32 = crc32(0, buf, sizeof(*metadata) - sizeof(u32));
> +
> +	if (calc_crc32 != metadata->crc32) {
> +		log_err("crc32 check failed for %s metadata partition\n",
> +			pri_part ? "primary" : "secondary");
> +		return -1;
> +	}
> +
> +	return 0;
> +}
> +
> +static int gpt_get_metadata_partitions(struct blk_desc *desc,
> +				       u32 *primary_mpart,
> +				       u32 *secondary_mpart)
> +{
> +	int i, ret;
> +	u32 nparts, mparts;
> +	gpt_entry *gpt_pte = NULL;
> +	const efi_guid_t fwu_metadata_guid = FWU_METADATA_GUID;
> +
> +	ALLOC_CACHE_ALIGN_BUFFER_PAD(gpt_header, gpt_head, 1,
> +				     desc->blksz);
> +
> +	ret = get_gpt_hdr_parts(desc, gpt_head, &gpt_pte);
> +	if (ret < 0) {
> +		log_err("Error getting GPT header and partitions\n");
> +		ret = -EIO;
> +		goto out;
> +	}
> +
> +	nparts = gpt_head->num_partition_entries;
> +	for (i = 0, mparts = 0; i < nparts; i++) {
> +		if (!guidcmp(&fwu_metadata_guid,
> +			     &gpt_pte[i].partition_type_guid)) {
> +			++mparts;
> +			if (!*primary_mpart)
> +				*primary_mpart = i + 1;
> +			else
> +				*secondary_mpart = i + 1;
> +		}
> +	}
> +
> +	if (mparts != 2) {
> +		log_err("Expect two copies of the metadata instead of %d\n",
> +			mparts);
> +		ret = -EINVAL;
> +	} else {
> +		ret = 0;
> +	}
> +out:
> +	free(gpt_pte);
> +
> +	return ret;
> +}
> +
> +static int gpt_get_metadata_disk_part(struct blk_desc *desc,
> +				      struct disk_partition *info,
> +				      u32 part_num)
> +{
> +	int ret;
> +	char *metadata_guid_str = "8a7a84a0-8387-40f6-ab41-a8b9a5a60d23";

Is this hard-code guid_string intentioned?

> +
> +	ret = part_get_info(desc, part_num, info);

Jason Liu
Sughosh Ganu Dec. 9, 2021, 9:46 a.m. UTC | #6
On Thu, 9 Dec 2021 at 15:06, Jason Liu <jason.hui.liu@nxp.com> wrote:

>
>
> > -----Original Message-----
> > From: Sughosh Ganu <sughosh.ganu@linaro.org>
> > Sent: 2021年11月25日 15:02
> > To: u-boot@lists.denx.de
> > Cc: Patrick Delaunay <patrick.delaunay@foss.st.com>; Patrice Chotard
> > <patrice.chotard@foss.st.com>; Heinrich Schuchardt <xypron.glpk@gmx.de>;
> > Alexander Graf <agraf@csgraf.de>; Simon Glass <sjg@chromium.org>; Bin
> > Meng <bmeng.cn@gmail.com>; Peng Fan <peng.fan@nxp.com>; AKASHI
> > Takahiro <takahiro.akashi@linaro.org>; Ilias Apalodimas
> > <ilias.apalodimas@linaro.org>; Jose Marinho <jose.marinho@arm.com>;
> Grant
> > Likely <grant.likely@arm.com>; Jason Liu <jason.hui.liu@nxp.com>;
> Sughosh
> > Ganu <sughosh.ganu@linaro.org>
> > Subject: [RFC PATCH 04/10] FWU: Add metadata access functions for GPT
> > partitioned block devices
> >
> > In the FWU Multi Bank Update feature, the information about the updatable
> > images is stored as part of the metadata, on a separate partition. Add
> > functions for reading from and writing to the metadata when the updatable
> > images and the metadata are stored on a block device which is formated
> with
> > GPT based partition scheme.
> >
> > Signed-off-by: Sughosh Ganu <sughosh.ganu@linaro.org>
> > ---
> >  lib/fwu_updates/fwu_metadata_gpt_blk.c | 716
> > +++++++++++++++++++++++++
> >  1 file changed, 716 insertions(+)
> >  create mode 100644 lib/fwu_updates/fwu_metadata_gpt_blk.c
> >
> > diff --git a/lib/fwu_updates/fwu_metadata_gpt_blk.c
> > b/lib/fwu_updates/fwu_metadata_gpt_blk.c
> > new file mode 100644
> > index 0000000000..98cc53f706
> > --- /dev/null
> > +++ b/lib/fwu_updates/fwu_metadata_gpt_blk.c
> > @@ -0,0 +1,716 @@
> > +// SPDX-License-Identifier: GPL-2.0+
> > +/*
> > + * Copyright (c) 2021, Linaro Limited
> > + */
> > +
> > +#include <blk.h>
> > +#include <efi_loader.h>
> > +#include <fwu_metadata.h>
> > +#include <malloc.h>
> > +#include <memalign.h>
> > +#include <part.h>
> > +#include <part_efi.h>
> > +
> > +#include <linux/errno.h>
> > +#include <linux/types.h>
> > +#include <u-boot/crc.h>
> > +
> > +#define PRIMARY_VALID                0x1
> > +#define SECONDARY_VALID              0x2
> > +
> > +#define MDATA_READ           (u8)0x1
> > +#define MDATA_WRITE          (u8)0x2
> > +
> > +#define IMAGE_ACCEPT_SET     (u8)0x1
> > +#define IMAGE_ACCEPT_CLEAR   (u8)0x2
>
> Better to define as the followings:
>
> #define MDATA_READ              ((u8)0x1)
> #define MDATA_WRITE             ((u8)0x2)
>
> #define IMAGE_ACCEPT_SET        ((u8)0x1)
> #define IMAGE_ACCEPT_CLEAR      ((u8)0x2)
>

Ilias had suggested using the BIT macros for this. I have made that change.
Hope that is fine.


>
> > +
> > +static int gpt_verify_metadata(struct fwu_metadata *metadata, bool
> > +pri_part) {
> > +     u32 calc_crc32;
> > +     void *buf;
> > +
> > +     buf = &metadata->version;
> > +     calc_crc32 = crc32(0, buf, sizeof(*metadata) - sizeof(u32));
> > +
> > +     if (calc_crc32 != metadata->crc32) {
> > +             log_err("crc32 check failed for %s metadata partition\n",
> > +                     pri_part ? "primary" : "secondary");
> > +             return -1;
> > +     }
> > +
> > +     return 0;
> > +}
> > +
> > +static int gpt_get_metadata_partitions(struct blk_desc *desc,
> > +                                    u32 *primary_mpart,
> > +                                    u32 *secondary_mpart)
> > +{
> > +     int i, ret;
> > +     u32 nparts, mparts;
> > +     gpt_entry *gpt_pte = NULL;
> > +     const efi_guid_t fwu_metadata_guid = FWU_METADATA_GUID;
> > +
> > +     ALLOC_CACHE_ALIGN_BUFFER_PAD(gpt_header, gpt_head, 1,
> > +                                  desc->blksz);
> > +
> > +     ret = get_gpt_hdr_parts(desc, gpt_head, &gpt_pte);
> > +     if (ret < 0) {
> > +             log_err("Error getting GPT header and partitions\n");
> > +             ret = -EIO;
> > +             goto out;
> > +     }
> > +
> > +     nparts = gpt_head->num_partition_entries;
> > +     for (i = 0, mparts = 0; i < nparts; i++) {
> > +             if (!guidcmp(&fwu_metadata_guid,
> > +                          &gpt_pte[i].partition_type_guid)) {
> > +                     ++mparts;
> > +                     if (!*primary_mpart)
> > +                             *primary_mpart = i + 1;
> > +                     else
> > +                             *secondary_mpart = i + 1;
> > +             }
> > +     }
> > +
> > +     if (mparts != 2) {
> > +             log_err("Expect two copies of the metadata instead of
> %d\n",
> > +                     mparts);
> > +             ret = -EINVAL;
> > +     } else {
> > +             ret = 0;
> > +     }
> > +out:
> > +     free(gpt_pte);
> > +
> > +     return ret;
> > +}
> > +
> > +static int gpt_get_metadata_disk_part(struct blk_desc *desc,
> > +                                   struct disk_partition *info,
> > +                                   u32 part_num)
> > +{
> > +     int ret;
> > +     char *metadata_guid_str = "8a7a84a0-8387-40f6-ab41-a8b9a5a60d23";
>
> Is this hard-code guid_string intentioned?
>

Yes, this is mentioned in the FWU spec[1], Table 3, pg 14.

-sughosh

[1] - https://developer.arm.com/documentation/den0118/a


>
> > +
> > +     ret = part_get_info(desc, part_num, info);
>
> Jason Liu
>
diff mbox series

Patch

diff --git a/lib/fwu_updates/fwu_metadata_gpt_blk.c b/lib/fwu_updates/fwu_metadata_gpt_blk.c
new file mode 100644
index 0000000000..98cc53f706
--- /dev/null
+++ b/lib/fwu_updates/fwu_metadata_gpt_blk.c
@@ -0,0 +1,716 @@ 
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2021, Linaro Limited
+ */
+
+#include <blk.h>
+#include <efi_loader.h>
+#include <fwu_metadata.h>
+#include <malloc.h>
+#include <memalign.h>
+#include <part.h>
+#include <part_efi.h>
+
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <u-boot/crc.h>
+
+#define PRIMARY_VALID		0x1
+#define SECONDARY_VALID		0x2
+
+#define MDATA_READ		(u8)0x1
+#define MDATA_WRITE		(u8)0x2
+
+#define IMAGE_ACCEPT_SET	(u8)0x1
+#define IMAGE_ACCEPT_CLEAR	(u8)0x2
+
+static int gpt_verify_metadata(struct fwu_metadata *metadata, bool pri_part)
+{
+	u32 calc_crc32;
+	void *buf;
+
+	buf = &metadata->version;
+	calc_crc32 = crc32(0, buf, sizeof(*metadata) - sizeof(u32));
+
+	if (calc_crc32 != metadata->crc32) {
+		log_err("crc32 check failed for %s metadata partition\n",
+			pri_part ? "primary" : "secondary");
+		return -1;
+	}
+
+	return 0;
+}
+
+static int gpt_get_metadata_partitions(struct blk_desc *desc,
+				       u32 *primary_mpart,
+				       u32 *secondary_mpart)
+{
+	int i, ret;
+	u32 nparts, mparts;
+	gpt_entry *gpt_pte = NULL;
+	const efi_guid_t fwu_metadata_guid = FWU_METADATA_GUID;
+
+	ALLOC_CACHE_ALIGN_BUFFER_PAD(gpt_header, gpt_head, 1,
+				     desc->blksz);
+
+	ret = get_gpt_hdr_parts(desc, gpt_head, &gpt_pte);
+	if (ret < 0) {
+		log_err("Error getting GPT header and partitions\n");
+		ret = -EIO;
+		goto out;
+	}
+
+	nparts = gpt_head->num_partition_entries;
+	for (i = 0, mparts = 0; i < nparts; i++) {
+		if (!guidcmp(&fwu_metadata_guid,
+			     &gpt_pte[i].partition_type_guid)) {
+			++mparts;
+			if (!*primary_mpart)
+				*primary_mpart = i + 1;
+			else
+				*secondary_mpart = i + 1;
+		}
+	}
+
+	if (mparts != 2) {
+		log_err("Expect two copies of the metadata instead of %d\n",
+			mparts);
+		ret = -EINVAL;
+	} else {
+		ret = 0;
+	}
+out:
+	free(gpt_pte);
+
+	return ret;
+}
+
+static int gpt_get_metadata_disk_part(struct blk_desc *desc,
+				      struct disk_partition *info,
+				      u32 part_num)
+{
+	int ret;
+	char *metadata_guid_str = "8a7a84a0-8387-40f6-ab41-a8b9a5a60d23";
+
+	ret = part_get_info(desc, part_num, info);
+	if (ret < 0) {
+		log_err("Unable to get the partition info for the metadata part %d",
+			part_num);
+		return -1;
+	}
+
+	/* Check that it is indeed the metadata partition */
+	if (!strncmp(info->type_guid, metadata_guid_str, UUID_STR_LEN)) {
+		/* Found the metadata partition */
+		return 0;
+	}
+
+	return -1;
+}
+
+static int gpt_read_write_metadata(struct blk_desc *desc,
+				   struct fwu_metadata *metadata,
+				   u8 access, u32 part_num)
+{
+	int ret;
+	u32 len, blk_start, blkcnt;
+	struct disk_partition info;
+
+	ALLOC_CACHE_ALIGN_BUFFER_PAD(struct fwu_metadata, mdata, 1, desc->blksz);
+
+	ret = gpt_get_metadata_disk_part(desc, &info, part_num);
+	if (ret < 0) {
+		printf("Unable to get the metadata partition\n");
+		return -ENODEV;
+	}
+
+	len = sizeof(*metadata);
+	blkcnt = BLOCK_CNT(len, desc);
+	if (blkcnt > info.size) {
+		log_err("Block count exceeds metadata partition size\n");
+		return -ERANGE;
+	}
+
+	blk_start = info.start;
+	if (access == MDATA_READ) {
+		if (blk_dread(desc, blk_start, blkcnt, mdata) != blkcnt) {
+			log_err("Error reading metadata from the device\n");
+			return -EIO;
+		}
+		memcpy(metadata, mdata, sizeof(struct fwu_metadata));
+	} else {
+		if (blk_dwrite(desc, blk_start, blkcnt, metadata) != blkcnt) {
+			log_err("Error writing metadata to the device\n");
+			return -EIO;
+		}
+	}
+
+	return 0;
+}
+
+static int gpt_read_metadata(struct blk_desc *desc,
+			     struct fwu_metadata *metadata, u32 part_num)
+{
+	return gpt_read_write_metadata(desc, metadata, MDATA_READ, part_num);
+}
+
+static int gpt_write_metadata_partition(struct blk_desc *desc,
+					struct fwu_metadata *metadata,
+					u32 part_num)
+{
+	return gpt_read_write_metadata(desc, metadata, MDATA_WRITE, part_num);
+}
+
+static int gpt_update_metadata(struct fwu_metadata *metadata)
+{
+	int ret;
+	struct blk_desc *desc;
+	u32 primary_mpart, secondary_mpart;
+
+	ret = fwu_plat_get_blk_desc(&desc);
+	if (ret < 0) {
+		log_err("Block device not found\n");
+		return -ENODEV;
+	}
+
+	primary_mpart = secondary_mpart = 0;
+	ret = gpt_get_metadata_partitions(desc, &primary_mpart,
+					  &secondary_mpart);
+
+	if (ret < 0) {
+		log_err("Error getting the metadata partitions\n");
+		return -ENODEV;
+	}
+
+	/* First write the primary partition*/
+	ret = gpt_write_metadata_partition(desc, metadata, primary_mpart);
+	if (ret < 0) {
+		log_err("Updating primary metadata partition failed\n");
+		return ret;
+	}
+
+	/* And now the replica */
+	ret = gpt_write_metadata_partition(desc, metadata, secondary_mpart);
+	if (ret < 0) {
+		log_err("Updating secondary metadata partition failed\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static int gpt_get_valid_metadata(struct fwu_metadata **metadata)
+{
+	int ret;
+	struct blk_desc *desc;
+	u32 primary_mpart, secondary_mpart;
+
+	ret = fwu_plat_get_blk_desc(&desc);
+	if (ret < 0) {
+		log_err("Block device not found\n");
+		return -ENODEV;
+	}
+
+	primary_mpart = secondary_mpart = 0;
+	ret = gpt_get_metadata_partitions(desc, &primary_mpart,
+					  &secondary_mpart);
+
+	if (ret < 0) {
+		log_err("Error getting the metadata partitions\n");
+		return -ENODEV;
+	}
+
+	*metadata = malloc(sizeof(struct fwu_metadata));
+	if (!*metadata) {
+		log_err("Unable to allocate memory for reading metadata\n");
+		return -ENOMEM;
+	}
+
+	ret = gpt_read_metadata(desc, *metadata, primary_mpart);
+	if (ret < 0) {
+		log_err("Failed to read the metadata from the device\n");
+		return -EIO;
+	}
+
+	ret = gpt_verify_metadata(*metadata, 1);
+	if (!ret)
+		return 0;
+
+	/*
+	 * Verification of the primary metadata copy failed.
+	 * Try to read the replica.
+	 */
+	memset(*metadata, 0, sizeof(struct fwu_metadata));
+	ret = gpt_read_metadata(desc, *metadata, secondary_mpart);
+	if (ret < 0) {
+		log_err("Failed to read the metadata from the device\n");
+		return -EIO;
+	}
+
+	ret = gpt_verify_metadata(*metadata, 0);
+	if (!ret)
+		return 0;
+
+	/* Both the metadata copies are corrupted. */
+	return -1;
+}
+
+static int gpt_check_metadata_validity(void)
+{
+	int ret;
+	struct blk_desc *desc;
+	struct fwu_metadata *pri_metadata;
+	struct fwu_metadata *secondary_metadata;
+	u32 primary_mpart, secondary_mpart;
+	u32 valid_partitions;
+
+	ret = fwu_plat_get_blk_desc(&desc);
+	if (ret < 0) {
+		log_err("Block device not found\n");
+		return -ENODEV;
+	}
+
+	/*
+	 * Two metadata partitions are expected.
+	 * If we don't have two, user needs to create
+	 * them first
+	 */
+	primary_mpart = secondary_mpart = 0;
+	valid_partitions = 0;
+	ret = gpt_get_metadata_partitions(desc, &primary_mpart,
+					  &secondary_mpart);
+
+	if (ret < 0) {
+		log_err("Error getting the metadata partitions\n");
+		return -ENODEV;
+	}
+
+	pri_metadata = malloc(sizeof(*pri_metadata));
+	if (!pri_metadata) {
+		log_err("Unable to allocate memory for reading metadata\n");
+		return -ENOMEM;
+	}
+
+	secondary_metadata = malloc(sizeof(*secondary_metadata));
+	if (!secondary_metadata) {
+		log_err("Unable to allocate memory for reading metadata\n");
+		return -ENOMEM;
+	}
+
+	ret = gpt_read_metadata(desc, pri_metadata, primary_mpart);
+	if (ret < 0) {
+		log_err("Failed to read the metadata from the device\n");
+		ret = -EIO;
+		goto out;
+	}
+
+	ret = gpt_verify_metadata(pri_metadata, 1);
+	if (!ret)
+		valid_partitions |= PRIMARY_VALID;
+
+	/* Now check the secondary partition */
+	ret = gpt_read_metadata(desc, secondary_metadata, secondary_mpart);
+	if (ret < 0) {
+		log_err("Failed to read the metadata from the device\n");
+		ret = -EIO;
+		goto out;
+	}
+
+	ret = gpt_verify_metadata(secondary_metadata, 0);
+	if (!ret)
+		valid_partitions |= SECONDARY_VALID;
+
+	if (valid_partitions == (PRIMARY_VALID | SECONDARY_VALID)) {
+		ret = -1;
+		/*
+		 * Before returning, check that both the
+		 * metadata copies are the same. If not,
+		 * the metadata copies need to be
+		 * re-populated.
+		 */
+		if (!memcmp(pri_metadata, secondary_metadata,
+			    sizeof(*pri_metadata)))
+			ret = 0;
+		goto out;
+	} else if (valid_partitions == SECONDARY_VALID) {
+		ret = gpt_write_metadata_partition(desc, secondary_metadata,
+						   primary_mpart);
+		if (ret < 0) {
+			log_err("Restoring primary metadata partition failed\n");
+			goto out;
+		}
+	} else if (valid_partitions == PRIMARY_VALID) {
+		ret = gpt_write_metadata_partition(desc, pri_metadata,
+						   secondary_mpart);
+		if (ret < 0) {
+			log_err("Restoring secondary metadata partition failed\n");
+			goto out;
+		}
+	} else {
+		ret = -1;
+	}
+
+out:
+	free(pri_metadata);
+
+	return ret;
+}
+
+static int gpt_fill_partition_guid_array(struct blk_desc *desc,
+					 efi_guid_t **part_guid_arr,
+					 u32 *nparts)
+{
+	int ret, i;
+	u32 parts;
+	gpt_entry *gpt_pte = NULL;
+	const efi_guid_t null_guid = NULL_GUID;
+
+	ALLOC_CACHE_ALIGN_BUFFER_PAD(gpt_header, gpt_head, 1,
+				     desc->blksz);
+
+	ret = get_gpt_hdr_parts(desc, gpt_head, &gpt_pte);
+	if (ret < 0) {
+		log_err("Error getting GPT header and partitions\n");
+		ret = -EIO;
+		goto out;
+	}
+
+	*nparts = gpt_head->num_partition_entries;
+
+	/*
+	 * There could be a scenario where the number of partition entries
+	 * configured in the GPT header is the default value of 128. Find
+	 * the actual number of populated partitioned entries
+	 */
+	for (i = 0, parts = 0; i < *nparts; i++) {
+		if (!guidcmp(&gpt_pte[i].partition_type_guid, &null_guid))
+			continue;
+		++parts;
+	}
+
+	*nparts = parts;
+	*part_guid_arr = malloc(sizeof(efi_guid_t) * *nparts);
+	if (!part_guid_arr) {
+		log_err("Unable to allocate memory for guid array\n");
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	for (i = 0; i < *nparts; i++) {
+		guidcpy((*part_guid_arr + i),
+			&gpt_pte[i].partition_type_guid);
+	}
+
+out:
+	free(gpt_pte);
+	return ret;
+}
+
+int fwu_gpt_get_active_index(u32 *active_idx)
+{
+	int ret;
+	struct fwu_metadata *metadata;
+
+	ret = gpt_get_valid_metadata(&metadata);
+	if (ret < 0) {
+		log_err("Unable to get valid metadata\n");
+		goto out;
+	}
+
+	/*
+	 * Found the metadata partition, now read the active_index
+	 * value
+	 */
+	*active_idx = metadata->active_index;
+	if (*active_idx > CONFIG_FWU_NUM_BANKS - 1) {
+		printf("Active index value read is incorrect\n");
+		ret = -EINVAL;
+		goto out;
+	}
+
+out:
+	free(metadata);
+
+	return ret;
+}
+
+static int gpt_get_image_alt_num(struct blk_desc *desc,
+				 efi_guid_t image_type_id,
+				 u32 update_bank, int *alt_no)
+{
+	int ret, i;
+	u32 nparts;
+	gpt_entry *gpt_pte = NULL;
+	struct fwu_metadata *metadata;
+	struct fwu_image_entry *img_entry;
+	struct fwu_image_bank_info *img_bank_info;
+	efi_guid_t unique_part_guid;
+	efi_guid_t image_guid = NULL_GUID;
+
+	ALLOC_CACHE_ALIGN_BUFFER_PAD(gpt_header, gpt_head, 1,
+				     desc->blksz);
+
+	ret = gpt_get_valid_metadata(&metadata);
+	if (ret < 0) {
+		log_err("Unable to read valid metadata\n");
+		goto out;
+	}
+
+	/*
+	 * The metadata has been read. Now get the image_uuid for the
+	 * image with the update_bank.
+	 */
+	for (i = 0; i < CONFIG_FWU_NUM_IMAGES_PER_BANK; i++) {
+		if (!guidcmp(&image_type_id,
+			     &metadata->img_entry[i].image_type_uuid)) {
+			img_entry = &metadata->img_entry[i];
+			img_bank_info = &img_entry->img_bank_info[update_bank];
+			guidcpy(&image_guid, &img_bank_info->image_uuid);
+		}
+	}
+
+	/*
+	 * Now read the GPT Partition Table Entries to find a matching
+	 * partition with UniquePartitionGuid value. We need to iterate
+	 * through all the GPT partitions since they might be in any
+	 * order
+	 */
+	ret = get_gpt_hdr_parts(desc, gpt_head, &gpt_pte);
+	if (ret < 0) {
+		log_err("Error getting GPT header and partitions\n");
+		ret = -EIO;
+		goto out;
+	}
+
+	nparts = gpt_head->num_partition_entries;
+
+	for (i = 0; i < nparts; i++) {
+		unique_part_guid = gpt_pte[i].unique_partition_guid;
+		if (!guidcmp(&unique_part_guid, &image_guid)) {
+			/* Found the partition */
+			*alt_no = i + 1;
+			break;
+		}
+	}
+
+	if (i == nparts) {
+		log_err("Partition with the image guid not found\n");
+		ret = -EINVAL;
+	}
+
+out:
+	free(metadata);
+	free(gpt_pte);
+	return ret;
+}
+
+int fwu_gpt_update_active_index(u32 active_idx)
+{
+	int ret;
+	void *buf;
+	u32 cur_active_index;
+	struct fwu_metadata *metadata;
+
+	if (active_idx > CONFIG_FWU_NUM_BANKS) {
+		printf("Active index value to be updated is incorrect\n");
+		return -1;
+	}
+
+	ret = gpt_get_valid_metadata(&metadata);
+	if (ret < 0) {
+		log_err("Unable to get valid metadata\n");
+		goto out;
+	}
+
+	/*
+	 * Update the active index and previous_active_index fields
+	 * in the metadata
+	 */
+	cur_active_index = metadata->active_index;
+	metadata->active_index = active_idx;
+	metadata->previous_active_index = cur_active_index;
+
+	/*
+	 * Calculate the crc32 for the updated metadata
+	 * and put the updated value in the metadata crc32
+	 * field
+	 */
+	buf = &metadata->version;
+	metadata->crc32 = crc32(0, buf, sizeof(*metadata) - sizeof(u32));
+
+	/*
+	 * Now write this updated metadata to both the
+	 * metadata partitions
+	 */
+	ret = gpt_update_metadata(metadata);
+	if (ret < 0) {
+		log_err("Failed to update metadata partitions\n");
+		ret = -EIO;
+	}
+
+out:
+	free(metadata);
+
+	return ret;
+}
+
+int fwu_gpt_fill_partition_guid_array(efi_guid_t **part_guid_arr, u32 *nparts)
+{
+	int ret;
+	struct blk_desc *desc;
+
+	ret = fwu_plat_get_blk_desc(&desc);
+	if (ret < 0) {
+		log_err("Block device not found\n");
+		return -ENODEV;
+	}
+
+	return gpt_fill_partition_guid_array(desc, part_guid_arr, nparts);
+}
+
+int fwu_gpt_get_image_alt_num(efi_guid_t image_type_id, u32 update_bank,
+			      int *alt_no)
+{
+	int ret;
+	struct blk_desc *desc;
+
+	ret = fwu_plat_get_blk_desc(&desc);
+	if (ret < 0) {
+		log_err("Block device not found\n");
+		return -ENODEV;
+	}
+
+	return gpt_get_image_alt_num(desc, image_type_id, update_bank, alt_no);
+}
+
+int fwu_gpt_metadata_check(void)
+{
+	/*
+	 * Check if both the copies of the metadata are valid.
+	 * If one has gone bad, restore it from the other good
+	 * copy.
+	 */
+	return gpt_check_metadata_validity();
+}
+
+int fwu_gpt_get_metadata(struct fwu_metadata **metadata)
+{
+	return gpt_get_valid_metadata(metadata);
+}
+
+int fwu_gpt_revert_boot_index(u32 *active_idx)
+{
+	int ret;
+	void *buf;
+	u32 cur_active_index;
+	struct fwu_metadata *metadata;
+
+	ret = gpt_get_valid_metadata(&metadata);
+	if (ret < 0) {
+		log_err("Unable to get valid metadata\n");
+		goto out;
+	}
+
+	/*
+	 * Swap the active index and previous_active_index fields
+	 * in the metadata
+	 */
+	cur_active_index = metadata->active_index;
+	metadata->active_index = metadata->previous_active_index;
+	metadata->previous_active_index = cur_active_index;
+	*active_idx = metadata->active_index;
+
+	/*
+	 * Calculate the crc32 for the updated metadata
+	 * and put the updated value in the metadata crc32
+	 * field
+	 */
+	buf = &metadata->version;
+	metadata->crc32 = crc32(0, buf, sizeof(*metadata) - sizeof(u32));
+
+	/*
+	 * Now write this updated metadata to both the
+	 * metadata partitions
+	 */
+	ret = gpt_update_metadata(metadata);
+	if (ret < 0) {
+		log_err("Failed to update metadata partitions\n");
+		ret = -EIO;
+	}
+
+out:
+	free(metadata);
+
+	return ret;
+}
+
+static int fwu_gpt_set_clear_image_accept(efi_guid_t *img_type_id,
+					  u32 bank, u8 action)
+{
+	void *buf;
+	int ret, i;
+	u32 nimages;
+	struct fwu_metadata *metadata;
+	struct fwu_image_entry *img_entry;
+	struct fwu_image_bank_info *img_bank_info;
+
+	ret = gpt_get_valid_metadata(&metadata);
+	if (ret < 0) {
+		log_err("Unable to get valid metadata\n");
+		goto out;
+	}
+
+	if (action == IMAGE_ACCEPT_SET)
+		bank = metadata->active_index;
+
+	nimages = CONFIG_FWU_NUM_IMAGES_PER_BANK;
+	img_entry = &metadata->img_entry[0];
+	for (i = 0; i < nimages; i++) {
+		if (!guidcmp(&img_entry[i].image_type_uuid, img_type_id)) {
+			img_bank_info = &img_entry[i].img_bank_info[bank];
+			if (action == IMAGE_ACCEPT_SET)
+				img_bank_info->accepted |= FWU_IMAGE_ACCEPTED;
+			else
+				img_bank_info->accepted = 0;
+
+			buf = &metadata->version;
+			metadata->crc32 = crc32(0, buf, sizeof(*metadata) -
+						sizeof(u32));
+
+			ret = gpt_update_metadata(metadata);
+			goto out;
+		}
+	}
+
+	/* Image not found */
+	ret = -EINVAL;
+
+out:
+	free(metadata);
+
+	return ret;
+}
+
+int fwu_gpt_accept_image(efi_guid_t *img_type_id)
+{
+	return fwu_gpt_set_clear_image_accept(img_type_id, 0,
+					      IMAGE_ACCEPT_SET);
+}
+
+int fwu_gpt_clear_accept_image(efi_guid_t *img_type_id, u32 bank)
+{
+	return fwu_gpt_set_clear_image_accept(img_type_id, bank,
+					      IMAGE_ACCEPT_CLEAR);
+}
+
+struct fwu_metadata_ops fwu_gpt_blk_ops = {
+	.get_active_index = fwu_gpt_get_active_index,
+	.update_active_index = fwu_gpt_update_active_index,
+	.fill_partition_guid_array = fwu_gpt_fill_partition_guid_array,
+	.get_image_alt_num = fwu_gpt_get_image_alt_num,
+	.metadata_check = fwu_gpt_metadata_check,
+	.revert_boot_index = fwu_gpt_revert_boot_index,
+	.set_accept_image = fwu_gpt_accept_image,
+	.clear_accept_image = fwu_gpt_clear_accept_image,
+	.get_metadata = fwu_gpt_get_metadata,
+};