File 0001-openSUSE-specific-EFI-booting-implementation.patch of Package syslinux
From f0276260ddc6092dec9485f4c0fd12e14cad7ac2 Mon Sep 17 00:00:00 2001
From: v-fox <virtuousfox@gmail.com>
Date: Mon, 26 Oct 2015 09:40:01 +0500
Subject: [PATCH] openSUSE-specific EFI booting implementation
---
utils/isohybrid.c | 349 ++++++++++++++++++++++++++++++++++--------------------
utils/isohybrid.h | 2 +-
2 files changed, 224 insertions(+), 127 deletions(-)
Index: b/utils/isohybrid.c
===================================================================
--- a/utils/isohybrid.c
+++ b/utils/isohybrid.c
@@ -49,14 +49,19 @@ unsigned int padding = 0;
uuid_t disk_uuid, part_uuid, iso_uuid;
uint8_t mode = 0;
-enum { VERBOSE = 1 , EFI = 2 , MAC = 4};
+enum { VERBOSE = 1 , EFI = 2 , MAC = 4, MODE_GPT = 8 , MODE_MBR = 0x10 };
+
+/* partition numbers (1 based) */
+int part_data = 0;
+int part_efi = 0;
+int part_mac = 0;
/* user options */
uint16_t head = 64; /* 1 <= head <= 256 */
uint8_t sector = 32; /* 1 <= sector <= 63 */
uint8_t entry = 0; /* partition number: 1 <= entry <= 4 */
-uint8_t offset = 0; /* partition offset: 0 <= offset <= 64 */
+uint32_t offset = 0; /* partition offset: 0 <= offset <= 0xFFFFFFFF(4294967296) */
uint16_t type = 0x17; /* partition type: 0 <= type <= 255 */
uint32_t id = 0; /* MBR: 0 <= id <= 0xFFFFFFFF(4294967296) */
@@ -67,10 +72,13 @@ char mbr_template_path[1024] = {0}; /*
uint16_t ve[16];
uint32_t catoffset = 0;
-uint32_t c = 0, cc = 0, cs = 0;
+uint32_t c = 0;
uint32_t psize = 0, isosize = 0;
+off_t iso_size = 0;
+off_t iso_filesize = 0;
+
/* boot catalogue parameters */
uint32_t de_lba = 0;
uint16_t de_seg = 0, de_count = 0, de_mbz2 = 0;
@@ -234,12 +242,14 @@ printh(void)
printf(FMT, " -h <X>", "Number of geometry heads (default 64)");
printf(FMT, " -s <X>", "Number of geometry sectors (default 32)");
printf(FMT, " -e --entry", "Specify partition entry number (1-4)");
- printf(FMT, " -o --offset", "Specify partition offset (default 0)");
+ printf(FMT, " -o --offset", "Specify partition offset (in 512 byte units, default 0)");
printf(FMT, " -t --type", "Specify partition type (default 0x17)");
printf(FMT, " -i --id", "Specify MBR ID (default random)");
printf(FMT, " -u --uefi", "Build EFI bootable image");
printf(FMT, " -m --mac", "Add AFP table support");
printf(FMT, " -b --mbr <PATH>", "Load MBR from PATH");
+ printf(FMT, " --gpt", "Force GPT");
+ printf(FMT, " --mbr", "Force MBR");
printf("\n");
printf(FMT, " --forcehd0", "Assume we are loaded as disk ID 0");
@@ -269,6 +279,8 @@ check_option(int argc, char *argv[])
{ "offset", required_argument, NULL, 'o' },
{ "type", required_argument, NULL, 't' },
{ "id", required_argument, NULL, 'i' },
+ { "gpt", no_argument, NULL, 1001 },
+ { "mbr", no_argument, NULL, 1002 },
{ "forcehd0", no_argument, NULL, 'f' },
{ "ctrlhd0", no_argument, NULL, 'c' },
@@ -311,8 +323,8 @@ check_option(int argc, char *argv[])
case 'o':
offset = strtoul(optarg, &err, 0);
- if (*err || offset > 64)
- errx(1, "invalid offset: `%s', 0 <= offset <= 64", optarg);
+ if (*err)
+ errx(1, "invalid offset: `%s', 0 <= offset <= 0xFFFFFFFF", optarg);
break;
case 't':
@@ -341,14 +353,10 @@ check_option(int argc, char *argv[])
case 'u':
mode |= EFI;
- if (entry)
- errx(1, "setting an entry is unsupported with EFI or Mac");
break;
case 'm':
mode |= MAC;
- if (entry)
- errx(1, "setting an entry is unsupported with EFI or Mac");
break;
case 'b':
@@ -361,6 +369,14 @@ check_option(int argc, char *argv[])
mode |= VERBOSE;
break;
+ case 1001:
+ mode |= MODE_GPT;
+ break;
+
+ case 1002:
+ mode |= MODE_MBR;
+ break;
+
case 'V':
printf("%s version %s\n", prog, VERSION);
exit(0);
@@ -476,7 +492,7 @@ check_banner(const uint8_t *buf)
int
check_catalogue(const uint8_t *buf)
{
- int i = 0;
+ int i, cs;
for (i = 0, cs = 0; i < 16; i++)
{
@@ -598,15 +614,24 @@ read_mbr_template(char *path, uint8_t *m
fclose(fp);
}
+uint32_t ofs2chs(uint32_t ofs)
+{
+ unsigned c, h, s;
+
+ s = (ofs % sector) + 1;
+ h = (ofs / sector) % head;
+ c = ofs / (sector * head);
+ if(c > 1023) c = 1023;
+
+ return ((c & 0xff) << 24) + ((s + ((c >> 8) << 6)) << 16) + (h << 8);
+}
int
initialise_mbr(uint8_t *mbr)
{
int i = 0;
- uint32_t tmp = 0;
- uint8_t ptype = 0, *rbm = mbr;
- uint8_t bhead = 0, bsect = 0, bcyle = 0;
- uint8_t ehead = 0, esect = 0, ecyle = 0;
+ uint32_t tmp = 0, chs;
+ uint8_t *rbm = mbr;
#ifndef ISOHYBRID_C_STANDALONE
extern unsigned char isohdpfx[][MBRSIZE];
@@ -628,16 +653,16 @@ initialise_mbr(uint8_t *mbr)
}
- if (mode & MAC) {
- memcpy(mbr, afp_header, sizeof(afp_header));
- }
-
if (!entry)
entry = 1;
if (mode & EFI)
type = 0;
+ if (mode & MAC) {
+ memcpy(mbr, afp_header, sizeof(afp_header));
+ }
+
mbr += MBRSIZE; /* offset 432 */
tmp = lendian_int(de_lba * 4);
@@ -656,50 +681,64 @@ initialise_mbr(uint8_t *mbr)
mbr[1] = '\0';
mbr += 2; /* offset 446 */
- ptype = type;
- psize = c * head * sector - offset;
-
- bhead = (offset / sector) % head;
- bsect = (offset % sector) + 1;
- bcyle = offset / (head * sector);
+ for (i = 1; i <= 4; i++, mbr += 16)
+ {
+ memset(mbr, 0, 16);
- bsect += (bcyle & 0x300) >> 2;
- bcyle &= 0xFF;
+ if (!(mode & MODE_MBR)) {
+ /* write protective mbr */
+ if(i == 1 && (mode & MODE_GPT)) {
+ chs = ofs2chs(1);
+ mbr[1] = chs >> 8;
+ mbr[2] = chs >> 16;
+ mbr[3] = chs >> 24;
+ mbr[4] = 0xee;
+ chs = ofs2chs(iso_filesize/512 - 1);
+ mbr[5] = chs >> 8;
+ mbr[6] = chs >> 16;
+ mbr[7] = chs >> 24;
- ehead = head - 1;
- esect = sector + (((cc - 1) & 0x300) >> 2);
- ecyle = (cc - 1) & 0xFF;
+ tmp = lendian_int(1);
+ memcpy(&mbr[8], &tmp, sizeof(tmp));
- for (i = 1; i <= 4; i++)
- {
- memset(mbr, 0, 16);
- if (i == entry)
+ tmp = lendian_int(iso_filesize/512 - 1);
+ memcpy(&mbr[12], &tmp, sizeof(tmp));
+ }
+
+ continue;
+ }
+
+ if (i == part_data)
{
- mbr[0] = 0x80;
- mbr[1] = bhead;
- mbr[2] = bsect;
- mbr[3] = bcyle;
- mbr[4] = ptype;
- mbr[5] = ehead;
- mbr[6] = esect;
- mbr[7] = ecyle;
+ chs = ofs2chs(offset);
+ mbr[0] = 0x80;
+ mbr[1] = chs >> 8;
+ mbr[2] = chs >> 16;
+ mbr[3] = chs >> 24;
+ chs = ofs2chs(c * head * sector - 1);
+ mbr[4] = type;
+ mbr[5] = chs >> 8;
+ mbr[6] = chs >> 16;
+ mbr[7] = chs >> 24;
tmp = lendian_int(offset);
memcpy(&mbr[8], &tmp, sizeof(tmp));
- tmp = lendian_int(psize);
+ tmp = lendian_int(c * head * sector - offset);
memcpy(&mbr[12], &tmp, sizeof(tmp));
}
- if (i == 2 && (mode & EFI))
+
+ if (i == part_efi)
{
- mbr[0] = 0x0;
- mbr[1] = 0xfe;
- mbr[2] = 0xff;
- mbr[3] = 0xff;
+ chs = ofs2chs(efi_lba * 4);
+ mbr[1] = chs >> 8;
+ mbr[2] = chs >> 16;
+ mbr[3] = chs >> 24;
+ chs = ofs2chs(efi_lba * 4 + efi_count - 1);
mbr[4] = 0xef;
- mbr[5] = 0xfe;
- mbr[6] = 0xff;
- mbr[7] = 0xff;
+ mbr[5] = chs >> 8;
+ mbr[6] = chs >> 16;
+ mbr[7] = chs >> 24;
tmp = lendian_int(efi_lba * 4);
memcpy(&mbr[8], &tmp, sizeof(tmp));
@@ -707,9 +746,9 @@ initialise_mbr(uint8_t *mbr)
tmp = lendian_int(efi_count);
memcpy(&mbr[12], &tmp, sizeof(tmp));
}
- if (i == 3 && (mode & MAC))
+
+ if (i == part_mac)
{
- mbr[0] = 0x0;
mbr[1] = 0xfe;
mbr[2] = 0xff;
mbr[3] = 0xff;
@@ -722,9 +761,8 @@ initialise_mbr(uint8_t *mbr)
memcpy(&mbr[8], &tmp, sizeof(tmp));
tmp = lendian_int(mac_count);
- memcpy(&mbr[12], &tmp, sizeof(tmp));
- }
- mbr += 16;
+ memcpy(&mbr[12], &tmp, sizeof(tmp));
+ }
}
mbr[0] = 0x55;
mbr[1] = 0xAA;
@@ -802,12 +840,28 @@ ascii_to_utf16le(uint16_t *dst, const ch
}
void
+set_gpt_part_name(struct gpt_part_header *part, const char *name)
+{
+ unsigned u, len = strlen(name);
+
+ if (len > sizeof part->name / sizeof *part->name)
+ len = sizeof part->name / sizeof *part->name;
+
+ // utf16le, actually
+ for (u = 0; u < len; u++)
+ part->name[u] = *name++;
+}
+
+void
initialise_gpt(uint8_t *gpt, uint32_t current, uint32_t alternate, int primary)
{
struct gpt_header *header = (struct gpt_header *)gpt;
struct gpt_part_header *part;
int hole = 0;
int gptsize = 128 / 4 + 2;
+ int i;
+
+ if(!(mode & MODE_GPT)) return;
if (mac_lba) {
/* 2048 bytes per partition, plus round to 2048 boundary */
@@ -848,40 +902,34 @@ initialise_gpt(uint8_t *gpt, uint32_t cu
reverse_uuid(iso_uuid);
}
- memcpy(part->partGUID, iso_uuid, sizeof(uuid_t));
- memcpy(part->partTypeGUID, basic_partition, sizeof(uuid_t));
- part->firstLBA = lendian_64(0);
- part->lastLBA = lendian_64(psize - 1);
- ascii_to_utf16le(part->name, "ISOHybrid ISO");
-
- gpt += sizeof(struct gpt_part_header);
- part++;
-
- memcpy(part->partGUID, part_uuid, sizeof(uuid_t));
- memcpy(part->partTypeGUID, basic_partition, sizeof(uuid_t));
- part->firstLBA = lendian_64(efi_lba * 4);
- part->lastLBA = lendian_64(part->firstLBA + efi_count - 1);
- ascii_to_utf16le(part->name, "ISOHybrid");
-
- gpt += sizeof(struct gpt_part_header);
-
- if (mac_lba) {
- gpt += sizeof(struct gpt_part_header);
-
- part++;
+ for(i = 1; i <= 4; i++, part++) {
+ if(i == part_data) {
+ memcpy(part->partGUID, iso_uuid, sizeof(uuid_t));
+ memcpy(part->partTypeGUID, basic_partition, sizeof(uuid_t));
+ part->firstLBA = lendian_64(offset);
+ part->lastLBA = lendian_64(iso_size/512 - 1);
+ set_gpt_part_name(part, "ISOHybrid ISO");
+ }
- memcpy(part->partGUID, part_uuid, sizeof(uuid_t));
- memcpy(part->partTypeGUID, hfs_partition, sizeof(uuid_t));
- part->firstLBA = lendian_64(mac_lba * 4);
- part->lastLBA = lendian_64(part->firstLBA + mac_count - 1);
- ascii_to_utf16le(part->name, "ISOHybrid");
+ if(i == part_efi) {
+ memcpy(part->partGUID, part_uuid, sizeof(uuid_t));
+ memcpy(part->partTypeGUID, efi_system_partition, sizeof(uuid_t));
+ part->firstLBA = lendian_64(efi_lba * 4);
+ part->lastLBA = lendian_64(part->firstLBA + efi_count - 1);
+ set_gpt_part_name(part, "ISOHybrid");
+ }
- part--;
+ if (i == part_mac) {
+ memcpy(part->partGUID, part_uuid, sizeof(uuid_t));
+ memcpy(part->partTypeGUID, hfs_partition, sizeof(uuid_t));
+ part->firstLBA = lendian_64(mac_lba * 4);
+ part->lastLBA = lendian_64(part->firstLBA + mac_count - 1);
+ set_gpt_part_name(part, "ISOHybrid");
+ }
}
- part--;
- header->partitionEntriesCRC = lendian_int (chksum_crc32((uint8_t *)part,
+ header->partitionEntriesCRC = lendian_int (chksum_crc32(gpt,
header->numParts * header->sizeOfPartitionEntries));
header->headerCRC = lendian_int(chksum_crc32((uint8_t *)header,
@@ -948,8 +996,10 @@ main(int argc, char *argv[])
FILE *fp = NULL;
uint8_t *buf = NULL, *bufz = NULL;
int cylsize = 0, frac = 0;
+ unsigned padding = 0;
size_t orig_gpt_size, free_space, gpt_size;
struct iso_primary_descriptor descriptor;
+ struct stat isostat;
prog = strcpy(alloca(strlen(argv[0]) + 1), argv[0]);
i = check_option(argc, argv);
@@ -962,8 +1012,19 @@ main(int argc, char *argv[])
return 1;
}
- if ((mode & EFI) && offset)
- errx(1, "%s: --offset is invalid with UEFI images\n", argv[0]);
+ if (!(mode & (MODE_MBR | MODE_GPT))) {
+ mode |= MODE_MBR;
+ }
+
+ if ((mode & EFI) && !offset) type = 0;
+
+ part_data = 1;
+ if(mode & EFI) part_efi = 2;
+ if(mode & MAC) part_mac = 3;
+
+ if(entry) {
+ if(entry != part_efi && entry != part_mac) part_data = entry;
+ }
srand(time(NULL) << (getppid() << getpid()));
@@ -1014,12 +1075,30 @@ main(int argc, char *argv[])
if (!read_efi_section(buf)) {
buf += 32;
if (!read_efi_catalogue(buf, &efi_count, &efi_lba) && efi_lba) {
- offset = 0;
+ if(efi_count < 2) {
+ unsigned char bpb[512];
+
+ if (fseek(fp, (efi_lba * 2048), SEEK_SET)) {
+ err(1, "%s: seek error - 3", argv[0]);
+ }
+
+ if (fread(bpb, 1, sizeof bpb, fp) != sizeof bpb) {
+ err(1, "%s", argv[0]);
+ }
+
+ if((bpb[511] << 8) + bpb[510] == 0xaa55) {
+ unsigned s = bpb[19] + (bpb[20] << 8);
+ if(!s) s = bpb[32] + (bpb[33] << 8) + (bpb[34] << 16) + (bpb[35] << 24);
+ if(s) efi_count = s;
+ }
+ }
} else {
errx(1, "%s: invalid efi catalogue", argv[0]);
}
} else {
- errx(1, "%s: unable to find efi image", argv[0]);
+ fprintf(stderr, "%s: warning: unable to find efi image\n", argv[0]);
+ mode &= ~EFI;
+ part_efi = 0;
}
}
@@ -1052,27 +1131,55 @@ main(int argc, char *argv[])
"signature. Note that isolinux-debug.bin does not support " \
"hybrid booting", argv[0]);
+ if (offset && efi_lba && offset > efi_lba * 4)
+ {
+ part_efi = 1;
+ part_data = 2;
+ if (part_mac)
+ {
+ part_mac = 2;
+ part_data = 3;
+ }
+ }
+
if (stat(argv[0], &isostat))
err(1, "%s", argv[0]);
- isosize = lendian_int(descriptor.size) * lendian_short(descriptor.block_size);
- free_space = isostat.st_size - isosize;
+ iso_size = (off_t) lendian_int(descriptor.size) * lendian_short(descriptor.block_size);
+ free_space = isostat.st_size - iso_size;
cylsize = head * sector * 512;
frac = isostat.st_size % cylsize;
padding = (frac > 0) ? cylsize - frac : 0;
if (mode & VERBOSE)
- printf("imgsize: %zu, padding: %d\n", (size_t)isostat.st_size, padding);
+ printf("imgsize: %zu, padding: %d\n", (size_t)iso_filesize, padding);
- cc = c = ( isostat.st_size + padding) / cylsize;
+ c = (isostat.st_size + padding) / cylsize;
if (c > 1024)
{
warnx("Warning: more than 1024 cylinders: %d", c);
warnx("Not all BIOSes will be able to boot this device");
- cc = 1024;
}
+ /* 512 byte header, 128 entries of 128 bytes */
+ orig_gpt_size = gpt_size = 512 + (128 * 128);
+
+ /* Leave space for the APM if necessary */
+ if (mac_lba)
+ gpt_size += (4 * 2048);
+
+ /*
+ * We need to ensure that we have enough space for the secondary GPT.
+ * Unlike the primary, this doesn't need a hole for the APM. We still
+ * want to be 1MB aligned so just bump the padding by a megabyte.
+ */
+ if ((mode & MODE_GPT) && free_space < orig_gpt_size && padding < orig_gpt_size) {
+ padding += 1024 * 1024;
+ }
+
+ iso_filesize = isostat.st_size + padding;
+
if (!id)
{
if (fseeko(fp, (off_t) 440, SEEK_SET))
@@ -1094,6 +1201,16 @@ main(int argc, char *argv[])
buf = bufz;
memset(buf, 0, BUFSIZE);
+
+ if (fseek(fp, 0, SEEK_SET))
+ err(1, "%s: seek error - 5", argv[0]);
+
+ /* clean up first 16 blocks */
+ for (i = 0; i < 16; i++) {
+ if (fwrite(buf, sizeof(char), BUFSIZE, fp) != BUFSIZE)
+ err(1, "%s: write error - 1", argv[0]);
+ }
+
i = initialise_mbr(buf);
if (mode & VERBOSE)
@@ -1105,42 +1222,22 @@ main(int argc, char *argv[])
if (fwrite(buf, sizeof(char), i, fp) != (size_t)i)
err(1, "%s: write error - 1", argv[0]);
- if (efi_lba) {
+ if (mode & MODE_GPT) {
+ /* always write primary gpt, if only to cleanup old data */
+ reverse_uuid(efi_system_partition);
reverse_uuid(basic_partition);
reverse_uuid(hfs_partition);
- /* 512 byte header, 128 entries of 128 bytes */
- orig_gpt_size = gpt_size = 512 + (128 * 128);
-
- /* Leave space for the APM if necessary */
- if (mac_lba)
- gpt_size += (4 * 2048);
-
buf = calloc(gpt_size, sizeof(char));
memset(buf, 0, gpt_size);
/*
- * We need to ensure that we have enough space for the secondary GPT.
- * Unlike the primary, this doesn't need a hole for the APM. We still
- * want to be 1MB aligned so just bump the padding by a megabyte.
- */
- if (free_space < orig_gpt_size && padding < orig_gpt_size) {
- padding += 1024 * 1024;
- }
-
- /*
- * Determine the size of the ISO filesystem. This will define the size
- * of the partition that covers it.
- */
- psize = isosize / 512;
-
- /*
* Primary GPT starts at sector 1, secondary GPT starts at 1 sector
* before the end of the image
*/
- initialise_gpt(buf, 1, (isostat.st_size + padding - 512) / 512, 1);
+ initialise_gpt(buf, 1, iso_filesize / 512 - 1, 1);
- if (fseeko(fp, (off_t) 512, SEEK_SET))
+ if (fseek(fp, 512, SEEK_SET))
err(1, "%s: seek error - 6", argv[0]);
if (fwrite(buf, sizeof(char), gpt_size, fp) != (size_t)gpt_size)
@@ -1166,17 +1263,17 @@ main(int argc, char *argv[])
if (fsync(fileno(fp)))
err(1, "%s: could not synchronise", argv[0]);
- if (ftruncate(fileno(fp), isostat.st_size + padding))
+ if (ftruncate(fileno(fp), iso_filesize))
err(1, "%s: could not add padding bytes", argv[0]);
}
- if (efi_lba) {
+ if (mode & MODE_GPT) {
buf = realloc(buf, orig_gpt_size);
memset(buf, 0, orig_gpt_size);
buf += orig_gpt_size - sizeof(struct gpt_header);
- initialise_gpt(buf, (isostat.st_size + padding - 512) / 512, 1, 0);
+ initialise_gpt(buf, iso_filesize / 512 - 1, 1, 0);
/* Shift back far enough to write the 128 GPT entries */
buf -= 128 * sizeof(struct gpt_part_header);
@@ -1186,7 +1283,7 @@ main(int argc, char *argv[])
* end of the image
*/
- if (fseeko(fp, (isostat.st_size + padding) - orig_gpt_size, SEEK_SET))
+ if (fseek(fp, iso_filesize - orig_gpt_size, SEEK_SET))
err(1, "%s: seek error - 8", argv[0]);
if (fwrite(buf, sizeof(char), orig_gpt_size, fp) != orig_gpt_size)
Index: b/utils/isohybrid.h
===================================================================
--- a/utils/isohybrid.h
+++ b/utils/isohybrid.h
@@ -20,7 +20,7 @@
*
*/
-#define VERSION "0.12"
+#define VERSION "0.12.1"
#define BUFSIZE 2048
#define MBRSIZE 432