feat(partition): add support to use backup GPT header

Currently we just use primary GPT header which is located in second
entry after MBR header, but if this block is corrupted or CRC
mismatch occurs we could try to use the backup GPT header located at
LBAn and GPT entries following this from LBA-33.

Add suitable warning messages before returning any errors to identify
the cause of issue.

Change-Id: I0018ae9eafbacb683a18784d2c8bd917c70f50e1
Signed-off-by: Govindraj Raja <govindraj.raja@arm.com>
diff --git a/drivers/partition/partition.c b/drivers/partition/partition.c
index 7b65511..cd7ffad 100644
--- a/drivers/partition/partition.c
+++ b/drivers/partition/partition.c
@@ -69,6 +69,7 @@
 	/* Check MBR boot signature. */
 	if ((mbr_sector[LEGACY_PARTITION_BLOCK_SIZE - 2] != MBR_SIGNATURE_FIRST) ||
 	    (mbr_sector[LEGACY_PARTITION_BLOCK_SIZE - 1] != MBR_SIGNATURE_SECOND)) {
+		WARN("MBR boot signature failure\n");
 		return -ENOENT;
 	}
 
@@ -102,15 +103,21 @@
 
 	result = io_seek(image_handle, IO_SEEK_SET, header_offset);
 	if (result != 0) {
+		WARN("Failed to seek into the GPT image at offset (%zu)\n",
+		     header_offset);
 		return result;
 	}
 	result = io_read(image_handle, (uintptr_t)&header,
 			 sizeof(gpt_header_t), &bytes_read);
 	if ((result != 0) || (sizeof(gpt_header_t) != bytes_read)) {
+		WARN("GPT header read error(%i) or read mismatch occurred"
+		     "expected(%zu) and actual(%zu)\n", result,
+		     sizeof(gpt_header_t), bytes_read);
 		return result;
 	}
 	if (memcmp(header.signature, GPT_SIGNATURE,
-		   sizeof(header.signature)) != 0) {
+			   sizeof(header.signature)) != 0) {
+		WARN("GPT header signature failure\n");
 		return -EINVAL;
 	}
 
@@ -141,6 +148,9 @@
 	return 0;
 }
 
+/*
+ * Load a single MBR entry based on details from MBR header.
+ */
 static int load_mbr_entry(uintptr_t image_handle, mbr_entry_t *mbr_entry,
 			  int part_number)
 {
@@ -165,6 +175,7 @@
 	/* Check MBR boot signature. */
 	if ((mbr_sector[LEGACY_PARTITION_BLOCK_SIZE - 2] != MBR_SIGNATURE_FIRST) ||
 	    (mbr_sector[LEGACY_PARTITION_BLOCK_SIZE - 1] != MBR_SIGNATURE_SECOND)) {
+		WARN("MBR Entry boot signature failure\n");
 		return -ENOENT;
 	}
 	offset = (uintptr_t)&mbr_sector +
@@ -175,6 +186,9 @@
 	return 0;
 }
 
+/*
+ * Load MBR entries based on max number of partition entries.
+ */
 static int load_mbr_entries(uintptr_t image_handle)
 {
 	mbr_entry_t mbr_entry;
@@ -192,33 +206,60 @@
 	return 0;
 }
 
+/*
+ * Try to read and load a single GPT entry.
+ */
 static int load_gpt_entry(uintptr_t image_handle, gpt_entry_t *entry)
 {
-	size_t bytes_read;
+	size_t bytes_read = 0U;
 	int result;
 
 	assert(entry != NULL);
 	result = io_read(image_handle, (uintptr_t)entry, sizeof(gpt_entry_t),
-			 &bytes_read);
-	if (sizeof(gpt_entry_t) != bytes_read)
+			&bytes_read);
+	if ((result != 0) || (sizeof(gpt_entry_t) != bytes_read)) {
+		WARN("GPT Entry read error(%i) or read mismatch occurred"
+		     "expected(%zu) and actual(%zu)\n", result,
+		     sizeof(gpt_entry_t), bytes_read);
 		return -EINVAL;
+	}
+
 	return result;
 }
 
-static int verify_partition_gpt(uintptr_t image_handle)
+/*
+ * Retrieve each entry in the partition table, parse the data from each
+ * entry and store them in the list of partition table entries.
+ */
+static int load_partition_gpt(uintptr_t image_handle,
+			      unsigned long long part_lba)
 {
+	const signed long long gpt_entry_offset = LBA(part_lba);
 	gpt_entry_t entry;
 	int result, i;
 
+	result = io_seek(image_handle, IO_SEEK_SET, gpt_entry_offset);
+	if (result != 0) {
+		WARN("Failed to seek (%i), Failed loading GPT partition"
+		     "table entries\n", result);
+		return result;
+	}
+
 	for (i = 0; i < list.entry_count; i++) {
 		result = load_gpt_entry(image_handle, &entry);
-		assert(result == 0);
+		if (result != 0) {
+			WARN("Failed to load gpt entry data(%i) error is (%i)\n",
+			     i, result);
+			return result;
+		}
+
 		result = parse_gpt_entry(&entry, &list.list[i]);
 		if (result != 0) {
 			break;
 		}
 	}
 	if (i == 0) {
+		WARN("No Valid GPT Entries found\n");
 		return -EINVAL;
 	}
 	/*
@@ -231,18 +272,104 @@
 	return 0;
 }
 
+/*
+ * Try retrieving and parsing the backup-GPT header and backup GPT entries.
+ * Last 33 blocks contains the backup-GPT entries and header.
+ */
+static int load_backup_gpt(unsigned int image_id, unsigned int sector_nums)
+{
+	int result;
+	unsigned long long part_lba = 0;
+	size_t gpt_header_offset;
+	uintptr_t dev_handle, image_spec, image_handle;
+	io_block_spec_t *block_spec;
+	int part_num_entries;
+
+	result = plat_get_image_source(image_id, &dev_handle, &image_spec);
+	if (result != 0) {
+		WARN("Failed to obtain reference to image id=%u (%i)\n",
+		     image_id, result);
+		return result;
+	}
+
+	block_spec = (io_block_spec_t *)image_spec;
+	/*
+	 * We need to read 32 blocks of GPT entries and one block of GPT header
+	 * try mapping only last 33 last blocks from the image to read the
+	 * Backup-GPT header and its entries.
+	 */
+	part_num_entries = (PLAT_PARTITION_MAX_ENTRIES / 4);
+	/* Move the offset base to LBA-33 */
+	block_spec->offset += LBA(sector_nums - part_num_entries);
+	/*
+	 * Set length as LBA-33, 32 blocks of backup-GPT entries and one
+	 * block of backup-GPT header.
+	 */
+	block_spec->length = LBA(part_num_entries + 1);
+
+	result = io_open(dev_handle, image_spec, &image_handle);
+	if (result != 0) {
+		WARN("Failed to access image id (%i)\n", result);
+		return result;
+	}
+
+	INFO("Trying to retrieve back-up GPT header\n");
+	/* Last block is backup-GPT header, after the end of GPT entries */
+	gpt_header_offset = LBA(part_num_entries);
+	result = load_gpt_header(image_handle, gpt_header_offset, &part_lba);
+	if ((result != 0) || (part_lba == 0)) {
+		ERROR("Failed to retrieve Backup GPT header,"
+		      "Partition maybe corrupted\n");
+		goto out;
+	}
+
+	/*
+	 * Note we mapped last 33 blocks(LBA-33), first block here starts with
+	 * entries while last block was header.
+	 */
+	result = load_partition_gpt(image_handle, 0);
+
+out:
+	io_close(image_handle);
+	return result;
+}
+
+/*
+ * Load a GPT partition, Try retrieving and parsing the primary GPT header,
+ * if its corrupted try loading backup GPT header and then retrieve list
+ * of partition table entries found from the GPT.
+ */
+static int load_primary_gpt(uintptr_t image_handle, unsigned int first_lba)
+{
+	int result;
+	unsigned long long part_lba;
+	size_t gpt_header_offset;
+
+	/* Try to load Primary GPT header from LBA1 */
+	gpt_header_offset = LBA(first_lba);
+	result = load_gpt_header(image_handle, gpt_header_offset, &part_lba);
+	if ((result != 0) || (part_lba == 0)) {
+		WARN("Failed to retrieve Primary GPT header,"
+		     "trying to retrieve back-up GPT header\n");
+		return result;
+	}
+
+	return load_partition_gpt(image_handle, part_lba);
+}
+
+/*
+ * Load the partition table info based on the image id provided.
+ */
 int load_partition_table(unsigned int image_id)
 {
 	uintptr_t dev_handle, image_handle, image_spec = 0;
 	mbr_entry_t mbr_entry;
 	int result;
-	size_t gpt_header_offset;
-	unsigned long long part_lba = 0, gpt_entry_offset = 0;
 
 	result = plat_get_image_source(image_id, &dev_handle, &image_spec);
 	if (result != 0) {
 		WARN("Failed to obtain reference to image id=%u (%i)\n",
-			image_id, result);
+		     image_id, result);
 		return result;
 	}
 
@@ -255,34 +382,27 @@
 	result = load_mbr_header(image_handle, &mbr_entry);
 	if (result != 0) {
 		WARN("Failed to access image id=%u (%i)\n", image_id, result);
-		return result;
+		goto out;
 	}
 	if (mbr_entry.type == PARTITION_TYPE_GPT) {
-		/* Try to load GPT header from LBA-1 */
-		gpt_header_offset = mbr_entry.first_lba * PLAT_PARTITION_BLOCK_SIZE;
-		result = load_gpt_header(image_handle, gpt_header_offset, &part_lba);
-		if (result != 0 || part_lba == 0) {
-			WARN("Failed to retrieve Primary GPT header\n");
-			return result;
-		}
-
-		gpt_entry_offset = part_lba * PLAT_PARTITION_BLOCK_SIZE;
-		result = io_seek(image_handle, IO_SEEK_SET, gpt_entry_offset);
+		result = load_primary_gpt(image_handle, mbr_entry.first_lba);
 		if (result != 0) {
-			WARN("Failed to seek (%i),"
-			     "Failed loading GPT partition table entries\n",
-			     result);
-			return result;
+			io_close(image_handle);
+			return load_backup_gpt(BKUP_GPT_IMAGE_ID,
+					       mbr_entry.sector_nums);
 		}
-		result = verify_partition_gpt(image_handle);
 	} else {
 		result = load_mbr_entries(image_handle);
 	}
 
+out:
 	io_close(image_handle);
 	return result;
 }
 
+/*
+ * Try retrieving a partition table entry based on the name of the partition.
+ */
 const partition_entry_t *get_partition_entry(const char *name)
 {
 	int i;
@@ -295,6 +415,9 @@
 	return NULL;
 }
 
+/*
+ * Try retrieving a partition table entry based on the GUID.
+ */
 const partition_entry_t *get_partition_entry_by_type(const uuid_t *type_uuid)
 {
 	int i;
@@ -308,6 +431,9 @@
 	return NULL;
 }
 
+/*
+ * Try retrieving a partition table entry based on the UUID.
+ */
 const partition_entry_t *get_partition_entry_by_uuid(const uuid_t *part_uuid)
 {
 	int i;
@@ -321,11 +447,17 @@
 	return NULL;
 }
 
+/*
+ * Return entry to the list of partition table entries.
+ */
 const partition_entry_list_t *get_partition_entry_list(void)
 {
 	return &list;
 }
 
+/*
+ * Try loading partition table info for the given image ID.
+ */
 void partition_init(unsigned int image_id)
 {
 	load_partition_table(image_id);