Index: src/platform/vboot_reference/cgptlib/tests/cgpt_test.c |
diff --git a/src/platform/vboot_reference/cgptlib/tests/cgpt_test.c b/src/platform/vboot_reference/cgptlib/tests/cgpt_test.c |
index 933d3a2cb9dc267a7251cc75cd3ce5aa09393a6c..964232d6e6c3f7f7f82b37a710541f31d5b76c01 100644 |
--- a/src/platform/vboot_reference/cgptlib/tests/cgpt_test.c |
+++ b/src/platform/vboot_reference/cgptlib/tests/cgpt_test.c |
@@ -15,17 +15,23 @@ |
/* Testing partition layout (sector_bytes=512) |
* |
* LBA Size Usage |
+ * --------------------------------------------------------- |
* 0 1 PMBR |
* 1 1 primary partition header |
* 2 32 primary partition entries (128B * 128) |
- * 34 100 kernel A |
- * 134 100 kernel B |
- * 234 100 root A |
- * 334 100 root B |
+ * 34 100 kernel A (index: 0) |
+ * 134 100 root A (index: 1) |
+ * 234 100 root B (index: 2) |
+ * 334 100 kernel B (index: 3) |
* 434 32 secondary partition entries |
* 466 1 secondary partition header |
* 467 |
*/ |
+#define KERNEL_A 0 |
+#define ROOTFS_A 1 |
+#define ROOTFS_B 2 |
+#define KERNEL_B 3 |
+ |
#define DEFAULT_SECTOR_SIZE 512 |
#define MAX_SECTOR_SIZE 4096 |
#define DEFAULT_DRIVE_SECTORS 467 |
@@ -87,6 +93,9 @@ GptData* GetEmptyGptData() { |
gpt.secondary_entries = secondary_entries; |
ZeroHeadersEntries(&gpt); |
+ /* Initialize GptData internal states. */ |
+ gpt.current_kernel = CGPT_KERNEL_ENTRY_NOT_FOUND; |
+ |
return &gpt; |
} |
@@ -99,9 +108,11 @@ void BuildTestGptData(GptData *gpt) { |
GptHeader *header, *header2; |
GptEntry *entries, *entries2; |
Guid chromeos_kernel = GPT_ENT_TYPE_CHROMEOS_KERNEL; |
+ Guid chromeos_rootfs = GPT_ENT_TYPE_CHROMEOS_ROOTFS; |
gpt->sector_bytes = DEFAULT_SECTOR_SIZE; |
gpt->drive_sectors = DEFAULT_DRIVE_SECTORS; |
+ gpt->current_kernel = CGPT_KERNEL_ENTRY_NOT_FOUND; |
/* build primary */ |
header = (GptHeader*)gpt->primary_header; |
@@ -119,10 +130,10 @@ void BuildTestGptData(GptData *gpt) { |
Memcpy(&entries[0].type, &chromeos_kernel, sizeof(chromeos_kernel)); |
entries[0].starting_lba = 34; |
entries[0].ending_lba = 133; |
- Memcpy(&entries[1].type, &chromeos_kernel, sizeof(chromeos_kernel)); |
+ Memcpy(&entries[1].type, &chromeos_rootfs, sizeof(chromeos_rootfs)); |
entries[1].starting_lba = 134; |
entries[1].ending_lba = 233; |
- Memcpy(&entries[2].type, &chromeos_kernel, sizeof(chromeos_kernel)); |
+ Memcpy(&entries[2].type, &chromeos_rootfs, sizeof(chromeos_rootfs)); |
entries[2].starting_lba = 234; |
entries[2].ending_lba = 333; |
Memcpy(&entries[3].type, &chromeos_kernel, sizeof(chromeos_kernel)); |
@@ -888,6 +899,223 @@ int CorruptCombinationTest() { |
return TEST_OK; |
} |
+/* Invalidate all kernel entries and expect GptNextKernelEntry() cannot find |
+ * any usable kernel entry. |
+ */ |
+int NoValidKernelEntryTest() { |
+ GptData *gpt; |
+ GptEntry *entries, *entries2; |
+ |
+ gpt = GetEmptyGptData(); |
+ entries = (GptEntry*)gpt->primary_entries; |
+ entries2 = (GptEntry*)gpt->secondary_entries; |
+ |
+ BuildTestGptData(gpt); |
+ entries[KERNEL_A].attributes |= CGPT_ATTRIBUTE_BAD_MASK; |
+ Memset(&entries[KERNEL_B].type, 0, sizeof(Guid)); |
+ RefreshCrc32(gpt); |
+ |
+ EXPECT(GPT_ERROR_NO_VALID_KERNEL == GptNextKernelEntry(gpt, NULL, NULL)); |
+ |
+ return TEST_OK; |
+} |
+ |
+/* This is the combination test. Both kernel A and B could be either inactive |
+ * or invalid. We expect GptNextKetnelEntry() returns good kernel or |
+ * GPT_ERROR_NO_VALID_KERNEL if no kernel is available. */ |
+enum FAILURE_MASK { |
+ MASK_INACTIVE = 1, |
+ MASK_BAD_ENTRY = 2, |
+ MASK_FAILURE_BOTH = 3, |
+}; |
+void BreakAnEntry(GptEntry *entry, enum FAILURE_MASK failure) { |
+ if (failure & MASK_INACTIVE) |
+ Memset(&entry->type, 0, sizeof(Guid)); |
+ if (failure & MASK_BAD_ENTRY) |
+ entry->attributes |= CGPT_ATTRIBUTE_BAD_MASK; |
+} |
+ |
+int CombinationalNextKernelEntryTest() { |
+ GptData *gpt; |
+ enum { |
+ MASK_KERNEL_A = 1, |
+ MASK_KERNEL_B = 2, |
+ MASK_KERNEL_BOTH = 3, |
+ } kernel; |
+ enum FAILURE_MASK failure; |
+ uint64_t start_sector, size; |
+ int retval; |
+ |
+ for (kernel = MASK_KERNEL_A; kernel <= MASK_KERNEL_BOTH; ++kernel) { |
+ for (failure = MASK_INACTIVE; failure < MASK_FAILURE_BOTH; ++failure) { |
+ gpt = GetEmptyGptData(); |
+ BuildTestGptData(gpt); |
+ |
+ if (kernel & MASK_KERNEL_A) |
+ BreakAnEntry(GetEntry(gpt, PRIMARY, KERNEL_A), failure); |
+ if (kernel & MASK_KERNEL_B) |
+ BreakAnEntry(GetEntry(gpt, PRIMARY, KERNEL_B), failure); |
+ |
+ retval = GptNextKernelEntry(gpt, &start_sector, &size); |
+ |
+ if (kernel == MASK_KERNEL_A) { |
+ EXPECT(retval == GPT_SUCCESS); |
+ EXPECT(start_sector == 334); |
+ } else if (kernel == MASK_KERNEL_B) { |
+ EXPECT(retval == GPT_SUCCESS); |
+ EXPECT(start_sector == 34); |
+ } else { /* MASK_KERNEL_BOTH */ |
+ EXPECT(retval == GPT_ERROR_NO_VALID_KERNEL); |
+ } |
+ } |
+ } |
+ return TEST_OK; |
+} |
+ |
+/* Increase tries value from zero, expect it won't explode/overflow after |
+ * CGPT_ATTRIBUTE_TRIES_MASK. |
+ */ |
+/* Tries would not count up after CGPT_ATTRIBUTE_MAX_TRIES. */ |
+#define EXPECTED_TRIES(tries) \ |
+ ((tries >= CGPT_ATTRIBUTE_MAX_TRIES) ? CGPT_ATTRIBUTE_MAX_TRIES \ |
+ : tries) |
+int IncreaseTriesTest() { |
+ GptData *gpt; |
+ int kernel_index[] = { |
+ KERNEL_B, |
+ KERNEL_A, |
+ }; |
+ int i, tries, j; |
+ |
+ gpt = GetEmptyGptData(); |
+ for (i = 0; i < ARRAY_SIZE(kernel_index); ++i) { |
+ GptEntry *entries[2] = { |
+ (GptEntry*)gpt->primary_entries, |
+ (GptEntry*)gpt->secondary_entries, |
+ }; |
+ int current; |
+ |
+ BuildTestGptData(gpt); |
+ current = gpt->current_kernel = kernel_index[i]; |
+ |
+ for (tries = 0; tries < 2 * CGPT_ATTRIBUTE_MAX_TRIES; ++tries) { |
+ for (j = 0; j < ARRAY_SIZE(entries); ++j) { |
+ EXPECT(EXPECTED_TRIES(tries) == |
+ ((entries[j][current].attributes & CGPT_ATTRIBUTE_TRIES_MASK) >> |
+ CGPT_ATTRIBUTE_TRIES_OFFSET)); |
+ } |
+ |
+ EXPECT(GPT_SUCCESS == GptUpdateKernelEntry(gpt, GPT_UPDATE_ENTRY_TRY)); |
+ /* The expected tries value will be checked in next iteration. */ |
+ |
+ if (tries < CGPT_ATTRIBUTE_MAX_TRIES) |
+ EXPECT((GPT_MODIFIED_HEADER1 | GPT_MODIFIED_ENTRIES1 | |
+ GPT_MODIFIED_HEADER2 | GPT_MODIFIED_ENTRIES2) == gpt->modified); |
+ gpt->modified = 0; /* reset before next test */ |
+ EXPECT(0 == |
+ Memcmp(entries[PRIMARY], entries[SECONDARY], TOTAL_ENTRIES_SIZE)); |
+ } |
+ } |
+ return TEST_OK; |
+} |
+ |
+/* Mark a kernel as bad. Expect: |
+ * 1. the both bad bits of kernel A in primary and secondary entries are set. |
+ * 2. headers and entries are marked as modified. |
+ * 3. primary and secondary entries are identical. |
+ */ |
+int MarkBadKernelEntryTest() { |
+ GptData *gpt; |
+ GptEntry *entries, *entries2; |
+ |
+ gpt = GetEmptyGptData(); |
+ entries = (GptEntry*)gpt->primary_entries; |
+ entries2 = (GptEntry*)gpt->secondary_entries; |
+ |
+ BuildTestGptData(gpt); |
+ gpt->current_kernel = KERNEL_A; |
+ EXPECT(GPT_SUCCESS == GptUpdateKernelEntry(gpt, GPT_UPDATE_ENTRY_BAD)); |
+ EXPECT((GPT_MODIFIED_HEADER1 | GPT_MODIFIED_ENTRIES1 | |
+ GPT_MODIFIED_HEADER2 | GPT_MODIFIED_ENTRIES2) == gpt->modified); |
+ EXPECT(entries[KERNEL_A].attributes & CGPT_ATTRIBUTE_BAD_MASK); |
+ EXPECT(entries2[KERNEL_A].attributes & CGPT_ATTRIBUTE_BAD_MASK); |
+ EXPECT(0 == Memcmp(entries, entries2, TOTAL_ENTRIES_SIZE)); |
+ |
+ return TEST_OK; |
+} |
+ |
+/* Given an invalid kernel type, and expect GptUpdateKernelEntry() returns |
+ * GPT_ERROR_INVALID_UPDATE_TYPE. */ |
+int UpdateInvalidKernelTypeTest() { |
+ GptData *gpt; |
+ |
+ gpt = GetEmptyGptData(); |
+ BuildTestGptData(gpt); |
+ gpt->current_kernel = 0; /* anything, but not CGPT_KERNEL_ENTRY_NOT_FOUND */ |
+ EXPECT(GPT_ERROR_INVALID_UPDATE_TYPE == |
+ GptUpdateKernelEntry(gpt, 99)); /* any invalid update_type value */ |
+ |
+ return TEST_OK; |
+} |
+ |
+/* A normal boot case: |
+ * GptInit() |
+ * GptNextKernelEntry() |
+ * GptUpdateKernelEntry() |
+ */ |
+int NormalBootCase() { |
+ GptData *gpt; |
+ GptEntry *entries; |
+ uint64_t start_sector, size; |
+ |
+ gpt = GetEmptyGptData(); |
+ entries = (GptEntry*)gpt->primary_entries; |
+ BuildTestGptData(gpt); |
+ |
+ EXPECT(GPT_SUCCESS == GptInit(gpt)); |
+ EXPECT(GPT_SUCCESS == GptNextKernelEntry(gpt, &start_sector, &size)); |
+ EXPECT(start_sector == 34); /* Kernel A, see top of this file. */ |
+ EXPECT(size == 100); |
+ |
+ EXPECT(GPT_SUCCESS == GptUpdateKernelEntry(gpt, GPT_UPDATE_ENTRY_TRY)); |
+ EXPECT(((entries[KERNEL_A].attributes & CGPT_ATTRIBUTE_TRIES_MASK) >> |
+ CGPT_ATTRIBUTE_TRIES_OFFSET) == 1); |
+ |
+ return TEST_OK; |
+} |
+ |
+/* Higher priority kernel should boot first. |
+ * KERNEL_A is low priority |
+ * KERNEL_B is high priority. |
+ * We expect KERNEL_B is selected in first run, and then KERNEL_A. |
+ * We also expect the GptNextKernelEntry() wraps back to KERNEL_B if it's called |
+ * after twice. |
+ */ |
+int HigherPriorityTest() { |
+ GptData *gpt; |
+ GptEntry *entries; |
+ |
+ gpt = GetEmptyGptData(); |
+ entries = (GptEntry*)gpt->primary_entries; |
+ BuildTestGptData(gpt); |
+ |
+ SetPriority(gpt, PRIMARY, KERNEL_A, 0); |
+ SetPriority(gpt, PRIMARY, KERNEL_B, 1); |
+ RefreshCrc32(gpt); |
+ |
+ EXPECT(GPT_SUCCESS == GptInit(gpt)); |
+ EXPECT(GPT_SUCCESS == GptNextKernelEntry(gpt, NULL, NULL)); |
+ EXPECT(KERNEL_B == gpt->current_kernel); |
+ |
+ EXPECT(GPT_SUCCESS == GptNextKernelEntry(gpt, NULL, NULL)); |
+ EXPECT(KERNEL_A == gpt->current_kernel); |
+ |
+ EXPECT(GPT_SUCCESS == GptNextKernelEntry(gpt, NULL, NULL)); |
+ EXPECT(KERNEL_B == gpt->current_kernel); |
+ |
+ return TEST_OK; |
+} |
+ |
int main(int argc, char *argv[]) { |
int i; |
int error_count = 0; |
@@ -916,6 +1144,13 @@ int main(int argc, char *argv[]) { |
{ TEST_CASE(CorruptCombinationTest), }, |
{ TEST_CASE(TestQuickSortFixed), }, |
{ TEST_CASE(TestQuickSortRandom), }, |
+ { TEST_CASE(NoValidKernelEntryTest), }, |
+ { TEST_CASE(CombinationalNextKernelEntryTest), }, |
+ { TEST_CASE(IncreaseTriesTest), }, |
+ { TEST_CASE(MarkBadKernelEntryTest), }, |
+ { TEST_CASE(UpdateInvalidKernelTypeTest), }, |
+ { TEST_CASE(NormalBootCase), }, |
+ { TEST_CASE(HigherPriorityTest), }, |
}; |
for (i = 0; i < sizeof(test_cases)/sizeof(test_cases[0]); ++i) { |