Add initial support for installing UEFI boot files to a GTP EFI System
authorkrw <krw@openbsd.org>
Wed, 7 Oct 2015 03:06:46 +0000 (03:06 +0000)
committerkrw <krw@openbsd.org>
Wed, 7 Oct 2015 03:06:46 +0000 (03:06 +0000)
Partition. Further work to be done in-tree.

ok deraadt@

usr.sbin/installboot/i386_installboot.c
usr.sbin/installboot/i386_installboot.h
usr.sbin/installboot/util.c

index 389ff42..6633347 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: i386_installboot.c,v 1.10 2015/10/05 16:07:57 krw Exp $       */
+/*     $OpenBSD: i386_installboot.c,v 1.11 2015/10/07 03:06:46 krw Exp $       */
 /*     $NetBSD: installboot.c,v 1.5 1995/11/17 23:23:50 gwr Exp $ */
 
 /*
@@ -57,6 +57,7 @@
 
 #include <elf_abi.h>
 #include <err.h>
+#include <errno.h>
 #include <fcntl.h>
 #include <nlist.h>
 #include <stdlib.h>
@@ -86,6 +87,7 @@ struct sym_data pbr_symbols[] = {
 
 static void    devread(int, void *, daddr_t, size_t, char *);
 static u_int   findopenbsd(int, struct disklabel *);
+static int     findgptefisys(int, struct disklabel *);
 static int     getbootparams(char *, int, struct disklabel *);
 static char    *loadproto(char *, long *);
 
@@ -124,6 +126,7 @@ void
 md_installboot(int devfd, char *dev)
 {
        struct disklabel dl;
+       int part;
 
        /* Get and check disklabel. */
        if (ioctl(devfd, DIOCGDINFO, &dl) != 0)
@@ -135,6 +138,12 @@ md_installboot(int devfd, char *dev)
        if (dl.d_type == 0)
                warnx("disklabel type unknown");
 
+       part = findgptefisys(devfd, &dl);
+       if (part != -1) {
+               write_efisystem(&dl, (char)part);
+               return;
+       }
+
        bootldr = fileprefix(root, bootldr);
        if (verbose)
                fprintf(stderr, "%s %s to %s\n",
@@ -205,6 +214,157 @@ write_bootblocks(int devfd, char *dev, struct disklabel *dl)
        }
 }
 
+void
+write_efisystem(struct disklabel *dl, char part)
+{
+       static char *fsckfmt = "/sbin/fsck_msdos -f %s >/dev/null";
+       static char *newfsfmt ="/sbin/newfs_msdos -t msdos %s >/dev/null";
+       struct msdosfs_args args;
+       char cmd[60];
+       char dst[50];   /* /tmp/installboot.XXXXXXXXXX/efi/BOOT/BOOTIA32.EFI */
+       char *src;
+       size_t mntlen, pathlen, srclen;
+       int rslt;
+
+       src = NULL;
+
+       /* Create directory for temporary mount point. */
+       strlcpy(dst, "/tmp/installboot.XXXXXXXXXX", sizeof(dst));
+       if (mkdtemp(dst) == NULL)
+               err(1, "mkdtemp('%s') failed", dst);
+       mntlen = strlen(dst);
+
+       /* Mount <duid>.<part> as msdos filesystem. */
+       memset(&args, 0, sizeof(args));
+       rslt = asprintf(&args.fspec,
+           "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx.%c",
+            dl->d_uid[0], dl->d_uid[1], dl->d_uid[2], dl->d_uid[3],
+            dl->d_uid[4], dl->d_uid[5], dl->d_uid[6], dl->d_uid[7],
+           part);
+       if (rslt == -1) {
+               warn("bad special device");
+               goto rmdir;
+       }
+
+       args.export_info.ex_root = -2;  /* unchecked anyway on DOS fs */
+       args.export_info.ex_flags = 0;
+
+       if (mount(MOUNT_MSDOS, dst, 0, &args) == -1) {
+               /* Try fsck'ing it. */
+               rslt = snprintf(cmd, sizeof(cmd), fsckfmt, args.fspec);
+               if (rslt >= sizeof(cmd)) {
+                       warnx("can't build for fsck command");
+                       rslt = -1;
+                       goto rmdir;
+               }
+               rslt = system(cmd);
+               if (rslt == -1) {
+                       warn("system('%s') failed", cmd);
+                       goto rmdir;
+               }
+               if (mount(MOUNT_MSDOS, dst, 0, &args) == -1) {
+                       /* Try newfs'ing it. */
+                       rslt = snprintf(cmd, sizeof(cmd), newfsfmt,
+                           args.fspec);
+                       if (rslt >= sizeof(cmd)) {
+                               warnx("can't build newfs command");
+                               rslt = -1;
+                               goto rmdir;
+                       }
+                       rslt = system(cmd);
+                       if (rslt == -1) {
+                               warn("system('%s') failed", cmd);
+                               goto rmdir;
+                       }
+                       rslt = mount(MOUNT_MSDOS, dst, 0, &args);
+                       if (rslt == -1) {
+                               warn("unable to mount EFI System partition");
+                               goto rmdir;
+                       }
+               }
+       }
+
+       /* Create "/efi/boot" directory in <duid>.<part>. */
+       if (strlcat(dst, "/efi", sizeof(dst)) >= sizeof(dst)) {
+               rslt = -1;
+               warn("unable to build /efi directory");
+               goto umount;
+       }
+       rslt = mkdir(dst, 0);
+       if (rslt == -1 && errno != EEXIST) {
+               warn("mkdir('%s') failed", dst);
+               goto umount;
+       }
+       if (strlcat(dst, "/BOOT", sizeof(dst)) >= sizeof(dst)) {
+               rslt = -1;
+               warn("unable to build /BOOT directory");
+               goto umount;
+       }
+       rslt = mkdir(dst, 0);
+       if (rslt == -1 && errno != EEXIST) {
+               warn("mkdir('%s') failed", dst);
+               goto umount;
+       }
+
+       /*
+        * Copy BOOTIA32.EFI and BOOTX64.EFI to /efi/boot.
+        *
+        * N.B.: BOOTIA32.EFI is longer than BOOTX64.EFI, so src can be reused!
+        */
+       pathlen = strlen(dst);
+       if (strlcat(dst, "/BOOTIA32.EFI", sizeof(dst)) >= sizeof(dst)) {
+               rslt = -1;
+               warn("unable to build /BOOTIA32.EFI path");
+               goto umount;
+       }
+       src = fileprefix(root, "/usr/mdec/BOOTIA32.EFI");
+       srclen = strlen(src);
+       if (verbose)
+               fprintf(stderr, "%s %s to %s\n",
+                   (nowrite ? "would copy" : "copying"), src, dst);
+       if (!nowrite)
+               filecopy(src, dst);
+       src[srclen - strlen("/BOOTIA32.EFI")] = '\0';
+
+       dst[pathlen] = '\0';
+       if (strlcat(dst, "/BOOTX64.EFI", sizeof(dst)) >= sizeof(dst)) {
+               rslt = -1;
+               warn("unable to build /BOOTX64.EFI dst path");
+               goto umount;
+       }
+       if (strlcat(src, "/BOOTX64.EFI", srclen+1) >= srclen+1) {
+               rslt = -1;
+               warn("unable to build /BOOTX64.EFI src path");
+               goto umount;
+       }
+       if (verbose)
+               fprintf(stderr, "%s %s to %s\n",
+                   (nowrite ? "would copy" : "copying"), src, dst);
+       if (!nowrite)
+               filecopy(src, dst);
+
+       free(src);
+       src = NULL;
+       rslt = 0;
+
+umount:
+       dst[mntlen] = '\0';
+       if (unmount(dst, MNT_FORCE) == -1)
+               err(1, "unmount('%s') failed", dst);
+
+rmdir:
+       free(args.fspec);
+       dst[mntlen] = '\0';
+       if (rmdir(dst) == -1)
+               err(1, "rmdir('%s') failed", dst);
+
+       if (src)
+               free(src);
+
+       if (rslt == -1)
+               exit(1);
+}
+
 u_int
 findopenbsd(int devfd, struct disklabel *dl)
 {
@@ -276,6 +436,90 @@ again:
        return ((u_int)-1);
 }
 
+static int
+findgptefisys(int devfd, struct disklabel *dl)
+{
+       struct gpt_partition     gp[NGPTPARTITIONS];
+       struct gpt_header        gh;
+       struct uuid              efisys_uuid;
+       const char               efisys_uuid_code[] = GPT_UUID_EFI_SYSTEM;
+       off_t                    off;
+       ssize_t                  len;
+       u_int64_t                start;
+       int                      i;
+       uint32_t                 orig_csum, new_csum;
+       uint32_t                 ghsize, ghpartsize, ghpartnum, ghpartspersec;
+       u_int8_t                *secbuf;
+
+       /* Prepare EFI System UUID */
+       uuid_dec_be(efisys_uuid_code, &efisys_uuid);
+
+       if ((secbuf = malloc(dl->d_secsize)) == NULL)
+               err(1, NULL);
+       off = dl->d_secsize;    /* Read header from sector 1. */
+       len = pread(devfd, secbuf, dl->d_secsize, off);
+       if (len != dl->d_secsize)
+               err(4, "can't read gpt header");
+
+       memcpy(&gh, secbuf, sizeof(gh));
+       free(secbuf);
+
+       /* Check signature */
+       if (letoh64(gh.gh_sig) != GPTSIGNATURE)
+               return (-1);
+
+       if (letoh32(gh.gh_rev) != GPTREVISION)
+               return (-1);
+
+       ghsize = letoh32(gh.gh_size);
+       if (ghsize < GPTMINHDRSIZE || ghsize > sizeof(struct gpt_header))
+               return (-1);
+
+       /* Check checksum */
+       orig_csum = gh.gh_csum;
+       gh.gh_csum = 0;
+       new_csum = crc32((unsigned char *)&gh, ghsize);
+       gh.gh_csum = orig_csum;
+       if (letoh32(orig_csum) != new_csum)
+               return (-1);
+
+       off = letoh64(gh.gh_part_lba) * dl->d_secsize;
+       ghpartsize = letoh32(gh.gh_part_size);
+       ghpartspersec = dl->d_secsize / ghpartsize;
+       ghpartnum = letoh32(gh.gh_part_num);
+       if ((secbuf = malloc(dl->d_secsize)) == NULL)
+               err(1, NULL);
+       for (i = 0; i < (ghpartnum + ghpartspersec - 1) / ghpartspersec; i++) {
+               len = pread(devfd, secbuf, dl->d_secsize, off);
+               if (len != dl->d_secsize)
+                       return (-1);
+               memcpy(gp + i * ghpartspersec, secbuf,
+                   ghpartspersec * sizeof(struct gpt_partition));
+               off += dl->d_secsize;
+       }
+       free(secbuf);
+       new_csum = crc32((unsigned char *)&gp, ghpartnum * ghpartsize);
+       if (new_csum != letoh32(gh.gh_part_csum))
+               return (-1);
+
+       start = 0;
+       for (i = 0; i < ghpartnum && start == 0; i++) {
+               if (memcmp(&gp[i].gp_type, &efisys_uuid,
+                   sizeof(struct uuid)) == 0)
+                       start = letoh64(gp[i].gp_lba_start);
+       }
+
+       if (start) {
+               for (i = 0; i < MAXPARTITIONS; i++) {
+                       if (DL_GETPSIZE(&dl->d_partitions[i]) > 0 &&
+                           DL_GETPOFFSET(&dl->d_partitions[i]) == start)
+                               return ('a' + i);
+               }
+       }
+
+       return (-1);
+}
+
 /*
  * Load the prototype boot sector (biosboot) into memory.
  */
index 5c87719..fcb09f7 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: i386_installboot.h,v 1.2 2014/06/25 18:23:02 tobias Exp $     */
+/*     $OpenBSD: i386_installboot.h,v 1.3 2015/10/07 03:06:46 krw Exp $        */
 /*
  * Copyright (c) 2011 Joel Sing <jsing@openbsd.org>
  * Copyright (c) 2010 Otto Moerbeek <otto@openbsd.org>
@@ -55,3 +55,4 @@ int   nlist_elf32(const char *, struct nlist *);
 void   pbr_set_symbols(char *, char *, struct sym_data *);
 void   sym_set_value(struct sym_data *, char *, u_int32_t);
 void   write_bootblocks(int, char *, struct disklabel *);
+void   write_efisystem(struct disklabel *, char);
index 05c63e3..6d939e0 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: util.c,v 1.5 2015/01/16 00:05:12 deraadt Exp $        */
+/*     $OpenBSD: util.c,v 1.6 2015/10/07 03:06:46 krw Exp $    */
 
 /*
  * Copyright (c) 2014 Joel Sing <jsing@openbsd.org>
@@ -18,6 +18,7 @@
 
 #include <sys/stat.h>
 #include <err.h>
+#include <errno.h>
 #include <fcntl.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -53,7 +54,8 @@ filecopy(const char *srcfile, const char *dstfile)
        if (dfd == -1)
                err(1, "open %s", dstfile);
        if (fchown(dfd, 0, 0) == -1)
-               err(1, "chown");
+               if (errno != EINVAL)
+                       err(1, "chown");
        if (fchmod(dfd, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) == -1)
                err(1, "chmod");
 
@@ -90,3 +92,34 @@ fileprefix(const char *base, const char *path)
 
        return r;
 }
+
+/*
+ * Adapted from Hacker's Delight crc32b().
+ *
+ * To quote http://www.hackersdelight.org/permissions.htm :
+ *
+ * "You are free to use, copy, and distribute any of the code on
+ *  this web site, whether modified by you or not. You need not give
+ *  attribution. This includes the algorithms (some of which appear
+ *  in Hacker's Delight), the Hacker's Assistant, and any code submitted
+ *  by readers. Submitters implicitly agree to this."
+ */
+u_int32_t
+crc32(const u_char *buf, const u_int32_t size)
+{
+       int j;
+       u_int32_t i, byte, crc, mask;
+
+       crc = 0xFFFFFFFF;
+
+       for (i = 0; i < size; i++) {
+               byte = buf[i];                  /* Get next byte. */
+               crc = crc ^ byte;
+               for (j = 7; j >= 0; j--) {      /* Do eight times. */
+                       mask = -(crc & 1);
+                       crc = (crc >> 1) ^ (0xEDB88320 & mask);
+               }
+       }
+
+       return ~crc;
+}