futility/cmd_validate_rec_mrc: Update futility to use new MRC struct

1. Use mrc_metadata structure with new fields for header checksum and
data checksum.
2. Use region file metadata to ensure there is only one metadata block
present and only one slot in recovery MRC cache. Use the offset and size
based on values in metadata block.

BUG=chrome-os-partner:61306
BRANCH=None
TEST=Verified that recovery MRC cache is verified using futility.

Change-Id: I68b2d75ea70fdaef6c87cdaa6ce97656e8a8bddc
Signed-off-by: Furquan Shaikh <furquan@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/424213
Reviewed-by: Aaron Durbin <adurbin@chromium.org>
diff --git a/futility/cmd_validate_rec_mrc.c b/futility/cmd_validate_rec_mrc.c
index 3013891..1fdd47c 100644
--- a/futility/cmd_validate_rec_mrc.c
+++ b/futility/cmd_validate_rec_mrc.c
@@ -35,19 +35,22 @@
 	printf("\n");
 }
 
-struct mrc_cache {
+struct mrc_metadata {
 	uint32_t signature;
-	uint32_t size;
-	uint32_t checksum;
+	uint32_t data_size;
+	uint16_t data_checksum;
+	uint16_t header_checksum;
 	uint32_t version;
-	uint8_t  data[0];
 } __attribute__((packed));
 
 #define MRC_DATA_SIGNATURE	(('M'<<0)|('R'<<8)|('C'<<16)|('D'<<24))
+#define REGF_BLOCK_SHIFT	4
+#define REGF_UNALLOCATED_BLOCK	0xffff
 
-unsigned long compute_ip_checksum(void *addr, unsigned long length)
+
+unsigned long compute_ip_checksum(const void *addr, unsigned long length)
 {
-	uint8_t *ptr;
+	const uint8_t *ptr;
 	volatile union {
 		uint8_t  byte[2];
 		uint16_t word;
@@ -77,29 +80,71 @@
 	return (~value.word) & 0xFFFF;
 }
 
-static int verify_checksum(struct mrc_cache *cache, unsigned long cache_len)
+static int verify_mrc_slot(struct mrc_metadata *md, unsigned long slot_len)
 {
-	uint32_t checksum;
-	if (cache->signature != MRC_DATA_SIGNATURE) {
+	uint32_t header_checksum;
+	if (md->signature != MRC_DATA_SIGNATURE) {
 		fprintf(stderr, "MRC signature mismatch\n");
 		return 1;
 	}
 
 	fprintf(stderr, "MRC signature match.. successful\n");
 
-	if (cache->size > cache_len) {
+	if (md->data_size > slot_len) {
 		fprintf(stderr, "MRC cache size overflow\n");
 		return 1;
 	}
 
-	checksum = compute_ip_checksum((void *)&cache->data[0], cache->size);
+	header_checksum = md->header_checksum;
+	md->header_checksum = 0;
 
-	if (checksum != cache->checksum) {
-		fprintf(stderr, "MRC checksum mismatch\n");
+	if (header_checksum != compute_ip_checksum(md, sizeof(*md))) {
+		fprintf(stderr, "MRC metadata header checksum mismatch\n");
 		return 1;
 	}
 
-	fprintf(stderr, "MRC checksum.. verified!\n");
+	md->header_checksum = header_checksum;
+
+	fprintf(stderr, "MRC metadata header checksum.. verified!\n");
+
+	if (md->data_checksum != compute_ip_checksum(&md[1], md->data_size)) {
+		fprintf(stderr, "MRC data checksum mismatch\n");
+		return 1;
+	}
+
+	fprintf(stderr, "MRC data checksum.. verified!\n");
+	return 0;
+}
+
+static int get_mrc_data_slot(uint16_t *mb, uint32_t *data_offset,
+			     uint32_t *data_size)
+{
+	/*
+	 * First block offset in metadata block tells the total number of
+	 * metadata blocks.
+	 * Currently, we expect only 1 metadata block to be present.
+	 */
+	if (*mb != 1) {
+		fprintf(stderr, "Only 1 metadata block is expected. "
+			"Actual %x\n", *mb);
+		return 1;
+	}
+
+	/*
+	 * RECOVERY_MRC_CACHE is expected to contain only one slot. Thus, there
+	 * should be only one block offset present, indicating size of the MRC
+	 * cache slot.
+	 */
+	mb++;
+	*data_offset = (1 << REGF_BLOCK_SHIFT);
+	*data_size = (*mb - 1) << REGF_BLOCK_SHIFT;
+
+	mb++;
+	if (*mb != REGF_UNALLOCATED_BLOCK) {
+		fprintf(stderr, "More than 1 slot in recovery mrc cache.\n");
+		return 1;
+	}
+
 	return 0;
 }
 
@@ -111,6 +156,8 @@
 	uint32_t file_size;
 	uint8_t *buff;
 	uint32_t offset = 0;
+	uint32_t data_offset;
+	uint32_t data_size;
 	char *e;
 
 	while (((i = getopt_long(argc, argv, ":", long_opts, NULL)) != -1) &&
@@ -162,12 +209,22 @@
 		return 1;
 	}
 
-	if (file_size > offset)
-		ret = verify_checksum((struct mrc_cache *)(buff + offset),
-				      file_size - offset);
+	if (get_mrc_data_slot((uint16_t *)(buff + offset), &data_offset,
+			      &data_size)) {
+		fprintf(stderr, "Metadata block error\n");
+		futil_unmap_file(fd, MAP_RO, buff, file_size);
+		close(fd);
+		return 1;
+	}
+	offset += data_offset;
+
+	if ((file_size > offset) && ((file_size - offset) >= data_size))
+		ret = verify_mrc_slot((struct mrc_metadata *)(buff + offset),
+				      data_size);
 	else
-		fprintf(stderr, "Offset greater than file size: offset=0x%x, "
-			"file size=0x%x\n", offset, file_size);
+		fprintf(stderr, "Offset or data size greater than file size: "
+			"offset=0x%x, file size=0x%x, data_size=0x%x\n",
+			offset, file_size, data_size);
 
 	if (futil_unmap_file(fd, MAP_RO, buff, file_size) != FILE_ERR_NONE) {
 		fprintf(stderr, "Failed to unmap file %s\n", infile);