1 /* $OpenBSD: efi_installboot.c,v 1.2 2022/02/03 10:25:14 visa Exp $ */ 2 /* $NetBSD: installboot.c,v 1.5 1995/11/17 23:23:50 gwr Exp $ */ 3 4 /* 5 * Copyright (c) 2011 Joel Sing <jsing@openbsd.org> 6 * Copyright (c) 2010 Otto Moerbeek <otto@openbsd.org> 7 * Copyright (c) 2003 Tom Cosgrove <tom.cosgrove@arches-consulting.com> 8 * Copyright (c) 1997 Michael Shalayeff 9 * Copyright (c) 1994 Paul Kranenburg 10 * All rights reserved. 11 * 12 * Redistribution and use in source and binary forms, with or without 13 * modification, are permitted provided that the following conditions 14 * are met: 15 * 1. Redistributions of source code must retain the above copyright 16 * notice, this list of conditions and the following disclaimer. 17 * 2. Redistributions in binary form must reproduce the above copyright 18 * notice, this list of conditions and the following disclaimer in the 19 * documentation and/or other materials provided with the distribution. 20 * 3. All advertising materials mentioning features or use of this software 21 * must display the following acknowledgement: 22 * This product includes software developed by Paul Kranenburg. 23 * 4. The name of the author may not be used to endorse or promote products 24 * derived from this software without specific prior written permission 25 * 26 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 27 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 28 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 29 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 30 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 31 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 32 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 33 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 34 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 35 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 36 */ 37 38 #include <sys/param.h> /* DEV_BSIZE */ 39 #include <sys/disklabel.h> 40 #include <sys/dkio.h> 41 #include <sys/ioctl.h> 42 #include <sys/mount.h> 43 #include <sys/stat.h> 44 45 #include <err.h> 46 #include <errno.h> 47 #include <fcntl.h> 48 #include <stdlib.h> 49 #include <stdio.h> 50 #include <stdint.h> 51 #include <string.h> 52 #include <unistd.h> 53 #include <util.h> 54 #include <uuid.h> 55 56 #include "installboot.h" 57 58 #if defined(__aarch64__) 59 #define BOOTEFI_SRC "BOOTAA64.EFI" 60 #define BOOTEFI_DST "bootaa64.efi" 61 #elif defined(__arm__) 62 #define BOOTEFI_SRC "BOOTARM.EFI" 63 #define BOOTEFI_DST "bootarm.efi" 64 #elif defined(__riscv) 65 #define BOOTEFI_SRC "BOOTRISCV64.EFI" 66 #define BOOTEFI_DST "bootriscv64.efi" 67 #else 68 #error "unhandled architecture" 69 #endif 70 71 static int create_filesystem(struct disklabel *, char); 72 static void write_filesystem(struct disklabel *, char); 73 static int findgptefisys(int, struct disklabel *); 74 static int findmbrfat(int, struct disklabel *); 75 76 void 77 md_init(void) 78 { 79 } 80 81 void 82 md_loadboot(void) 83 { 84 } 85 86 void 87 md_prepareboot(int devfd, char *dev) 88 { 89 struct disklabel dl; 90 int part; 91 92 /* Get and check disklabel. */ 93 if (ioctl(devfd, DIOCGDINFO, &dl) == -1) 94 err(1, "disklabel: %s", dev); 95 if (dl.d_magic != DISKMAGIC) 96 errx(1, "bad disklabel magic=0x%08x", dl.d_magic); 97 98 /* Warn on unknown disklabel types. */ 99 if (dl.d_type == 0) 100 warnx("disklabel type unknown"); 101 102 part = findgptefisys(devfd, &dl); 103 if (part != -1) { 104 create_filesystem(&dl, (char)part); 105 return; 106 } 107 108 part = findmbrfat(devfd, &dl); 109 if (part != -1) { 110 create_filesystem(&dl, (char)part); 111 return; 112 } 113 } 114 115 void 116 md_installboot(int devfd, char *dev) 117 { 118 struct disklabel dl; 119 int part; 120 121 /* Get and check disklabel. */ 122 if (ioctl(devfd, DIOCGDINFO, &dl) == -1) 123 err(1, "disklabel: %s", dev); 124 if (dl.d_magic != DISKMAGIC) 125 errx(1, "bad disklabel magic=0x%08x", dl.d_magic); 126 127 /* Warn on unknown disklabel types. */ 128 if (dl.d_type == 0) 129 warnx("disklabel type unknown"); 130 131 part = findgptefisys(devfd, &dl); 132 if (part != -1) { 133 write_filesystem(&dl, (char)part); 134 return; 135 } 136 137 part = findmbrfat(devfd, &dl); 138 if (part != -1) { 139 write_filesystem(&dl, (char)part); 140 return; 141 } 142 } 143 144 static int 145 create_filesystem(struct disklabel *dl, char part) 146 { 147 static char *newfsfmt ="/sbin/newfs_msdos %s >/dev/null"; 148 struct msdosfs_args args; 149 char cmd[60]; 150 int rslt; 151 152 /* Mount <duid>.<part> as msdos filesystem. */ 153 memset(&args, 0, sizeof(args)); 154 rslt = asprintf(&args.fspec, 155 "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx.%c", 156 dl->d_uid[0], dl->d_uid[1], dl->d_uid[2], dl->d_uid[3], 157 dl->d_uid[4], dl->d_uid[5], dl->d_uid[6], dl->d_uid[7], 158 part); 159 if (rslt == -1) { 160 warn("bad special device"); 161 return rslt; 162 } 163 164 rslt = snprintf(cmd, sizeof(cmd), newfsfmt, args.fspec); 165 if (rslt >= sizeof(cmd)) { 166 warnx("can't build newfs command"); 167 rslt = -1; 168 return rslt; 169 } 170 171 if (verbose) 172 fprintf(stderr, "%s %s\n", 173 (nowrite ? "would newfs" : "newfsing"), args.fspec); 174 if (!nowrite) { 175 rslt = system(cmd); 176 if (rslt == -1) { 177 warn("system('%s') failed", cmd); 178 return rslt; 179 } 180 } 181 182 return 0; 183 } 184 185 static void 186 write_filesystem(struct disklabel *dl, char part) 187 { 188 static char *fsckfmt = "/sbin/fsck_msdos %s >/dev/null"; 189 struct msdosfs_args args; 190 char cmd[60]; 191 char dst[PATH_MAX]; 192 char *src; 193 size_t mntlen, pathlen, srclen; 194 int rslt; 195 196 src = NULL; 197 198 /* Create directory for temporary mount point. */ 199 strlcpy(dst, "/tmp/installboot.XXXXXXXXXX", sizeof(dst)); 200 if (mkdtemp(dst) == NULL) 201 err(1, "mkdtemp('%s') failed", dst); 202 mntlen = strlen(dst); 203 204 /* Mount <duid>.<part> as msdos filesystem. */ 205 memset(&args, 0, sizeof(args)); 206 rslt = asprintf(&args.fspec, 207 "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx.%c", 208 dl->d_uid[0], dl->d_uid[1], dl->d_uid[2], dl->d_uid[3], 209 dl->d_uid[4], dl->d_uid[5], dl->d_uid[6], dl->d_uid[7], 210 part); 211 if (rslt == -1) { 212 warn("bad special device"); 213 goto rmdir; 214 } 215 216 args.export_info.ex_root = -2; 217 args.export_info.ex_flags = 0; 218 args.flags = MSDOSFSMNT_LONGNAME; 219 220 if (mount(MOUNT_MSDOS, dst, 0, &args) == -1) { 221 /* Try fsck'ing it. */ 222 rslt = snprintf(cmd, sizeof(cmd), fsckfmt, args.fspec); 223 if (rslt >= sizeof(cmd)) { 224 warnx("can't build fsck command"); 225 rslt = -1; 226 goto rmdir; 227 } 228 rslt = system(cmd); 229 if (rslt == -1) { 230 warn("system('%s') failed", cmd); 231 goto rmdir; 232 } 233 if (mount(MOUNT_MSDOS, dst, 0, &args) == -1) { 234 /* Try newfs'ing it. */ 235 rslt = create_filesystem(dl, part); 236 if (rslt == -1) 237 goto rmdir; 238 rslt = mount(MOUNT_MSDOS, dst, 0, &args); 239 if (rslt == -1) { 240 warn("unable to mount EFI System partition"); 241 goto rmdir; 242 } 243 } 244 } 245 246 /* Create "/efi/boot" directory in <duid>.<part>. */ 247 if (strlcat(dst, "/efi", sizeof(dst)) >= sizeof(dst)) { 248 rslt = -1; 249 warn("unable to build /efi directory"); 250 goto umount; 251 } 252 rslt = mkdir(dst, 0755); 253 if (rslt == -1 && errno != EEXIST) { 254 warn("mkdir('%s') failed", dst); 255 goto umount; 256 } 257 if (strlcat(dst, "/boot", sizeof(dst)) >= sizeof(dst)) { 258 rslt = -1; 259 warn("unable to build /boot directory"); 260 goto umount; 261 } 262 rslt = mkdir(dst, 0755); 263 if (rslt == -1 && errno != EEXIST) { 264 warn("mkdir('%s') failed", dst); 265 goto umount; 266 } 267 268 /* Copy EFI bootblocks to /efi/boot/. */ 269 pathlen = strlen(dst); 270 if (strlcat(dst, "/" BOOTEFI_DST, sizeof(dst)) >= sizeof(dst)) { 271 rslt = -1; 272 warn("unable to build /%s path", BOOTEFI_DST); 273 goto umount; 274 } 275 src = fileprefix(root, "/usr/mdec/" BOOTEFI_SRC); 276 if (src == NULL) { 277 rslt = -1; 278 goto umount; 279 } 280 srclen = strlen(src); 281 if (verbose) 282 fprintf(stderr, "%s %s to %s\n", 283 (nowrite ? "would copy" : "copying"), src, dst); 284 if (!nowrite) { 285 rslt = filecopy(src, dst); 286 if (rslt == -1) 287 goto umount; 288 } 289 290 /* Write /efi/boot/startup.nsh. */ 291 dst[pathlen] = '\0'; 292 if (strlcat(dst, "/startup.nsh", sizeof(dst)) >= sizeof(dst)) { 293 rslt = -1; 294 warn("unable to build /startup.nsh path"); 295 goto umount; 296 } 297 if (verbose) 298 fprintf(stderr, "%s %s\n", 299 (nowrite ? "would write" : "writing"), dst); 300 if (!nowrite) { 301 rslt = fileprintf(dst, "%s\n", BOOTEFI_DST); 302 if (rslt == -1) 303 goto umount; 304 } 305 306 rslt = 0; 307 308 umount: 309 dst[mntlen] = '\0'; 310 if (unmount(dst, MNT_FORCE) == -1) 311 err(1, "unmount('%s') failed", dst); 312 313 rmdir: 314 free(args.fspec); 315 dst[mntlen] = '\0'; 316 if (rmdir(dst) == -1) 317 err(1, "rmdir('%s') failed", dst); 318 319 free(src); 320 321 if (rslt == -1) 322 exit(1); 323 } 324 325 /* 326 * Returns 0 if the MBR with the provided partition array is a GPT protective 327 * MBR, and returns 1 otherwise. A GPT protective MBR would have one and only 328 * one MBR partition, an EFI partition that either covers the whole disk or as 329 * much of it as is possible with a 32bit size field. 330 * 331 * NOTE: MS always uses a size of UINT32_MAX for the EFI partition!** 332 */ 333 static int 334 gpt_chk_mbr(struct dos_partition *dp, u_int64_t dsize) 335 { 336 struct dos_partition *dp2; 337 int efi, found, i; 338 u_int32_t psize; 339 340 found = efi = 0; 341 for (dp2=dp, i=0; i < NDOSPART; i++, dp2++) { 342 if (dp2->dp_typ == DOSPTYP_UNUSED) 343 continue; 344 found++; 345 if (dp2->dp_typ != DOSPTYP_EFI) 346 continue; 347 if (letoh32(dp2->dp_start) != GPTSECTOR) 348 continue; 349 psize = letoh32(dp2->dp_size); 350 if (psize <= (dsize - GPTSECTOR) || psize == UINT32_MAX) 351 efi++; 352 } 353 if (found == 1 && efi == 1) 354 return (0); 355 356 return (1); 357 } 358 359 int 360 findgptefisys(int devfd, struct disklabel *dl) 361 { 362 struct gpt_partition gp[NGPTPARTITIONS]; 363 struct gpt_header gh; 364 struct dos_partition dp[NDOSPART]; 365 struct uuid efisys_uuid; 366 const char efisys_uuid_code[] = GPT_UUID_EFI_SYSTEM; 367 off_t off; 368 ssize_t len; 369 u_int64_t start; 370 int i; 371 uint32_t orig_csum, new_csum; 372 uint32_t ghsize, ghpartsize, ghpartnum, ghpartspersec; 373 u_int8_t *secbuf; 374 375 /* Prepare EFI System UUID */ 376 uuid_dec_be(efisys_uuid_code, &efisys_uuid); 377 378 if ((secbuf = malloc(dl->d_secsize)) == NULL) 379 err(1, NULL); 380 381 /* Check that there is a protective MBR. */ 382 len = pread(devfd, secbuf, dl->d_secsize, 0); 383 if (len != dl->d_secsize) 384 err(4, "can't read mbr"); 385 memcpy(dp, &secbuf[DOSPARTOFF], sizeof(dp)); 386 if (gpt_chk_mbr(dp, DL_GETDSIZE(dl))) { 387 free(secbuf); 388 return (-1); 389 } 390 391 /* Check GPT Header. */ 392 off = dl->d_secsize; /* Read header from sector 1. */ 393 len = pread(devfd, secbuf, dl->d_secsize, off); 394 if (len != dl->d_secsize) 395 err(4, "can't pread gpt header"); 396 397 memcpy(&gh, secbuf, sizeof(gh)); 398 free(secbuf); 399 400 /* Check signature */ 401 if (letoh64(gh.gh_sig) != GPTSIGNATURE) 402 return (-1); 403 404 if (letoh32(gh.gh_rev) != GPTREVISION) 405 return (-1); 406 407 ghsize = letoh32(gh.gh_size); 408 if (ghsize < GPTMINHDRSIZE || ghsize > sizeof(struct gpt_header)) 409 return (-1); 410 411 /* Check checksum */ 412 orig_csum = gh.gh_csum; 413 gh.gh_csum = 0; 414 new_csum = crc32((unsigned char *)&gh, ghsize); 415 gh.gh_csum = orig_csum; 416 if (letoh32(orig_csum) != new_csum) 417 return (-1); 418 419 off = letoh64(gh.gh_part_lba) * dl->d_secsize; 420 ghpartsize = letoh32(gh.gh_part_size); 421 ghpartspersec = dl->d_secsize / ghpartsize; 422 ghpartnum = letoh32(gh.gh_part_num); 423 if ((secbuf = malloc(dl->d_secsize)) == NULL) 424 err(1, NULL); 425 for (i = 0; i < (ghpartnum + ghpartspersec - 1) / ghpartspersec; i++) { 426 len = pread(devfd, secbuf, dl->d_secsize, off); 427 if (len != dl->d_secsize) { 428 free(secbuf); 429 return (-1); 430 } 431 memcpy(gp + i * ghpartspersec, secbuf, 432 ghpartspersec * sizeof(struct gpt_partition)); 433 off += dl->d_secsize; 434 } 435 free(secbuf); 436 new_csum = crc32((unsigned char *)&gp, ghpartnum * ghpartsize); 437 if (new_csum != letoh32(gh.gh_part_csum)) 438 return (-1); 439 440 start = 0; 441 for (i = 0; i < ghpartnum && start == 0; i++) { 442 if (memcmp(&gp[i].gp_type, &efisys_uuid, 443 sizeof(struct uuid)) == 0) 444 start = letoh64(gp[i].gp_lba_start); 445 } 446 447 if (start) { 448 for (i = 0; i < MAXPARTITIONS; i++) { 449 if (DL_GETPSIZE(&dl->d_partitions[i]) > 0 && 450 DL_GETPOFFSET(&dl->d_partitions[i]) == start) 451 return ('a' + i); 452 } 453 } 454 455 return (-1); 456 } 457 458 int 459 findmbrfat(int devfd, struct disklabel *dl) 460 { 461 struct dos_partition dp[NDOSPART]; 462 ssize_t len; 463 u_int64_t start = 0; 464 int i; 465 u_int8_t *secbuf; 466 467 if ((secbuf = malloc(dl->d_secsize)) == NULL) 468 err(1, NULL); 469 470 /* Read MBR. */ 471 len = pread(devfd, secbuf, dl->d_secsize, 0); 472 if (len != dl->d_secsize) 473 err(4, "can't read mbr"); 474 memcpy(dp, &secbuf[DOSPARTOFF], sizeof(dp)); 475 476 for (i = 0; i < NDOSPART; i++) { 477 if (dp[i].dp_typ == DOSPTYP_UNUSED) 478 continue; 479 if (dp[i].dp_typ == DOSPTYP_FAT16L || 480 dp[i].dp_typ == DOSPTYP_FAT32L || 481 dp[i].dp_typ == DOSPTYP_EFISYS) 482 start = dp[i].dp_start; 483 } 484 485 free(secbuf); 486 487 if (start) { 488 for (i = 0; i < MAXPARTITIONS; i++) { 489 if (DL_GETPSIZE(&dl->d_partitions[i]) > 0 && 490 DL_GETPOFFSET(&dl->d_partitions[i]) == start) 491 return ('a' + i); 492 } 493 } 494 495 return (-1); 496 } 497