blob: bfc138647e4e219ac1a53f6af06c51722dbce53e [file] [log] [blame]
Bill Richardsonf1372d92010-06-11 09:15:55 -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 * Utility for ChromeOS-specific GPT partitions, Please see corresponding .c
6 * files for more details.
7 */
8
Bill Richardsonf1372d92010-06-11 09:15:55 -07009#include <errno.h>
10#include <fcntl.h>
11#include <getopt.h>
Bill Richardsonc4e92af2010-10-12 07:33:15 -070012#include <stdarg.h>
Bill Richardsonf1372d92010-06-11 09:15:55 -070013#include <stdint.h>
14#include <stdio.h>
15#include <stdlib.h>
16#include <string.h>
17#include <sys/ioctl.h>
18#include <sys/mount.h>
19#include <sys/stat.h>
20#include <sys/types.h>
21#include <unistd.h>
Bill Richardsonf1372d92010-06-11 09:15:55 -070022
Bill Richardson0c3ba242013-03-29 11:09:30 -070023#include "cgpt.h"
Bill Richardsonf1372d92010-06-11 09:15:55 -070024#include "cgptlib_internal.h"
25#include "crc32.h"
Albert Chaulk534723a2013-03-20 14:46:50 -070026#include "flash_ts.h"
Albert Chaulk44643542013-07-09 14:38:08 -070027#include "flash_ts_api.h"
Bill Richardson0c3ba242013-03-29 11:09:30 -070028#include "vboot_host.h"
Bill Richardsonf1372d92010-06-11 09:15:55 -070029
Albert Chaulkd41000e2013-07-19 11:12:42 -070030struct nand_layout nand;
31
32void EnableNandImage(int bytes_per_page, int pages_per_block,
33 int fts_block_offset, int fts_block_size) {
34 nand.enabled = 1;
35 nand.bytes_per_page = bytes_per_page;
36 nand.pages_per_block = pages_per_block;
37 nand.fts_block_offset = fts_block_offset;
38 nand.fts_block_size = fts_block_size;
39}
Albert Chaulka75071c2013-03-29 15:02:38 -070040
Bill Richardsonf1372d92010-06-11 09:15:55 -070041void Error(const char *format, ...) {
42 va_list ap;
43 va_start(ap, format);
Bill Richardson4cb54972014-06-20 14:33:00 -070044 fprintf(stderr, "ERROR: ");
Bill Richardsonf1372d92010-06-11 09:15:55 -070045 vfprintf(stderr, format, ap);
46 va_end(ap);
47}
48
Bill Richardsonf1372d92010-06-11 09:15:55 -070049int CheckValid(const struct drive *drive) {
50 if ((drive->gpt.valid_headers != MASK_BOTH) ||
51 (drive->gpt.valid_entries != MASK_BOTH)) {
Bill Richardson4cb54972014-06-20 14:33:00 -070052 fprintf(stderr,
53 "\nWARNING: one of the GPT header/entries is invalid\n\n");
Bill Richardsonf1372d92010-06-11 09:15:55 -070054 return CGPT_FAILED;
55 }
56 return CGPT_OK;
57}
58
Albert Chaulk534723a2013-03-20 14:46:50 -070059int Load(struct drive *drive, uint8_t **buf,
Bill Richardsonf1372d92010-06-11 09:15:55 -070060 const uint64_t sector,
61 const uint64_t sector_bytes,
62 const uint64_t sector_count) {
63 int count; /* byte count to read */
64 int nread;
Albert Chaulk534723a2013-03-20 14:46:50 -070065 int fd = drive->fd;
Bill Richardsonf1372d92010-06-11 09:15:55 -070066
Bill Richardsonc4e92af2010-10-12 07:33:15 -070067 require(buf);
68 if (!sector_count || !sector_bytes) {
69 Error("%s() failed at line %d: sector_count=%d, sector_bytes=%d\n",
70 __FUNCTION__, __LINE__, sector_count, sector_bytes);
71 return CGPT_FAILED;
72 }
73 /* Make sure that sector_bytes * sector_count doesn't roll over. */
74 if (sector_bytes > (UINT64_MAX / sector_count)) {
75 Error("%s() failed at line %d: sector_count=%d, sector_bytes=%d\n",
76 __FUNCTION__, __LINE__, sector_count, sector_bytes);
77 return CGPT_FAILED;
78 }
Bill Richardsonf1372d92010-06-11 09:15:55 -070079 count = sector_bytes * sector_count;
80 *buf = malloc(count);
Bill Richardsonc4e92af2010-10-12 07:33:15 -070081 require(*buf);
Bill Richardsonf1372d92010-06-11 09:15:55 -070082
Bill Richardsonc4e92af2010-10-12 07:33:15 -070083 if (-1 == lseek(fd, sector * sector_bytes, SEEK_SET)) {
84 Error("Can't lseek: %s\n", strerror(errno));
Bill Richardsonf1372d92010-06-11 09:15:55 -070085 goto error_free;
Bill Richardsonc4e92af2010-10-12 07:33:15 -070086 }
Bill Richardsonf1372d92010-06-11 09:15:55 -070087
88 nread = read(fd, *buf, count);
Bill Richardsonc4e92af2010-10-12 07:33:15 -070089 if (nread < count) {
90 Error("Can't read enough: %d, not %d\n", nread, count);
Bill Richardsonf1372d92010-06-11 09:15:55 -070091 goto error_free;
Bill Richardsonc4e92af2010-10-12 07:33:15 -070092 }
Bill Richardsonf1372d92010-06-11 09:15:55 -070093
94 return CGPT_OK;
95
96error_free:
97 free(*buf);
98 *buf = 0;
99 return CGPT_FAILED;
100}
101
102
103int ReadPMBR(struct drive *drive) {
104 if (-1 == lseek(drive->fd, 0, SEEK_SET))
105 return CGPT_FAILED;
106
107 int nread = read(drive->fd, &drive->pmbr, sizeof(struct pmbr));
108 if (nread != sizeof(struct pmbr))
109 return CGPT_FAILED;
110
111 return CGPT_OK;
112}
113
114int WritePMBR(struct drive *drive) {
115 if (-1 == lseek(drive->fd, 0, SEEK_SET))
116 return CGPT_FAILED;
117
118 int nwrote = write(drive->fd, &drive->pmbr, sizeof(struct pmbr));
119 if (nwrote != sizeof(struct pmbr))
120 return CGPT_FAILED;
121
122 return CGPT_OK;
123}
124
Albert Chaulk534723a2013-03-20 14:46:50 -0700125int Save(struct drive *drive, const uint8_t *buf,
Bill Richardsonf1372d92010-06-11 09:15:55 -0700126 const uint64_t sector,
127 const uint64_t sector_bytes,
128 const uint64_t sector_count) {
129 int count; /* byte count to write */
130 int nwrote;
Albert Chaulk534723a2013-03-20 14:46:50 -0700131 int fd = drive->fd;
Bill Richardsonf1372d92010-06-11 09:15:55 -0700132
Bill Richardsonc4e92af2010-10-12 07:33:15 -0700133 require(buf);
Bill Richardsonf1372d92010-06-11 09:15:55 -0700134 count = sector_bytes * sector_count;
135
136 if (-1 == lseek(fd, sector * sector_bytes, SEEK_SET))
137 return CGPT_FAILED;
138
139 nwrote = write(fd, buf, count);
140 if (nwrote < count)
141 return CGPT_FAILED;
142
143 return CGPT_OK;
144}
145
Albert Chaulk534723a2013-03-20 14:46:50 -0700146static int get_hex_char_value(char ch) {
147 if (ch >= '0' && ch <= '9') {
148 return ch - '0';
149 }
150 if (ch >= 'a' && ch <= 'f') {
151 return ch - 'a' + 10;
152 }
153 if (ch >= 'A' && ch <= 'F') {
154 return ch - 'A' + 10;
155 }
156 return -1;
157}
158
Bill Richardson18e03702014-06-23 17:48:33 -0700159static int TryInitMtd(const char *dev) {
Albert Chaulk44643542013-07-09 14:38:08 -0700160 static int already_inited = 0;
Albert Chaulkd41000e2013-07-19 11:12:42 -0700161 if (already_inited)
162 return nand.use_host_ioctl;
Albert Chaulk44643542013-07-09 14:38:08 -0700163
164 already_inited = 1;
165
166 /* If we're running on the live system, we can just use /dev/fts and not
Albert Chaulkd41000e2013-07-19 11:12:42 -0700167 * actually need the specific parameters. This needs to be accessed via
168 * ioctl and not normal I/O.
Albert Chaulk44643542013-07-09 14:38:08 -0700169 */
Albert Chaulkd41000e2013-07-19 11:12:42 -0700170 if (!strcmp(dev, FTS_DEVICE) && !access(FTS_DEVICE, R_OK | W_OK)) {
Albert Chaulk44643542013-07-09 14:38:08 -0700171 nand.enabled = 1;
172 nand.use_host_ioctl = 1;
Albert Chaulkd41000e2013-07-19 11:12:42 -0700173 return 1;
Albert Chaulk44643542013-07-09 14:38:08 -0700174 }
Albert Chaulkd41000e2013-07-19 11:12:42 -0700175 return 0;
Albert Chaulk44643542013-07-09 14:38:08 -0700176}
177
Albert Chaulk534723a2013-03-20 14:46:50 -0700178int FlashGet(const char *key, uint8_t *data, uint32_t *bufsz) {
179 char *hex = (char*)malloc(*bufsz * 2);
180 char *read;
181 uint32_t written = 0;
182
Albert Chaulk44643542013-07-09 14:38:08 -0700183 if (nand.use_host_ioctl) {
184 struct flash_ts_io_req req;
185 strncpy(req.key, key, sizeof(req.key));
Albert Chaulkd41000e2013-07-19 11:12:42 -0700186 int fd = open(FTS_DEVICE, O_RDWR);
Albert Chaulk44643542013-07-09 14:38:08 -0700187 if (fd < 0)
188 return -1;
189 if (ioctl(fd, FLASH_TS_IO_GET, &req))
190 return -1;
191 strncpy(hex, req.val, *bufsz * 2);
192 close(fd);
193 } else {
194 flash_ts_get(key, hex, *bufsz * 2);
195 }
Albert Chaulk534723a2013-03-20 14:46:50 -0700196
197 /* Hex -> binary */
198 for (read = hex; read < hex + *bufsz * 2 && *read != '\0'; read += 2) {
199 int c0, c1;
200 c0 = get_hex_char_value(read[0]);
201 c1 = get_hex_char_value(read[1]);
202 if (c0 < 0 || c1 < 0) {
203 free(hex);
204 return -1;
205 }
206
207 data[written++] = (c0 << 4) + c1;
208 }
209 *bufsz = written;
210 free(hex);
211 return 0;
212}
213
214int FlashSet(const char *key, const uint8_t *data, uint32_t bufsz) {
215 char *hex = (char*)malloc(bufsz * 2 + 1);
216 const char *hex_chars = "0123456789ABCDEF";
217 int ret;
218 uint32_t i;
219
220 /* Binary -> hex, we need some encoding because FTS only stores C strings */
221 for (i = 0; i < bufsz; i++) {
222 hex[i * 2] = hex_chars[data[i] >> 4];
223 hex[i * 2 + 1] = hex_chars[data[i] & 0xF];
224 }
225 /* Buffer must be NUL-terminated. */
226 hex[bufsz * 2] = '\0';
Albert Chaulk44643542013-07-09 14:38:08 -0700227 if (nand.use_host_ioctl) {
228 struct flash_ts_io_req req;
229 strncpy(req.key, key, sizeof(req.key));
230 strncpy(req.val, hex, sizeof(req.val));
231 free(hex);
Albert Chaulkd41000e2013-07-19 11:12:42 -0700232 int fd = open(FTS_DEVICE, O_RDWR);
Albert Chaulk44643542013-07-09 14:38:08 -0700233 if (fd < 0)
234 return -1;
235 if (ioctl(fd, FLASH_TS_IO_SET, &req))
236 return -1;
237 close(fd);
238 return 0;
239 }
Albert Chaulk534723a2013-03-20 14:46:50 -0700240 ret = flash_ts_set(key, hex);
241 free(hex);
242 return ret;
243}
244
245int MtdLoad(struct drive *drive, int sector_bytes) {
246 int ret;
Albert Chaulk534723a2013-03-20 14:46:50 -0700247 uint32_t sz;
248 MtdData *mtd = &drive->mtd;
249
250 mtd->sector_bytes = sector_bytes;
Albert Chaulk92f22e72013-04-02 13:20:52 -0700251 mtd->drive_sectors = drive->size / mtd->sector_bytes;
Albert Chaulk534723a2013-03-20 14:46:50 -0700252
Albert Chaulk44643542013-07-09 14:38:08 -0700253 if (!nand.use_host_ioctl) {
254 ret = flash_ts_init(mtd->fts_block_offset,
255 mtd->fts_block_size,
256 mtd->flash_page_bytes,
257 mtd->flash_block_bytes,
258 mtd->sector_bytes, /* Needed for Load() and Save() */
259 drive);
Albert Chaulkd41000e2013-07-19 11:12:42 -0700260 if (ret)
261 return ret;
Albert Chaulk44643542013-07-09 14:38:08 -0700262 }
Albert Chaulk534723a2013-03-20 14:46:50 -0700263
264 memset(&mtd->primary, 0, sizeof(mtd->primary));
265 sz = sizeof(mtd->primary);
266 ret = FlashGet(MTD_DRIVE_SIGNATURE, (uint8_t *)&mtd->primary, &sz);
267 if (ret)
268 return ret;
269
270 /* Read less than expected */
271 if (sz < MTD_DRIVE_V1_SIZE)
Albert Chaulk92f22e72013-04-02 13:20:52 -0700272 memset(&mtd->primary, 0, sizeof(mtd->primary));
Albert Chaulk534723a2013-03-20 14:46:50 -0700273
Albert Chaulkd41000e2013-07-19 11:12:42 -0700274 if (nand.use_host_ioctl) {
275 /* If we are using /dev/fts, we can't stat() the size, so re-use
276 * our internal value to set it.
277 */
278 drive->size = mtd->primary.last_offset + 1;
279 mtd->drive_sectors = drive->size / mtd->sector_bytes;
280 }
281
Albert Chaulk534723a2013-03-20 14:46:50 -0700282 mtd->current_kernel = -1;
283 mtd->current_priority = 0;
284 mtd->modified = 0;
285 return 0;
286}
287
288int MtdSave(struct drive *drive) {
289 MtdData *mtd = &drive->mtd;
290
Albert Chaulk92f22e72013-04-02 13:20:52 -0700291 if (!mtd->modified)
292 return 0;
293
Albert Chaulk534723a2013-03-20 14:46:50 -0700294 mtd->primary.crc32 = 0;
295 mtd->primary.crc32 = Crc32(&mtd->primary, MTD_DRIVE_V1_SIZE);
296
297 return FlashSet(MTD_DRIVE_SIGNATURE, (uint8_t *)&mtd->primary,
298 sizeof(mtd->primary));
299}
300
Bill Richardson18e03702014-06-23 17:48:33 -0700301static int GptLoad(struct drive *drive, uint32_t sector_bytes) {
Albert Chaulk534723a2013-03-20 14:46:50 -0700302 drive->gpt.sector_bytes = sector_bytes;
303 if (drive->size % drive->gpt.sector_bytes) {
304 Error("Media size (%llu) is not a multiple of sector size(%d)\n",
305 (long long unsigned int)drive->size, drive->gpt.sector_bytes);
306 return -1;
307 }
308 drive->gpt.drive_sectors = drive->size / drive->gpt.sector_bytes;
309
310 // Read the data.
311 if (CGPT_OK != Load(drive, &drive->gpt.primary_header,
312 GPT_PMBR_SECTOR,
313 drive->gpt.sector_bytes, GPT_HEADER_SECTOR)) {
314 return -1;
315 }
316 if (CGPT_OK != Load(drive, &drive->gpt.secondary_header,
317 drive->gpt.drive_sectors - GPT_PMBR_SECTOR,
318 drive->gpt.sector_bytes, GPT_HEADER_SECTOR)) {
319 return -1;
320 }
321 if (CGPT_OK != Load(drive, &drive->gpt.primary_entries,
322 GPT_PMBR_SECTOR + GPT_HEADER_SECTOR,
323 drive->gpt.sector_bytes, GPT_ENTRIES_SECTORS)) {
324 return -1;
325 }
326 if (CGPT_OK != Load(drive, &drive->gpt.secondary_entries,
327 drive->gpt.drive_sectors - GPT_HEADER_SECTOR
328 - GPT_ENTRIES_SECTORS,
329 drive->gpt.sector_bytes, GPT_ENTRIES_SECTORS)) {
330 return -1;
331 }
332 return 0;
333}
334
Bill Richardson18e03702014-06-23 17:48:33 -0700335static int GptSave(struct drive *drive) {
Albert Chaulk534723a2013-03-20 14:46:50 -0700336 int errors = 0;
337 if (drive->gpt.modified & GPT_MODIFIED_HEADER1) {
338 if (CGPT_OK != Save(drive, drive->gpt.primary_header,
339 GPT_PMBR_SECTOR,
340 drive->gpt.sector_bytes, GPT_HEADER_SECTOR)) {
341 errors++;
342 Error("Cannot write primary header: %s\n", strerror(errno));
343 }
344 }
345
346 if (drive->gpt.modified & GPT_MODIFIED_HEADER2) {
347 if(CGPT_OK != Save(drive, drive->gpt.secondary_header,
348 drive->gpt.drive_sectors - GPT_PMBR_SECTOR,
349 drive->gpt.sector_bytes, GPT_HEADER_SECTOR)) {
350 errors++;
351 Error("Cannot write secondary header: %s\n", strerror(errno));
352 }
353 }
354 if (drive->gpt.modified & GPT_MODIFIED_ENTRIES1) {
355 if (CGPT_OK != Save(drive, drive->gpt.primary_entries,
356 GPT_PMBR_SECTOR + GPT_HEADER_SECTOR,
357 drive->gpt.sector_bytes, GPT_ENTRIES_SECTORS)) {
358 errors++;
359 Error("Cannot write primary entries: %s\n", strerror(errno));
360 }
361 }
362 if (drive->gpt.modified & GPT_MODIFIED_ENTRIES2) {
363 if (CGPT_OK != Save(drive, drive->gpt.secondary_entries,
364 drive->gpt.drive_sectors - GPT_HEADER_SECTOR
365 - GPT_ENTRIES_SECTORS,
366 drive->gpt.sector_bytes, GPT_ENTRIES_SECTORS)) {
367 errors++;
368 Error("Cannot write secondary entries: %s\n", strerror(errno));
369 }
370 }
371
372 if (drive->gpt.primary_header)
373 free(drive->gpt.primary_header);
374 drive->gpt.primary_header = 0;
375 if (drive->gpt.primary_entries)
376 free(drive->gpt.primary_entries);
377 drive->gpt.primary_entries = 0;
378 if (drive->gpt.secondary_header)
379 free(drive->gpt.secondary_header);
380 drive->gpt.secondary_header = 0;
381 if (drive->gpt.secondary_entries)
382 free(drive->gpt.secondary_entries);
383 drive->gpt.secondary_entries = 0;
384 return errors ? -1 : 0;
385}
386
Bill Richardsonf1372d92010-06-11 09:15:55 -0700387
388// Opens a block device or file, loads raw GPT data from it.
Bill Richardson23429d32012-04-30 11:33:13 -0700389// mode should be O_RDONLY or O_RDWR
Bill Richardsonc4e92af2010-10-12 07:33:15 -0700390//
Bill Richardsonf1372d92010-06-11 09:15:55 -0700391// Returns CGPT_FAILED if any error happens.
392// Returns CGPT_OK if success and information are stored in 'drive'. */
Bill Richardson23429d32012-04-30 11:33:13 -0700393int DriveOpen(const char *drive_path, struct drive *drive, int mode) {
Bill Richardsonf1372d92010-06-11 09:15:55 -0700394 struct stat stat;
Albert Chaulk534723a2013-03-20 14:46:50 -0700395 uint32_t sector_bytes;
Albert Chaulka75071c2013-03-29 15:02:38 -0700396 int is_mtd = nand.enabled;
Bill Richardsonf1372d92010-06-11 09:15:55 -0700397
Bill Richardsonc4e92af2010-10-12 07:33:15 -0700398 require(drive_path);
399 require(drive);
Bill Richardsonf1372d92010-06-11 09:15:55 -0700400
401 // Clear struct for proper error handling.
402 memset(drive, 0, sizeof(struct drive));
403
Albert Chaulkd41000e2013-07-19 11:12:42 -0700404 if (TryInitMtd(drive_path)) {
405 is_mtd = 1;
Albert Chaulk534723a2013-03-20 14:46:50 -0700406 sector_bytes = 512; /* bytes */
Albert Chaulkd41000e2013-07-19 11:12:42 -0700407 } else {
408 drive->fd = open(drive_path, mode | O_LARGEFILE | O_NOFOLLOW);
409 if (drive->fd == -1) {
410 Error("Can't open %s: %s\n", drive_path, strerror(errno));
411 return CGPT_FAILED;
412 }
413
414 if (fstat(drive->fd, &stat) == -1) {
415 Error("Can't fstat %s: %s\n", drive_path, strerror(errno));
416 goto error_close;
417 }
418 if ((stat.st_mode & S_IFMT) != S_IFREG) {
419 if (ioctl(drive->fd, BLKGETSIZE64, &drive->size) < 0) {
420 Error("Can't read drive size from %s: %s\n", drive_path,
421 strerror(errno));
422 goto error_close;
423 }
424 if (ioctl(drive->fd, BLKSSZGET, &sector_bytes) < 0) {
425 Error("Can't read sector size from %s: %s\n",
426 drive_path, strerror(errno));
427 goto error_close;
428 }
429 } else {
430 sector_bytes = 512; /* bytes */
431 drive->size = stat.st_size;
432 }
Bill Richardsonf1372d92010-06-11 09:15:55 -0700433 }
Albert Chaulkd41000e2013-07-19 11:12:42 -0700434 drive->is_mtd = is_mtd;
Bill Richardsonf1372d92010-06-11 09:15:55 -0700435
Albert Chaulk534723a2013-03-20 14:46:50 -0700436 if (is_mtd) {
Albert Chaulka75071c2013-03-29 15:02:38 -0700437 drive->mtd.fts_block_offset = nand.fts_block_offset;
438 drive->mtd.fts_block_size = nand.fts_block_size;
439 drive->mtd.flash_page_bytes = nand.bytes_per_page;
440 drive->mtd.flash_block_bytes = nand.pages_per_block * nand.bytes_per_page;
Albert Chaulk534723a2013-03-20 14:46:50 -0700441 if (MtdLoad(drive, sector_bytes)) {
442 goto error_close;
443 }
444 } else {
445 if (GptLoad(drive, sector_bytes)) {
446 goto error_close;
447 }
Bill Richardsonf1372d92010-06-11 09:15:55 -0700448 }
Bill Richardsonc4e92af2010-10-12 07:33:15 -0700449
Bill Richardsonf1372d92010-06-11 09:15:55 -0700450 // We just load the data. Caller must validate it.
451 return CGPT_OK;
452
453error_close:
454 (void) DriveClose(drive, 0);
455 return CGPT_FAILED;
456}
457
458
459int DriveClose(struct drive *drive, int update_as_needed) {
460 int errors = 0;
461
462 if (update_as_needed) {
Albert Chaulk534723a2013-03-20 14:46:50 -0700463 if (drive->is_mtd) {
464 if (MtdSave(drive)) {
Bill Richardsonf1372d92010-06-11 09:15:55 -0700465 errors++;
Bill Richardsonf1372d92010-06-11 09:15:55 -0700466 }
Albert Chaulk534723a2013-03-20 14:46:50 -0700467 } else {
468 if (GptSave(drive)) {
Bill Richardsonf1372d92010-06-11 09:15:55 -0700469 errors++;
Bill Richardsonf1372d92010-06-11 09:15:55 -0700470 }
471 }
472 }
473
Louis Yung-Chieh Lo57cdad32013-01-16 11:52:17 +0800474 // Sync early! Only sync file descriptor here, and leave the whole system sync
475 // outside cgpt because whole system sync would trigger tons of disk accesses
476 // and timeout tests.
477 fsync(drive->fd);
478
Bill Richardsonf1372d92010-06-11 09:15:55 -0700479 close(drive->fd);
480
Bill Richardsonf1372d92010-06-11 09:15:55 -0700481 return errors ? CGPT_FAILED : CGPT_OK;
482}
483
484
Bill Richardsonf1372d92010-06-11 09:15:55 -0700485/* GUID conversion functions. Accepted format:
486 *
487 * "C12A7328-F81F-11D2-BA4B-00A0C93EC93B"
488 *
489 * Returns CGPT_OK if parsing is successful; otherwise CGPT_FAILED.
490 */
491int StrToGuid(const char *str, Guid *guid) {
492 uint32_t time_low;
493 uint16_t time_mid;
494 uint16_t time_high_and_version;
495 unsigned int chunk[11];
496
497 if (11 != sscanf(str, "%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X",
498 chunk+0,
499 chunk+1,
500 chunk+2,
501 chunk+3,
502 chunk+4,
503 chunk+5,
504 chunk+6,
505 chunk+7,
506 chunk+8,
507 chunk+9,
Bill Richardsonc4e92af2010-10-12 07:33:15 -0700508 chunk+10)) {
Bill Richardsonf1372d92010-06-11 09:15:55 -0700509 printf("FAILED\n");
510 return CGPT_FAILED;
511 }
512
513 time_low = chunk[0] & 0xffffffff;
514 time_mid = chunk[1] & 0xffff;
515 time_high_and_version = chunk[2] & 0xffff;
516
517 guid->u.Uuid.time_low = htole32(time_low);
518 guid->u.Uuid.time_mid = htole16(time_mid);
519 guid->u.Uuid.time_high_and_version = htole16(time_high_and_version);
520
521 guid->u.Uuid.clock_seq_high_and_reserved = chunk[3] & 0xff;
522 guid->u.Uuid.clock_seq_low = chunk[4] & 0xff;
523 guid->u.Uuid.node[0] = chunk[5] & 0xff;
524 guid->u.Uuid.node[1] = chunk[6] & 0xff;
525 guid->u.Uuid.node[2] = chunk[7] & 0xff;
526 guid->u.Uuid.node[3] = chunk[8] & 0xff;
527 guid->u.Uuid.node[4] = chunk[9] & 0xff;
528 guid->u.Uuid.node[5] = chunk[10] & 0xff;
529
530 return CGPT_OK;
531}
Bill Richardsonc4e92af2010-10-12 07:33:15 -0700532void GuidToStr(const Guid *guid, char *str, unsigned int buflen) {
533 require(buflen >= GUID_STRLEN);
534 require(snprintf(str, buflen,
535 "%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X",
536 le32toh(guid->u.Uuid.time_low),
537 le16toh(guid->u.Uuid.time_mid),
538 le16toh(guid->u.Uuid.time_high_and_version),
539 guid->u.Uuid.clock_seq_high_and_reserved,
540 guid->u.Uuid.clock_seq_low,
541 guid->u.Uuid.node[0], guid->u.Uuid.node[1],
542 guid->u.Uuid.node[2], guid->u.Uuid.node[3],
543 guid->u.Uuid.node[4], guid->u.Uuid.node[5]) == GUID_STRLEN-1);
Bill Richardsonf1372d92010-06-11 09:15:55 -0700544}
545
Bill Richardsonc4e92af2010-10-12 07:33:15 -0700546/* Convert possibly unterminated UTF16 string to UTF8.
547 * Caller must prepare enough space for UTF8, which could be up to
Louis Yung-Chieh Lo500b3c22010-11-22 18:19:11 +0800548 * twice the byte length of UTF16 string plus the terminating '\0'.
549 * See the following table for encoding lengths.
550 *
551 * Code point UTF16 UTF8
552 * 0x0000-0x007F 2 bytes 1 byte
553 * 0x0080-0x07FF 2 bytes 2 bytes
554 * 0x0800-0xFFFF 2 bytes 3 bytes
555 * 0x10000-0x10FFFF 4 bytes 4 bytes
556 *
557 * This function uses a simple state meachine to convert UTF-16 char(s) to
558 * a code point. Once a code point is parsed out, the state machine throws
559 * out sequencial UTF-8 chars in one time.
560 *
561 * Return: CGPT_OK --- all character are converted successfully.
562 * CGPT_FAILED --- convert error, i.e. output buffer is too short.
Bill Richardsonf1372d92010-06-11 09:15:55 -0700563 */
Louis Yung-Chieh Lo500b3c22010-11-22 18:19:11 +0800564int UTF16ToUTF8(const uint16_t *utf16, unsigned int maxinput,
565 uint8_t *utf8, unsigned int maxoutput)
Bill Richardsonf1372d92010-06-11 09:15:55 -0700566{
Bill Richardsonc4e92af2010-10-12 07:33:15 -0700567 size_t s16idx, s8idx;
Louis Yung-Chieh Lo500b3c22010-11-22 18:19:11 +0800568 uint32_t code_point = 0;
569 int code_point_ready = 1; // code point is ready to output.
570 int retval = CGPT_OK;
Bill Richardsonf1372d92010-06-11 09:15:55 -0700571
Bill Richardsonc4e92af2010-10-12 07:33:15 -0700572 if (!utf16 || !maxinput || !utf8 || !maxoutput)
Louis Yung-Chieh Lo500b3c22010-11-22 18:19:11 +0800573 return CGPT_FAILED;
Bill Richardsonf1372d92010-06-11 09:15:55 -0700574
Bill Richardsonc4e92af2010-10-12 07:33:15 -0700575 maxoutput--; /* plan for termination now */
576
577 for (s16idx = s8idx = 0;
578 s16idx < maxinput && utf16[s16idx] && maxoutput;
Louis Yung-Chieh Lo500b3c22010-11-22 18:19:11 +0800579 s16idx++) {
580 uint16_t codeunit = le16toh(utf16[s16idx]);
581
582 if (code_point_ready) {
583 if (codeunit >= 0xD800 && codeunit <= 0xDBFF) {
584 /* high surrogate, need the low surrogate. */
585 code_point_ready = 0;
586 code_point = (codeunit & 0x03FF) + 0x0040;
587 } else {
588 /* BMP char, output it. */
589 code_point = codeunit;
590 }
591 } else {
592 /* expect the low surrogate */
593 if (codeunit >= 0xDC00 && codeunit <= 0xDFFF) {
594 code_point = (code_point << 10) | (codeunit & 0x03FF);
595 code_point_ready = 1;
596 } else {
597 /* the second code unit is NOT the low surrogate. Unexpected. */
598 code_point_ready = 0;
599 retval = CGPT_FAILED;
600 break;
601 }
602 }
603
604 /* If UTF code point is ready, output it. */
605 if (code_point_ready) {
606 require(code_point <= 0x10FFFF);
607 if (code_point <= 0x7F && maxoutput >= 1) {
608 maxoutput -= 1;
609 utf8[s8idx++] = code_point & 0x7F;
610 } else if (code_point <= 0x7FF && maxoutput >= 2) {
611 maxoutput -= 2;
612 utf8[s8idx++] = 0xC0 | (code_point >> 6);
613 utf8[s8idx++] = 0x80 | (code_point & 0x3F);
614 } else if (code_point <= 0xFFFF && maxoutput >= 3) {
615 maxoutput -= 3;
616 utf8[s8idx++] = 0xE0 | (code_point >> 12);
617 utf8[s8idx++] = 0x80 | ((code_point >> 6) & 0x3F);
618 utf8[s8idx++] = 0x80 | (code_point & 0x3F);
619 } else if (code_point <= 0x10FFFF && maxoutput >= 4) {
620 maxoutput -= 4;
621 utf8[s8idx++] = 0xF0 | (code_point >> 18);
622 utf8[s8idx++] = 0x80 | ((code_point >> 12) & 0x3F);
623 utf8[s8idx++] = 0x80 | ((code_point >> 6) & 0x3F);
624 utf8[s8idx++] = 0x80 | (code_point & 0x3F);
625 } else {
626 /* buffer underrun */
627 retval = CGPT_FAILED;
628 break;
629 }
630 }
Bill Richardsonf1372d92010-06-11 09:15:55 -0700631 }
632 utf8[s8idx++] = 0;
Louis Yung-Chieh Lo500b3c22010-11-22 18:19:11 +0800633 return retval;
Bill Richardsonf1372d92010-06-11 09:15:55 -0700634}
635
Bill Richardsonc4e92af2010-10-12 07:33:15 -0700636/* Convert UTF8 string to UTF16. The UTF8 string must be null-terminated.
637 * Caller must prepare enough space for UTF16, including a terminating 0x0000.
Louis Yung-Chieh Lo500b3c22010-11-22 18:19:11 +0800638 * See the following table for encoding lengths. In any case, the caller
639 * just needs to prepare the byte length of UTF8 plus the terminating 0x0000.
640 *
641 * Code point UTF16 UTF8
642 * 0x0000-0x007F 2 bytes 1 byte
643 * 0x0080-0x07FF 2 bytes 2 bytes
644 * 0x0800-0xFFFF 2 bytes 3 bytes
645 * 0x10000-0x10FFFF 4 bytes 4 bytes
646 *
647 * This function converts UTF8 chars to a code point first. Then, convrts it
648 * to UTF16 code unit(s).
649 *
650 * Return: CGPT_OK --- all character are converted successfully.
651 * CGPT_FAILED --- convert error, i.e. output buffer is too short.
Bill Richardsonf1372d92010-06-11 09:15:55 -0700652 */
Louis Yung-Chieh Lo500b3c22010-11-22 18:19:11 +0800653int UTF8ToUTF16(const uint8_t *utf8, uint16_t *utf16, unsigned int maxoutput)
Bill Richardsonf1372d92010-06-11 09:15:55 -0700654{
Bill Richardsonc4e92af2010-10-12 07:33:15 -0700655 size_t s16idx, s8idx;
Louis Yung-Chieh Lo500b3c22010-11-22 18:19:11 +0800656 uint32_t code_point = 0;
657 unsigned int expected_units = 1;
658 unsigned int decoded_units = 1;
659 int retval = CGPT_OK;
Bill Richardsonf1372d92010-06-11 09:15:55 -0700660
Bill Richardsonc4e92af2010-10-12 07:33:15 -0700661 if (!utf8 || !utf16 || !maxoutput)
Louis Yung-Chieh Lo500b3c22010-11-22 18:19:11 +0800662 return CGPT_FAILED;
Bill Richardsonf1372d92010-06-11 09:15:55 -0700663
Bill Richardsonc4e92af2010-10-12 07:33:15 -0700664 maxoutput--; /* plan for termination */
665
666 for (s8idx = s16idx = 0;
667 utf8[s8idx] && maxoutput;
Louis Yung-Chieh Lo500b3c22010-11-22 18:19:11 +0800668 s8idx++) {
669 uint8_t code_unit;
670 code_unit = utf8[s8idx];
671
672 if (expected_units != decoded_units) {
673 /* Trailing bytes of multi-byte character */
674 if ((code_unit & 0xC0) == 0x80) {
675 code_point = (code_point << 6) | (code_unit & 0x3F);
676 ++decoded_units;
677 } else {
678 /* Unexpected code unit. */
679 retval = CGPT_FAILED;
680 break;
681 }
682 } else {
683 /* parsing a new code point. */
684 decoded_units = 1;
685 if (code_unit <= 0x7F) {
686 code_point = code_unit;
687 expected_units = 1;
688 } else if (code_unit <= 0xBF) {
689 /* 0x80-0xBF must NOT be the heading byte unit of a new code point. */
690 retval = CGPT_FAILED;
691 break;
692 } else if (code_unit >= 0xC2 && code_unit <= 0xDF) {
693 code_point = code_unit & 0x1F;
694 expected_units = 2;
695 } else if (code_unit >= 0xE0 && code_unit <= 0xEF) {
696 code_point = code_unit & 0x0F;
697 expected_units = 3;
698 } else if (code_unit >= 0xF0 && code_unit <= 0xF4) {
699 code_point = code_unit & 0x07;
700 expected_units = 4;
701 } else {
702 /* illegal code unit: 0xC0-0xC1, 0xF5-0xFF */
703 retval = CGPT_FAILED;
704 break;
705 }
706 }
707
708 /* If no more unit is needed, output the UTF16 unit(s). */
709 if ((retval == CGPT_OK) &&
710 (expected_units == decoded_units)) {
711 /* Check if the encoding is the shortest possible UTF-8 sequence. */
712 switch (expected_units) {
713 case 2:
714 if (code_point <= 0x7F) retval = CGPT_FAILED;
715 break;
716 case 3:
717 if (code_point <= 0x7FF) retval = CGPT_FAILED;
718 break;
719 case 4:
720 if (code_point <= 0xFFFF) retval = CGPT_FAILED;
721 break;
722 }
723 if (retval == CGPT_FAILED) break; /* leave immediately */
724
725 if ((code_point <= 0xD7FF) ||
726 (code_point >= 0xE000 && code_point <= 0xFFFF)) {
727 utf16[s16idx++] = code_point;
728 maxoutput -= 1;
729 } else if (code_point >= 0x10000 && code_point <= 0x10FFFF &&
730 maxoutput >= 2) {
731 utf16[s16idx++] = 0xD800 | ((code_point >> 10) - 0x0040);
732 utf16[s16idx++] = 0xDC00 | (code_point & 0x03FF);
733 maxoutput -= 2;
734 } else {
735 /* Three possibilities fall into here. Both are failure cases.
736 * a. surrogate pair (non-BMP characters; 0xD800~0xDFFF)
737 * b. invalid code point > 0x10FFFF
738 * c. buffer underrun
739 */
740 retval = CGPT_FAILED;
741 break;
742 }
743 }
Bill Richardsonc4e92af2010-10-12 07:33:15 -0700744 }
Louis Yung-Chieh Lo500b3c22010-11-22 18:19:11 +0800745
746 /* A null-terminator shows up before the UTF8 sequence ends. */
747 if (expected_units != decoded_units) {
748 retval = CGPT_FAILED;
749 }
750
Bill Richardsonc4e92af2010-10-12 07:33:15 -0700751 utf16[s16idx++] = 0;
Louis Yung-Chieh Lo500b3c22010-11-22 18:19:11 +0800752 return retval;
Bill Richardsonf1372d92010-06-11 09:15:55 -0700753}
754
Bill Richardson3430b322010-11-29 14:24:51 -0800755/* global types to compare against */
Gabe Black93cf15e2011-07-07 16:00:00 -0700756const Guid guid_chromeos_firmware = GPT_ENT_TYPE_CHROMEOS_FIRMWARE;
Bill Richardson3430b322010-11-29 14:24:51 -0800757const Guid guid_chromeos_kernel = GPT_ENT_TYPE_CHROMEOS_KERNEL;
758const Guid guid_chromeos_rootfs = GPT_ENT_TYPE_CHROMEOS_ROOTFS;
759const Guid guid_linux_data = GPT_ENT_TYPE_LINUX_DATA;
760const Guid guid_chromeos_reserved = GPT_ENT_TYPE_CHROMEOS_RESERVED;
761const Guid guid_efi = GPT_ENT_TYPE_EFI;
762const Guid guid_unused = GPT_ENT_TYPE_UNUSED;
763
Albert Chaulkb334e652013-03-28 15:25:33 -0700764const static struct {
Bill Richardson3430b322010-11-29 14:24:51 -0800765 const Guid *type;
Bill Richardsonf1372d92010-06-11 09:15:55 -0700766 char *name;
767 char *description;
Albert Chaulkb334e652013-03-28 15:25:33 -0700768 int mtd_type;
Bill Richardsonf1372d92010-06-11 09:15:55 -0700769} supported_types[] = {
Albert Chaulkb334e652013-03-28 15:25:33 -0700770 {&guid_chromeos_firmware, "firmware", "ChromeOS firmware",
771 MTD_PARTITION_TYPE_CHROMEOS_FIRMWARE},
772 {&guid_chromeos_kernel, "kernel", "ChromeOS kernel",
773 MTD_PARTITION_TYPE_CHROMEOS_KERNEL},
774 {&guid_chromeos_rootfs, "rootfs", "ChromeOS rootfs",
775 MTD_PARTITION_TYPE_CHROMEOS_ROOTFS},
776 {&guid_linux_data, "data", "Linux data",
777 MTD_PARTITION_TYPE_LINUX_DATA},
778 {&guid_chromeos_reserved, "reserved", "ChromeOS reserved",
779 MTD_PARTITION_TYPE_CHROMEOS_RESERVED},
780 {&guid_efi, "efi", "EFI System Partition",
781 MTD_PARTITION_TYPE_EFI},
782 {&guid_unused, "unused", "Unused (nonexistent) partition",
783 MTD_PARTITION_TYPE_UNUSED},
Bill Richardsonf1372d92010-06-11 09:15:55 -0700784};
785
Albert Chaulkb334e652013-03-28 15:25:33 -0700786int LookupMtdTypeForGuid(const Guid *type) {
787 int i;
788 for (i = 0; i < ARRAY_COUNT(supported_types); ++i) {
789 if (!memcmp(type, supported_types[i].type, sizeof(Guid))) {
790 return supported_types[i].mtd_type;
791 }
792 }
793 return MTD_PARTITION_TYPE_OTHER;
794}
795
Albert Chaulk92f22e72013-04-02 13:20:52 -0700796const Guid *LookupGuidForMtdType(int type) {
797 int i;
798 for (i = 0; i < ARRAY_COUNT(supported_types); ++i) {
799 if (supported_types[i].mtd_type == type) {
800 return supported_types[i].type;
801 }
802 }
803 return NULL;
804}
805
Bill Richardsonf1372d92010-06-11 09:15:55 -0700806/* Resolves human-readable GPT type.
807 * Returns CGPT_OK if found.
808 * Returns CGPT_FAILED if no known type found. */
809int ResolveType(const Guid *type, char *buf) {
810 int i;
811 for (i = 0; i < ARRAY_COUNT(supported_types); ++i) {
Bill Richardson3430b322010-11-29 14:24:51 -0800812 if (!memcmp(type, supported_types[i].type, sizeof(Guid))) {
Bill Richardsonf1372d92010-06-11 09:15:55 -0700813 strcpy(buf, supported_types[i].description);
814 return CGPT_OK;
815 }
816 }
817 return CGPT_FAILED;
818}
819
820int SupportedType(const char *name, Guid *type) {
821 int i;
822 for (i = 0; i < ARRAY_COUNT(supported_types); ++i) {
823 if (!strcmp(name, supported_types[i].name)) {
Bill Richardson3430b322010-11-29 14:24:51 -0800824 memcpy(type, supported_types[i].type, sizeof(Guid));
Bill Richardsonf1372d92010-06-11 09:15:55 -0700825 return CGPT_OK;
826 }
827 }
828 return CGPT_FAILED;
829}
830
831void PrintTypes(void) {
832 int i;
833 printf("The partition type may also be given as one of these aliases:\n\n");
834 for (i = 0; i < ARRAY_COUNT(supported_types); ++i) {
835 printf(" %-10s %s\n", supported_types[i].name,
836 supported_types[i].description);
837 }
838 printf("\n");
839}
840
Bill Richardson18e03702014-06-23 17:48:33 -0700841static GptHeader* GetGptHeader(const GptData *gpt) {
Bill Richardsonf1372d92010-06-11 09:15:55 -0700842 if (gpt->valid_headers & MASK_PRIMARY)
Albert Chaulkfa6b35c2013-03-26 13:43:02 -0700843 return (GptHeader*)gpt->primary_header;
Bill Richardsonf1372d92010-06-11 09:15:55 -0700844 else if (gpt->valid_headers & MASK_SECONDARY)
Albert Chaulkfa6b35c2013-03-26 13:43:02 -0700845 return (GptHeader*)gpt->secondary_header;
Bill Richardsonf1372d92010-06-11 09:15:55 -0700846 else
847 return 0;
Albert Chaulkfa6b35c2013-03-26 13:43:02 -0700848}
849
850uint32_t GetNumberOfEntries(const struct drive *drive) {
Albert Chaulkb334e652013-03-28 15:25:33 -0700851 if (drive->is_mtd)
852 return MTD_MAX_PARTITIONS;
853
Albert Chaulkfa6b35c2013-03-26 13:43:02 -0700854 GptHeader *header = GetGptHeader(&drive->gpt);
855 if (!header)
856 return 0;
Bill Richardsonf1372d92010-06-11 09:15:55 -0700857 return header->number_of_entries;
858}
859
Bill Richardsonf1372d92010-06-11 09:15:55 -0700860
Bill Richardsonc4e92af2010-10-12 07:33:15 -0700861GptEntry *GetEntry(GptData *gpt, int secondary, uint32_t entry_index) {
Albert Chaulkfa6b35c2013-03-26 13:43:02 -0700862 GptHeader *header = GetGptHeader(gpt);
Bill Richardsonf1372d92010-06-11 09:15:55 -0700863 uint8_t *entries;
Albert Chaulkfa6b35c2013-03-26 13:43:02 -0700864 uint32_t stride = header->size_of_entry;
Bill Richardsonc4e92af2010-10-12 07:33:15 -0700865 require(stride);
Albert Chaulkfa6b35c2013-03-26 13:43:02 -0700866 require(entry_index < header->number_of_entries);
Bill Richardsonf1372d92010-06-11 09:15:55 -0700867
868 if (secondary == PRIMARY) {
869 entries = gpt->primary_entries;
Louis Yung-Chieh Lo2b23c022010-11-18 09:53:10 +0800870 } else if (secondary == SECONDARY) {
Bill Richardsonf1372d92010-06-11 09:15:55 -0700871 entries = gpt->secondary_entries;
Louis Yung-Chieh Lo2b23c022010-11-18 09:53:10 +0800872 } else { /* ANY_VALID */
873 require(secondary == ANY_VALID);
874 if (gpt->valid_entries & MASK_PRIMARY) {
875 entries = gpt->primary_entries;
876 } else {
877 require(gpt->valid_entries & MASK_SECONDARY);
878 entries = gpt->secondary_entries;
879 }
Bill Richardsonf1372d92010-06-11 09:15:55 -0700880 }
881
882 return (GptEntry*)(&entries[stride * entry_index]);
883}
884
Albert Chaulkb334e652013-03-28 15:25:33 -0700885MtdDiskPartition* MtdGetEntry(MtdData *mtd, int secondary, uint32_t index) {
886 if (index >= MTD_MAX_PARTITIONS)
887 return NULL;
888 return &mtd->primary.partitions[index];
889}
890
Albert Chaulkfa6b35c2013-03-26 13:43:02 -0700891void SetPriority(struct drive *drive, int secondary, uint32_t entry_index,
Bill Richardsonc4e92af2010-10-12 07:33:15 -0700892 int priority) {
Bill Richardsonc4e92af2010-10-12 07:33:15 -0700893 require(priority >= 0 && priority <= CGPT_ATTRIBUTE_MAX_PRIORITY);
Albert Chaulkb334e652013-03-28 15:25:33 -0700894 if (drive->is_mtd) {
895 MtdDiskPartition *e = MtdGetEntry(&drive->mtd, secondary, entry_index);
896 MtdSetEntryPriority(e, priority);
897 } else {
898 GptEntry *entry;
899 entry = GetEntry(&drive->gpt, secondary, entry_index);
900 SetEntryPriority(entry, priority);
901 }
Bill Richardsonf1372d92010-06-11 09:15:55 -0700902}
903
Albert Chaulkfa6b35c2013-03-26 13:43:02 -0700904int GetPriority(struct drive *drive, int secondary, uint32_t entry_index) {
Albert Chaulkb334e652013-03-28 15:25:33 -0700905 if (drive->is_mtd) {
906 MtdDiskPartition *e = MtdGetEntry(&drive->mtd, secondary, entry_index);
907 return MtdGetEntryPriority(e);
908 } else {
909 GptEntry *entry;
910 entry = GetEntry(&drive->gpt, secondary, entry_index);
911 return GetEntryPriority(entry);
912 }
Bill Richardsonf1372d92010-06-11 09:15:55 -0700913}
914
Albert Chaulkfa6b35c2013-03-26 13:43:02 -0700915void SetTries(struct drive *drive, int secondary, uint32_t entry_index,
916 int tries) {
Bill Richardsonc4e92af2010-10-12 07:33:15 -0700917 require(tries >= 0 && tries <= CGPT_ATTRIBUTE_MAX_TRIES);
Albert Chaulkb334e652013-03-28 15:25:33 -0700918 if (drive->is_mtd) {
919 MtdDiskPartition *e = MtdGetEntry(&drive->mtd, secondary, entry_index);
920 MtdSetEntryTries(e, tries);
921 } else {
922 GptEntry *entry;
923 entry = GetEntry(&drive->gpt, secondary, entry_index);
924 SetEntryTries(entry, tries);
925 }
Bill Richardsonf1372d92010-06-11 09:15:55 -0700926}
927
Albert Chaulkfa6b35c2013-03-26 13:43:02 -0700928int GetTries(struct drive *drive, int secondary, uint32_t entry_index) {
Albert Chaulkb334e652013-03-28 15:25:33 -0700929 if (drive->is_mtd) {
930 MtdDiskPartition *e = MtdGetEntry(&drive->mtd, secondary, entry_index);
931 return MtdGetEntryTries(e);
932 } else {
933 GptEntry *entry;
934 entry = GetEntry(&drive->gpt, secondary, entry_index);
935 return GetEntryTries(entry);
936 }
Bill Richardsonf1372d92010-06-11 09:15:55 -0700937}
938
Albert Chaulkfa6b35c2013-03-26 13:43:02 -0700939void SetSuccessful(struct drive *drive, int secondary, uint32_t entry_index,
Bill Richardsonc4e92af2010-10-12 07:33:15 -0700940 int success) {
Bill Richardsonc4e92af2010-10-12 07:33:15 -0700941 require(success >= 0 && success <= CGPT_ATTRIBUTE_MAX_SUCCESSFUL);
Albert Chaulkb334e652013-03-28 15:25:33 -0700942 if (drive->is_mtd) {
943 MtdDiskPartition *e = MtdGetEntry(&drive->mtd, secondary, entry_index);
944 MtdSetEntrySuccessful(e, success);
945 } else {
946 GptEntry *entry;
947 entry = GetEntry(&drive->gpt, secondary, entry_index);
948 SetEntrySuccessful(entry, success);
949 }
Bill Richardsonf1372d92010-06-11 09:15:55 -0700950}
951
Albert Chaulkfa6b35c2013-03-26 13:43:02 -0700952int GetSuccessful(struct drive *drive, int secondary, uint32_t entry_index) {
Albert Chaulkb334e652013-03-28 15:25:33 -0700953 if (drive->is_mtd) {
954 MtdDiskPartition *e = MtdGetEntry(&drive->mtd, secondary, entry_index);
955 return MtdGetEntrySuccessful(e);
956 } else {
957 GptEntry *entry;
958 entry = GetEntry(&drive->gpt, secondary, entry_index);
959 return GetEntrySuccessful(entry);
960 }
Albert Chaulkfa6b35c2013-03-26 13:43:02 -0700961}
962
963void SetRaw(struct drive *drive, int secondary, uint32_t entry_index,
Albert Chaulkb334e652013-03-28 15:25:33 -0700964 uint32_t raw) {
965 if (drive->is_mtd) {
966 MtdDiskPartition *e = MtdGetEntry(&drive->mtd, secondary, entry_index);
967 e->flags = raw;
968 } else {
969 GptEntry *entry;
970 entry = GetEntry(&drive->gpt, secondary, entry_index);
971 entry->attrs.fields.gpt_att = (uint16_t)raw;
972 }
Albert Chaulkfa6b35c2013-03-26 13:43:02 -0700973}
974
975void UpdateAllEntries(struct drive *drive) {
Albert Chaulkb334e652013-03-28 15:25:33 -0700976 if (drive->is_mtd) {
977 drive->mtd.modified = 1;
978 drive->mtd.primary.crc32 = MtdHeaderCrc(&drive->mtd.primary);
979 } else {
980 RepairEntries(&drive->gpt, MASK_PRIMARY);
981 RepairHeader(&drive->gpt, MASK_PRIMARY);
Albert Chaulkfa6b35c2013-03-26 13:43:02 -0700982
Albert Chaulkb334e652013-03-28 15:25:33 -0700983 drive->gpt.modified |= (GPT_MODIFIED_HEADER1 | GPT_MODIFIED_ENTRIES1 |
984 GPT_MODIFIED_HEADER2 | GPT_MODIFIED_ENTRIES2);
985 UpdateCrc(&drive->gpt);
986 }
Albert Chaulkfa6b35c2013-03-26 13:43:02 -0700987}
988
989int IsUnused(struct drive *drive, int secondary, uint32_t index) {
Albert Chaulkb334e652013-03-28 15:25:33 -0700990 if (drive->is_mtd) {
991 MtdDiskPartition *e = MtdGetEntry(&drive->mtd, secondary, index);
992 return MtdGetEntryType(e) == MTD_PARTITION_TYPE_UNUSED;
993 } else {
994 GptEntry *entry;
995 entry = GetEntry(&drive->gpt, secondary, index);
996 return GuidIsZero(&entry->type);
997 }
Albert Chaulkfa6b35c2013-03-26 13:43:02 -0700998}
999
1000int IsKernel(struct drive *drive, int secondary, uint32_t index) {
Albert Chaulkb334e652013-03-28 15:25:33 -07001001 if (drive->is_mtd) {
1002 MtdDiskPartition *e = MtdGetEntry(&drive->mtd, secondary, index);
1003 return MtdGetEntryType(e) == MTD_PARTITION_TYPE_CHROMEOS_KERNEL;
1004 } else {
1005 GptEntry *entry;
1006 entry = GetEntry(&drive->gpt, secondary, index);
1007 return GuidEqual(&entry->type, &guid_chromeos_kernel);
1008 }
Bill Richardsonf1372d92010-06-11 09:15:55 -07001009}
1010
1011
1012#define TOSTRING(A) #A
1013const char *GptError(int errnum) {
1014 const char *error_string[] = {
1015 TOSTRING(GPT_SUCCESS),
1016 TOSTRING(GPT_ERROR_NO_VALID_KERNEL),
1017 TOSTRING(GPT_ERROR_INVALID_HEADERS),
1018 TOSTRING(GPT_ERROR_INVALID_ENTRIES),
1019 TOSTRING(GPT_ERROR_INVALID_SECTOR_SIZE),
1020 TOSTRING(GPT_ERROR_INVALID_SECTOR_NUMBER),
1021 TOSTRING(GPT_ERROR_INVALID_UPDATE_TYPE)
1022 };
1023 if (errnum < 0 || errnum >= ARRAY_COUNT(error_string))
1024 return "<illegal value>";
1025 return error_string[errnum];
1026}
1027
1028/* Update CRC value if necessary. */
1029void UpdateCrc(GptData *gpt) {
1030 GptHeader *primary_header, *secondary_header;
1031
1032 primary_header = (GptHeader*)gpt->primary_header;
1033 secondary_header = (GptHeader*)gpt->secondary_header;
1034
Stefan Reinauerb7b865c2012-08-23 15:06:25 -07001035 if (gpt->modified & GPT_MODIFIED_ENTRIES1 &&
1036 memcmp(primary_header, GPT_HEADER_SIGNATURE2,
1037 GPT_HEADER_SIGNATURE_SIZE)) {
Bill Richardsonf1372d92010-06-11 09:15:55 -07001038 primary_header->entries_crc32 =
1039 Crc32(gpt->primary_entries, TOTAL_ENTRIES_SIZE);
1040 }
1041 if (gpt->modified & GPT_MODIFIED_ENTRIES2) {
1042 secondary_header->entries_crc32 =
1043 Crc32(gpt->secondary_entries, TOTAL_ENTRIES_SIZE);
1044 }
1045 if (gpt->modified & GPT_MODIFIED_HEADER1) {
1046 primary_header->header_crc32 = 0;
1047 primary_header->header_crc32 = Crc32(
Louis Yung-Chieh Lo2b23c022010-11-18 09:53:10 +08001048 (const uint8_t *)primary_header, sizeof(GptHeader));
Bill Richardsonf1372d92010-06-11 09:15:55 -07001049 }
1050 if (gpt->modified & GPT_MODIFIED_HEADER2) {
1051 secondary_header->header_crc32 = 0;
1052 secondary_header->header_crc32 = Crc32(
Louis Yung-Chieh Lo2b23c022010-11-18 09:53:10 +08001053 (const uint8_t *)secondary_header, sizeof(GptHeader));
Bill Richardsonf1372d92010-06-11 09:15:55 -07001054 }
1055}
1056/* Two headers are NOT bitwise identical. For example, my_lba pointers to header
1057 * itself so that my_lba in primary and secondary is definitely different.
1058 * Only the following fields should be identical.
1059 *
1060 * first_usable_lba
1061 * last_usable_lba
1062 * number_of_entries
1063 * size_of_entry
1064 * disk_uuid
1065 *
1066 * If any of above field are not matched, overwrite secondary with primary since
1067 * we always trust primary.
1068 * If any one of header is invalid, copy from another. */
1069int IsSynonymous(const GptHeader* a, const GptHeader* b) {
1070 if ((a->first_usable_lba == b->first_usable_lba) &&
1071 (a->last_usable_lba == b->last_usable_lba) &&
1072 (a->number_of_entries == b->number_of_entries) &&
1073 (a->size_of_entry == b->size_of_entry) &&
1074 (!memcmp(&a->disk_uuid, &b->disk_uuid, sizeof(Guid))))
1075 return 1;
1076 return 0;
1077}
1078
1079/* Primary entries and secondary entries should be bitwise identical.
1080 * If two entries tables are valid, compare them. If not the same,
1081 * overwrites secondary with primary (primary always has higher priority),
1082 * and marks secondary as modified.
1083 * If only one is valid, overwrites invalid one.
1084 * If all are invalid, does nothing.
1085 * This function returns bit masks for GptData.modified field.
1086 * Note that CRC is NOT re-computed in this function.
1087 */
1088uint8_t RepairEntries(GptData *gpt, const uint32_t valid_entries) {
Stefan Reinauerb7b865c2012-08-23 15:06:25 -07001089 /* If we have an alternate GPT header signature, don't overwrite
1090 * the secondary GPT with the primary one as that might wipe the
1091 * partition table. Also don't overwrite the primary one with the
1092 * secondary one as that will stop Windows from booting. */
1093 GptHeader* h = (GptHeader*)(gpt->primary_header);
1094 if (!memcmp(h->signature, GPT_HEADER_SIGNATURE2, GPT_HEADER_SIGNATURE_SIZE))
1095 return 0;
1096
Bill Richardsonf1372d92010-06-11 09:15:55 -07001097 if (valid_entries == MASK_BOTH) {
1098 if (memcmp(gpt->primary_entries, gpt->secondary_entries,
1099 TOTAL_ENTRIES_SIZE)) {
1100 memcpy(gpt->secondary_entries, gpt->primary_entries, TOTAL_ENTRIES_SIZE);
1101 return GPT_MODIFIED_ENTRIES2;
1102 }
1103 } else if (valid_entries == MASK_PRIMARY) {
1104 memcpy(gpt->secondary_entries, gpt->primary_entries, TOTAL_ENTRIES_SIZE);
1105 return GPT_MODIFIED_ENTRIES2;
1106 } else if (valid_entries == MASK_SECONDARY) {
1107 memcpy(gpt->primary_entries, gpt->secondary_entries, TOTAL_ENTRIES_SIZE);
1108 return GPT_MODIFIED_ENTRIES1;
1109 }
1110
1111 return 0;
1112}
1113
1114/* The above five fields are shared between primary and secondary headers.
1115 * We can recover one header from another through copying those fields. */
1116void CopySynonymousParts(GptHeader* target, const GptHeader* source) {
1117 target->first_usable_lba = source->first_usable_lba;
1118 target->last_usable_lba = source->last_usable_lba;
1119 target->number_of_entries = source->number_of_entries;
1120 target->size_of_entry = source->size_of_entry;
1121 memcpy(&target->disk_uuid, &source->disk_uuid, sizeof(Guid));
1122}
1123
1124/* This function repairs primary and secondary headers if possible.
1125 * If both headers are valid (CRC32 is correct) but
1126 * a) indicate inconsistent usable LBA ranges,
1127 * b) inconsistent partition entry size and number,
1128 * c) inconsistent disk_uuid,
1129 * we will use the primary header to overwrite secondary header.
1130 * If primary is invalid (CRC32 is wrong), then we repair it from secondary.
1131 * If secondary is invalid (CRC32 is wrong), then we repair it from primary.
1132 * This function returns the bitmasks for modified header.
1133 * Note that CRC value is NOT re-computed in this function. UpdateCrc() will
1134 * do it later.
1135 */
1136uint8_t RepairHeader(GptData *gpt, const uint32_t valid_headers) {
1137 GptHeader *primary_header, *secondary_header;
1138
1139 primary_header = (GptHeader*)gpt->primary_header;
1140 secondary_header = (GptHeader*)gpt->secondary_header;
1141
1142 if (valid_headers == MASK_BOTH) {
1143 if (!IsSynonymous(primary_header, secondary_header)) {
1144 CopySynonymousParts(secondary_header, primary_header);
1145 return GPT_MODIFIED_HEADER2;
1146 }
1147 } else if (valid_headers == MASK_PRIMARY) {
Louis Yung-Chieh Lo2b23c022010-11-18 09:53:10 +08001148 memcpy(secondary_header, primary_header, sizeof(GptHeader));
Bill Richardsonf1372d92010-06-11 09:15:55 -07001149 secondary_header->my_lba = gpt->drive_sectors - 1; /* the last sector */
1150 secondary_header->alternate_lba = primary_header->my_lba;
1151 secondary_header->entries_lba = secondary_header->my_lba -
1152 GPT_ENTRIES_SECTORS;
1153 return GPT_MODIFIED_HEADER2;
1154 } else if (valid_headers == MASK_SECONDARY) {
Louis Yung-Chieh Lo2b23c022010-11-18 09:53:10 +08001155 memcpy(primary_header, secondary_header, sizeof(GptHeader));
Bill Richardsonf1372d92010-06-11 09:15:55 -07001156 primary_header->my_lba = GPT_PMBR_SECTOR; /* the second sector on drive */
1157 primary_header->alternate_lba = secondary_header->my_lba;
1158 primary_header->entries_lba = primary_header->my_lba + GPT_HEADER_SECTOR;
1159 return GPT_MODIFIED_HEADER1;
1160 }
1161
1162 return 0;
1163}
1164
Bill Richardson78299022014-06-20 14:33:00 -07001165int CgptGetNumNonEmptyPartitions(CgptShowParams *params) {
1166 struct drive drive;
1167 int gpt_retval;
1168 int retval;
1169
1170 if (params == NULL)
1171 return CGPT_FAILED;
1172
1173 if (CGPT_OK != DriveOpen(params->drive_name, &drive, O_RDONLY))
1174 return CGPT_FAILED;
1175
1176 if (GPT_SUCCESS != (gpt_retval = GptSanityCheck(&drive.gpt))) {
1177 Error("GptSanityCheck() returned %d: %s\n",
1178 gpt_retval, GptError(gpt_retval));
1179 retval = CGPT_FAILED;
1180 goto done;
1181 }
1182
1183 params->num_partitions = 0;
1184 int numEntries = GetNumberOfEntries(&drive);
1185 int i;
1186 for(i = 0; i < numEntries; i++) {
1187 GptEntry *entry = GetEntry(&drive.gpt, ANY_VALID, i);
1188 if (GuidIsZero(&entry->type))
1189 continue;
1190
1191 params->num_partitions++;
1192 }
1193
1194 retval = CGPT_OK;
1195
1196done:
1197 DriveClose(&drive, 0);
1198 return retval;
1199}
1200
Bill Richardson3430b322010-11-29 14:24:51 -08001201int GuidEqual(const Guid *guid1, const Guid *guid2) {
1202 return (0 == memcmp(guid1, guid2, sizeof(Guid)));
1203}
Bill Richardsonf1372d92010-06-11 09:15:55 -07001204
Bill Richardson3f806a22013-03-20 15:02:34 -07001205int GuidIsZero(const Guid *gp) {
Bill Richardson3430b322010-11-29 14:24:51 -08001206 return GuidEqual(gp, &guid_unused);
Bill Richardsonf1372d92010-06-11 09:15:55 -07001207}
1208
Bill Richardsonc4e92af2010-10-12 07:33:15 -07001209void PMBRToStr(struct pmbr *pmbr, char *str, unsigned int buflen) {
1210 char buf[GUID_STRLEN];
Bill Richardson3f806a22013-03-20 15:02:34 -07001211 if (GuidIsZero(&pmbr->boot_guid)) {
Bill Richardsonc4e92af2010-10-12 07:33:15 -07001212 require(snprintf(str, buflen, "PMBR") < buflen);
Bill Richardsonf1372d92010-06-11 09:15:55 -07001213 } else {
Bill Richardsonc4e92af2010-10-12 07:33:15 -07001214 GuidToStr(&pmbr->boot_guid, buf, sizeof(buf));
1215 require(snprintf(str, buflen, "PMBR (Boot GUID: %s)", buf) < buflen);
Bill Richardsonf1372d92010-06-11 09:15:55 -07001216 }
1217}
Bill Richardson4cb54972014-06-20 14:33:00 -07001218
1219/* Optional */
1220int __GenerateGuid(Guid *newguid) { return CGPT_FAILED; };
1221int GenerateGuid(Guid *newguid) __attribute__((weak, alias("__GenerateGuid")));