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 
61 static    void      getmachine(ib_params *, const char *, const char *);
62 static    void      getfstype(ib_params *, const char *, const char *);
63 static    void      getubootpaths(ib_params *, const char *);
64 static    void      parseoptions(ib_params *, const char *);
65 __dead static       void      usage(void);
66 static    void      options_usage(void);
67 static    void      machine_usage(void);
68 static    void      fstype_usage(void);
69 
70 static    ib_params installboot_params;
71 
72 #define OFFSET(field)    offsetof(ib_params, field)
73 const 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 
115 int
main(int argc,char * argv[])116 main(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, &params->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, &params->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 
392 static void
parseoptions(ib_params * params,const char * option)393 parseoptions(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 
461 static void
options_usage(void)462 options_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 
492 int
no_setboot(ib_params * params)493 no_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 
503 int
no_clearboot(ib_params * params)504 no_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 
514 int
no_editboot(ib_params * params)515 no_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 
526 static void
getmachine(ib_params * param,const char * mach,const char * provider)527 getmachine(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 
548 static void
machine_usage(void)549 machine_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 
581 static void
getfstype(ib_params * param,const char * fstype,const char * provider)582 getfstype(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 
601 static void
fstype_usage(void)602 fstype_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 
621 static void
getubootpaths(ib_params * param,const char * paths)622 getubootpaths(ib_params *param, const char *paths)
623 {
624           assert(param != NULL);
625           assert(paths != NULL);
626 
627           param->uboot_paths = paths;
628 }
629 
630 static void
usage(void)631 usage(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