cgpt: Handle read errors gracefully

When a read fails in getting the GPT, just zero the contents of the
buffer and carry on.

Some testing changes are required for this. When a read of the GPT
fails, it is no longer fatal, so tests of that have been adjusted.
Tests have been improved to show that the GPT is automatically
repaired when a read error occurs.
There was one test which checked that a zero-sized disk would fail
to load a kernel, but it was surrounded by a number of mocked
functions which normally do that error checking, and it amounted
to the same test as read failure; that test was deleted.

BUG=chrome-os-partner:35440
TEST=vboot tests pass
BRANCH=none

Change-Id: I0c05813e7492920433733947d3fb74a7e4aa66f2
Signed-off-by: Dan Ehrenberg <dehrenberg@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/266882
Reviewed-by: Randall Spangler <rspangler@chromium.org>
diff --git a/firmware/lib/gpt_misc.c b/firmware/lib/gpt_misc.c
index 4061bdd..cff9221 100644
--- a/firmware/lib/gpt_misc.c
+++ b/firmware/lib/gpt_misc.c
@@ -43,8 +43,10 @@
 		return 1;
 
 	/* Read primary header from the drive, skipping the protective MBR */
-	if (0 != VbExDiskRead(disk_handle, 1, 1, gptdata->primary_header))
-		return 1;
+	if (0 != VbExDiskRead(disk_handle, 1, 1, gptdata->primary_header)) {
+		VBDEBUG(("Read error in primary GPT header\n"));
+		Memset(gptdata->primary_header, 0, gptdata->sector_bytes);
+	}
 
 	/* Only read primary GPT if the primary header is valid */
 	GptHeader* primary_header = (GptHeader*)gptdata->primary_header;
@@ -60,16 +62,20 @@
 		if (0 != VbExDiskRead(disk_handle,
 				      primary_header->entries_lba,
 				      entries_sectors,
-				      gptdata->primary_entries))
-			return 1;
+				      gptdata->primary_entries)) {
+			VBDEBUG(("Read error in primary GPT entries\n"));
+			primary_valid = 0;
+		}
 	} else {
 		VBDEBUG(("Primary GPT header invalid!\n"));
 	}
 
 	/* Read secondary header from the end of the drive */
 	if (0 != VbExDiskRead(disk_handle, gptdata->gpt_drive_sectors - 1, 1,
-			      gptdata->secondary_header))
-		return 1;
+			      gptdata->secondary_header)) {
+		VBDEBUG(("Read error in secondary GPT header\n"));
+		Memset(gptdata->secondary_header, 0, gptdata->sector_bytes);
+	}
 
 	/* Only read secondary GPT if the secondary header is valid */
 	GptHeader* secondary_header = (GptHeader*)gptdata->secondary_header;
@@ -85,8 +91,10 @@
 		if (0 != VbExDiskRead(disk_handle,
 				      secondary_header->entries_lba,
 				      entries_sectors,
-				      gptdata->secondary_entries))
-			return 1;
+				      gptdata->secondary_entries)) {
+			VBDEBUG(("Read error in secondary GPT entries\n"));
+			secondary_valid = 0;
+		}
 	} else {
 		VBDEBUG(("Secondary GPT header invalid!\n"));
 	}
diff --git a/tests/vboot_kernel_tests.c b/tests/vboot_kernel_tests.c
index 03e77f9..e690bc3 100644
--- a/tests/vboot_kernel_tests.c
+++ b/tests/vboot_kernel_tests.c
@@ -12,6 +12,7 @@
 
 #include "cgptlib.h"
 #include "cgptlib_internal.h"
+#include "crc32.h"
 #include "gbb_header.h"
 #include "gpt.h"
 #include "host_common.h"
@@ -467,27 +468,43 @@
 	/* Error reading */
 	ResetMocks();
 	disk_read_to_fail = 1;
-	TEST_NEQ(AllocAndReadGptData(handle, &g), 0, "AllocAndRead disk fail");
-	Memset(g.primary_header, '\0', g.sector_bytes);
-	WriteAndFreeGptData(handle, &g);
+	TEST_EQ(AllocAndReadGptData(handle, &g), 0, "AllocAndRead disk fail");
+	g.valid_headers = g.valid_entries = MASK_SECONDARY;
+	GptRepair(&g);
+	ResetCallLog();
+	TEST_EQ(WriteAndFreeGptData(handle, &g), 0, "WriteAndFree mod 1");
+	TEST_CALLS("VbExDiskWrite(h, 1, 1)\n"
+		   "VbExDiskWrite(h, 2, 32)\n");
 
 	ResetMocks();
 	disk_read_to_fail = 2;
-	TEST_NEQ(AllocAndReadGptData(handle, &g), 0, "AllocAndRead disk fail");
-	Memset(g.primary_header, '\0', g.sector_bytes);
-	WriteAndFreeGptData(handle, &g);
+	TEST_EQ(AllocAndReadGptData(handle, &g), 0, "AllocAndRead disk fail");
+	g.valid_headers = MASK_BOTH;
+	g.valid_entries = MASK_SECONDARY;
+	GptRepair(&g);
+	ResetCallLog();
+	TEST_EQ(WriteAndFreeGptData(handle, &g), 0, "WriteAndFree mod 1");
+	TEST_CALLS("VbExDiskWrite(h, 2, 32)\n");
 
 	ResetMocks();
 	disk_read_to_fail = 991;
-	TEST_NEQ(AllocAndReadGptData(handle, &g), 0, "AllocAndRead disk fail");
-	Memset(g.primary_header, '\0', g.sector_bytes);
-	WriteAndFreeGptData(handle, &g);
+	TEST_EQ(AllocAndReadGptData(handle, &g), 0, "AllocAndRead disk fail");
+	g.valid_headers = MASK_BOTH;
+	g.valid_entries = MASK_PRIMARY;
+	GptRepair(&g);
+	ResetCallLog();
+	TEST_EQ(WriteAndFreeGptData(handle, &g), 0, "WriteAndFree mod 2");
+	TEST_CALLS("VbExDiskWrite(h, 991, 32)\n");
 
 	ResetMocks();
 	disk_read_to_fail = 1023;
-	TEST_NEQ(AllocAndReadGptData(handle, &g), 0, "AllocAndRead disk fail");
-	Memset(g.primary_header, '\0', g.sector_bytes);
-	WriteAndFreeGptData(handle, &g);
+	TEST_EQ(AllocAndReadGptData(handle, &g), 0, "AllocAndRead disk fail");
+	g.valid_headers = g.valid_entries = MASK_PRIMARY;
+	GptRepair(&g);
+	ResetCallLog();
+	TEST_EQ(WriteAndFreeGptData(handle, &g), 0, "WriteAndFree mod 2");
+	TEST_CALLS("VbExDiskWrite(h, 1023, 1)\n"
+		   "VbExDiskWrite(h, 991, 32)\n");
 
 	/* Error writing */
 	ResetMocks();
@@ -543,20 +560,10 @@
 		"Huge lba size");
 
 	ResetMocks();
-	disk_read_to_fail = 1;
-	TEST_EQ(LoadKernel(&lkp, &cparams), VBERROR_NO_KERNEL_FOUND,
-		"Can't read disk");
-
-	ResetMocks();
 	gpt_init_fail = 1;
 	TEST_EQ(LoadKernel(&lkp, &cparams), VBERROR_NO_KERNEL_FOUND,
 		"Bad GPT");
 
-	ResetMocks();
-	lkp.gpt_lba_count = 0;
-	TEST_EQ(LoadKernel(&lkp, &cparams), VBERROR_NO_KERNEL_FOUND,
-		"GPT size = 0");
-
 	/* This causes the stream open call to fail */
 	ResetMocks();
 	lkp.disk_handle = NULL;
@@ -765,6 +772,11 @@
 	lkp.boot_flags |= BOOT_FLAG_EXTERNAL_GPT;
 	TEST_EQ(LoadKernel(&lkp, &cparams), 0, "Succeed external GPT");
 	TEST_EQ(gpt_flag_external, 1, "GPT was external");
+
+	/* Check recovery from unreadble primary GPT */
+	ResetMocks();
+	disk_read_to_fail = 1;
+	TEST_EQ(LoadKernel(&lkp, &cparams), 0, "Can't read disk");
 }
 
 int main(void)