diff options
Diffstat (limited to 'tools/firmware-utils/src')
-rw-r--r-- | tools/firmware-utils/src/ptgen.c | 341 |
1 files changed, 318 insertions, 23 deletions
diff --git a/tools/firmware-utils/src/ptgen.c b/tools/firmware-utils/src/ptgen.c index 0192bb65e5..83067c104d 100644 --- a/tools/firmware-utils/src/ptgen.c +++ b/tools/firmware-utils/src/ptgen.c @@ -5,6 +5,9 @@ * uses parts of afdisk * Copyright (C) 2002 by David Roetzel <david@roetzel.de> * + * UUID/GUID definition stolen from kernel/include/uapi/linux/uuid.h + * Copyright (C) 2010, Intel Corp. Huang Ying <ying.huang@intel.com> + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -29,17 +32,69 @@ #include <stdint.h> #include <stdbool.h> #include <ctype.h> +#include <inttypes.h> #include <fcntl.h> #include <stdint.h> +#include "cyg_crc.h" #if __BYTE_ORDER == __BIG_ENDIAN +#define cpu_to_le16(x) bswap_16(x) #define cpu_to_le32(x) bswap_32(x) +#define cpu_to_le64(x) bswap_64(x) #elif __BYTE_ORDER == __LITTLE_ENDIAN +#define cpu_to_le16(x) (x) #define cpu_to_le32(x) (x) +#define cpu_to_le64(x) (x) #else #error unknown endianness! #endif +#define swap(a, b) \ + do { typeof(a) __tmp = (a); (a) = (b); (b) = __tmp; } while (0) + +typedef struct { + uint8_t b[16]; +} guid_t; + +#define GUID_INIT(a, b, c, d0, d1, d2, d3, d4, d5, d6, d7) \ +((guid_t) \ +{{ (a) & 0xff, ((a) >> 8) & 0xff, ((a) >> 16) & 0xff, ((a) >> 24) & 0xff, \ + (b) & 0xff, ((b) >> 8) & 0xff, \ + (c) & 0xff, ((c) >> 8) & 0xff, \ + (d0), (d1), (d2), (d3), (d4), (d5), (d6), (d7) }}) + +#define GUID_STRING_LENGTH 36 + +#define GPT_SIGNATURE 0x5452415020494645ULL +#define GPT_REVISION 0x00010000 + +#define GUID_PARTITION_SYSTEM \ + GUID_INIT( 0xC12A7328, 0xF81F, 0x11d2, \ + 0xBA, 0x4B, 0x00, 0xA0, 0xC9, 0x3E, 0xC9, 0x3B) + +#define GUID_PARTITION_BASIC_DATA \ + GUID_INIT( 0xEBD0A0A2, 0xB9E5, 0x4433, \ + 0x87, 0xC0, 0x68, 0xB6, 0xB7, 0x26, 0x99, 0xC7) + +#define GUID_PARTITION_BIOS_BOOT \ + GUID_INIT( 0x21686148, 0x6449, 0x6E6F, \ + 0x74, 0x4E, 0x65, 0x65, 0x64, 0x45, 0x46, 0x49) + +#define GPT_HEADER_SIZE 92 +#define GPT_ENTRY_SIZE 128 +#define GPT_ENTRY_MAX 128 +#define GPT_ENTRY_NAME_SIZE 72 + +#define GPT_HEADER_SECTOR 1 +#define GPT_FIRST_ENTRY_SECTOR 2 + +#define MBR_ENTRY_MAX 4 +#define MBR_DISK_SIGNATURE_OFFSET 440 +#define MBR_PARTITION_ENTRY_OFFSET 446 +#define MBR_BOOT_SIGNATURE_OFFSET 510 + +#define DISK_SECTOR_SIZE 512 + /* Partition table entry */ struct pte { uint8_t active; @@ -55,13 +110,43 @@ struct partinfo { int type; }; +/* GPT Partition table header */ +struct gpth { + uint64_t signature; + uint32_t revision; + uint32_t size; + uint32_t crc32; + uint32_t reserved; + uint64_t self; + uint64_t alternate; + uint64_t first_usable; + uint64_t last_usable; + guid_t disk_guid; + uint64_t first_entry; + uint32_t entry_num; + uint32_t entry_size; + uint32_t entry_crc32; +} __attribute__((packed)); + +/* GPT Partition table entry */ +struct gpte { + guid_t type; + guid_t guid; + uint64_t start; + uint64_t end; + uint64_t attr; + char name[GPT_ENTRY_NAME_SIZE]; +} __attribute__((packed)); + + int verbose = 0; int active = 1; int heads = -1; int sectors = -1; int kb_align = 0; bool ignore_null_sized_partition = false; -struct partinfo parts[4]; +bool use_guid_partition_table = false; +struct partinfo parts[GPT_ENTRY_MAX]; char *filename = NULL; @@ -91,7 +176,7 @@ static long to_kbytes(const char *string) end++; if (*end) { - fprintf(stderr, "garbage after end of number\n"); + fputs("garbage after end of number\n", stderr); return 0; } @@ -132,20 +217,73 @@ static inline unsigned long round_to_kb(long sect) { return ((sect - 1) / kb_align + 1) * kb_align; } +/* Compute a CRC for guid partition table */ +static inline unsigned long gpt_crc32(void *buf, unsigned long len) +{ + return cyg_crc32_accumulate(~0L, buf, len) ^ ~0L; +} + +/* Parse a guid string to guid_t struct */ +static inline int guid_parse(char *buf, guid_t *guid) +{ + char b[4] = {0}; + char *p = buf; + unsigned i = 0; + if (strnlen(buf, GUID_STRING_LENGTH) != GUID_STRING_LENGTH) + return -1; + for (i = 0; i < sizeof(guid_t); i++) { + if (*p == '-') + p++; + if (*p == '\0') + return -1; + memcpy(b, p, 2); + guid->b[i] = strtol(b, 0, 16); + p += 2; + } + swap(guid->b[0], guid->b[3]); + swap(guid->b[1], guid->b[2]); + swap(guid->b[4], guid->b[5]); + swap(guid->b[6], guid->b[7]); + return 0; +} + +/* init an utf-16 string from utf-8 string */ +static inline void init_utf16(char *str, uint16_t *buf, unsigned bufsize) +{ + unsigned i, n = 0; + for (i = 0; i < bufsize; i++) { + if (str[n] == 0x00) { + buf[i] = 0x00; + return ; + } else if ((str[n] & 0x80) == 0x00) {//0xxxxxxx + buf[i] = cpu_to_le16(str[n++]); + } else if ((str[n] & 0xE0) == 0xC0) {//110xxxxx + buf[i] = cpu_to_le16((str[n] & 0x1F) << 6 | (str[n + 1] & 0x3F)); + n += 2; + } else if ((str[n] & 0xF0) == 0xE0) {//1110xxxx + buf[i] = cpu_to_le16((str[n] & 0x0F) << 12 | (str[n + 1] & 0x3F) << 6 | (str[n + 2] & 0x3F)); + n += 3; + } else { + buf[i] = cpu_to_le16('?'); + n++; + } + } +} + /* check the partition sizes and write the partition table */ static int gen_ptable(uint32_t signature, int nr) { - struct pte pte[4]; - unsigned long sect = 0; - int i, fd, ret = -1, start, len; + struct pte pte[MBR_ENTRY_MAX]; + unsigned long start, len, sect = 0; + int i, fd, ret = -1; - memset(pte, 0, sizeof(struct pte) * 4); + memset(pte, 0, sizeof(struct pte) * MBR_ENTRY_MAX); for (i = 0; i < nr; i++) { if (!parts[i].size) { if (ignore_null_sized_partition) continue; fprintf(stderr, "Invalid size in partition %d!\n", i); - return -1; + return ret; } pte[i].active = ((i + 1) == active) ? 0x80 : 0; @@ -165,32 +303,175 @@ static int gen_ptable(uint32_t signature, int nr) to_chs(start + len - 1, pte[i].chs_end); if (verbose) - fprintf(stderr, "Partition %d: start=%ld, end=%ld, size=%ld\n", i, (long)start * 512, ((long)start + (long)len) * 512, (long)len * 512); - printf("%ld\n", (long)start * 512); - printf("%ld\n", (long)len * 512); + fprintf(stderr, "Partition %d: start=%ld, end=%ld, size=%ld\n", + i, + (long)start * DISK_SECTOR_SIZE, + (long)(start + len) * DISK_SECTOR_SIZE, + (long)len * DISK_SECTOR_SIZE); + printf("%ld\n", (long)start * DISK_SECTOR_SIZE); + printf("%ld\n", (long)len * DISK_SECTOR_SIZE); } if ((fd = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0644)) < 0) { fprintf(stderr, "Can't open output file '%s'\n",filename); - return -1; + return ret; + } + + lseek(fd, MBR_DISK_SIGNATURE_OFFSET, SEEK_SET); + if (write(fd, &signature, sizeof(signature)) != sizeof(signature)) { + fputs("write failed.\n", stderr); + goto fail; + } + + lseek(fd, MBR_PARTITION_ENTRY_OFFSET, SEEK_SET); + if (write(fd, pte, sizeof(struct pte) * MBR_ENTRY_MAX) != sizeof(struct pte) * MBR_ENTRY_MAX) { + fputs("write failed.\n", stderr); + goto fail; + } + lseek(fd, MBR_BOOT_SIGNATURE_OFFSET, SEEK_SET); + if (write(fd, "\x55\xaa", 2) != 2) { + fputs("write failed.\n", stderr); + goto fail; + } + + ret = 0; +fail: + close(fd); + return ret; +} + +/* check the partition sizes and write the guid partition table */ +static int gen_gptable(uint32_t signature, guid_t guid, unsigned nr) +{ + struct pte pte; + struct gpth gpth = { + .signature = cpu_to_le64(GPT_SIGNATURE), + .revision = cpu_to_le32(GPT_REVISION), + .size = cpu_to_le32(GPT_HEADER_SIZE), + .self = cpu_to_le64(GPT_HEADER_SECTOR), + .first_usable = cpu_to_le64(GPT_FIRST_ENTRY_SECTOR + GPT_ENTRY_SIZE * GPT_ENTRY_MAX / DISK_SECTOR_SIZE), + .first_entry = cpu_to_le64(GPT_FIRST_ENTRY_SECTOR), + .disk_guid = guid, + .entry_num = cpu_to_le32(GPT_ENTRY_MAX), + .entry_size = cpu_to_le32(GPT_ENTRY_SIZE), + }; + struct gpte gpte[GPT_ENTRY_MAX]; + uint64_t start, end, sect = 0; + int fd, ret = -1; + unsigned i; + + memset(gpte, 0, GPT_ENTRY_SIZE * GPT_ENTRY_MAX); + for (i = 0; i < nr; i++) { + if (!parts[i].size) { + if (ignore_null_sized_partition) + continue; + fprintf(stderr, "Invalid size in partition %d!\n", i); + return ret; + } + start = sect + sectors; + if (kb_align != 0) + start = round_to_kb(start); + gpte[i].start = cpu_to_le64(start); + + sect = start + parts[i].size * 2; + if (kb_align == 0) + sect = round_to_cyl(sect); + gpte[i].end = cpu_to_le64(sect -1); + gpte[i].guid = guid; + gpte[i].guid.b[sizeof(guid_t) -1] += i + 1; + if (parts[i].type == 0xEF || (i + 1) == (unsigned)active) { + gpte[i].type = GUID_PARTITION_SYSTEM; + init_utf16("EFI System Partition", (uint16_t *)gpte[i].name, GPT_ENTRY_NAME_SIZE / sizeof(uint16_t)); + } else { + gpte[i].type = GUID_PARTITION_BASIC_DATA; + } + + if (verbose) + fprintf(stderr, "Partition %d: start=%" PRIu64 ", end=%" PRIu64 ", size=%" PRIu64 "\n", + i, + start * DISK_SECTOR_SIZE, sect * DISK_SECTOR_SIZE, + (sect - start) * DISK_SECTOR_SIZE); + printf("%" PRIu64 "\n", start * DISK_SECTOR_SIZE); + printf("%" PRIu64 "\n", (sect - start) * DISK_SECTOR_SIZE); + } + + gpte[GPT_ENTRY_MAX - 1].start = cpu_to_le64(GPT_FIRST_ENTRY_SECTOR + GPT_ENTRY_SIZE * GPT_ENTRY_MAX / DISK_SECTOR_SIZE); + gpte[GPT_ENTRY_MAX - 1].end = cpu_to_le64((kb_align ? round_to_kb(sectors) : (unsigned long)sectors) - 1); + gpte[GPT_ENTRY_MAX - 1].type = GUID_PARTITION_BIOS_BOOT; + gpte[GPT_ENTRY_MAX - 1].guid = guid; + gpte[GPT_ENTRY_MAX - 1].guid.b[sizeof(guid_t) -1] += GPT_ENTRY_MAX; + + end = sect + sectors - 1; + + pte.type = 0xEE; + pte.start = cpu_to_le32(GPT_HEADER_SECTOR); + pte.length = cpu_to_le32(end); + to_chs(GPT_HEADER_SECTOR, pte.chs_start); + to_chs(end, pte.chs_end); + + gpth.last_usable = cpu_to_le64(end - GPT_ENTRY_SIZE * GPT_ENTRY_MAX / DISK_SECTOR_SIZE - 1); + gpth.alternate = cpu_to_le64(end); + gpth.entry_crc32 = cpu_to_le32(gpt_crc32(gpte, GPT_ENTRY_SIZE * GPT_ENTRY_MAX)); + gpth.crc32 = cpu_to_le32(gpt_crc32((char *)&gpth, GPT_HEADER_SIZE)); + + if ((fd = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0644)) < 0) { + fprintf(stderr, "Can't open output file '%s'\n",filename); + return ret; } - lseek(fd, 440, SEEK_SET); + lseek(fd, MBR_DISK_SIGNATURE_OFFSET, SEEK_SET); if (write(fd, &signature, sizeof(signature)) != sizeof(signature)) { - fprintf(stderr, "write failed.\n"); + fputs("write failed.\n", stderr); goto fail; } - lseek(fd, 446, SEEK_SET); - if (write(fd, pte, sizeof(struct pte) * 4) != sizeof(struct pte) * 4) { - fprintf(stderr, "write failed.\n"); + lseek(fd, MBR_PARTITION_ENTRY_OFFSET, SEEK_SET); + if (write(fd, &pte, sizeof(struct pte)) != sizeof(struct pte)) { + fputs("write failed.\n", stderr); goto fail; } - lseek(fd, 510, SEEK_SET); + + lseek(fd, MBR_BOOT_SIGNATURE_OFFSET, SEEK_SET); if (write(fd, "\x55\xaa", 2) != 2) { - fprintf(stderr, "write failed.\n"); + fputs("write failed.\n", stderr); + goto fail; + } + + if (write(fd, &gpth, GPT_HEADER_SIZE) != GPT_HEADER_SIZE) { + fputs("write failed.\n", stderr); + goto fail; + } + + lseek(fd, GPT_FIRST_ENTRY_SECTOR * DISK_SECTOR_SIZE, SEEK_SET); + if (write(fd, &gpte, GPT_ENTRY_SIZE * GPT_ENTRY_MAX) != GPT_ENTRY_SIZE * GPT_ENTRY_MAX) { + fputs("write failed.\n", stderr); + goto fail; + } + +#ifdef WANT_ALTERNATE_PTABLE + /* The alternate partition table (We omit it by default) */ + swap(gpth.self, gpth.alternate); + gpth.first_entry = cpu_to_le64(end - GPT_ENTRY_SIZE * GPT_ENTRY_MAX / DISK_SECTOR_SIZE), + gpth.crc32 = 0; + gpth.crc32 = cpu_to_le32(gpt_crc32(&gpth, GPT_HEADER_SIZE)); + + lseek(fd, end * DISK_SECTOR_SIZE - GPT_ENTRY_SIZE * GPT_ENTRY_MAX, SEEK_SET); + if (write(fd, &gpte, GPT_ENTRY_SIZE * GPT_ENTRY_MAX) != GPT_ENTRY_SIZE * GPT_ENTRY_MAX) { + fputs("write failed.\n", stderr); + goto fail; + } + + lseek(fd, end * DISK_SECTOR_SIZE, SEEK_SET); + if (write(fd, &gpth, GPT_HEADER_SIZE) != GPT_HEADER_SIZE) { + fputs("write failed.\n", stderr); + goto fail; + } + lseek(fd, (end + 1) * DISK_SECTOR_SIZE -1, SEEK_SET); + if (write(fd, "\x00", 1) != 1) { + fputs("write failed.\n", stderr); goto fail; } +#endif ret = 0; fail: @@ -200,18 +481,20 @@ fail: static void usage(char *prog) { - fprintf(stderr, "Usage: %s [-v] [-n] -h <heads> -s <sectors> -o <outputfile> [-a 0..4] [-l <align kB>] [[-t <type>] -p <size>...] \n", prog); + fprintf(stderr, "Usage: %s [-v] [-n] [-g] -h <heads> -s <sectors> -o <outputfile> [-a 0..4] [-l <align kB>] [-G <guid>] [[-t <type>] -p <size>...] \n", prog); exit(EXIT_FAILURE); } int main (int argc, char **argv) { - char type = 0x83; + unsigned char type = 0x83; int ch; int part = 0; uint32_t signature = 0x5452574F; /* 'OWRT' */ + guid_t guid = GUID_INIT( signature, 0x2211, 0x4433, \ + 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0x00); - while ((ch = getopt(argc, argv, "h:s:p:a:t:o:vnl:S:")) != -1) { + while ((ch = getopt(argc, argv, "h:s:p:a:t:o:vngl:S:G:")) != -1) { switch (ch) { case 'o': filename = optarg; @@ -222,6 +505,9 @@ int main (int argc, char **argv) case 'n': ignore_null_sized_partition = true; break; + case 'g': + use_guid_partition_table = 1; + break; case 'h': heads = (int)strtoul(optarg, NULL, 0); break; @@ -229,8 +515,8 @@ int main (int argc, char **argv) sectors = (int)strtoul(optarg, NULL, 0); break; case 'p': - if (part > 3) { - fprintf(stderr, "Too many partitions\n"); + if (part > GPT_ENTRY_MAX - 1 || (!use_guid_partition_table && part > 3)) { + fputs("Too many partitions\n", stderr); exit(EXIT_FAILURE); } parts[part].size = to_kbytes(optarg); @@ -250,6 +536,12 @@ int main (int argc, char **argv) case 'S': signature = strtoul(optarg, NULL, 0); break; + case 'G': + if (guid_parse(optarg, &guid)) { + fputs("Invalid guid string\n", stderr); + exit(EXIT_FAILURE); + } + break; case '?': default: usage(argv[0]); @@ -259,5 +551,8 @@ int main (int argc, char **argv) if (argc || (heads <= 0) || (sectors <= 0) || !filename) usage(argv[0]); + if (use_guid_partition_table) + return gen_gptable(signature, guid, part) ? EXIT_FAILURE : EXIT_SUCCESS; + return gen_ptable(signature, part) ? EXIT_FAILURE : EXIT_SUCCESS; } |