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
 
openSUSE Build Service is sponsored by