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