blob: 9bcfbd81192d25180bab060e72440e6631a28823 [file] [log] [blame]
Randall Spanglercefe12c2013-01-30 12:52:02 -08001/* Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
Randall Spangler3dcf9dc2010-06-02 12:46:17 -07002 * Use of this source code is governed by a BSD-style license that can be
3 * found in the LICENSE file.
4 */
5
Bill Richardson0c3ba242013-03-29 11:09:30 -07006#include "sysincludes.h"
7
Randall Spangler3dcf9dc2010-06-02 12:46:17 -07008#include "cgptlib.h"
9#include "cgptlib_internal.h"
10#include "crc32.h"
11#include "gpt.h"
Dan Ehrenberg7c2beb02014-10-21 16:15:54 -070012#include "gpt_misc.h"
Randall Spangler3dcf9dc2010-06-02 12:46:17 -070013#include "utility.h"
14
15
Randall Spanglercefe12c2013-01-30 12:52:02 -080016int CheckParameters(GptData *gpt)
Vadim Bendebury65d3c272012-09-24 17:41:18 -070017{
Randall Spanglercefe12c2013-01-30 12:52:02 -080018 /* Currently, we only support 512-byte sectors. */
19 if (gpt->sector_bytes != 512)
20 return GPT_ERROR_INVALID_SECTOR_SIZE;
Vadim Bendebury65d3c272012-09-24 17:41:18 -070021
Randall Spanglercefe12c2013-01-30 12:52:02 -080022 /*
Nam T. Nguyen6ee52d92014-10-24 13:20:39 -070023 * gpt_drive_sectors should be reasonable. It cannot be unset, and it cannot
24 * differ from drive_sectors if the GPT structs are stored on same device.
25 */
26 if (gpt->gpt_drive_sectors == 0 ||
27 (gpt->stored_on_device == GPT_STORED_ON_DEVICE &&
28 gpt->gpt_drive_sectors != gpt->drive_sectors)) {
29 return GPT_ERROR_INVALID_SECTOR_NUMBER;
30 }
31
32 /*
Randall Spanglercefe12c2013-01-30 12:52:02 -080033 * Sector count of a drive should be reasonable. If the given value is
34 * too small to contain basic GPT structure (PMBR + Headers + Entries),
35 * the value is wrong.
36 */
Nam T. Nguyenab899592014-11-13 19:30:46 -080037 if (gpt->gpt_drive_sectors < (1 + 2 * (1 + GPT_ENTRIES_SECTORS)))
Randall Spanglercefe12c2013-01-30 12:52:02 -080038 return GPT_ERROR_INVALID_SECTOR_NUMBER;
Vadim Bendebury65d3c272012-09-24 17:41:18 -070039
Randall Spanglercefe12c2013-01-30 12:52:02 -080040 return GPT_SUCCESS;
41}
Vadim Bendebury65d3c272012-09-24 17:41:18 -070042
Randall Spanglercefe12c2013-01-30 12:52:02 -080043uint32_t HeaderCrc(GptHeader *h)
44{
45 uint32_t crc32, original_crc32;
Vadim Bendebury65d3c272012-09-24 17:41:18 -070046
Randall Spanglercefe12c2013-01-30 12:52:02 -080047 /* Original CRC is calculated with the CRC field 0. */
48 original_crc32 = h->header_crc32;
49 h->header_crc32 = 0;
50 crc32 = Crc32((const uint8_t *)h, h->size);
51 h->header_crc32 = original_crc32;
Vadim Bendebury65d3c272012-09-24 17:41:18 -070052
Randall Spanglercefe12c2013-01-30 12:52:02 -080053 return crc32;
54}
Vadim Bendebury65d3c272012-09-24 17:41:18 -070055
Nam T. Nguyen6ee52d92014-10-24 13:20:39 -070056int CheckHeader(GptHeader *h, int is_secondary, uint64_t drive_sectors,
Dan Ehrenberga524a3a2014-11-06 16:22:24 -080057 uint64_t gpt_drive_sectors, uint8_t stored_on_device)
Randall Spanglercefe12c2013-01-30 12:52:02 -080058{
59 if (!h)
60 return 1;
Vadim Bendebury65d3c272012-09-24 17:41:18 -070061
Randall Spanglercefe12c2013-01-30 12:52:02 -080062 /*
63 * Make sure we're looking at a header of reasonable size before
64 * attempting to calculate CRC.
65 */
66 if (Memcmp(h->signature, GPT_HEADER_SIGNATURE,
67 GPT_HEADER_SIGNATURE_SIZE) &&
68 Memcmp(h->signature, GPT_HEADER_SIGNATURE2,
69 GPT_HEADER_SIGNATURE_SIZE))
70 return 1;
71 if (h->revision != GPT_HEADER_REVISION)
72 return 1;
73 if (h->size < MIN_SIZE_OF_HEADER || h->size > MAX_SIZE_OF_HEADER)
74 return 1;
Vadim Bendebury65d3c272012-09-24 17:41:18 -070075
Randall Spanglercefe12c2013-01-30 12:52:02 -080076 /* Check CRC before looking at remaining fields */
77 if (HeaderCrc(h) != h->header_crc32)
78 return 1;
Vadim Bendebury65d3c272012-09-24 17:41:18 -070079
Randall Spanglercefe12c2013-01-30 12:52:02 -080080 /* Reserved fields must be zero. */
81 if (h->reserved_zero)
82 return 1;
Vadim Bendebury65d3c272012-09-24 17:41:18 -070083
Randall Spanglercefe12c2013-01-30 12:52:02 -080084 /* Could check that padding is zero, but that doesn't matter to us. */
Vadim Bendebury65d3c272012-09-24 17:41:18 -070085
Randall Spanglercefe12c2013-01-30 12:52:02 -080086 /*
87 * If entry size is different than our struct, we won't be able to
88 * parse it. Technically, any size 2^N where N>=7 is valid.
89 */
90 if (h->size_of_entry != sizeof(GptEntry))
91 return 1;
92 if ((h->number_of_entries < MIN_NUMBER_OF_ENTRIES) ||
93 (h->number_of_entries > MAX_NUMBER_OF_ENTRIES) ||
Nam T. Nguyen6ee52d92014-10-24 13:20:39 -070094 (stored_on_device == GPT_STORED_ON_DEVICE &&
95 h->number_of_entries * h->size_of_entry != TOTAL_ENTRIES_SIZE))
Randall Spanglercefe12c2013-01-30 12:52:02 -080096 return 1;
Vadim Bendebury65d3c272012-09-24 17:41:18 -070097
Randall Spanglercefe12c2013-01-30 12:52:02 -080098 /*
99 * Check locations for the header and its entries. The primary
100 * immediately follows the PMBR, and is followed by its entries. The
101 * secondary is at the end of the drive, preceded by its entries.
102 */
103 if (is_secondary) {
Dan Ehrenberga524a3a2014-11-06 16:22:24 -0800104 if (h->my_lba != gpt_drive_sectors - GPT_HEADER_SECTORS)
Randall Spanglercefe12c2013-01-30 12:52:02 -0800105 return 1;
106 if (h->entries_lba != h->my_lba - GPT_ENTRIES_SECTORS)
107 return 1;
108 } else {
Nam T. Nguyen88458d92014-08-28 10:58:47 -0700109 if (h->my_lba != GPT_PMBR_SECTORS)
Randall Spanglercefe12c2013-01-30 12:52:02 -0800110 return 1;
Nam T. Nguyena2d72f72014-08-22 15:01:38 -0700111 if (h->entries_lba < h->my_lba + 1)
Randall Spanglercefe12c2013-01-30 12:52:02 -0800112 return 1;
113 }
114
Nam T. Nguyen6ee52d92014-10-24 13:20:39 -0700115 /* FirstUsableLBA <= LastUsableLBA. */
116 if (h->first_usable_lba > h->last_usable_lba)
117 return 1;
118
119 if (stored_on_device != GPT_STORED_ON_DEVICE) {
120 if (h->last_usable_lba >= drive_sectors) {
121 return 1;
122 }
123 return 0;
124 }
125
Randall Spanglercefe12c2013-01-30 12:52:02 -0800126 /*
127 * FirstUsableLBA must be after the end of the primary GPT table array.
128 * LastUsableLBA must be before the start of the secondary GPT table
Nam T. Nguyen6ee52d92014-10-24 13:20:39 -0700129 * array.
Randall Spanglercefe12c2013-01-30 12:52:02 -0800130 */
Nam T. Nguyena2d72f72014-08-22 15:01:38 -0700131 /* TODO(namnguyen): Also check for padding between header & entries. */
Randall Spanglercefe12c2013-01-30 12:52:02 -0800132 if (h->first_usable_lba < 2 + GPT_ENTRIES_SECTORS)
133 return 1;
134 if (h->last_usable_lba >= drive_sectors - 1 - GPT_ENTRIES_SECTORS)
135 return 1;
Randall Spanglercefe12c2013-01-30 12:52:02 -0800136
137 /* Success */
138 return 0;
139}
140
141int IsUnusedEntry(const GptEntry *e)
142{
143 static Guid zero = {{{0, 0, 0, 0, 0, {0, 0, 0, 0, 0, 0}}}};
144 return !Memcmp(&zero, (const uint8_t*)(&e->type), sizeof(zero));
145}
146
147int IsKernelEntry(const GptEntry *e)
148{
149 static Guid chromeos_kernel = GPT_ENT_TYPE_CHROMEOS_KERNEL;
150 return !Memcmp(&e->type, &chromeos_kernel, sizeof(Guid));
151}
152
153int CheckEntries(GptEntry *entries, GptHeader *h)
154{
Nam T. Nguyen5ce83252014-10-30 15:09:43 -0700155 if (!entries)
156 return GPT_ERROR_INVALID_ENTRIES;
Randall Spanglercefe12c2013-01-30 12:52:02 -0800157 GptEntry *entry;
158 uint32_t crc32;
159 uint32_t i;
160
161 /* Check CRC before examining entries. */
162 crc32 = Crc32((const uint8_t *)entries,
163 h->size_of_entry * h->number_of_entries);
164 if (crc32 != h->entries_crc32)
165 return GPT_ERROR_CRC_CORRUPTED;
166
167 /* Check all entries. */
168 for (i = 0, entry = entries; i < h->number_of_entries; i++, entry++) {
169 GptEntry *e2;
170 uint32_t i2;
171
172 if (IsUnusedEntry(entry))
173 continue;
174
175 /* Entry must be in valid region. */
176 if ((entry->starting_lba < h->first_usable_lba) ||
177 (entry->ending_lba > h->last_usable_lba) ||
178 (entry->ending_lba < entry->starting_lba))
179 return GPT_ERROR_OUT_OF_REGION;
180
181 /* Entry must not overlap other entries. */
182 for (i2 = 0, e2 = entries; i2 < h->number_of_entries;
183 i2++, e2++) {
184 if (i2 == i || IsUnusedEntry(e2))
185 continue;
186
187 if ((entry->starting_lba >= e2->starting_lba) &&
188 (entry->starting_lba <= e2->ending_lba))
189 return GPT_ERROR_START_LBA_OVERLAP;
190 if ((entry->ending_lba >= e2->starting_lba) &&
191 (entry->ending_lba <= e2->ending_lba))
192 return GPT_ERROR_END_LBA_OVERLAP;
193
194 /* UniqueGuid field must be unique. */
195 if (0 == Memcmp(&entry->unique, &e2->unique,
196 sizeof(Guid)))
197 return GPT_ERROR_DUP_GUID;
198 }
199 }
200
201 /* Success */
202 return 0;
203}
204
205int HeaderFieldsSame(GptHeader *h1, GptHeader *h2)
206{
207 if (Memcmp(h1->signature, h2->signature, sizeof(h1->signature)))
208 return 1;
209 if (h1->revision != h2->revision)
210 return 1;
211 if (h1->size != h2->size)
212 return 1;
213 if (h1->reserved_zero != h2->reserved_zero)
214 return 1;
215 if (h1->first_usable_lba != h2->first_usable_lba)
216 return 1;
217 if (h1->last_usable_lba != h2->last_usable_lba)
218 return 1;
219 if (Memcmp(&h1->disk_uuid, &h2->disk_uuid, sizeof(Guid)))
220 return 1;
221 if (h1->number_of_entries != h2->number_of_entries)
222 return 1;
223 if (h1->size_of_entry != h2->size_of_entry)
224 return 1;
225 if (h1->entries_crc32 != h2->entries_crc32)
226 return 1;
227
228 return 0;
229}
230
231int GptSanityCheck(GptData *gpt)
232{
233 int retval;
234 GptHeader *header1 = (GptHeader *)(gpt->primary_header);
235 GptHeader *header2 = (GptHeader *)(gpt->secondary_header);
236 GptEntry *entries1 = (GptEntry *)(gpt->primary_entries);
237 GptEntry *entries2 = (GptEntry *)(gpt->secondary_entries);
238 GptHeader *goodhdr = NULL;
239
240 gpt->valid_headers = 0;
241 gpt->valid_entries = 0;
242
243 retval = CheckParameters(gpt);
244 if (retval != GPT_SUCCESS)
245 return retval;
246
247 /* Check both headers; we need at least one valid header. */
Dan Ehrenberga524a3a2014-11-06 16:22:24 -0800248 if (0 == CheckHeader(header1, 0, gpt->drive_sectors,
249 gpt->gpt_drive_sectors, gpt->stored_on_device)) {
Randall Spanglercefe12c2013-01-30 12:52:02 -0800250 gpt->valid_headers |= MASK_PRIMARY;
251 goodhdr = header1;
252 }
Dan Ehrenberga524a3a2014-11-06 16:22:24 -0800253 if (0 == CheckHeader(header2, 1, gpt->drive_sectors,
254 gpt->gpt_drive_sectors, gpt->stored_on_device)) {
Randall Spanglercefe12c2013-01-30 12:52:02 -0800255 gpt->valid_headers |= MASK_SECONDARY;
256 if (!goodhdr)
257 goodhdr = header2;
258 }
259
260 if (!gpt->valid_headers)
261 return GPT_ERROR_INVALID_HEADERS;
262
263 /*
264 * Check if entries are valid.
265 *
266 * Note that we use the same header in both checks. This way we'll
267 * catch the case where (header1,entries1) and (header2,entries2) are
268 * both valid, but (entries1 != entries2).
269 */
270 if (0 == CheckEntries(entries1, goodhdr))
271 gpt->valid_entries |= MASK_PRIMARY;
272 if (0 == CheckEntries(entries2, goodhdr))
273 gpt->valid_entries |= MASK_SECONDARY;
274
275 /*
276 * If both headers are good but neither entries were good, check the
277 * entries with the secondary header.
278 */
279 if (MASK_BOTH == gpt->valid_headers && !gpt->valid_entries) {
280 if (0 == CheckEntries(entries1, header2))
281 gpt->valid_entries |= MASK_PRIMARY;
282 if (0 == CheckEntries(entries2, header2))
283 gpt->valid_entries |= MASK_SECONDARY;
284 if (gpt->valid_entries) {
285 /*
286 * Sure enough, header2 had a good CRC for one of the
287 * entries. Mark header1 invalid, so we'll update its
288 * entries CRC.
289 */
290 gpt->valid_headers &= ~MASK_PRIMARY;
291 goodhdr = header2;
292 }
293 }
294
295 if (!gpt->valid_entries)
296 return GPT_ERROR_INVALID_ENTRIES;
297
298 /*
299 * Now that we've determined which header contains a good CRC for
300 * the entries, make sure the headers are otherwise identical.
301 */
302 if (MASK_BOTH == gpt->valid_headers &&
303 0 != HeaderFieldsSame(header1, header2))
304 gpt->valid_headers &= ~MASK_SECONDARY;
305
306 return GPT_SUCCESS;
307}
308
309void GptRepair(GptData *gpt)
310{
311 GptHeader *header1 = (GptHeader *)(gpt->primary_header);
312 GptHeader *header2 = (GptHeader *)(gpt->secondary_header);
313 GptEntry *entries1 = (GptEntry *)(gpt->primary_entries);
314 GptEntry *entries2 = (GptEntry *)(gpt->secondary_entries);
315 int entries_size;
316
317 /* Need at least one good header and one good set of entries. */
318 if (MASK_NONE == gpt->valid_headers || MASK_NONE == gpt->valid_entries)
319 return;
320
321 /* Repair headers if necessary */
322 if (MASK_PRIMARY == gpt->valid_headers) {
323 /* Primary is good, secondary is bad */
324 Memcpy(header2, header1, sizeof(GptHeader));
Dan Ehrenberga524a3a2014-11-06 16:22:24 -0800325 header2->my_lba = gpt->gpt_drive_sectors - GPT_HEADER_SECTORS;
Nam T. Nguyen88458d92014-08-28 10:58:47 -0700326 header2->alternate_lba = GPT_PMBR_SECTORS; /* Second sector. */
Randall Spanglercefe12c2013-01-30 12:52:02 -0800327 header2->entries_lba = header2->my_lba - GPT_ENTRIES_SECTORS;
328 header2->header_crc32 = HeaderCrc(header2);
329 gpt->modified |= GPT_MODIFIED_HEADER2;
330 }
331 else if (MASK_SECONDARY == gpt->valid_headers) {
332 /* Secondary is good, primary is bad */
333 Memcpy(header1, header2, sizeof(GptHeader));
Nam T. Nguyen88458d92014-08-28 10:58:47 -0700334 header1->my_lba = GPT_PMBR_SECTORS; /* Second sector. */
335 header1->alternate_lba = gpt->drive_sectors - GPT_HEADER_SECTORS;
Nam T. Nguyena2d72f72014-08-22 15:01:38 -0700336 /* TODO (namnguyen): Preserve (header, entries) padding. */
Randall Spanglercefe12c2013-01-30 12:52:02 -0800337 header1->entries_lba = header1->my_lba + 1;
338 header1->header_crc32 = HeaderCrc(header1);
339 gpt->modified |= GPT_MODIFIED_HEADER1;
340 }
341 gpt->valid_headers = MASK_BOTH;
342
343 /* Repair entries if necessary */
344 entries_size = header1->size_of_entry * header1->number_of_entries;
345 if (MASK_PRIMARY == gpt->valid_entries) {
346 /* Primary is good, secondary is bad */
347 Memcpy(entries2, entries1, entries_size);
348 gpt->modified |= GPT_MODIFIED_ENTRIES2;
349 }
350 else if (MASK_SECONDARY == gpt->valid_entries) {
351 /* Secondary is good, primary is bad */
352 Memcpy(entries1, entries2, entries_size);
353 gpt->modified |= GPT_MODIFIED_ENTRIES1;
354 }
355 gpt->valid_entries = MASK_BOTH;
356}
357
358int GetEntrySuccessful(const GptEntry *e)
359{
360 return (e->attrs.fields.gpt_att & CGPT_ATTRIBUTE_SUCCESSFUL_MASK) >>
361 CGPT_ATTRIBUTE_SUCCESSFUL_OFFSET;
362}
363
364int GetEntryPriority(const GptEntry *e)
365{
366 return (e->attrs.fields.gpt_att & CGPT_ATTRIBUTE_PRIORITY_MASK) >>
367 CGPT_ATTRIBUTE_PRIORITY_OFFSET;
368}
369
370int GetEntryTries(const GptEntry *e)
371{
372 return (e->attrs.fields.gpt_att & CGPT_ATTRIBUTE_TRIES_MASK) >>
373 CGPT_ATTRIBUTE_TRIES_OFFSET;
374}
375
376void SetEntrySuccessful(GptEntry *e, int successful)
377{
378 if (successful)
379 e->attrs.fields.gpt_att |= CGPT_ATTRIBUTE_SUCCESSFUL_MASK;
380 else
381 e->attrs.fields.gpt_att &= ~CGPT_ATTRIBUTE_SUCCESSFUL_MASK;
382}
383
384void SetEntryPriority(GptEntry *e, int priority)
385{
386 e->attrs.fields.gpt_att &= ~CGPT_ATTRIBUTE_PRIORITY_MASK;
387 e->attrs.fields.gpt_att |=
388 (priority << CGPT_ATTRIBUTE_PRIORITY_OFFSET) &
389 CGPT_ATTRIBUTE_PRIORITY_MASK;
390}
391
392void SetEntryTries(GptEntry *e, int tries)
393{
394 e->attrs.fields.gpt_att &= ~CGPT_ATTRIBUTE_TRIES_MASK;
395 e->attrs.fields.gpt_att |= (tries << CGPT_ATTRIBUTE_TRIES_OFFSET) &
396 CGPT_ATTRIBUTE_TRIES_MASK;
397}
398
399void GetCurrentKernelUniqueGuid(GptData *gpt, void *dest)
400{
401 GptEntry *entries = (GptEntry *)gpt->primary_entries;
402 GptEntry *e = entries + gpt->current_kernel;
403 Memcpy(dest, &e->unique, sizeof(Guid));
404}
405
Albert Chaulk5c9e4532013-03-20 16:03:49 -0700406void GptModified(GptData *gpt) {
407 GptHeader *header = (GptHeader *)gpt->primary_header;
408
409 /* Update the CRCs */
410 header->entries_crc32 = Crc32(gpt->primary_entries,
411 header->size_of_entry *
412 header->number_of_entries);
413 header->header_crc32 = HeaderCrc(header);
414 gpt->modified |= GPT_MODIFIED_HEADER1 | GPT_MODIFIED_ENTRIES1;
415
416 /*
417 * Use the repair function to update the other copy of the GPT. This
418 * is a tad inefficient, but is much faster than the disk I/O to update
419 * the GPT on disk so it doesn't matter.
420 */
421 gpt->valid_headers = MASK_PRIMARY;
422 gpt->valid_entries = MASK_PRIMARY;
423 GptRepair(gpt);
424}
425
426
Randall Spanglercefe12c2013-01-30 12:52:02 -0800427const char *GptErrorText(int error_code)
428{
429 switch(error_code) {
430 case GPT_SUCCESS:
431 return "none";
432
433 case GPT_ERROR_NO_VALID_KERNEL:
434 return "Invalid kernel";
435
436 case GPT_ERROR_INVALID_HEADERS:
437 return "Invalid headers";
438
439 case GPT_ERROR_INVALID_ENTRIES:
440 return "Invalid entries";
441
442 case GPT_ERROR_INVALID_SECTOR_SIZE:
443 return "Invalid sector size";
444
445 case GPT_ERROR_INVALID_SECTOR_NUMBER:
446 return "Invalid sector number";
447
448 case GPT_ERROR_INVALID_UPDATE_TYPE:
449 return "Invalid update type";
450
451 case GPT_ERROR_CRC_CORRUPTED:
452 return "Entries' crc corrupted";
453
454 case GPT_ERROR_OUT_OF_REGION:
455 return "Entry outside of valid region";
456
457 case GPT_ERROR_START_LBA_OVERLAP:
458 return "Starting LBA overlaps";
459
460 case GPT_ERROR_END_LBA_OVERLAP:
461 return "Ending LBA overlaps";
462
463 case GPT_ERROR_DUP_GUID:
464 return "Duplicated GUID";
465
Albert Chaulk5c9e4532013-03-20 16:03:49 -0700466 case GPT_ERROR_INVALID_FLASH_GEOMETRY:
467 return "Invalid flash geometry";
468
469 case GPT_ERROR_NO_SUCH_ENTRY:
470 return "No entry found";
471
Randall Spanglercefe12c2013-01-30 12:52:02 -0800472 default:
473 break;
474 };
475 return "Unknown";
Vadim Bendebury65d3c272012-09-24 17:41:18 -0700476}