From f66515a4f4169a0418f77e7642b5703eebe055be Mon Sep 17 00:00:00 2001 From: krw Date: Wed, 7 Oct 2015 03:06:46 +0000 Subject: [PATCH] Add initial support for installing UEFI boot files to a GTP EFI System Partition. Further work to be done in-tree. ok deraadt@ --- usr.sbin/installboot/i386_installboot.c | 246 +++++++++++++++++++++++- usr.sbin/installboot/i386_installboot.h | 3 +- usr.sbin/installboot/util.c | 37 +++- 3 files changed, 282 insertions(+), 4 deletions(-) diff --git a/usr.sbin/installboot/i386_installboot.c b/usr.sbin/installboot/i386_installboot.c index 389ff428138..66333477e21 100644 --- a/usr.sbin/installboot/i386_installboot.c +++ b/usr.sbin/installboot/i386_installboot.c @@ -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 #include +#include #include #include #include @@ -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 . 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 .. */ + 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. */ diff --git a/usr.sbin/installboot/i386_installboot.h b/usr.sbin/installboot/i386_installboot.h index 5c87719fb88..fcb09f772a3 100644 --- a/usr.sbin/installboot/i386_installboot.h +++ b/usr.sbin/installboot/i386_installboot.h @@ -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 * Copyright (c) 2010 Otto Moerbeek @@ -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); diff --git a/usr.sbin/installboot/util.c b/usr.sbin/installboot/util.c index 05c63e38acd..6d939e0902b 100644 --- a/usr.sbin/installboot/util.c +++ b/usr.sbin/installboot/util.c @@ -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 @@ -18,6 +18,7 @@ #include #include +#include #include #include #include @@ -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; +} -- 2.20.1