platform: msm_shared: check for CRC of GPT header and partition entries

Validate gpt header CRC and partition entires CRC for GPT

Change-Id: I553ce43c57e03acde9e0ae5211b5c1d0467a3a9d
diff --git a/platform/msm_shared/partition_parser.c b/platform/msm_shared/partition_parser.c
index 387e9fc..b10a85c 100644
--- a/platform/msm_shared/partition_parser.c
+++ b/platform/msm_shared/partition_parser.c
@@ -28,8 +28,14 @@
 
 #include <stdlib.h>
 #include <string.h>
+#include <crc32.h>
 #include "mmc.h"
 #include "partition_parser.h"
+#define GPT_HEADER_SIZE 92
+#define GPT_LBA 1
+#define PARTITION_ENTRY_SIZE 128
+static bool flashing_gpt = 0;
+static bool parse_secondary_gpt = 0;
 
 static uint32_t mmc_boot_read_gpt(uint32_t block_size);
 static uint32_t mmc_boot_read_mbr(uint32_t block_size);
@@ -260,7 +266,6 @@
 		dprintf(CRITICAL, "GPT: Could not read primary gpt from mmc\n");
 		goto end;
 	}
-
 	ret = partition_parse_gpt_header(data, &first_usable_lba,
 					 &partition_entry_size, &header_size,
 					 &max_partition_count);
@@ -647,10 +652,12 @@
 	}
 	offset = (2 * partition_entry_array_size);
 	secondary_gpt_header = offset + block_size + primary_gpt_header;
+	parse_secondary_gpt = 1;
 	ret =
 	    partition_parse_gpt_header(secondary_gpt_header, &first_usable_lba,
 				       &partition_entry_size, &header_size,
 				       &max_partition_count);
+	parse_secondary_gpt = 0;
 	if (ret) {
 		dprintf(CRITICAL,
 			"GPT: Backup signature invalid cannot write GPT\n");
@@ -725,6 +732,7 @@
 			"GPT: Failure to re- read the GPT Partition table\n");
 		goto end;
 	}
+	flashing_gpt = 0;
 	partition_dump();
 	dprintf(CRITICAL, "GPT: Partition Table written\n");
 	memset(primary_gpt_header, 0x00, size);
@@ -757,6 +765,7 @@
 
 	case PARTITION_TYPE_GPT:
 		dprintf(INFO, "Writing GPT partition\n");
+		flashing_gpt = 1;
 		ret = write_gpt(size, partition, block_size);
 		dprintf(CRITICAL, "Re-Flash all the partitions\n");
 		break;
@@ -987,20 +996,129 @@
 			   unsigned int *header_size,
 			   unsigned int *max_partition_count)
 {
+	uint32_t crc_val_org = 0;
+	uint32_t crc_val = 0;
+	uint32_t ret = 0;
+	uint32_t partitions_for_block = 0;
+	uint32_t blocks_to_read = 0;
+	unsigned char *new_buffer = NULL;
+	unsigned long long last_usable_lba = 0;
+	unsigned long long partition_0 = 0;
+	unsigned long long current_lba = 0;
+	uint32_t block_size = mmc_get_device_blocksize();
+	/* Get the density of the mmc device */
+	uint64_t device_density = mmc_get_device_capacity();
+
 	/* Check GPT Signature */
 	if (((uint32_t *) buffer)[0] != GPT_SIGNATURE_2 ||
 	    ((uint32_t *) buffer)[1] != GPT_SIGNATURE_1)
 		return 1;
 
 	*header_size = GET_LWORD_FROM_BYTE(&buffer[HEADER_SIZE_OFFSET]);
+	/*check for header size too small*/
+	if (*header_size < GPT_HEADER_SIZE) {
+		dprintf(CRITICAL,"GPT Header size is too small\n");
+		return 1;
+	}
+	/*check for header size too large*/
+	if (*header_size > block_size) {
+		dprintf(CRITICAL,"GPT Header size is too large\n");
+		return 1;
+	}
+
+	crc_val_org = GET_LWORD_FROM_BYTE(&buffer[HEADER_CRC_OFFSET]);
+	/*Write CRC to 0 before we calculate the crc of the GPT header*/
+	crc_val = 0;
+	PUT_LONG(&buffer[HEADER_CRC_OFFSET], crc_val);
+
+	crc_val  = crc32(~0L,buffer, *header_size) ^ (~0L);
+	if (crc_val != crc_val_org) {
+		dprintf(CRITICAL,"Header crc mismatch crc_val = %u with crc_val_org = %u\n", crc_val,crc_val_org);
+		return 1;
+	}
+	else
+		PUT_LONG(&buffer[HEADER_CRC_OFFSET], crc_val);
+
+	current_lba =
+	    GET_LLWORD_FROM_BYTE(&buffer[PRIMARY_HEADER_OFFSET]);
 	*first_usable_lba =
 	    GET_LLWORD_FROM_BYTE(&buffer[FIRST_USABLE_LBA_OFFSET]);
 	*max_partition_count =
 	    GET_LWORD_FROM_BYTE(&buffer[PARTITION_COUNT_OFFSET]);
 	*partition_entry_size =
 	    GET_LWORD_FROM_BYTE(&buffer[PENTRY_SIZE_OFFSET]);
+	last_usable_lba =
+	    GET_LLWORD_FROM_BYTE(&buffer[LAST_USABLE_LBA_OFFSET]);
 
-	return 0;
+	/*current lba and GPT lba should be same*/
+	if (!parse_secondary_gpt) {
+		if (current_lba != GPT_LBA) {
+			dprintf(CRITICAL,"GPT first usable LBA mismatch\n");
+			return 1;
+		}
+	}
+	/*check for first lba should be with in the valid range*/
+	if (*first_usable_lba > (device_density/block_size)) {
+		dprintf(CRITICAL,"Invalid first_usable_lba\n");
+		return 1;
+	}
+	/*check for last lba should be with in the valid range*/
+	if (last_usable_lba > (device_density/block_size)) {
+		dprintf(CRITICAL,"Invalid last_usable_lba\n");
+		return 1;
+	}
+	/*check for partition entry size*/
+	if (*partition_entry_size != PARTITION_ENTRY_SIZE) {
+		dprintf(CRITICAL,"Invalid parition entry size\n");
+		return 1;
+	}
+
+	if ((*max_partition_count) > (MIN_PARTITION_ARRAY_SIZE /(*partition_entry_size))) {
+		dprintf(CRITICAL, "Invalid maximum partition count\n");
+		return 1;
+	}
+
+	partitions_for_block = block_size / (*partition_entry_size);
+
+	blocks_to_read = (*max_partition_count)/ partitions_for_block;
+	if ((*max_partition_count) % partitions_for_block) {
+		blocks_to_read += 1;
+	}
+
+	new_buffer = (uint8_t *)memalign(CACHE_LINE, ROUNDUP((blocks_to_read * block_size),CACHE_LINE));
+
+	if (!new_buffer)
+	{
+		dprintf(CRITICAL, "Failed to Allocate memory to read partition table\n");
+		return 1;
+	}
+
+	if (!flashing_gpt) {
+		partition_0 = GET_LLWORD_FROM_BYTE(&buffer[PARTITION_ENTRIES_OFFSET]);
+		/*start LBA should always be 2 in primary GPT*/
+		if(partition_0 != 0x2) {
+			dprintf(CRITICAL, "Starting LBA mismatch\n");
+			goto fail;
+
+		}
+		/*read the partition entries to new_buffer*/
+		ret = mmc_read((partition_0) * (block_size), (unsigned int *)new_buffer, (blocks_to_read * block_size));
+		if (ret)
+		{
+			dprintf(CRITICAL, "GPT: Could not read primary gpt from mmc\n");
+			goto fail;
+		}
+		crc_val_org = GET_LWORD_FROM_BYTE(&buffer[PARTITION_CRC_OFFSET]);
+
+		crc_val  = crc32(~0L,new_buffer, ((*max_partition_count) * (*partition_entry_size))) ^ (~0L);
+		if (crc_val != crc_val_org) {
+			dprintf(CRITICAL,"Partition entires crc mismatch crc_val= %u with crc_val_org= %u\n",crc_val,crc_val_org);
+			ret = 1;
+		}
+	}
+fail:
+	free(new_buffer);
+	return ret;
 }
 
 bool partition_gpt_exists()