[OpenWrt-Devel,1/3] firmware-utils: ptgen: add GPT support
diff mbox series

Message ID 1554371186-25036-1-git-send-email-uxgood.org@gmail.com
State Superseded
Headers show
Series
  • [OpenWrt-Devel,1/3] firmware-utils: ptgen: add GPT support
Related show

Commit Message

李国 April 4, 2019, 9:46 a.m. UTC
Add GPT support to ptgen, so we can generate EFI bootable images.

Introduced two options:
    -g        generate GPT partition table
    -G GUID   use GUID for disk and increase last bit for all partitions

We drop The alternate partition table to reduce size, This may cause
problems when generate vmdk images or vdi images. We must pad enough
sectors when generate these images.

Signed-off-by: 李国 <uxgood.org@gmail.com>
---
 tools/firmware-utils/Makefile    |   2 +-
 tools/firmware-utils/src/ptgen.c | 279 ++++++++++++++++++++++++++++++++++++++-
 2 files changed, 274 insertions(+), 7 deletions(-)

Patch
diff mbox series

diff --git a/tools/firmware-utils/Makefile b/tools/firmware-utils/Makefile
index 864a3df..5686dcc 100644
--- a/tools/firmware-utils/Makefile
+++ b/tools/firmware-utils/Makefile
@@ -25,7 +25,7 @@  define Host/Compile
 	$(call cc,dgfirmware)
 	$(call cc,mksenaofw md5)
 	$(call cc,trx2usr)
-	$(call cc,ptgen)
+	$(call cc,ptgen cyg_crc32)
 	$(call cc,srec2bin)
 	$(call cc,mkmylofw)
 	$(call cc,mkcsysimg)
diff --git a/tools/firmware-utils/src/ptgen.c b/tools/firmware-utils/src/ptgen.c
index 0192bb6..7f7c74e 100644
--- a/tools/firmware-utils/src/ptgen.c
+++ b/tools/firmware-utils/src/ptgen.c
@@ -31,15 +31,50 @@ 
 #include <ctype.h>
 #include <fcntl.h>
 #include <stdint.h>
+#include <linux/uuid.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)
+
+#ifndef GUID_INIT
+typedef uuid_le guid_t;
+#define GUID_INIT(a, b, c, d0, d1, d2, d3, d4, d5, d6, d7)      \
+	UUID_LE(a, b, c, d0, d1, d2, d3, d4, d5, d6, d7)
+#endif
+
+#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
+
+
 /* Partition table entry */
 struct pte {
 	uint8_t active;
@@ -55,13 +90,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;
+	uint16_t name[72 / sizeof(uint16_t)];
+} __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;
 
 
@@ -132,6 +197,57 @@  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;
+	int i = 0;
+	if (strnlen(buf, 36) != 36)
+		return -1;
+	for (i = 0; i < 16; 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, int bufsize)
+{
+	int 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++] & 0x3F);
+		} else if((str[n] & 0xF0) == 0xE0) {//1110xxxx
+			buf[i] = cpu_to_le16((str[n++] & 0x0F) << 12 | (str[n++] & 0x3F) << 6 | str[n++] & 0x3F);
+		} 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)
 {
@@ -198,20 +314,158 @@  fail:
 	return ret;
 }
 
+/* check the partition sizes and write the guid partition table */
+static int gen_gptable(uint32_t signature, guid_t guid, int 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(1),
+		.first_usable = cpu_to_le64(GPT_ENTRY_SIZE * GPT_ENTRY_MAX / 512 + 2),
+		.first_entry = cpu_to_le64(2),
+		.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];
+	unsigned long long start, end, sect = 0;
+	int i, fd, ret = -1;
+
+	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 -1;
+		}
+		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[15] += i + 1;
+		if (parts[i].type == 0xEF || (i + 1) == active) {
+			gpte[i].type = GUID_PARTITION_SYSTEM;
+			init_utf16("EFI System Partition", gpte[i].name, 36) ;
+		} else {
+			gpte[i].type = GUID_PARTITION_BASIC_DATA;
+		}
+
+		if (verbose)
+			fprintf(stderr, "Partition %d: start=%lld, end=%lld, size=%lld\n", i, start * 512, sect * 512, (sect - start) * 512);
+		printf("%lld\n", start * 512);
+		printf("%lld\n", (sect - start) * 512);
+	}
+
+	gpte[GPT_ENTRY_MAX - 1].start = cpu_to_le64(GPT_ENTRY_SIZE * GPT_ENTRY_MAX / 512 + 2);
+	gpte[GPT_ENTRY_MAX - 1].end = cpu_to_le64((kb_align ? round_to_kb(sectors) : 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[15] += GPT_ENTRY_MAX;
+
+	end = sect + sectors - 1;
+
+	pte.type = 0xEE;
+	pte.start = cpu_to_le32(1);
+	pte.length = cpu_to_le32(end);
+	to_chs(1, pte.chs_start);
+	to_chs(end, pte.chs_end);
+
+	gpth.last_usable = cpu_to_le64(end - GPT_ENTRY_SIZE * GPT_ENTRY_MAX / 512 - 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 -1;
+	}
+
+	lseek(fd, 440, SEEK_SET);
+	if (write(fd, &signature, sizeof(signature)) != sizeof(signature)) {
+		fprintf(stderr, "write failed.\n");
+		goto fail;
+	}
+
+	lseek(fd, 446, SEEK_SET);
+	if (write(fd, &pte, sizeof(struct pte)) != sizeof(struct pte)) {
+		fprintf(stderr, "write failed.\n");
+		goto fail;
+	}
+
+	lseek(fd, 510, SEEK_SET);
+	if (write(fd, "\x55\xaa", 2) != 2) {
+		fprintf(stderr, "write failed.\n");
+		goto fail;
+	}
+
+	lseek(fd, 512, SEEK_SET);
+	if (write(fd, &gpth, GPT_HEADER_SIZE) != GPT_HEADER_SIZE) {
+		fprintf(stderr, "write failed.\n");
+		goto fail;
+	}
+
+	lseek(fd, 1024, SEEK_SET);
+	if (write(fd, &gpte, GPT_ENTRY_SIZE * GPT_ENTRY_MAX) != GPT_ENTRY_SIZE * GPT_ENTRY_MAX) {
+		fprintf(stderr, "write failed.\n");
+		goto fail;
+	}
+
+#if 0
+	/* The alternate partition table (We omit it) */
+	swap(gpth.self, gpth.alternate);
+	gpth.first_entry = cpu_to_le64(end - GPT_ENTRY_SIZE * GPT_ENTRY_MAX / 512),
+	gpth.crc32 = 0;
+	gpth.crc32 = cpu_to_le32(gpt_crc32(&gpth, GPT_HEADER_SIZE));
+
+	lseek(fd, end * 512 - GPT_ENTRY_SIZE * GPT_ENTRY_MAX, SEEK_SET);
+	if (write(fd, &gpte, GPT_ENTRY_SIZE * GPT_ENTRY_MAX) != GPT_ENTRY_SIZE * GPT_ENTRY_MAX) {
+		fprintf(stderr, "write failed.\n");
+		goto fail;
+	}
+
+	lseek(fd, end * 512, SEEK_SET);
+	if (write(fd, &gpth, GPT_HEADER_SIZE) != GPT_HEADER_SIZE) {
+		fprintf(stderr, "write failed.\n");
+		goto fail;
+	}
+	lseek(fd, end * 512 + 511, SEEK_SET);
+	if (write(fd, "\x00", 1) != 1) {
+		fprintf(stderr, "write failed.\n");
+		goto fail;
+	}
+#endif
+
+	ret = 0;
+fail:
+	close(fd);
+	return ret;
+}
+
 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 +476,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,7 +486,7 @@  int main (int argc, char **argv)
 			sectors = (int)strtoul(optarg, NULL, 0);
 			break;
 		case 'p':
-			if (part > 3) {
+			if (part > GPT_ENTRY_MAX - 1 || (use_guid_partition_table == false && part > 3)) {
 				fprintf(stderr, "Too many partitions\n");
 				exit(EXIT_FAILURE);
 			}
@@ -250,6 +507,12 @@  int main (int argc, char **argv)
 		case 'S':
 			signature = strtoul(optarg, NULL, 0);
 			break;
+		case 'G':
+			if (guid_parse(optarg, &guid)) {
+				fprintf(stderr, "Invalid guid string\n");
+				exit(EXIT_FAILURE);
+			}
+			break;
 		case '?':
 		default:
 			usage(argv[0]);
@@ -259,5 +522,9 @@  int main (int argc, char **argv)
 	if (argc || (heads <= 0) || (sectors <= 0) || !filename)
 		usage(argv[0]);
 
-	return gen_ptable(signature, part) ? EXIT_FAILURE : EXIT_SUCCESS;
+	if (use_guid_partition_table) {
+		return gen_gptable(signature, guid, part) ? EXIT_FAILURE : EXIT_SUCCESS;
+	} else {
+		return gen_ptable(signature, part) ? EXIT_FAILURE : EXIT_SUCCESS;
+	}
 }