1/* $NetBSD: installboot.c,v 1.41 2022/07/10 19:28:00 brook Exp $ */ 2 3/*- 4 * Copyright (c) 2002 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Luke Mewburn of Wasabi Systems. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32#if HAVE_NBTOOL_CONFIG_H 33#include "nbtool_config.h" 34#endif 35 36#include <sys/cdefs.h> 37#if !defined(__lint) 38__RCSID("$NetBSD: installboot.c,v 1.41 2022/07/10 19:28:00 brook Exp $"); 39#endif /* !__lint */ 40 41#include <sys/param.h> 42#include <sys/ioctl.h> 43#include <sys/utsname.h> 44 45#include <assert.h> 46#include <err.h> 47#include <fcntl.h> 48#include <limits.h> 49#include <stdio.h> 50#include <stdlib.h> 51#include <stddef.h> 52#include <string.h> 53#include <unistd.h> 54#if !HAVE_NBTOOL_CONFIG_H 55#include <util.h> 56#endif 57 58#include "installboot.h" 59#include "evboards.h" 60 61static void getmachine(ib_params *, const char *, const char *); 62static void getfstype(ib_params *, const char *, const char *); 63static void getubootpaths(ib_params *, const char *); 64static void parseoptions(ib_params *, const char *); 65__dead static void usage(void); 66static void options_usage(void); 67static void machine_usage(void); 68static void fstype_usage(void); 69 70static ib_params installboot_params; 71 72#define OFFSET(field) offsetof(ib_params, field) 73const struct option { 74 const char *name; /* Name of option */ 75 ib_flags flag; /* Corresponding IB_xxx flag */ 76 enum { /* Type of option value... */ 77 OPT_BOOL, /* no value */ 78 OPT_INT, /* numeric value */ 79 OPT_WORD, /* space/tab/, terminated */ 80 OPT_STRING /* null terminated */ 81 } type; 82 int offset; /* of field in ib_params */ 83} options[] = { 84 { "alphasum", IB_ALPHASUM, OPT_BOOL, 0 }, 85 { "append", IB_APPEND, OPT_BOOL, 0 }, 86 { "command", IB_COMMAND, OPT_STRING, OFFSET(command) }, 87 { "console", IB_CONSOLE, OPT_WORD, OFFSET(console) }, 88 { "ioaddr", IB_CONSADDR, OPT_INT, OFFSET(consaddr) }, 89 { "keymap", IB_KEYMAP, OPT_WORD, OFFSET(keymap) }, 90 { "password", IB_PASSWORD, OPT_WORD, OFFSET(password) }, 91 { "resetvideo", IB_RESETVIDEO, OPT_BOOL, 0 }, 92 { "speed", IB_CONSPEED, OPT_INT, OFFSET(conspeed) }, 93 { "sunsum", IB_SUNSUM, OPT_BOOL, 0 }, 94 { "timeout", IB_TIMEOUT, OPT_INT, OFFSET(timeout) }, 95 { "modules", IB_MODULES, OPT_BOOL, 0 }, 96 { "bootconf", IB_BOOTCONF, OPT_BOOL, 0 }, 97 { "board", IB_BOARD, OPT_STRING, OFFSET(board) }, 98 { "dtb", IB_DTB, OPT_STRING, OFFSET(dtb) }, 99 { "media", IB_MEDIA, OPT_WORD, OFFSET(media) }, 100 { .name = NULL }, 101}; 102#undef OFFSET 103#define OPTION(params, type, opt) (*(type *)((char *)(params) + (opt)->offset)) 104 105#define DFL_SECSIZE 512 /* Don't use DEV_BSIZE. It's host's value. */ 106 107#ifndef DEFAULT_UBOOT_PKG_PATH 108#define DEFAULT_UBOOT_PKG_PATH "/usr/pkg/share/u-boot" 109#endif 110 111#ifndef UBOOT_PATHS_ENV_VAR 112#define UBOOT_PATHS_ENV_VAR "INSTALLBOOT_UBOOT_PATHS" 113#endif 114 115int 116main(int argc, char *argv[]) 117{ 118 struct utsname utsname; 119 ib_params *params; 120 unsigned long lval; 121 int ch, rv, mode; 122 char *p; 123 const char *op; 124 ib_flags unsupported_flags; 125#if !HAVE_NBTOOL_CONFIG_H 126 char specname[MAXPATHLEN]; 127 char rawname[MAXPATHLEN]; 128 const char *special, *raw; 129#endif 130 131 setprogname(argv[0]); 132 params = &installboot_params; 133 memset(params, 0, sizeof(*params)); 134 params->fsfd = -1; 135 params->s1fd = -1; 136 if ((p = getenv("MACHINE")) != NULL) 137 getmachine(params, p, "$MACHINE"); 138 getubootpaths(params, DEFAULT_UBOOT_PKG_PATH); 139 if ((p = getenv(UBOOT_PATHS_ENV_VAR)) != NULL) { 140 getubootpaths(params, p); 141 } 142 143 while ((ch = getopt(argc, argv, "b:B:cefm:no:t:u:v")) != -1) { 144 switch (ch) { 145 146 case 'b': 147 case 'B': 148 if (*optarg == '\0') 149 goto badblock; 150 lval = strtoul(optarg, &p, 0); 151 if (lval > UINT32_MAX || *p != '\0') { 152 badblock: 153 errx(1, "Invalid block number `%s'", optarg); 154 } 155 if (ch == 'b') { 156 params->s1start = (uint32_t)lval; 157 params->flags |= IB_STAGE1START; 158 } else { 159 params->s2start = (uint32_t)lval; 160 params->flags |= IB_STAGE2START; 161 } 162 break; 163 164 case 'c': 165 params->flags |= IB_CLEAR; 166 break; 167 168 case 'e': 169 params->flags |= IB_EDIT; 170 break; 171 172 case 'f': 173 params->flags |= IB_FORCE; 174 break; 175 176 case 'm': 177 getmachine(params, optarg, "-m"); 178 break; 179 180 case 'n': 181 params->flags |= IB_NOWRITE; 182 break; 183 184 case 'o': 185 parseoptions(params, optarg); 186 break; 187 188 case 't': 189 getfstype(params, optarg, "-t"); 190 break; 191 192 case 'u': 193 getubootpaths(params, optarg); 194 break; 195 196 case 'v': 197 params->flags |= IB_VERBOSE; 198 break; 199 200 case '?': 201 default: 202 usage(); 203 /* NOTREACHED */ 204 205 } 206 } 207 argc -= optind; 208 argv += optind; 209 210 if (params->flags & IB_CLEAR && params->flags & IB_EDIT) 211 usage(); 212 if (argc < 1 || argc + 2 * !!(params->flags & (IB_CLEAR | IB_EDIT)) > 3) 213 usage(); 214 215 /* set missing defaults */ 216 if (params->machine == NULL) { 217 if (uname(&utsname) == -1) 218 err(1, "Determine uname"); 219 getmachine(params, utsname.machine, "uname()"); 220 } 221 222 /* Check that options are supported by this system */ 223 unsupported_flags = params->flags & ~params->machine->valid_flags; 224 unsupported_flags &= ~(IB_VERBOSE | IB_NOWRITE | IB_CLEAR | IB_EDIT 225 | IB_FORCE); 226 if (unsupported_flags != 0) { 227 int ndx; 228 for (ndx = 0; options[ndx].name != NULL; ndx++) { 229 if (unsupported_flags & options[ndx].flag) { 230 unsupported_flags &= ~options[ndx].flag; 231 warnx("`-o %s' is not supported for %s", 232 options[ndx].name, params->machine->name); 233 } 234 } 235 if (unsupported_flags & IB_STAGE1START) 236 warnx("`-b bno' is not supported for %s", 237 params->machine->name); 238 if (unsupported_flags & IB_STAGE2START) 239 warnx("`-B bno' is not supported for %s", 240 params->machine->name); 241 unsupported_flags &= ~(IB_STAGE1START | IB_STAGE2START); 242 if (unsupported_flags != 0) 243 warnx("Unknown unsupported flag %#x (coding error!)", 244 unsupported_flags); 245 exit(1); 246 } 247 /* and some illegal combinations */ 248 if (params->flags & IB_STAGE1START && params->flags & IB_APPEND) { 249 warnx("Can't use `-b bno' with `-o append'"); 250 exit(1); 251 } 252 if (params->flags & IB_CLEAR && 253 params->flags & (IB_STAGE1START | IB_STAGE2START | IB_APPEND)) { 254 warnx("Can't use `-b bno', `-B bno' or `-o append' with `-c'"); 255 exit(1); 256 } 257 258 if (argc >= 3) { 259 params->stage2 = argv[2]; 260 } 261 262#if !HAVE_NBTOOL_CONFIG_H 263 special = getfsspecname(specname, sizeof(specname), argv[0]); 264 if (special == NULL) 265 err(1, "%s: %s", argv[0], specname); 266 raw = getdiskrawname(rawname, sizeof(rawname), special); 267 if (raw != NULL) 268 special = raw; 269 params->filesystem = special; 270#else 271 params->filesystem = argv[0]; 272#endif 273 274 if (params->flags & IB_NOWRITE) { 275 op = "only"; 276 mode = O_RDONLY; 277 } else { 278 op = "write"; 279 mode = O_RDWR; 280 } 281 /* XXX should be specified via option */ 282 params->sectorsize = DFL_SECSIZE; 283 if ((params->fsfd = open(params->filesystem, mode, 0600)) == -1) 284 err(1, "Opening file system `%s' read-%s", 285 params->filesystem, op); 286 if (fstat(params->fsfd, ¶ms->fsstat) == -1) 287 err(1, "Examining file system `%s'", params->filesystem); 288 if (params->fstype != NULL) { 289 if (! params->fstype->match(params)) 290 errx(1, "File system `%s' is not of type %s", 291 params->filesystem, params->fstype->name); 292 } else { 293 if (params->stage2 != NULL) { 294 params->fstype = &fstypes[0]; 295 while (params->fstype->name != NULL && 296 !params->fstype->match(params)) 297 params->fstype++; 298 if (params->fstype->name == NULL) 299 errx(1, "File system `%s' is of an unknown type", 300 params->filesystem); 301 } 302 } 303 304 assert(params->machine != NULL); 305 306 if (argc >= 2) { 307 if ((params->s1fd = open(argv[1], O_RDONLY, 0600)) == -1) 308 err(1, "Opening primary bootstrap `%s'", argv[1]); 309 if (fstat(params->s1fd, ¶ms->s1stat) == -1) 310 err(1, "Examining primary bootstrap `%s'", argv[1]); 311 if (!S_ISREG(params->s1stat.st_mode)) { 312 /* 313 * If the platform uses u-boot, then the stage1 314 * spec might be the directory where the u-boot 315 * binaries for the system are located. 316 */ 317 if (params->machine->mach_flags & MF_UBOOT) { 318 if (!S_ISDIR(params->s1stat.st_mode)) { 319 errx(1, "`%s' must be a regular file " 320 "or a directory", argv[1]); 321 } 322 (void) close(params->s1fd); 323 params->s1fd = -1; 324 } else { 325 errx(1, "`%s' must be a regular file", argv[1]); 326 } 327 } 328 params->stage1 = argv[1]; 329 } 330 331 if (params->flags & IB_VERBOSE) { 332 printf("File system: %s\n", params->filesystem); 333 if (params->fstype) 334 printf("File system type: %s (blocksize %u, " 335 "needswap %d)\n", 336 params->fstype->name, params->fstype->blocksize, 337 params->fstype->needswap); 338 if (!(params->flags & IB_EDIT)) 339 printf("Primary bootstrap: %s%s\n", 340 (params->flags & IB_CLEAR) ? "(to be cleared)" 341 : params->stage1 ? params->stage1 : "(none)", 342 S_ISDIR(params->s1stat.st_mode) ? " (directory)" 343 : ""); 344 if (params->stage2 != NULL) 345 printf("Secondary bootstrap: %s\n", params->stage2); 346 } 347 348 if (params->flags & IB_EDIT) { 349 op = "Edit"; 350 rv = params->machine->editboot(params); 351 } else if (params->flags & IB_CLEAR) { 352 op = "Clear"; 353 rv = params->machine->clearboot(params); 354 } else { 355 if (argc < 2) { 356 /* 357 * If the platform uses u-boot, then the stage1 spec is 358 * optional iff they specified a board (because we can 359 * infer a default location for u-boot binaries if the 360 * board type is given). 361 */ 362 if (!(params->machine->mach_flags & MF_UBOOT)) { 363 errx(EXIT_FAILURE, 364 "Please specify the primary bootstrap file"); 365 } 366 } 367 op = "Set"; 368 rv = params->machine->setboot(params); 369 } 370 if (rv == 0) 371 errx(1, "%s bootstrap operation failed", op); 372 373 if (S_ISREG(params->fsstat.st_mode)) { 374 if (fsync(params->fsfd) == -1) 375 err(1, "Synchronising file system `%s'", 376 params->filesystem); 377 } else { 378 /* Sync filesystems (to clean in-memory superblock?) */ 379 sync(); 380 } 381 if (close(params->fsfd) == -1) 382 err(1, "Closing file system `%s'", params->filesystem); 383 if (params->s1fd != -1) 384 if (close(params->s1fd) == -1) 385 err(1, "Closing primary bootstrap `%s'", 386 params->stage1); 387 388 exit(0); 389 /* NOTREACHED */ 390} 391 392static void 393parseoptions(ib_params *params, const char *option) 394{ 395 char *cp; 396 const struct option *opt; 397 int len; 398 unsigned long val; 399 400 assert(params != NULL); 401 assert(option != NULL); 402 403 for (;; option += len) { 404 option += strspn(option, ", \t"); 405 if (*option == 0) 406 return; 407 len = strcspn(option, "=,"); 408 for (opt = options; opt->name != NULL; opt++) { 409 if (memcmp(option, opt->name, len) == 0 410 && opt->name[len] == 0) 411 break; 412 } 413 if (opt->name == NULL) { 414 len = strcspn(option, ","); 415 warnx("Unknown option `-o %.*s'", len, option); 416 break; 417 } 418 params->flags |= opt->flag; 419 if (opt->type == OPT_BOOL) { 420 if (option[len] != '=') 421 continue; 422 warnx("Option `%s' must not have a value", opt->name); 423 break; 424 } 425 if (option[len] != '=') { 426 warnx("Option `%s' must have a value", opt->name); 427 break; 428 } 429 option += len + 1; 430 len = strcspn(option, ","); 431 switch (opt->type) { 432 case OPT_STRING: 433 len = strlen(option); 434 /* FALLTHROUGH */ 435 case OPT_WORD: 436 cp = strdup(option); 437 if (cp == NULL) 438 err(1, "strdup"); 439 cp[len] = 0; 440 OPTION(params, char *, opt) = cp; 441 continue; 442 case OPT_INT: 443 val = strtoul(option, &cp, 0); 444 if (cp > option + len || (*cp != 0 && *cp != ',')) 445 break; 446 if (val > INT_MAX) 447 break; 448 OPTION(params, int, opt) = (int)val; 449 continue; 450 default: 451 errx(1, "Internal error: option `%s' has invalid type %d", 452 opt->name, opt->type); 453 } 454 warnx("Invalid option value `%s=%.*s'", opt->name, len, option); 455 break; 456 } 457 options_usage(); 458 exit(1); 459} 460 461static void 462options_usage(void) 463{ 464 int ndx; 465 const char *pfx; 466 467 warnx("Valid options are:"); 468 pfx = "\t"; 469 for (ndx = 0; options[ndx].name != 0; ndx++) { 470 fprintf(stderr, "%s%s", pfx, options[ndx].name); 471 switch (options[ndx].type) { 472 case OPT_INT: 473 fprintf(stderr, "=number"); 474 break; 475 case OPT_WORD: 476 fprintf(stderr, "=word"); 477 break; 478 case OPT_STRING: 479 fprintf(stderr, "=string"); 480 break; 481 default: 482 break; 483 } 484 if ((ndx % 5) == 4) 485 pfx = ",\n\t"; 486 else 487 pfx = ", "; 488 } 489 fprintf(stderr, "\n"); 490} 491 492int 493no_setboot(ib_params *params) 494{ 495 496 assert(params != NULL); 497 498 warnx("%s: bootstrap installation is not supported", 499 params->machine->name); 500 return (0); 501} 502 503int 504no_clearboot(ib_params *params) 505{ 506 507 assert(params != NULL); 508 509 warnx("%s: bootstrap removal is not supported", 510 params->machine->name); 511 return (0); 512} 513 514int 515no_editboot(ib_params *params) 516{ 517 518 assert(params != NULL); 519 520 warnx("%s: bootstrap editing is not supported", 521 params->machine->name); 522 return (0); 523} 524 525 526static void 527getmachine(ib_params *param, const char *mach, const char *provider) 528{ 529 int i; 530 531 assert(param != NULL); 532 assert(mach != NULL); 533 assert(provider != NULL); 534 535 for (i = 0; machines[i] != NULL; i++) { 536 if (machines[i]->name == NULL) 537 continue; 538 if (strcmp(machines[i]->name, mach) == 0) { 539 param->machine = machines[i]; 540 return; 541 } 542 } 543 warnx("Invalid machine `%s' from %s", mach, provider); 544 machine_usage(); 545 exit(1); 546} 547 548static void 549machine_usage(void) 550{ 551 const char *prefix; 552 int i; 553 int col, len; 554 const char *name; 555 int wincol=80; 556#ifdef TIOCGWINSZ 557 struct winsize win; 558 559 if (ioctl(fileno(stderr), TIOCGWINSZ, &win) == 0 && win.ws_col > 0) 560 wincol = win.ws_col; 561#endif 562 563 warnx("Supported machines are:"); 564 prefix="\t"; 565 col = 8 + 3; 566 for (i = 0; machines[i] != NULL; i++) { 567 name = machines[i]->name; 568 if (name == NULL) 569 continue; 570 len = strlen(name); 571 if (col + len > wincol) { 572 prefix=",\n\t"; 573 col = -2 + 8 + 3; 574 } 575 col += fprintf(stderr, "%s%s", prefix, name); 576 prefix=", "; 577 } 578 fputs("\n", stderr); 579} 580 581static void 582getfstype(ib_params *param, const char *fstype, const char *provider) 583{ 584 int i; 585 586 assert(param != NULL); 587 assert(fstype != NULL); 588 assert(provider != NULL); 589 590 for (i = 0; fstypes[i].name != NULL; i++) { 591 if (strcmp(fstypes[i].name, fstype) == 0) { 592 param->fstype = &fstypes[i]; 593 return; 594 } 595 } 596 warnx("Invalid file system type `%s' from %s", fstype, provider); 597 fstype_usage(); 598 exit(1); 599} 600 601static void 602fstype_usage(void) 603{ 604#ifndef NO_STAGE2 605 const char *prefix; 606 int i; 607 608 warnx("Supported file system types are:"); 609#define FSTYPES_PER_LINE 9 610 prefix="\t"; 611 for (i = 0; fstypes[i].name != NULL; i++) { 612 if (i && (i % FSTYPES_PER_LINE) == 0) 613 prefix=",\n\t"; 614 fprintf(stderr, "%s%s", prefix, fstypes[i].name); 615 prefix=", "; 616 } 617 fputs("\n", stderr); 618#endif 619} 620 621static void 622getubootpaths(ib_params *param, const char *paths) 623{ 624 assert(param != NULL); 625 assert(paths != NULL); 626 627 param->uboot_paths = paths; 628} 629 630static void 631usage(void) 632{ 633 const char *prog; 634 635 prog = getprogname(); 636 fprintf(stderr, 637"usage: %s [-fnv] [-B s2bno] [-b s1bno] [-m machine] [-o options]\n" 638"\t\t [-t fstype] [-u uboot-paths] filesystem primary [secondary]\n" 639"usage: %s -c [-fnv] [-m machine] [-o options] [-t fstype] filesystem\n" 640"usage: %s -e [-fnv] [-m machine] [-o options] bootstrap\n", 641 prog, prog, prog); 642 machine_usage(); 643 fstype_usage(); 644 options_usage(); 645 if (installboot_params.machine != NULL && 646 installboot_params.machine->usage != NULL) 647 installboot_params.machine->usage(&installboot_params); 648 exit(1); 649} 650