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