Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(644)

Side by Side Diff: src/platform/vboot_reference/vboot_firmware/lib/cgptlib/cgptlib.c

Issue 2438005: Much rearranging of cgptlib. Passes all its (new) unit tests. (Closed) Base URL: ssh://gitrw.chromium.org/chromiumos
Patch Set: Pre commit Created 10 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 /* Copyright (c) 2010 The Chromium OS Authors. All rights reserved. 1 /* Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
2 * Use of this source code is governed by a BSD-style license that can be 2 * Use of this source code is governed by a BSD-style license that can be
3 * found in the LICENSE file. 3 * found in the LICENSE file.
4 */ 4 */
5 5
6 #include "cgptlib.h" 6 #include "cgptlib.h"
7 #include <string.h>
8 #include "cgptlib_internal.h" 7 #include "cgptlib_internal.h"
9 #include "crc32.h" 8 #include "crc32.h"
10 #include "gpt.h" 9 #include "gpt.h"
11 #include "quick_sort.h"
12 #include "utility.h" 10 #include "utility.h"
13 11
14 /* Macro to invalidate a GPT header/entries */
15 #define INVALIDATE_HEADER(valid_headers, index) \
16 do { \
17 debug("- INVALIDATE_HEADER() at %s():%d\n", __FUNCTION__, __LINE__); \
18 valid_headers &= ~(1<<index); \
19 } while (0)
20 #define INVALIDATE_ENTRIES(valid_entries, index) \
21 do { \
22 debug("- INVALIDATE_ENTRIES() at %s():%d\n", __FUNCTION__, __LINE__); \
23 valid_entries &= ~(1<<index); \
24 } while (0)
25 12
26 const char *GptError(int errno) { 13 int GptInit(GptData *gpt) {
27 const char *error_string[] = { 14 int retval;
28 /* GPT_SUCCESS */ "Success", 15
29 /* GPT_ERROR_NO_VALID_KERNEL */ "No valid kernel entry", 16 gpt->modified = 0;
30 /* GPT_ERROR_INVALID_HEADERS */ "Both primary and secondary headers are " 17 gpt->current_kernel = CGPT_KERNEL_ENTRY_NOT_FOUND;
31 "invalid.", 18 gpt->current_priority = 999;
32 /* GPT_ERROR_INVALID_ENTRIES */ "Both primary and secondary entries are " 19
33 "invalid.", 20 retval = GptSanityCheck(gpt);
34 /* GPT_ERROR_INVALID_SECTOR_SIZE */ "Invalid sector size", 21 if (GPT_SUCCESS != retval)
35 /* GPT_ERROR_INVALID_SECTOR_NUMBER */ "Invalid sector number", 22 return retval;
36 /* GPT_ERROR_INVALID_UPDATE_TYPE */ "Invalid update type", 23
37 }; 24 GptRepair(gpt);
38 return error_string[errno]; 25 return GPT_SUCCESS;
39 } 26 }
40 27
41 /* Checks if sector_bytes and drive_sectors are valid values. */
42 int CheckParameters(GptData *gpt) {
43 /* Currently, we only support 512-byte sector. In the future, we may support
44 * larger sector. */
45 if (gpt->sector_bytes != 512)
46 return GPT_ERROR_INVALID_SECTOR_SIZE;
47 28
48 /* The sector number of a drive should be reasonable. If the given value is 29 int GptNextKernelEntry(GptData* gpt, uint64_t* start_sector, uint64_t* size) {
49 * too small to contain basic GPT structure (PMBR + Headers + Entries), 30 GptHeader* header = (GptHeader*)gpt->primary_header;
50 * the value is wrong. */ 31 GptEntry* entries = (GptEntry*)gpt->primary_entries;
51 if (gpt->drive_sectors < (GPT_PMBR_SECTOR + 32 GptEntry* e;
52 GPT_HEADER_SECTOR * 2 + 33 int new_kernel = CGPT_KERNEL_ENTRY_NOT_FOUND;
53 GPT_ENTRIES_SECTORS * 2)) 34 int new_prio = 0;
54 return GPT_ERROR_INVALID_SECTOR_NUMBER; 35 int i;
36
37 /* If we already found a kernel, continue the scan at the current
38 * kernel's prioity, in case there is another kernel with the same
39 * priority. */
40 if (gpt->current_kernel != CGPT_KERNEL_ENTRY_NOT_FOUND) {
41 for (i = gpt->current_kernel + 1; i < header->number_of_entries; i++) {
42 e = entries + i;
43 if (!IsKernelEntry(e))
44 continue;
45 if (!(GetEntrySuccessful(e) || GetEntryTries(e)))
46 continue;
47 if (GetEntryPriority(e) == gpt->current_priority) {
48 gpt->current_kernel = i;
49 *start_sector = e->starting_lba;
50 *size = e->ending_lba - e->starting_lba + 1;
51 return GPT_SUCCESS;
52 }
53 }
54 }
55
56 /* We're still here, so scan for the remaining kernel with the
57 * highest priority less than the previous attempt. */
58 for (i = 0, e = entries; i < header->number_of_entries; i++, e++) {
59 int current_prio = GetEntryPriority(e);
60 if (!IsKernelEntry(e))
61 continue;
62 if (!(GetEntrySuccessful(e) || GetEntryTries(e)))
63 continue;
64 if (current_prio >= gpt->current_priority)
65 continue; /* Already returned this kernel in a previous call */
66 if (current_prio > new_prio) {
67 new_kernel = i;
68 new_prio = current_prio;
69 }
70 }
71
72 /* Save what we found. Note that if we didn't find a new kernel,
73 * new_prio will still be -1, so future calls to this function will
74 * also fail. */
75 gpt->current_kernel = new_kernel;
76 gpt->current_priority = new_prio;
77
78 if (CGPT_KERNEL_ENTRY_NOT_FOUND == new_kernel)
79 return GPT_ERROR_NO_VALID_KERNEL;
80
81 e = entries + new_kernel;
82 *start_sector = e->starting_lba;
83 *size = e->ending_lba - e->starting_lba + 1;
84 return GPT_SUCCESS;
85 }
86
87
88 int GptUpdateKernelEntry(GptData* gpt, uint32_t update_type) {
89 GptHeader* header = (GptHeader*)gpt->primary_header;
90 GptEntry* entries = (GptEntry*)gpt->primary_entries;
91 GptEntry* e = entries + gpt->current_kernel;
92 uint64_t previous_attr = e->attributes;
93
94 /* TODO: need a better return code for these errors? */
95 if (gpt->current_kernel == CGPT_KERNEL_ENTRY_NOT_FOUND)
96 return GPT_ERROR_INVALID_UPDATE_TYPE;
97 if (!IsKernelEntry(e))
98 return GPT_ERROR_INVALID_UPDATE_TYPE;
99
100 switch (update_type) {
101 case GPT_UPDATE_ENTRY_TRY: {
102 /* Used up a try */
103 int tries;
104 if (GetEntrySuccessful(e))
105 return GPT_SUCCESS; /* Successfully booted this partition, so
106 * tries field is ignored. */
107 tries = GetEntryTries(e);
108 if (tries > 1) {
109 /* Still have tries left */
110 SetEntryTries(e, tries - 1);
111 break;
112 }
113 /* Out of tries, so drop through and mark partition bad. */
114 }
115 case GPT_UPDATE_ENTRY_BAD: {
116 /* Giving up on this partition entirely. */
117 e->attributes &= ~(CGPT_ATTRIBUTE_SUCCESSFUL_MASK |
118 CGPT_ATTRIBUTE_TRIES_MASK |
119 CGPT_ATTRIBUTE_PRIORITY_MASK);
120 break;
121 }
122 default:
123 return GPT_ERROR_INVALID_UPDATE_TYPE;
124 }
125
126 /* If no change to attributes, we're done */
127 if (e->attributes == previous_attr)
128 return GPT_SUCCESS;
129
130 /* Update the CRCs */
131 header->entries_crc32 = Crc32((const uint8_t *)entries,
132 header->size_of_entry *
133 header->number_of_entries);
134 header->header_crc32 = HeaderCrc(header);
135 gpt->modified |= GPT_MODIFIED_HEADER1 | GPT_MODIFIED_ENTRIES1;
136
137 /* Use the repair function to update the other copy of the GPT.
138 * This is a tad inefficient, but is much faster than the disk I/O
139 * to update the GPT on disk so it doesn't matter. */
140 gpt->valid_headers = MASK_PRIMARY;
141 gpt->valid_entries = MASK_PRIMARY;
142 GptRepair(gpt);
55 143
56 return GPT_SUCCESS; 144 return GPT_SUCCESS;
57 } 145 }
58
59 /* Expects header signature should be GPT_HEADER_SIGNATURE. */
60 uint32_t CheckHeaderSignature(GptData *gpt) {
61 GptHeader *headers[] = {
62 (GptHeader*)gpt->primary_header,
63 (GptHeader*)gpt->secondary_header,
64 };
65 int i;
66
67 for (i = PRIMARY; i <= SECONDARY; ++i) {
68 if (Memcmp(headers[i]->signature,
69 GPT_HEADER_SIGNATURE,
70 GPT_HEADER_SIGNATURE_SIZE)) {
71 INVALIDATE_HEADER(gpt->valid_headers, i);
72 }
73 }
74 return gpt->valid_headers;
75 }
76
77 /* The header revision should be GPT_HEADER_REVISION. */
78 uint32_t CheckRevision(GptData *gpt) {
79 GptHeader *headers[] = {
80 (GptHeader*)gpt->primary_header,
81 (GptHeader*)gpt->secondary_header,
82 };
83 int i;
84
85 for (i = PRIMARY; i <= SECONDARY; ++i) {
86 if (headers[i]->revision != GPT_HEADER_REVISION)
87 INVALIDATE_HEADER(gpt->valid_headers, i);
88 }
89 return gpt->valid_headers;
90 }
91
92 /* A valid header size should be between MIN_SIZE_OF_HEADER and
93 * MAX_SIZE_OF_HEADER. */
94 uint32_t CheckSize(GptData *gpt) {
95 GptHeader *headers[] = {
96 (GptHeader*)gpt->primary_header,
97 (GptHeader*)gpt->secondary_header,
98 };
99 int i;
100
101 for (i = PRIMARY; i <= SECONDARY; ++i) {
102 if ((headers[i]->size < MIN_SIZE_OF_HEADER) ||
103 (headers[i]->size > MAX_SIZE_OF_HEADER))
104 INVALIDATE_HEADER(gpt->valid_headers, i);
105 }
106 return gpt->valid_headers;
107 }
108
109 /* Reserved and padding fields should be zero. */
110 uint32_t CheckReservedFields(GptData *gpt) {
111 GptHeader *headers[] = {
112 (GptHeader*)gpt->primary_header,
113 (GptHeader*)gpt->secondary_header,
114 };
115 int i;
116
117 for (i = PRIMARY; i <= SECONDARY; ++i) {
118 if (headers[i]->reserved || headers[i]->padding)
119 INVALIDATE_HEADER(gpt->valid_headers, i);
120 }
121 return gpt->valid_headers;
122 }
123
124 /* my_lba field points to the header itself.
125 * So that the my_lba of primary header should be 1 (right after PMBR).
126 * The my_lba of secondary header should be the last secotr on drive. */
127 uint32_t CheckMyLba(GptData *gpt) {
128 GptHeader *primary_header, *secondary_header;
129
130 primary_header = (GptHeader*)gpt->primary_header;
131 secondary_header = (GptHeader*)gpt->secondary_header;
132
133 if (primary_header->my_lba != GPT_PMBR_SECTOR) /* 2nd sector on drive */
134 INVALIDATE_HEADER(gpt->valid_headers, PRIMARY);
135 if (secondary_header->my_lba != (gpt->drive_sectors - 1)) /* last sector */
136 INVALIDATE_HEADER(gpt->valid_headers, SECONDARY);
137 return gpt->valid_headers;
138 }
139
140 /* SizeOfPartitionEntry must be between MIN_SIZE_OF_ENTRY and
141 * MAX_SIZE_OF_ENTRY, and a multiple of SIZE_OF_ENTRY_MULTIPLE. */
142 uint32_t CheckSizeOfPartitionEntry(GptData *gpt) {
143 GptHeader *headers[] = {
144 (GptHeader*)gpt->primary_header,
145 (GptHeader*)gpt->secondary_header,
146 };
147 int i;
148
149 for (i = PRIMARY; i <= SECONDARY; ++i) {
150 uint32_t size_of_entry = headers[i]->size_of_entry;
151 if ((size_of_entry < MIN_SIZE_OF_ENTRY) ||
152 (size_of_entry > MAX_SIZE_OF_ENTRY) ||
153 (size_of_entry & (SIZE_OF_ENTRY_MULTIPLE - 1)))
154 INVALIDATE_HEADER(gpt->valid_headers, i);
155 }
156 return gpt->valid_headers;
157 }
158
159 /* number_of_entries must be between MIN_NUMBER_OF_ENTRIES and
160 * MAX_NUMBER_OF_ENTRIES, and size_of_entry * number_of_entries must be
161 * equal to TOTAL_ENTRIES_SIZE. */
162 uint32_t CheckNumberOfEntries(GptData *gpt) {
163 GptHeader *headers[] = {
164 (GptHeader*)gpt->primary_header,
165 (GptHeader*)gpt->secondary_header,
166 };
167 int i;
168
169 for (i = PRIMARY; i <= SECONDARY; ++i) {
170 uint32_t number_of_entries = headers[i]->number_of_entries;
171 if ((number_of_entries < MIN_NUMBER_OF_ENTRIES) ||
172 (number_of_entries > MAX_NUMBER_OF_ENTRIES) ||
173 (number_of_entries * headers[i]->size_of_entry != TOTAL_ENTRIES_SIZE))
174 INVALIDATE_HEADER(gpt->valid_headers, i);
175 }
176 return gpt->valid_headers;
177 }
178
179 /* Make sure entries_lba is correct.
180 * 2 for primary entries
181 * drive_sectors-1-GPT_ENTRIES_SECTORS for secondary entries. */
182 uint32_t CheckEntriesLba(GptData *gpt) {
183 GptHeader *primary_header, *secondary_header;
184
185 primary_header = (GptHeader*)gpt->primary_header;
186 secondary_header = (GptHeader*)gpt->secondary_header;
187
188 /* We assume the primary partition entry table is located at the sector
189 * right after primary partition header. */
190 if (primary_header->entries_lba != (GPT_PMBR_SECTOR + GPT_HEADER_SECTOR))
191 INVALIDATE_HEADER(gpt->valid_headers, PRIMARY);
192 /* We assume the secondary partition entry table is the 32 sectors
193 * right before the secondary partition header. */
194 if (secondary_header->entries_lba !=
195 (gpt->drive_sectors - 1 - GPT_ENTRIES_SECTORS))
196 INVALIDATE_HEADER(gpt->valid_headers, SECONDARY);
197 return gpt->valid_headers;
198 }
199
200 /* FirstUsableLBA must be after the end of the primary GPT table array.
201 * LastUsableLBA must be before the start of the secondary GPT table array.
202 * FirstUsableLBA <= LastUsableLBA. */
203 uint32_t CheckValidUsableLbas(GptData *gpt) {
204 uint64_t end_of_primary_entries;
205 uint64_t start_of_secondary_entries;
206 GptHeader *headers[] = {
207 (GptHeader*)gpt->primary_header,
208 (GptHeader*)gpt->secondary_header,
209 };
210 int i;
211
212 end_of_primary_entries = GPT_PMBR_SECTOR + GPT_HEADER_SECTOR +
213 GPT_ENTRIES_SECTORS;
214 start_of_secondary_entries = (gpt->drive_sectors - 1 - GPT_ENTRIES_SECTORS);
215
216 for (i = PRIMARY; i <= SECONDARY; ++i) {
217 if (headers[i]->first_usable_lba < end_of_primary_entries)
218 INVALIDATE_HEADER(gpt->valid_headers, i);
219 if (headers[i]->last_usable_lba >= start_of_secondary_entries)
220 INVALIDATE_HEADER(gpt->valid_headers, i);
221 if (headers[i]->first_usable_lba > headers[i]->last_usable_lba)
222 INVALIDATE_HEADER(gpt->valid_headers, i);
223 }
224
225 if (headers[PRIMARY]->first_usable_lba - headers[PRIMARY]->entries_lba <
226 GPT_ENTRIES_SECTORS)
227 INVALIDATE_HEADER(gpt->valid_headers, PRIMARY);
228 if (headers[SECONDARY]->last_usable_lba >= headers[SECONDARY]->entries_lba)
229 INVALIDATE_HEADER(gpt->valid_headers, SECONDARY);
230
231 return gpt->valid_headers;
232 }
233
234 /* Checks header CRC */
235 uint32_t CheckHeaderCrc(GptData *gpt) {
236 uint32_t crc32, original_crc32;
237 GptHeader *headers[] = {
238 (GptHeader*)gpt->primary_header,
239 (GptHeader*)gpt->secondary_header,
240 };
241 int i;
242
243 for (i = PRIMARY; i <= SECONDARY; ++i) {
244 if (!(gpt->valid_headers & (1 << i))) continue;
245 original_crc32 = headers[i]->header_crc32;
246 headers[i]->header_crc32 = 0;
247 crc32 = Crc32((const uint8_t *)headers[i], headers[i]->size);
248 headers[i]->header_crc32 = original_crc32;
249 if (crc32 != original_crc32)
250 INVALIDATE_HEADER(gpt->valid_headers, i);
251 }
252 return gpt->valid_headers;
253 }
254
255 /* Checks entries CRC */
256 uint32_t CheckEntriesCrc(GptData *gpt) {
257 uint32_t crc32;
258 GptHeader *headers[] = {
259 (GptHeader*)gpt->primary_header,
260 (GptHeader*)gpt->secondary_header,
261 };
262 GptEntry *entries[] = {
263 (GptEntry*)gpt->primary_entries,
264 (GptEntry*)gpt->secondary_entries,
265 };
266 uint32_t entries_crc32;
267 int i;
268
269 if (gpt->valid_headers & MASK_PRIMARY)
270 entries_crc32 = headers[PRIMARY]->entries_crc32;
271 else
272 entries_crc32 = headers[SECONDARY]->entries_crc32;
273
274 for (i = PRIMARY; i <= SECONDARY; ++i) {
275 crc32 = Crc32((const uint8_t *)entries[i], TOTAL_ENTRIES_SIZE);
276 if (crc32 != entries_crc32)
277 INVALIDATE_ENTRIES(gpt->valid_entries, i);
278 }
279 return gpt->valid_entries;
280 }
281
282 /* Returns non-zero if the given GUID is non-zero. */
283 int NonZeroGuid(const Guid *guid) {
284 static Guid zero = {{{0, 0, 0, 0, 0, {0, 0, 0, 0, 0, 0}}}};
285 return Memcmp(&zero, guid, sizeof(zero));
286 }
287
288 /* Checks if entries geometry is valid.
289 * All active (non-zero PartitionTypeGUID) partition entries should have:
290 * entry.StartingLBA >= header.FirstUsableLBA
291 * entry.EndingLBA <= header.LastUsableLBA
292 * entry.StartingLBA <= entry.EndingLBA
293 */
294 uint32_t CheckValidEntries(GptData *gpt) {
295 uint32_t valid_entries = MASK_BOTH;
296 GptHeader *headers[] = {
297 (GptHeader*)gpt->primary_header,
298 (GptHeader*)gpt->secondary_header,
299 };
300 GptEntry *entries[] = {
301 (GptEntry*)gpt->primary_entries,
302 (GptEntry*)gpt->secondary_entries,
303 };
304 uint32_t number_of_entries, size_of_entry;
305 uint64_t first_usable_lba, last_usable_lba;
306 int copy, entry_index;
307 GptEntry *entry;
308
309 if (gpt->valid_headers & MASK_PRIMARY)
310 copy = PRIMARY;
311 else
312 copy = SECONDARY;
313 number_of_entries = headers[copy]->number_of_entries;
314 size_of_entry = headers[copy]->size_of_entry;
315 first_usable_lba = headers[copy]->first_usable_lba;
316 last_usable_lba = headers[copy]->last_usable_lba;
317
318 for (copy = PRIMARY; copy <= SECONDARY; ++copy) {
319 for (entry_index = 0;
320 entry_index < number_of_entries;
321 ++entry_index) {
322 entry = (GptEntry*)&(((uint8_t*)entries[copy])
323 [entry_index * size_of_entry]);
324 if (NonZeroGuid(&entry->type)) {
325 if ((entry->starting_lba < first_usable_lba) ||
326 (entry->ending_lba > last_usable_lba) ||
327 (entry->ending_lba < entry->starting_lba))
328 INVALIDATE_ENTRIES(valid_entries, copy);
329 }
330 }
331 }
332 return valid_entries;
333 }
334
335 static pair_t pairs[MAX_NUMBER_OF_ENTRIES];
336 /* Callback function for QuickSort(). Returns 1 if 'a_' should precede 'b_'. */
337 int compare_pair(const void *a_, const void *b_) {
338 const pair_t *a = a_;
339 const pair_t *b = b_;
340 if (a->starting <= b->starting) return 1;
341 return 0;
342 }
343
344 /* First sorts by starting_lba, and traverse everyone once if its starting_lba
345 * is between previous starting_lba and ending_lba. If yes, overlapped.
346 * Returns 1 if overlap is found. */
347 int OverlappedEntries(GptEntry *entries, uint32_t number_of_entries) {
348 int i, num_of_pair = 0;
349 for (i = 0; i < number_of_entries; ++i) {
350 if (NonZeroGuid(&entries[i].type)) {
351 pairs[num_of_pair].starting = entries[i].starting_lba;
352 pairs[num_of_pair].ending = entries[i].ending_lba;
353 ++num_of_pair;
354 }
355 }
356 QuickSort(&pairs, num_of_pair, sizeof(pair_t), compare_pair);
357
358 for (i = 1; i < num_of_pair; ++i) {
359 if ((pairs[i].starting >= pairs[i-1].starting) &&
360 (pairs[i].starting <= pairs[i-1].ending))
361 return 1;
362 }
363
364 return 0;
365 }
366
367 /* Checks if any two partitions are overlapped in primary and secondary entries.
368 */
369 uint32_t CheckOverlappedPartition(GptData *gpt) {
370 GptHeader *headers[] = {
371 (GptHeader*)gpt->primary_header,
372 (GptHeader*)gpt->secondary_header,
373 };
374 GptEntry *entries[] = {
375 (GptEntry*)gpt->primary_entries,
376 (GptEntry*)gpt->secondary_entries,
377 };
378 int i;
379 uint32_t number_of_entries;
380
381 if (gpt->valid_headers & MASK_PRIMARY)
382 number_of_entries = headers[PRIMARY]->number_of_entries;
383 else
384 number_of_entries = headers[SECONDARY]->number_of_entries;
385
386 for (i = PRIMARY; i <= SECONDARY; ++i) {
387 if (OverlappedEntries(entries[i], number_of_entries))
388 INVALIDATE_ENTRIES(gpt->valid_entries, i);
389 }
390 return gpt->valid_entries;
391 }
392
393 /* Primary entries and secondary entries should be bitwise identical.
394 * If two entries tables are valid, compare them. If not the same,
395 * overwrites secondary with primary (primary always has higher priority),
396 * and marks secondary as modified.
397 * If only one is valid, overwrites invalid one.
398 * If all are invalid, does nothing.
399 * This function returns bit masks for GptData.modified field.
400 * Note that CRC is NOT re-computed in this function.
401 */
402 uint8_t RepairEntries(GptData *gpt, const uint32_t valid_entries) {
403 if (valid_entries == MASK_BOTH) {
404 if (Memcmp(gpt->primary_entries, gpt->secondary_entries,
405 TOTAL_ENTRIES_SIZE)) {
406 Memcpy(gpt->secondary_entries, gpt->primary_entries, TOTAL_ENTRIES_SIZE);
407 return GPT_MODIFIED_ENTRIES2;
408 }
409 } else if (valid_entries == MASK_PRIMARY) {
410 Memcpy(gpt->secondary_entries, gpt->primary_entries, TOTAL_ENTRIES_SIZE);
411 return GPT_MODIFIED_ENTRIES2;
412 } else if (valid_entries == MASK_SECONDARY) {
413 Memcpy(gpt->primary_entries, gpt->secondary_entries, TOTAL_ENTRIES_SIZE);
414 return GPT_MODIFIED_ENTRIES1;
415 }
416
417 return 0;
418 }
419
420 /* Two headers are NOT bitwise identical. For example, my_lba pointers to header
421 * itself so that my_lba in primary and secondary is definitely different.
422 * Only the following fields should be identical.
423 *
424 * first_usable_lba
425 * last_usable_lba
426 * number_of_entries
427 * size_of_entry
428 * disk_uuid
429 *
430 * If any of above field are not matched, overwrite secondary with primary since
431 * we always trust primary.
432 * If any one of header is invalid, copy from another. */
433 int IsSynonymous(const GptHeader* a, const GptHeader* b) {
434 if ((a->first_usable_lba == b->first_usable_lba) &&
435 (a->last_usable_lba == b->last_usable_lba) &&
436 (a->number_of_entries == b->number_of_entries) &&
437 (a->size_of_entry == b->size_of_entry) &&
438 (!Memcmp(&a->disk_uuid, &b->disk_uuid, sizeof(Guid))))
439 return 1;
440 return 0;
441 }
442
443 /* The above five fields are shared between primary and secondary headers.
444 * We can recover one header from another through copying those fields. */
445 void CopySynonymousParts(GptHeader* target, const GptHeader* source) {
446 target->first_usable_lba = source->first_usable_lba;
447 target->last_usable_lba = source->last_usable_lba;
448 target->number_of_entries = source->number_of_entries;
449 target->size_of_entry = source->size_of_entry;
450 Memcpy(&target->disk_uuid, &source->disk_uuid, sizeof(Guid));
451 }
452
453 /* This function repairs primary and secondary headers if possible.
454 * If both headers are valid (CRC32 is correct) but
455 * a) indicate inconsistent usable LBA ranges,
456 * b) inconsistent partition entry size and number,
457 * c) inconsistent disk_uuid,
458 * we will use the primary header to overwrite secondary header.
459 * If primary is invalid (CRC32 is wrong), then we repair it from secondary.
460 * If secondary is invalid (CRC32 is wrong), then we repair it from primary.
461 * This function returns the bitmasks for modified header.
462 * Note that CRC value is NOT re-computed in this function. UpdateCrc() will
463 * do it later.
464 */
465 uint8_t RepairHeader(GptData *gpt, const uint32_t valid_headers) {
466 GptHeader *primary_header, *secondary_header;
467
468 primary_header = (GptHeader*)gpt->primary_header;
469 secondary_header = (GptHeader*)gpt->secondary_header;
470
471 if (valid_headers == MASK_BOTH) {
472 if (!IsSynonymous(primary_header, secondary_header)) {
473 CopySynonymousParts(secondary_header, primary_header);
474 return GPT_MODIFIED_HEADER2;
475 }
476 } else if (valid_headers == MASK_PRIMARY) {
477 Memcpy(secondary_header, primary_header, primary_header->size);
478 secondary_header->my_lba = gpt->drive_sectors - 1; /* the last sector */
479 secondary_header->entries_lba = secondary_header->my_lba -
480 GPT_ENTRIES_SECTORS;
481 return GPT_MODIFIED_HEADER2;
482 } else if (valid_headers == MASK_SECONDARY) {
483 Memcpy(primary_header, secondary_header, secondary_header->size);
484 primary_header->my_lba = GPT_PMBR_SECTOR; /* the second sector on drive */
485 primary_header->entries_lba = primary_header->my_lba + GPT_HEADER_SECTOR;
486 return GPT_MODIFIED_HEADER1;
487 }
488
489 return 0;
490 }
491
492 /* Update CRC value if necessary. */
493 void UpdateCrc(GptData *gpt) {
494 GptHeader *primary_header, *secondary_header;
495
496 primary_header = (GptHeader*)gpt->primary_header;
497 secondary_header = (GptHeader*)gpt->secondary_header;
498
499 if (gpt->modified & GPT_MODIFIED_ENTRIES1) {
500 primary_header->entries_crc32 =
501 Crc32(gpt->primary_entries, TOTAL_ENTRIES_SIZE);
502 }
503 if (gpt->modified & GPT_MODIFIED_ENTRIES2) {
504 secondary_header->entries_crc32 =
505 Crc32(gpt->secondary_entries, TOTAL_ENTRIES_SIZE);
506 }
507 if (gpt->modified & GPT_MODIFIED_HEADER1) {
508 primary_header->header_crc32 = 0;
509 primary_header->header_crc32 = Crc32(
510 (const uint8_t *)primary_header, primary_header->size);
511 }
512 if (gpt->modified & GPT_MODIFIED_HEADER2) {
513 secondary_header->header_crc32 = 0;
514 secondary_header->header_crc32 = Crc32(
515 (const uint8_t *)secondary_header, secondary_header->size);
516 }
517 }
518
519 /* This function only checks GptData.
520 * valid_headers and valid_entries are used to store the checking results.
521 *
522 * Returns:
523 * GPT_ERROR_INVALID_HEADERS -- both headers are invalid.
524 * GPT_ERROR_INVALID_ENTRIES -- both entries are invalid.
525 * GPT_SUCCESS -- everything looks fine.
526 */
527 int GptSanityCheck(GptData *gpt) {
528 int retval;
529
530 assert(gpt);
531
532 retval = CheckParameters(gpt);
533 if (retval != GPT_SUCCESS)
534 return retval;
535
536 /* Initialize values */
537 gpt->valid_headers = MASK_BOTH;
538 gpt->valid_entries = MASK_BOTH;
539
540 /* Start checking if header parameters are valid. */
541 CheckHeaderSignature(gpt);
542 CheckRevision(gpt);
543 CheckSize(gpt);
544 CheckReservedFields(gpt);
545 CheckMyLba(gpt);
546 CheckSizeOfPartitionEntry(gpt);
547 CheckNumberOfEntries(gpt);
548 CheckEntriesLba(gpt);
549 CheckValidUsableLbas(gpt);
550 CheckHeaderCrc(gpt);
551
552 /* Returns error if we don't have any valid header to use. */
553 if (!gpt->valid_headers)
554 return GPT_ERROR_INVALID_HEADERS;
555
556 /* Checks if entries are valid. */
557 CheckEntriesCrc(gpt);
558 CheckValidEntries(gpt);
559 CheckOverlappedPartition(gpt);
560
561 /* Returns error if we don't have any valid entries to use. */
562 if (!gpt->valid_entries)
563 return GPT_ERROR_INVALID_ENTRIES;
564
565 return GPT_SUCCESS;
566 }
567
568 void GptRepair(GptData *gpt) {
569 gpt->modified |= RepairHeader(gpt, gpt->valid_headers);
570 gpt->modified |= RepairEntries(gpt, gpt->valid_entries);
571 UpdateCrc(gpt);
572 }
573
574 /* Does every sanity check, and returns if any header/entries needs to be
575 * written back. */
576 int GptInit(GptData *gpt) {
577 int retval;
578
579 retval = GptSanityCheck(gpt);
580 if (GPT_SUCCESS != retval) return retval;
581
582 gpt->modified = 0;
583 GptRepair(gpt);
584
585 gpt->current_kernel = CGPT_KERNEL_ENTRY_NOT_FOUND;
586
587 return GPT_SUCCESS;
588 }
589
590 /* Helper function to get a pointer to the partition entry.
591 * 'secondary' is either PRIMARY or SECONDARY.
592 * 'entry_index' is the partition index: [0, number_of_entries).
593 */
594 GptEntry *GetEntry(GptData *gpt, int secondary, int entry_index) {
595 uint8_t *entries;
596
597 if (secondary == PRIMARY) {
598 entries = gpt->primary_entries;
599 } else {
600 entries = gpt->secondary_entries;
601 }
602
603 return (GptEntry*)(&entries[GetNumberOfEntries(gpt) * entry_index]);
604 }
605
606 /* The following functions are helpers to access attributes bit more easily.
607 * 'secondary' is either PRIMARY or SECONDARY.
608 * 'entry_index' is the partition index: [0, number_of_entries).
609 *
610 * Get*() return the exact value (shifted and masked).
611 */
612 void SetPriority(GptData *gpt, int secondary, int entry_index, int priority) {
613 GptEntry *entry;
614 entry = GetEntry(gpt, secondary, entry_index);
615
616 assert(priority >= 0 && priority <= CGPT_ATTRIBUTE_MAX_PRIORITY);
617 entry->attributes &= ~CGPT_ATTRIBUTE_PRIORITY_MASK;
618 entry->attributes |= (uint64_t)priority << CGPT_ATTRIBUTE_PRIORITY_OFFSET;
619 }
620
621 int GetPriority(GptData *gpt, int secondary, int entry_index) {
622 GptEntry *entry;
623 entry = GetEntry(gpt, secondary, entry_index);
624 return (entry->attributes & CGPT_ATTRIBUTE_PRIORITY_MASK) >>
625 CGPT_ATTRIBUTE_PRIORITY_OFFSET;
626 }
627
628 void SetBad(GptData *gpt, int secondary, int entry_index, int bad) {
629 GptEntry *entry;
630 entry = GetEntry(gpt, secondary, entry_index);
631
632 assert(bad >= 0 && bad <= CGPT_ATTRIBUTE_MAX_BAD);
633 entry->attributes &= ~CGPT_ATTRIBUTE_BAD_MASK;
634 entry->attributes |= (uint64_t)bad << CGPT_ATTRIBUTE_BAD_OFFSET;
635 }
636
637 int GetBad(GptData *gpt, int secondary, int entry_index) {
638 GptEntry *entry;
639 entry = GetEntry(gpt, secondary, entry_index);
640 return (entry->attributes & CGPT_ATTRIBUTE_BAD_MASK) >>
641 CGPT_ATTRIBUTE_BAD_OFFSET;
642 }
643
644 void SetTries(GptData *gpt, int secondary, int entry_index, int tries) {
645 GptEntry *entry;
646 entry = GetEntry(gpt, secondary, entry_index);
647
648 assert(tries >= 0 && tries <= CGPT_ATTRIBUTE_MAX_TRIES);
649 entry->attributes &= ~CGPT_ATTRIBUTE_TRIES_MASK;
650 entry->attributes |= (uint64_t)tries << CGPT_ATTRIBUTE_TRIES_OFFSET;
651 }
652
653 int GetTries(GptData *gpt, int secondary, int entry_index) {
654 GptEntry *entry;
655 entry = GetEntry(gpt, secondary, entry_index);
656 return (entry->attributes & CGPT_ATTRIBUTE_TRIES_MASK) >>
657 CGPT_ATTRIBUTE_TRIES_OFFSET;
658 }
659
660 void SetSuccessful(GptData *gpt, int secondary, int entry_index, int success) {
661 GptEntry *entry;
662 entry = GetEntry(gpt, secondary, entry_index);
663
664 assert(success >= 0 && success <= CGPT_ATTRIBUTE_MAX_SUCCESSFUL);
665 entry->attributes &= ~CGPT_ATTRIBUTE_SUCCESSFUL_MASK;
666 entry->attributes |= (uint64_t)success << CGPT_ATTRIBUTE_SUCCESSFUL_OFFSET;
667 }
668
669 int GetSuccessful(GptData *gpt, int secondary, int entry_index) {
670 GptEntry *entry;
671 entry = GetEntry(gpt, secondary, entry_index);
672 return (entry->attributes & CGPT_ATTRIBUTE_SUCCESSFUL_MASK) >>
673 CGPT_ATTRIBUTE_SUCCESSFUL_OFFSET;
674 }
675
676 uint32_t GetNumberOfEntries(const GptData *gpt) {
677 GptHeader *header = 0;
678 if (gpt->valid_headers & MASK_PRIMARY)
679 header = (GptHeader*)gpt->primary_header;
680 else if (gpt->valid_headers & MASK_SECONDARY)
681 header = (GptHeader*)gpt->secondary_header;
682 else
683 assert(0);
684 return header->number_of_entries;
685 }
686
687
688 /* Compare two priority values. Actually it is a circular priority, which is:
689 * 3 > 2 > 1 > 0, but 0 > 3. (-1 means very low, and anyone is higher than -1)
690 *
691 * Return 1 if 'a' has higher priority than 'b'.
692 */
693 int IsHigherPriority(int a, int b) {
694 if ((a == 0) && (b == CGPT_ATTRIBUTE_MAX_PRIORITY))
695 return 1;
696 else if ((a == CGPT_ATTRIBUTE_MAX_PRIORITY) && (b == 0))
697 return 0;
698 else
699 return (a > b) ? 1 : 0;
700 }
701
702 /* This function walks through the whole partition table (see note below),
703 * and pick up the active and valid (not marked as bad) kernel entry with
704 * *highest* priority (except gpt->current_kernel itself).
705 *
706 * Returns start_sector and its size if a candidate kernel is found.
707 *
708 * Note: in the first walk (gpt->current_kernel==CGPT_KERNEL_ENTRY_NOT_FOUND),
709 * the scan range is whole table. But in later scans, we only scan
710 * (header->number_of_entries - 1) entries because we are looking for
711 * next kernel with lower priority (consider the case that highest
712 * priority kernel is still active and valid).
713 */
714 int GptNextKernelEntry(GptData *gpt, uint64_t *start_sector, uint64_t *size) {
715 GptHeader *header;
716 GptEntry *entry;
717 int scan, current_priority;
718 int begin, end; /* [begin, end], which end is included. */
719 Guid chromeos_kernel = GPT_ENT_TYPE_CHROMEOS_KERNEL;
720
721 header = (GptHeader*)gpt->primary_header;
722 current_priority = -1; /* pretty low priority */
723 if (gpt->current_kernel == CGPT_KERNEL_ENTRY_NOT_FOUND) {
724 begin = 0;
725 end = header->number_of_entries - 1;
726 } else {
727 begin = (gpt->current_kernel + 1) % header->number_of_entries;
728 end = (gpt->current_kernel - 1 + header->number_of_entries) %
729 header->number_of_entries;
730 }
731
732 scan = begin;
733 do {
734 entry = GetEntry(gpt, PRIMARY, scan);
735 if (!Memcmp(&entry->type, &chromeos_kernel, sizeof(Guid)) &&
736 !GetBad(gpt, PRIMARY, scan) &&
737 ((gpt->current_kernel == CGPT_KERNEL_ENTRY_NOT_FOUND) ||
738 (IsHigherPriority(GetPriority(gpt, PRIMARY, scan),
739 current_priority)))) {
740 gpt->current_kernel = scan;
741 current_priority = GetPriority(gpt, PRIMARY, gpt->current_kernel);
742 }
743
744 if (scan == end) break;
745 scan = (scan + 1) % header->number_of_entries;
746 } while (1);
747
748 if (gpt->current_kernel == CGPT_KERNEL_ENTRY_NOT_FOUND)
749 return GPT_ERROR_NO_VALID_KERNEL;
750
751 entry = GetEntry(gpt, PRIMARY, gpt->current_kernel);
752 assert(entry->starting_lba <= entry->ending_lba);
753
754 if (start_sector) *start_sector = entry->starting_lba;
755 if (size) *size = entry->ending_lba - entry->starting_lba + 1;
756
757 return GPT_SUCCESS;
758 }
759
760 /* Given a update_type, this function updates the corresponding bits in GptData.
761 *
762 * Returns GPT_SUCCESS if no error. gpt->modified is set if any header and
763 * entries needs to be updated to hard drive.
764 * GPT_ERROR_INVALID_UPDATE_TYPE if given an invalid update_type.
765 */
766 int GptUpdateKernelEntry(GptData *gpt, uint32_t update_type) {
767 Guid chromeos_type = GPT_ENT_TYPE_CHROMEOS_KERNEL;
768 int primary_is_modified = 0;
769
770 assert(gpt->current_kernel != CGPT_KERNEL_ENTRY_NOT_FOUND);
771 assert(!Memcmp(&(GetEntry(gpt, PRIMARY, gpt->current_kernel)->type),
772 &chromeos_type, sizeof(Guid)));
773
774 /* Modify primary entries first, then copy to secondary later. */
775 switch (update_type) {
776 case GPT_UPDATE_ENTRY_TRY: {
777 /* Increase tries value until CGPT_ATTRIBUTE_MAX_TRIES. */
778 int tries;
779 tries = GetTries(gpt, PRIMARY, gpt->current_kernel);
780 if (tries < CGPT_ATTRIBUTE_MAX_TRIES) {
781 ++tries;
782 SetTries(gpt, PRIMARY, gpt->current_kernel, tries);
783 primary_is_modified = 1;
784 }
785 break;
786 }
787 case GPT_UPDATE_ENTRY_BAD: {
788 GetEntry(gpt, PRIMARY, gpt->current_kernel)->attributes |=
789 CGPT_ATTRIBUTE_BAD_MASK;
790 primary_is_modified = 1;
791 break;
792 }
793 default: {
794 return GPT_ERROR_INVALID_UPDATE_TYPE;
795 }
796 }
797
798 if (primary_is_modified) {
799 /* Claim only primary is valid so that secondary is overwritten. */
800 RepairEntries(gpt, MASK_PRIMARY);
801 /* Actually two entries are dirty now.
802 * Also two headers are dirty because entries_crc32 has been updated. */
803 gpt->modified |= (GPT_MODIFIED_HEADER1 | GPT_MODIFIED_ENTRIES1 |
804 GPT_MODIFIED_HEADER2 | GPT_MODIFIED_ENTRIES2);
805 UpdateCrc(gpt);
806 }
807
808 return GPT_SUCCESS;
809 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698