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