Index: src/platform/vboot_reference/cgptlib/cgpt.c |
diff --git a/src/platform/vboot_reference/cgptlib/cgpt.c b/src/platform/vboot_reference/cgptlib/cgpt.c |
index a8d449d13d55b799b116a7f016c57bb1f4f21ff1..d509f00c409076237d28ef8fd0a2ebfc1924d6cf 100644 |
--- a/src/platform/vboot_reference/cgptlib/cgpt.c |
+++ b/src/platform/vboot_reference/cgptlib/cgpt.c |
@@ -465,6 +465,14 @@ void UpdateCrc(GptData *gpt) { |
primary_header = (GptHeader*)gpt->primary_header; |
secondary_header = (GptHeader*)gpt->secondary_header; |
+ if (gpt->modified & GPT_MODIFIED_ENTRIES1) { |
+ primary_header->entries_crc32 = |
+ Crc32(gpt->primary_entries, TOTAL_ENTRIES_SIZE); |
+ } |
+ if (gpt->modified & GPT_MODIFIED_ENTRIES2) { |
+ secondary_header->entries_crc32 = |
+ Crc32(gpt->secondary_entries, TOTAL_ENTRIES_SIZE); |
+ } |
if (gpt->modified & GPT_MODIFIED_HEADER1) { |
primary_header->header_crc32 = 0; |
primary_header->header_crc32 = Crc32( |
@@ -475,14 +483,6 @@ void UpdateCrc(GptData *gpt) { |
secondary_header->header_crc32 = Crc32( |
(const uint8_t *)secondary_header, secondary_header->size); |
} |
- if (gpt->modified & GPT_MODIFIED_ENTRIES1) { |
- primary_header->entries_crc32 = |
- Crc32(gpt->primary_entries, TOTAL_ENTRIES_SIZE); |
- } |
- if (gpt->modified & GPT_MODIFIED_ENTRIES2) { |
- secondary_header->entries_crc32 = |
- Crc32(gpt->secondary_entries, TOTAL_ENTRIES_SIZE); |
- } |
} |
/* Does every sanity check, and returns if any header/entries needs to be |
@@ -528,25 +528,219 @@ int GptInit(GptData *gpt) { |
UpdateCrc(gpt); |
- /* FIXME: will remove the next line soon. */ |
- gpt->current_kernel = 1; |
+ gpt->current_kernel = CGPT_KERNEL_ENTRY_NOT_FOUND; |
+ |
return GPT_SUCCESS; |
} |
-/* stub code */ |
-static int start[] = { 34, 10034 }; |
+/* Helper function to get a pointer to the partition entry. |
+ * 'secondary' is either PRIMARY or SECONDARY. |
+ * 'entry_index' is the partition index: [0, number_of_entries). |
+ */ |
+GptEntry *GetEntry(GptData *gpt, int secondary, int entry_index) { |
+ GptHeader *header; |
+ uint8_t *entries; |
+ |
+ if (secondary == PRIMARY) { |
+ header = (GptHeader*)gpt->primary_header; |
+ entries = gpt->primary_entries; |
+ } else { |
+ header = (GptHeader*)gpt->secondary_header; |
+ entries = gpt->secondary_entries; |
+ } |
+ |
+ return (GptEntry*)(&entries[header->size_of_entry * entry_index]); |
+} |
+ |
+/* The following functions are helpers to access attributes bit more easily. |
+ * 'secondary' is either PRIMARY or SECONDARY. |
+ * 'entry_index' is the partition index: [0, number_of_entries). |
+ * |
+ * Get*() return the exact value (shifted and masked). |
+ */ |
+void SetPriority(GptData *gpt, int secondary, int entry_index, int priority) { |
+ GptEntry *entry; |
+ entry = GetEntry(gpt, secondary, entry_index); |
+ |
+ assert(priority >= 0 && priority <= CGPT_ATTRIBUTE_MAX_PRIORITY); |
+ entry->attributes &= ~CGPT_ATTRIBUTE_PRIORITY_MASK; |
+ entry->attributes |= (uint64_t)priority << CGPT_ATTRIBUTE_PRIORITY_OFFSET; |
+} |
+ |
+int GetPriority(GptData *gpt, int secondary, int entry_index) { |
+ GptEntry *entry; |
+ entry = GetEntry(gpt, secondary, entry_index); |
+ return (entry->attributes & CGPT_ATTRIBUTE_PRIORITY_MASK) >> |
+ CGPT_ATTRIBUTE_PRIORITY_OFFSET; |
+} |
+ |
+void SetBad(GptData *gpt, int secondary, int entry_index, int bad) { |
+ GptEntry *entry; |
+ entry = GetEntry(gpt, secondary, entry_index); |
+ |
+ assert(bad >= 0 && bad <= CGPT_ATTRIBUTE_MAX_BAD); |
+ entry->attributes &= ~CGPT_ATTRIBUTE_BAD_MASK; |
+ entry->attributes |= (uint64_t)bad << CGPT_ATTRIBUTE_BAD_OFFSET; |
+} |
+ |
+int GetBad(GptData *gpt, int secondary, int entry_index) { |
+ GptEntry *entry; |
+ entry = GetEntry(gpt, secondary, entry_index); |
+ return (entry->attributes & CGPT_ATTRIBUTE_BAD_MASK) >> |
+ CGPT_ATTRIBUTE_BAD_OFFSET; |
+} |
+ |
+void SetTries(GptData *gpt, int secondary, int entry_index, int tries) { |
+ GptEntry *entry; |
+ entry = GetEntry(gpt, secondary, entry_index); |
+ assert(tries >= 0 && tries <= CGPT_ATTRIBUTE_MAX_TRIES); |
+ entry->attributes &= ~CGPT_ATTRIBUTE_TRIES_MASK; |
+ entry->attributes |= (uint64_t)tries << CGPT_ATTRIBUTE_TRIES_OFFSET; |
+} |
+ |
+int GetTries(GptData *gpt, int secondary, int entry_index) { |
+ GptEntry *entry; |
+ entry = GetEntry(gpt, secondary, entry_index); |
+ return (entry->attributes & CGPT_ATTRIBUTE_TRIES_MASK) >> |
+ CGPT_ATTRIBUTE_TRIES_OFFSET; |
+} |
+ |
+void SetSuccess(GptData *gpt, int secondary, int entry_index, int success) { |
+ GptEntry *entry; |
+ entry = GetEntry(gpt, secondary, entry_index); |
+ |
+ assert(success >= 0 && success <= CGPT_ATTRIBUTE_MAX_SUCCESS); |
+ entry->attributes &= ~CGPT_ATTRIBUTE_SUCCESS_MASK; |
+ entry->attributes |= (uint64_t)success << CGPT_ATTRIBUTE_SUCCESS_OFFSET; |
+} |
+ |
+int GetSuccess(GptData *gpt, int secondary, int entry_index) { |
+ GptEntry *entry; |
+ entry = GetEntry(gpt, secondary, entry_index); |
+ return (entry->attributes & CGPT_ATTRIBUTE_SUCCESS_MASK) >> |
+ CGPT_ATTRIBUTE_SUCCESS_OFFSET; |
+} |
+ |
+/* Compare two priority values. Actually it is a circular priority, which is: |
+ * 3 > 2 > 1 > 0, but 0 > 3. (-1 means very low, and anyone is higher than -1) |
+ * |
+ * Return 1 if 'a' has higher priority than 'b'. |
+ */ |
+int IsHigherPriority(int a, int b) { |
+ if ((a == 0) && (b == CGPT_ATTRIBUTE_MAX_PRIORITY)) |
+ return 1; |
+ else if ((a == CGPT_ATTRIBUTE_MAX_PRIORITY) && (b == 0)) |
+ return 0; |
+ else |
+ return (a > b) ? 1 : 0; |
+} |
+ |
+/* This function walks through the whole partition table (see note below), |
+ * and pick up the active and valid (not marked as bad) kernel entry with |
+ * *highest* priority (except gpt->current_kernel itself). |
+ * |
+ * Returns start_sector and its size if a candidate kernel is found. |
+ * |
+ * Note: in the first walk (gpt->current_kernel==CGPT_KERNEL_ENTRY_NOT_FOUND), |
+ * the scan range is whole table. But in later scans, we only scan |
+ * (header->number_of_entries - 1) entries because we are looking for |
+ * next kernel with lower priority (consider the case that highest |
+ * priority kernel is still active and valid). |
+ */ |
int GptNextKernelEntry(GptData *gpt, uint64_t *start_sector, uint64_t *size) { |
- /* FIXME: the following code is not really code, just returns anything */ |
- gpt->current_kernel ^= 1; |
- if (start_sector) *start_sector = start[gpt->current_kernel]; |
- if (size) *size = 10000; |
+ GptHeader *header; |
+ GptEntry *entry; |
+ int scan, current_priority; |
+ int begin, end; /* [begin, end], which end is included. */ |
+ Guid chromeos_kernel = GPT_ENT_TYPE_CHROMEOS_KERNEL; |
+ |
+ header = (GptHeader*)gpt->primary_header; |
+ current_priority = -1; /* pretty low priority */ |
+ if (gpt->current_kernel == CGPT_KERNEL_ENTRY_NOT_FOUND) { |
+ begin = 0; |
+ end = header->number_of_entries - 1; |
+ } else { |
+ begin = (gpt->current_kernel + 1) % header->number_of_entries; |
+ end = (gpt->current_kernel - 1 + header->number_of_entries) % |
+ header->number_of_entries; |
+ } |
+ |
+ scan = begin; |
+ do { |
+ entry = GetEntry(gpt, PRIMARY, scan); |
+ if (!Memcmp(&entry->type, &chromeos_kernel, sizeof(Guid)) && |
+ !GetBad(gpt, PRIMARY, scan) && |
+ ((gpt->current_kernel == CGPT_KERNEL_ENTRY_NOT_FOUND) || |
+ (IsHigherPriority(GetPriority(gpt, PRIMARY, scan), |
+ current_priority)))) { |
+ gpt->current_kernel = scan; |
+ current_priority = GetPriority(gpt, PRIMARY, gpt->current_kernel); |
+ } |
+ |
+ if (scan == end) break; |
+ scan = (scan + 1) % header->number_of_entries; |
+ } while (1); |
+ |
+ if (gpt->current_kernel == CGPT_KERNEL_ENTRY_NOT_FOUND) |
+ return GPT_ERROR_NO_VALID_KERNEL; |
+ |
+ entry = GetEntry(gpt, PRIMARY, gpt->current_kernel); |
+ assert(entry->starting_lba <= entry->ending_lba); |
+ |
+ if (start_sector) *start_sector = entry->starting_lba; |
+ if (size) *size = entry->ending_lba - entry->starting_lba + 1; |
+ |
return GPT_SUCCESS; |
} |
+/* Given a update_type, this function updates the corresponding bits in GptData. |
+ * |
+ * Returns GPT_SUCCESS if no error. gpt->modified is set if any header and |
+ * entries needs to be updated to hard drive. |
+ * GPT_ERROR_INVALID_UPDATE_TYPE if given an invalid update_type. |
+ */ |
int GptUpdateKernelEntry(GptData *gpt, uint32_t update_type) { |
- /* FIXME: the following code is not really code, just return anything */ |
- gpt->modified |= (GPT_MODIFIED_HEADER1 | GPT_MODIFIED_ENTRIES1) << |
- gpt->current_kernel; |
+ Guid chromeos_type = GPT_ENT_TYPE_CHROMEOS_KERNEL; |
+ int primary_is_modified = 0; |
+ |
+ assert(gpt->current_kernel != CGPT_KERNEL_ENTRY_NOT_FOUND); |
+ assert(!Memcmp(&(GetEntry(gpt, PRIMARY, gpt->current_kernel)->type), |
+ &chromeos_type, sizeof(Guid))); |
+ |
+ /* Modify primary entries first, then copy to secondary later. */ |
+ switch (update_type) { |
+ case GPT_UPDATE_ENTRY_TRY: { |
+ /* Increase tries value until CGPT_ATTRIBUTE_MAX_TRIES. */ |
+ int tries; |
+ tries = GetTries(gpt, PRIMARY, gpt->current_kernel); |
+ if (tries < CGPT_ATTRIBUTE_MAX_TRIES) { |
+ ++tries; |
+ SetTries(gpt, PRIMARY, gpt->current_kernel, tries); |
+ primary_is_modified = 1; |
+ } |
+ break; |
+ } |
+ case GPT_UPDATE_ENTRY_BAD: { |
+ GetEntry(gpt, PRIMARY, gpt->current_kernel)->attributes |= |
+ CGPT_ATTRIBUTE_BAD_MASK; |
+ primary_is_modified = 1; |
+ break; |
+ } |
+ default: { |
+ return GPT_ERROR_INVALID_UPDATE_TYPE; |
+ } |
+ } |
+ |
+ if (primary_is_modified) { |
+ /* Claim only primary is valid so that secondary is overwritten. */ |
+ RepairEntries(gpt, MASK_PRIMARY); |
+ /* Actually two entries are dirty now. |
+ * Also two headers are dirty because entries_crc32 has been updated. */ |
+ gpt->modified |= (GPT_MODIFIED_HEADER1 | GPT_MODIFIED_ENTRIES1 | |
+ GPT_MODIFIED_HEADER2 | GPT_MODIFIED_ENTRIES2); |
+ UpdateCrc(gpt); |
+ } |
+ |
return GPT_SUCCESS; |
} |