blob: a871b1aa09fa12f4215c9d8f5fb9bead67b210fb [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;
Louis Yung-Chieh Lo49fa8e52010-04-30 16:10:48 -0700246 GptHeader *headers[] = {
247 (GptHeader*)gpt->primary_header,
248 (GptHeader*)gpt->secondary_header,
249 };
250 int i;
251
252 for (i = PRIMARY; i <= SECONDARY; ++i) {
Louis Yung-Chieh Lo418ad3b2010-05-27 11:21:17 +0800253 if (!(gpt->valid_headers & (1 << i))) continue;
Louis Yung-Chieh Lo49fa8e52010-04-30 16:10:48 -0700254 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)
Louis Yung-Chieh Lo418ad3b2010-05-27 11:21:17 +0800259 INVALIDATE_HEADER(gpt->valid_headers, i);
Louis Yung-Chieh Lo49fa8e52010-04-30 16:10:48 -0700260 }
Louis Yung-Chieh Lo418ad3b2010-05-27 11:21:17 +0800261 return gpt->valid_headers;
Louis Yung-Chieh Lo49fa8e52010-04-30 16:10:48 -0700262}
263
264/* Checks entries CRC */
265uint32_t CheckEntriesCrc(GptData *gpt) {
266 uint32_t crc32;
Louis Yung-Chieh Lo49fa8e52010-04-30 16:10:48 -0700267 GptHeader *headers[] = {
268 (GptHeader*)gpt->primary_header,
269 (GptHeader*)gpt->secondary_header,
270 };
271 GptEntry *entries[] = {
272 (GptEntry*)gpt->primary_entries,
273 (GptEntry*)gpt->secondary_entries,
274 };
Louis Yung-Chieh Lo418ad3b2010-05-27 11:21:17 +0800275 uint32_t entries_crc32;
Louis Yung-Chieh Lo49fa8e52010-04-30 16:10:48 -0700276 int i;
277
Louis Yung-Chieh Lo418ad3b2010-05-27 11:21:17 +0800278 if (gpt->valid_headers & MASK_PRIMARY)
279 entries_crc32 = headers[PRIMARY]->entries_crc32;
280 else
281 entries_crc32 = headers[SECONDARY]->entries_crc32;
282
Louis Yung-Chieh Lo49fa8e52010-04-30 16:10:48 -0700283 for (i = PRIMARY; i <= SECONDARY; ++i) {
284 crc32 = Crc32((const uint8_t *)entries[i], TOTAL_ENTRIES_SIZE);
Louis Yung-Chieh Lo418ad3b2010-05-27 11:21:17 +0800285 if (crc32 != entries_crc32)
286 INVALIDATE_ENTRIES(gpt->valid_entries, i);
Louis Yung-Chieh Lo49fa8e52010-04-30 16:10:48 -0700287 }
Louis Yung-Chieh Lo418ad3b2010-05-27 11:21:17 +0800288 return gpt->valid_entries;
Louis Yung-Chieh Lo49fa8e52010-04-30 16:10:48 -0700289}
290
291/* Returns non-zero if the given GUID is non-zero. */
292static int NonZeroGuid(const Guid *guid) {
293 static Guid zero = {{{0, 0, 0, 0, 0, {0, 0, 0, 0, 0, 0}}}};
294 return Memcmp(&zero, guid, sizeof(zero));
295}
296
297/* Checks if entries geometry is valid.
298 * All active (non-zero PartitionTypeGUID) partition entries should have:
299 * entry.StartingLBA >= header.FirstUsableLBA
300 * entry.EndingLBA <= header.LastUsableLBA
301 * entry.StartingLBA <= entry.EndingLBA
302 */
303uint32_t CheckValidEntries(GptData *gpt) {
304 uint32_t valid_entries = MASK_BOTH;
305 GptHeader *headers[] = {
306 (GptHeader*)gpt->primary_header,
307 (GptHeader*)gpt->secondary_header,
308 };
309 GptEntry *entries[] = {
310 (GptEntry*)gpt->primary_entries,
311 (GptEntry*)gpt->secondary_entries,
312 };
Louis Yung-Chieh Lo418ad3b2010-05-27 11:21:17 +0800313 uint32_t number_of_entries, size_of_entry;
314 uint64_t first_usable_lba, last_usable_lba;
Louis Yung-Chieh Lo49fa8e52010-04-30 16:10:48 -0700315 int copy, entry_index;
316 GptEntry *entry;
317
Louis Yung-Chieh Lo418ad3b2010-05-27 11:21:17 +0800318 if (gpt->valid_headers & MASK_PRIMARY)
319 copy = PRIMARY;
320 else
321 copy = SECONDARY;
322 number_of_entries = headers[copy]->number_of_entries;
323 size_of_entry = headers[copy]->size_of_entry;
324 first_usable_lba = headers[copy]->first_usable_lba;
325 last_usable_lba = headers[copy]->last_usable_lba;
326
Louis Yung-Chieh Lo49fa8e52010-04-30 16:10:48 -0700327 for (copy = PRIMARY; copy <= SECONDARY; ++copy) {
328 for (entry_index = 0;
Louis Yung-Chieh Lo418ad3b2010-05-27 11:21:17 +0800329 entry_index < number_of_entries;
Louis Yung-Chieh Lo49fa8e52010-04-30 16:10:48 -0700330 ++entry_index) {
331 entry = (GptEntry*)&(((uint8_t*)entries[copy])
Louis Yung-Chieh Lo418ad3b2010-05-27 11:21:17 +0800332 [entry_index * size_of_entry]);
Louis Yung-Chieh Lo49fa8e52010-04-30 16:10:48 -0700333 if (NonZeroGuid(&entry->type)) {
Louis Yung-Chieh Lo418ad3b2010-05-27 11:21:17 +0800334 if ((entry->starting_lba < first_usable_lba) ||
335 (entry->ending_lba > last_usable_lba) ||
Louis Yung-Chieh Lo49fa8e52010-04-30 16:10:48 -0700336 (entry->ending_lba < entry->starting_lba))
Louis Yung-Chieh Lo418ad3b2010-05-27 11:21:17 +0800337 INVALIDATE_ENTRIES(valid_entries, copy);
Louis Yung-Chieh Lo49fa8e52010-04-30 16:10:48 -0700338 }
339 }
340 }
341 return valid_entries;
342}
343
344static pair_t pairs[MAX_NUMBER_OF_ENTRIES];
345/* Callback function for QuickSort(). Returns 1 if 'a_' should precede 'b_'. */
346int compare_pair(const void *a_, const void *b_) {
347 const pair_t *a = a_;
348 const pair_t *b = b_;
349 if (a->starting <= b->starting) return 1;
350 return 0;
351}
352
353/* First sorts by starting_lba, and traverse everyone once if its starting_lba
354 * is between previous starting_lba and ending_lba. If yes, overlapped.
355 * Returns 1 if overlap is found. */
356int OverlappedEntries(GptEntry *entries, uint32_t number_of_entries) {
357 int i, num_of_pair = 0;
358 for (i = 0; i < number_of_entries; ++i) {
359 if (NonZeroGuid(&entries[i].type)) {
360 pairs[num_of_pair].starting = entries[i].starting_lba;
361 pairs[num_of_pair].ending = entries[i].ending_lba;
362 ++num_of_pair;
363 }
364 }
365 QuickSort(&pairs, num_of_pair, sizeof(pair_t), compare_pair);
366
367 for (i = 1; i < num_of_pair; ++i) {
368 if ((pairs[i].starting >= pairs[i-1].starting) &&
369 (pairs[i].starting <= pairs[i-1].ending))
370 return 1;
371 }
372
373 return 0;
374}
375
376/* Checks if any two partitions are overlapped in primary and secondary entries.
377 */
378uint32_t CheckOverlappedPartition(GptData *gpt) {
379 uint32_t valid_entries = MASK_BOTH;
380 GptHeader *headers[] = {
381 (GptHeader*)gpt->primary_header,
382 (GptHeader*)gpt->secondary_header,
383 };
384 GptEntry *entries[] = {
385 (GptEntry*)gpt->primary_entries,
386 (GptEntry*)gpt->secondary_entries,
387 };
388 int i;
Louis Yung-Chieh Lo418ad3b2010-05-27 11:21:17 +0800389 uint32_t number_of_entries;
390
391 if (gpt->valid_headers & MASK_PRIMARY)
392 number_of_entries = headers[PRIMARY]->number_of_entries;
393 else
394 number_of_entries = headers[SECONDARY]->number_of_entries;
Louis Yung-Chieh Lo49fa8e52010-04-30 16:10:48 -0700395
396 for (i = PRIMARY; i <= SECONDARY; ++i) {
Louis Yung-Chieh Lo418ad3b2010-05-27 11:21:17 +0800397 if (OverlappedEntries(entries[i], number_of_entries))
Louis Yung-Chieh Lo49fa8e52010-04-30 16:10:48 -0700398 INVALIDATE_ENTRIES(valid_entries, i);
399 }
400 return valid_entries;
401}
402
403/* Primary entries and secondary entries should be bitwise identical.
404 * If two entries tables are valid, compare them. If not the same,
405 * overwrites secondary with primary (primary always has higher priority),
406 * and marks secondary as modified.
407 * If only one is valid, overwrites invalid one.
408 * If all are invalid, does nothing.
Louis Yung-Chieh Lo418ad3b2010-05-27 11:21:17 +0800409 * This function returns bit masks for GptData.modified field.
410 * Note that CRC is NOT re-computed in this function.
411 */
Louis Yung-Chieh Lo49fa8e52010-04-30 16:10:48 -0700412uint8_t RepairEntries(GptData *gpt, const uint32_t valid_entries) {
413 if (valid_entries == MASK_BOTH) {
414 if (Memcmp(gpt->primary_entries, gpt->secondary_entries,
415 TOTAL_ENTRIES_SIZE)) {
416 Memcpy(gpt->secondary_entries, gpt->primary_entries, TOTAL_ENTRIES_SIZE);
417 return GPT_MODIFIED_ENTRIES2;
418 }
419 } else if (valid_entries == MASK_PRIMARY) {
420 Memcpy(gpt->secondary_entries, gpt->primary_entries, TOTAL_ENTRIES_SIZE);
421 return GPT_MODIFIED_ENTRIES2;
422 } else if (valid_entries == MASK_SECONDARY) {
423 Memcpy(gpt->primary_entries, gpt->secondary_entries, TOTAL_ENTRIES_SIZE);
424 return GPT_MODIFIED_ENTRIES1;
425 }
426
427 return 0;
428}
429
430/* Two headers are NOT bitwise identical. For example, my_lba pointers to header
431 * itself so that my_lba in primary and secondary is definitely different.
432 * Only the following fields should be identical.
433 *
434 * first_usable_lba
435 * last_usable_lba
436 * number_of_entries
437 * size_of_entry
438 * disk_uuid
439 *
440 * If any of above field are not matched, overwrite secondary with primary since
441 * we always trust primary.
442 * If any one of header is invalid, copy from another. */
443int IsSynonymous(const GptHeader* a, const GptHeader* b) {
444 if ((a->first_usable_lba == b->first_usable_lba) &&
445 (a->last_usable_lba == b->last_usable_lba) &&
446 (a->number_of_entries == b->number_of_entries) &&
447 (a->size_of_entry == b->size_of_entry) &&
448 (!Memcmp(&a->disk_uuid, &b->disk_uuid, sizeof(Guid))))
449 return 1;
450 return 0;
451}
452
453/* The above five fields are shared between primary and secondary headers.
454 * We can recover one header from another through copying those fields. */
455void CopySynonymousParts(GptHeader* target, const GptHeader* source) {
456 target->first_usable_lba = source->first_usable_lba;
457 target->last_usable_lba = source->last_usable_lba;
458 target->number_of_entries = source->number_of_entries;
459 target->size_of_entry = source->size_of_entry;
460 Memcpy(&target->disk_uuid, &source->disk_uuid, sizeof(Guid));
461}
462
463/* This function repairs primary and secondary headers if possible.
464 * If both headers are valid (CRC32 is correct) but
465 * a) indicate inconsistent usable LBA ranges,
466 * b) inconsistent partition entry size and number,
467 * c) inconsistent disk_uuid,
468 * we will use the primary header to overwrite secondary header.
469 * If primary is invalid (CRC32 is wrong), then we repair it from secondary.
470 * If secondary is invalid (CRC32 is wrong), then we repair it from primary.
471 * This function returns the bitmasks for modified header.
Louis Yung-Chieh Lo418ad3b2010-05-27 11:21:17 +0800472 * Note that CRC value is NOT re-computed in this function. UpdateCrc() will
Louis Yung-Chieh Lo49fa8e52010-04-30 16:10:48 -0700473 * do it later.
474 */
475uint8_t RepairHeader(GptData *gpt, const uint32_t valid_headers) {
476 GptHeader *primary_header, *secondary_header;
477
478 primary_header = (GptHeader*)gpt->primary_header;
479 secondary_header = (GptHeader*)gpt->secondary_header;
480
481 if (valid_headers == MASK_BOTH) {
482 if (!IsSynonymous(primary_header, secondary_header)) {
483 CopySynonymousParts(secondary_header, primary_header);
484 return GPT_MODIFIED_HEADER2;
485 }
486 } else if (valid_headers == MASK_PRIMARY) {
487 Memcpy(secondary_header, primary_header, primary_header->size);
488 secondary_header->my_lba = gpt->drive_sectors - 1; /* the last sector */
489 secondary_header->entries_lba = secondary_header->my_lba -
490 GPT_ENTRIES_SECTORS;
491 return GPT_MODIFIED_HEADER2;
492 } else if (valid_headers == MASK_SECONDARY) {
493 Memcpy(primary_header, secondary_header, secondary_header->size);
494 primary_header->my_lba = GPT_PMBR_SECTOR; /* the second sector on drive */
495 primary_header->entries_lba = primary_header->my_lba + GPT_HEADER_SECTOR;
496 return GPT_MODIFIED_HEADER1;
497 }
498
499 return 0;
500}
501
502/* Update CRC value if necessary. */
503void UpdateCrc(GptData *gpt) {
504 GptHeader *primary_header, *secondary_header;
505
506 primary_header = (GptHeader*)gpt->primary_header;
507 secondary_header = (GptHeader*)gpt->secondary_header;
508
Louis Yung-Chieh Lob17db3c2010-05-05 11:21:08 -0700509 if (gpt->modified & GPT_MODIFIED_ENTRIES1) {
510 primary_header->entries_crc32 =
511 Crc32(gpt->primary_entries, TOTAL_ENTRIES_SIZE);
512 }
513 if (gpt->modified & GPT_MODIFIED_ENTRIES2) {
514 secondary_header->entries_crc32 =
515 Crc32(gpt->secondary_entries, TOTAL_ENTRIES_SIZE);
516 }
Louis Yung-Chieh Lo49fa8e52010-04-30 16:10:48 -0700517 if (gpt->modified & GPT_MODIFIED_HEADER1) {
518 primary_header->header_crc32 = 0;
519 primary_header->header_crc32 = Crc32(
520 (const uint8_t *)primary_header, primary_header->size);
521 }
522 if (gpt->modified & GPT_MODIFIED_HEADER2) {
523 secondary_header->header_crc32 = 0;
524 secondary_header->header_crc32 = Crc32(
525 (const uint8_t *)secondary_header, secondary_header->size);
526 }
Louis Yung-Chieh Lo49fa8e52010-04-30 16:10:48 -0700527}
528
Louis Yung-Chieh Lo418ad3b2010-05-27 11:21:17 +0800529/* This function only checks GptData.
530 * valid_headers and valid_entries are used to store the checking results.
531 *
532 * Returns:
533 * GPT_ERROR_INVALID_HEADERS -- both headers are invalid.
534 * GPT_ERROR_INVALID_ENTRIES -- both entries are invalid.
535 * GPT_SUCCESS -- everything looks fine.
536 */
537int GptSanityCheck(GptData *gpt) {
Louis Yung-Chieh Lo49fa8e52010-04-30 16:10:48 -0700538 int retval;
539
Louis Yung-Chieh Lo418ad3b2010-05-27 11:21:17 +0800540 assert(gpt);
541
Louis Yung-Chieh Lo49fa8e52010-04-30 16:10:48 -0700542 retval = CheckParameters(gpt);
543 if (retval != GPT_SUCCESS)
544 return retval;
545
546 /* Initialize values */
Louis Yung-Chieh Lo418ad3b2010-05-27 11:21:17 +0800547 gpt->valid_headers = MASK_BOTH;
548 gpt->valid_entries = MASK_BOTH;
Louis Yung-Chieh Lo49fa8e52010-04-30 16:10:48 -0700549
550 /* Start checking if header parameters are valid. */
Louis Yung-Chieh Lo418ad3b2010-05-27 11:21:17 +0800551 CheckHeaderSignature(gpt);
552 CheckRevision(gpt);
553 CheckSize(gpt);
554 CheckReservedFields(gpt);
555 CheckMyLba(gpt);
556 CheckSizeOfPartitionEntry(gpt);
557 CheckNumberOfEntries(gpt);
558 CheckEntriesLba(gpt);
559 CheckValidUsableLbas(gpt);
560 CheckHeaderCrc(gpt);
Louis Yung-Chieh Lo49fa8e52010-04-30 16:10:48 -0700561
Louis Yung-Chieh Lo418ad3b2010-05-27 11:21:17 +0800562 /* Returns error if we don't have any valid header to use. */
563 if (!gpt->valid_headers)
564 return GPT_ERROR_INVALID_HEADERS;
Louis Yung-Chieh Lo49fa8e52010-04-30 16:10:48 -0700565
566 /* Checks if entries are valid. */
Louis Yung-Chieh Lo418ad3b2010-05-27 11:21:17 +0800567 CheckEntriesCrc(gpt);
568 CheckValidEntries(gpt);
569 CheckOverlappedPartition(gpt);
Louis Yung-Chieh Lo49fa8e52010-04-30 16:10:48 -0700570
Louis Yung-Chieh Lo418ad3b2010-05-27 11:21:17 +0800571 /* Returns error if we don't have any valid entries to use. */
572 if (!gpt->valid_entries)
Louis Yung-Chieh Lo49fa8e52010-04-30 16:10:48 -0700573 return GPT_ERROR_INVALID_ENTRIES;
Louis Yung-Chieh Lo37f6b552010-04-22 21:22:22 -0700574
Louis Yung-Chieh Lo418ad3b2010-05-27 11:21:17 +0800575 return GPT_SUCCESS;
576}
577
578void GptRepair(GptData *gpt) {
579 gpt->modified |= RepairHeader(gpt, gpt->valid_headers);
580 gpt->modified |= RepairEntries(gpt, gpt->valid_entries);
Louis Yung-Chieh Lo49fa8e52010-04-30 16:10:48 -0700581 UpdateCrc(gpt);
Louis Yung-Chieh Lo418ad3b2010-05-27 11:21:17 +0800582}
583
584/* Does every sanity check, and returns if any header/entries needs to be
585 * written back. */
586int GptInit(GptData *gpt) {
587 int retval;
588
589 retval = GptSanityCheck(gpt);
590 if (GPT_SUCCESS != retval) return retval;
591
592 gpt->modified = 0;
593 GptRepair(gpt);
Louis Yung-Chieh Lo49fa8e52010-04-30 16:10:48 -0700594
Louis Yung-Chieh Lob17db3c2010-05-05 11:21:08 -0700595 gpt->current_kernel = CGPT_KERNEL_ENTRY_NOT_FOUND;
596
Louis Yung-Chieh Lo37f6b552010-04-22 21:22:22 -0700597 return GPT_SUCCESS;
Louis Yung-Chieh Loe1a25ab2010-04-20 10:52:41 -0700598}
599
Louis Yung-Chieh Lob17db3c2010-05-05 11:21:08 -0700600/* Helper function to get a pointer to the partition entry.
601 * 'secondary' is either PRIMARY or SECONDARY.
602 * 'entry_index' is the partition index: [0, number_of_entries).
603 */
604GptEntry *GetEntry(GptData *gpt, int secondary, int entry_index) {
Louis Yung-Chieh Lob17db3c2010-05-05 11:21:08 -0700605 uint8_t *entries;
Louis Yung-Chieh Lo49fa8e52010-04-30 16:10:48 -0700606
Louis Yung-Chieh Lob17db3c2010-05-05 11:21:08 -0700607 if (secondary == PRIMARY) {
Louis Yung-Chieh Lob17db3c2010-05-05 11:21:08 -0700608 entries = gpt->primary_entries;
609 } else {
Louis Yung-Chieh Lob17db3c2010-05-05 11:21:08 -0700610 entries = gpt->secondary_entries;
611 }
612
Louis Yung-Chieh Lo418ad3b2010-05-27 11:21:17 +0800613 return (GptEntry*)(&entries[GetNumberOfEntries(gpt) * entry_index]);
Louis Yung-Chieh Lob17db3c2010-05-05 11:21:08 -0700614}
615
616/* The following functions are helpers to access attributes bit more easily.
617 * 'secondary' is either PRIMARY or SECONDARY.
618 * 'entry_index' is the partition index: [0, number_of_entries).
619 *
620 * Get*() return the exact value (shifted and masked).
621 */
622void SetPriority(GptData *gpt, int secondary, int entry_index, int priority) {
623 GptEntry *entry;
624 entry = GetEntry(gpt, secondary, entry_index);
625
626 assert(priority >= 0 && priority <= CGPT_ATTRIBUTE_MAX_PRIORITY);
627 entry->attributes &= ~CGPT_ATTRIBUTE_PRIORITY_MASK;
628 entry->attributes |= (uint64_t)priority << CGPT_ATTRIBUTE_PRIORITY_OFFSET;
629}
630
631int GetPriority(GptData *gpt, int secondary, int entry_index) {
632 GptEntry *entry;
633 entry = GetEntry(gpt, secondary, entry_index);
634 return (entry->attributes & CGPT_ATTRIBUTE_PRIORITY_MASK) >>
635 CGPT_ATTRIBUTE_PRIORITY_OFFSET;
636}
637
638void SetBad(GptData *gpt, int secondary, int entry_index, int bad) {
639 GptEntry *entry;
640 entry = GetEntry(gpt, secondary, entry_index);
641
642 assert(bad >= 0 && bad <= CGPT_ATTRIBUTE_MAX_BAD);
643 entry->attributes &= ~CGPT_ATTRIBUTE_BAD_MASK;
644 entry->attributes |= (uint64_t)bad << CGPT_ATTRIBUTE_BAD_OFFSET;
645}
646
647int GetBad(GptData *gpt, int secondary, int entry_index) {
648 GptEntry *entry;
649 entry = GetEntry(gpt, secondary, entry_index);
650 return (entry->attributes & CGPT_ATTRIBUTE_BAD_MASK) >>
651 CGPT_ATTRIBUTE_BAD_OFFSET;
652}
653
654void SetTries(GptData *gpt, int secondary, int entry_index, int tries) {
655 GptEntry *entry;
656 entry = GetEntry(gpt, secondary, entry_index);
657
658 assert(tries >= 0 && tries <= CGPT_ATTRIBUTE_MAX_TRIES);
659 entry->attributes &= ~CGPT_ATTRIBUTE_TRIES_MASK;
660 entry->attributes |= (uint64_t)tries << CGPT_ATTRIBUTE_TRIES_OFFSET;
661}
662
663int GetTries(GptData *gpt, int secondary, int entry_index) {
664 GptEntry *entry;
665 entry = GetEntry(gpt, secondary, entry_index);
666 return (entry->attributes & CGPT_ATTRIBUTE_TRIES_MASK) >>
667 CGPT_ATTRIBUTE_TRIES_OFFSET;
668}
669
Louis Yung-Chieh Lob31ddce2010-05-21 16:35:44 +0800670void SetSuccessful(GptData *gpt, int secondary, int entry_index, int success) {
Louis Yung-Chieh Lob17db3c2010-05-05 11:21:08 -0700671 GptEntry *entry;
672 entry = GetEntry(gpt, secondary, entry_index);
673
Louis Yung-Chieh Lob31ddce2010-05-21 16:35:44 +0800674 assert(success >= 0 && success <= CGPT_ATTRIBUTE_MAX_SUCCESSFUL);
675 entry->attributes &= ~CGPT_ATTRIBUTE_SUCCESSFUL_MASK;
676 entry->attributes |= (uint64_t)success << CGPT_ATTRIBUTE_SUCCESSFUL_OFFSET;
Louis Yung-Chieh Lob17db3c2010-05-05 11:21:08 -0700677}
678
Louis Yung-Chieh Lob31ddce2010-05-21 16:35:44 +0800679int GetSuccessful(GptData *gpt, int secondary, int entry_index) {
Louis Yung-Chieh Lob17db3c2010-05-05 11:21:08 -0700680 GptEntry *entry;
681 entry = GetEntry(gpt, secondary, entry_index);
Louis Yung-Chieh Lob31ddce2010-05-21 16:35:44 +0800682 return (entry->attributes & CGPT_ATTRIBUTE_SUCCESSFUL_MASK) >>
683 CGPT_ATTRIBUTE_SUCCESSFUL_OFFSET;
Louis Yung-Chieh Lob17db3c2010-05-05 11:21:08 -0700684}
685
Louis Yung-Chieh Lob31ddce2010-05-21 16:35:44 +0800686uint32_t GetNumberOfEntries(const GptData *gpt) {
Louis Yung-Chieh Lo418ad3b2010-05-27 11:21:17 +0800687 GptHeader *header = 0;
688 if (gpt->valid_headers & MASK_PRIMARY)
689 header = (GptHeader*)gpt->primary_header;
690 else if (gpt->valid_headers & MASK_SECONDARY)
691 header = (GptHeader*)gpt->secondary_header;
692 else
693 assert(0);
Louis Yung-Chieh Lob31ddce2010-05-21 16:35:44 +0800694 return header->number_of_entries;
695}
696
697
Louis Yung-Chieh Lob17db3c2010-05-05 11:21:08 -0700698/* Compare two priority values. Actually it is a circular priority, which is:
699 * 3 > 2 > 1 > 0, but 0 > 3. (-1 means very low, and anyone is higher than -1)
700 *
701 * Return 1 if 'a' has higher priority than 'b'.
702 */
703int IsHigherPriority(int a, int b) {
704 if ((a == 0) && (b == CGPT_ATTRIBUTE_MAX_PRIORITY))
705 return 1;
706 else if ((a == CGPT_ATTRIBUTE_MAX_PRIORITY) && (b == 0))
707 return 0;
708 else
709 return (a > b) ? 1 : 0;
710}
711
712/* This function walks through the whole partition table (see note below),
713 * and pick up the active and valid (not marked as bad) kernel entry with
714 * *highest* priority (except gpt->current_kernel itself).
715 *
716 * Returns start_sector and its size if a candidate kernel is found.
717 *
718 * Note: in the first walk (gpt->current_kernel==CGPT_KERNEL_ENTRY_NOT_FOUND),
719 * the scan range is whole table. But in later scans, we only scan
720 * (header->number_of_entries - 1) entries because we are looking for
721 * next kernel with lower priority (consider the case that highest
722 * priority kernel is still active and valid).
723 */
Louis Yung-Chieh Lo49fa8e52010-04-30 16:10:48 -0700724int GptNextKernelEntry(GptData *gpt, uint64_t *start_sector, uint64_t *size) {
Louis Yung-Chieh Lob17db3c2010-05-05 11:21:08 -0700725 GptHeader *header;
726 GptEntry *entry;
727 int scan, current_priority;
728 int begin, end; /* [begin, end], which end is included. */
729 Guid chromeos_kernel = GPT_ENT_TYPE_CHROMEOS_KERNEL;
730
731 header = (GptHeader*)gpt->primary_header;
732 current_priority = -1; /* pretty low priority */
733 if (gpt->current_kernel == CGPT_KERNEL_ENTRY_NOT_FOUND) {
734 begin = 0;
735 end = header->number_of_entries - 1;
736 } else {
737 begin = (gpt->current_kernel + 1) % header->number_of_entries;
738 end = (gpt->current_kernel - 1 + header->number_of_entries) %
739 header->number_of_entries;
740 }
741
742 scan = begin;
743 do {
744 entry = GetEntry(gpt, PRIMARY, scan);
745 if (!Memcmp(&entry->type, &chromeos_kernel, sizeof(Guid)) &&
746 !GetBad(gpt, PRIMARY, scan) &&
747 ((gpt->current_kernel == CGPT_KERNEL_ENTRY_NOT_FOUND) ||
748 (IsHigherPriority(GetPriority(gpt, PRIMARY, scan),
749 current_priority)))) {
750 gpt->current_kernel = scan;
751 current_priority = GetPriority(gpt, PRIMARY, gpt->current_kernel);
752 }
753
754 if (scan == end) break;
755 scan = (scan + 1) % header->number_of_entries;
756 } while (1);
757
758 if (gpt->current_kernel == CGPT_KERNEL_ENTRY_NOT_FOUND)
759 return GPT_ERROR_NO_VALID_KERNEL;
760
761 entry = GetEntry(gpt, PRIMARY, gpt->current_kernel);
762 assert(entry->starting_lba <= entry->ending_lba);
763
764 if (start_sector) *start_sector = entry->starting_lba;
765 if (size) *size = entry->ending_lba - entry->starting_lba + 1;
766
Louis Yung-Chieh Lo37f6b552010-04-22 21:22:22 -0700767 return GPT_SUCCESS;
Louis Yung-Chieh Loe1a25ab2010-04-20 10:52:41 -0700768}
769
Louis Yung-Chieh Lob17db3c2010-05-05 11:21:08 -0700770/* Given a update_type, this function updates the corresponding bits in GptData.
771 *
772 * Returns GPT_SUCCESS if no error. gpt->modified is set if any header and
773 * entries needs to be updated to hard drive.
774 * GPT_ERROR_INVALID_UPDATE_TYPE if given an invalid update_type.
775 */
Louis Yung-Chieh Lo49fa8e52010-04-30 16:10:48 -0700776int GptUpdateKernelEntry(GptData *gpt, uint32_t update_type) {
Louis Yung-Chieh Lob17db3c2010-05-05 11:21:08 -0700777 Guid chromeos_type = GPT_ENT_TYPE_CHROMEOS_KERNEL;
778 int primary_is_modified = 0;
779
780 assert(gpt->current_kernel != CGPT_KERNEL_ENTRY_NOT_FOUND);
781 assert(!Memcmp(&(GetEntry(gpt, PRIMARY, gpt->current_kernel)->type),
782 &chromeos_type, sizeof(Guid)));
783
784 /* Modify primary entries first, then copy to secondary later. */
785 switch (update_type) {
786 case GPT_UPDATE_ENTRY_TRY: {
787 /* Increase tries value until CGPT_ATTRIBUTE_MAX_TRIES. */
788 int tries;
789 tries = GetTries(gpt, PRIMARY, gpt->current_kernel);
790 if (tries < CGPT_ATTRIBUTE_MAX_TRIES) {
791 ++tries;
792 SetTries(gpt, PRIMARY, gpt->current_kernel, tries);
793 primary_is_modified = 1;
794 }
795 break;
796 }
797 case GPT_UPDATE_ENTRY_BAD: {
798 GetEntry(gpt, PRIMARY, gpt->current_kernel)->attributes |=
799 CGPT_ATTRIBUTE_BAD_MASK;
800 primary_is_modified = 1;
801 break;
802 }
803 default: {
804 return GPT_ERROR_INVALID_UPDATE_TYPE;
805 }
806 }
807
808 if (primary_is_modified) {
809 /* Claim only primary is valid so that secondary is overwritten. */
810 RepairEntries(gpt, MASK_PRIMARY);
811 /* Actually two entries are dirty now.
812 * Also two headers are dirty because entries_crc32 has been updated. */
813 gpt->modified |= (GPT_MODIFIED_HEADER1 | GPT_MODIFIED_ENTRIES1 |
814 GPT_MODIFIED_HEADER2 | GPT_MODIFIED_ENTRIES2);
815 UpdateCrc(gpt);
816 }
817
Louis Yung-Chieh Lo37f6b552010-04-22 21:22:22 -0700818 return GPT_SUCCESS;
Louis Yung-Chieh Loe1a25ab2010-04-20 10:52:41 -0700819}