1 /*        $NetBSD: boot.c,v 1.8 2016/06/11 06:58:42 dholland Exp $    */
2 
3 /*
4  * Copyright (c) 2009 NONAKA Kimihiro <nonaka@netbsd.org>
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 #include <sys/types.h>
29 #include <sys/bootblock.h>
30 #include <sys/boot_flag.h>
31 
32 #include "boot.h"
33 #include "bootinfo.h"
34 #include "bootmenu.h"
35 #include "disk.h"
36 #include "unixdev.h"
37 #include "pathnames.h"
38 
39 #include <lib/libsa/loadfile.h>
40 #include <lib/libsa/ufs.h>
41 
42 #include "compat_linux.h"
43 
44 static const char * const names[][2] = {
45           { "netbsd", "netbsd.gz" },
46           { "netbsd.old", "netbsd.old.gz", },
47           { "onetbsd", "onetbsd.gz" },
48 };
49 
50 char *default_devname;
51 uint default_unit, default_partition;
52 const char *default_filename;
53 int default_timeout = 5;
54 
55 static char probed_disks[256];
56 static char bootconfpath[1024];
57 
58 static void bootcmd_help(char *);
59 static void bootcmd_ls(char *);
60 static void bootcmd_quit(char *);
61 static void bootcmd_boot(char *);
62 static void bootcmd_disk(char *);
63 #ifdef SUPPORT_CONSDEV
64 static void bootcmd_consdev(char *);
65 #endif
66 
67 static const struct bootblk_command {
68           const char *c_name;
69           void (*c_fn)(char *arg);
70 } bootcmds[] = {
71           { "help", bootcmd_help },
72           { "?",              bootcmd_help },
73           { "quit", bootcmd_quit },
74           { "ls",             bootcmd_ls },
75           { "boot", bootcmd_boot },
76           { "disk", bootcmd_disk },
77 #ifdef SUPPORT_CONSDEV
78           { "consdev",        bootcmd_consdev },
79 #endif
80           { NULL,             NULL },
81 };
82 
83 static struct btinfo_howto bi_howto;
84 
85 static void print_banner(void);
86 static int exec_netbsd(const char *file, int howto);
87 
88 int
parsebootfile(const char * fname,char ** fsname,char ** devname,uint * unit,uint * partition,const char ** file)89 parsebootfile(const char *fname, char **fsname, char **devname,
90           uint *unit, uint *partition, const char **file)
91 {
92           const char *col;
93 
94           *fsname = "ufs";
95           *devname = default_devname;
96           *unit = default_unit;
97           *partition = default_partition;
98           *file = default_filename;
99 
100           if (fname == NULL)
101                     return 0;
102 
103           if ((col = strchr(fname, ':'))) {       /* device given */
104                     static char savedevname[MAXDEVNAME+1];
105                     int devlen;
106                     unsigned int u = 0, p = 0;
107                     int i = 0;
108 
109                     devlen = col - fname;
110                     if (devlen > MAXDEVNAME)
111                               return EINVAL;
112 
113 #define isvalidname(c) ((c) >= 'a' && (c) <= 'z')
114                     if (!isvalidname(fname[i]))
115                               return EINVAL;
116                     do {
117                               savedevname[i] = fname[i];
118                               i++;
119                     } while (isvalidname(fname[i]));
120                     savedevname[i] = '\0';
121 
122 #define isnum(c) ((c) >= '0' && (c) <= '9')
123                     if (i < devlen) {
124                               if (!isnum(fname[i]))
125                                         return (EUNIT);
126                               do {
127                                         u *= 10;
128                                         u += fname[i++] - '0';
129                               } while (isnum(fname[i]));
130                     }
131 
132 #define isvalidpart(c) ((c) >= 'a' && (c) < 'a' + MAXPARTITIONS)
133                     if (i < devlen) {
134                               if (!isvalidpart(fname[i]))
135                                         return (EPART);
136                               p = fname[i++] - 'a';
137                     }
138 
139                     if (i != devlen)
140                               return ENXIO;
141 
142                     *devname = savedevname;
143                     *unit = u;
144                     *partition = p;
145                     fname = col + 1;
146           }
147 
148           if (*fname)
149                     *file = fname;
150 
151           return 0;
152 }
153 
154 char *
sprint_bootsel(const char * filename)155 sprint_bootsel(const char *filename)
156 {
157           static char buf[80];
158           char *fsname, *devname;
159           uint unit, partition;
160           const char *file;
161 
162           if (parsebootfile(filename, &fsname, &devname, &unit, &partition,
163               &file) == 0) {
164                     snprintf(buf, sizeof(buf), "%s%d%c:%s", devname, unit,
165                         'a' + partition, file);
166                     return buf;
167           }
168           return "(invalid)";
169 }
170 
171 static void
print_banner(void)172 print_banner(void)
173 {
174           extern const char bootprog_name[];
175           extern const char bootprog_rev[];
176 
177           printf("\n");
178           printf(">> %s, Revision %s\n", bootprog_name, bootprog_rev);
179 }
180 
181 void
boot(dev_t bootdev)182 boot(dev_t bootdev)
183 {
184           extern char twiddle_toggle;
185           int currname;
186           int c;
187 
188           consinit(CONSDEV_GLASS, -1);
189 
190           twiddle_toggle = 1; /* no twiddling until we're ready */
191 
192           /* set default value: hd0a:netbsd */
193           default_devname = "hd";
194           default_unit = 0;
195           default_partition = 0;
196           default_filename = names[0][0];
197 
198           diskprobe(probed_disks, sizeof(probed_disks));
199 
200           snprintf(bootconfpath, sizeof(bootconfpath), "%s%d%c:%s",
201               default_devname, default_unit, 'a' + default_partition,
202               BOOTCFG_FILENAME);
203           parsebootconf(bootconfpath);
204 
205 #ifdef SUPPORT_CONSDEV
206           /*
207            * If console set in boot.cfg, switch to it.
208            * This will print the banner, so we don't need to explicitly do it
209            */
210           if (bootcfg_info.consdev)
211                     bootcmd_consdev(bootcfg_info.consdev);
212           else
213 #endif
214                     print_banner();
215 
216           printf("\ndisks: %s\n", probed_disks);
217 
218           /* Display the menu, if applicable */
219           twiddle_toggle = 0;
220           if (bootcfg_info.nummenu > 0) {
221                     /* Does not return */
222                     doboottypemenu();
223           }
224 
225           printf("Press return to boot now, any other key for boot menu\n");
226           currname = 0;
227           for (currname = 0; currname < __arraycount(names); currname++) {
228                     printf("booting %s - starting in ",
229                         sprint_bootsel(names[currname][0]));
230 
231                     c = awaitkey((bootcfg_info.timeout < 0) ? 0
232                         : bootcfg_info.timeout, 1);
233                     if ((c != '\r') && (c != '\n') && (c != '\0')) {
234                               printf("type \"?\" or \"help\" for help.\n");
235                               bootmenu(); /* does not return */
236                     }
237 
238                     /*
239                      * try pairs of names[] entries, foo and foo.gz
240                      */
241                     /* don't print "booting..." again */
242                     bootit(names[currname][0], 0, 0);
243                     /* since it failed, try compressed bootfile. */
244                     bootit(names[currname][1], 0, 1);
245           }
246 
247           bootmenu(); /* does not return */
248 }
249 
250 void
bootit(const char * filename,int howto,int tell)251 bootit(const char *filename, int howto, int tell)
252 {
253 
254           if (tell) {
255                     printf("booting %s", sprint_bootsel(filename));
256                     if (howto)
257                               printf(" (howto 0x%x)", howto);
258                     printf("\n");
259           }
260 
261           if (exec_netbsd(filename, howto) < 0) {
262                     printf("boot: %s: %s\n", sprint_bootsel(filename),
263                            strerror(errno));
264           } else {
265                     printf("boot returned\n");
266           }
267 }
268 
269 static int
exec_netbsd(const char * file,int howto)270 exec_netbsd(const char *file, int howto)
271 {
272           u_long marks[MARK_MAX];
273 
274           BI_ALLOC(BTINFO_MAX);
275 
276           bi_howto.howto = howto;
277           BI_ADD(&bi_howto, BTINFO_HOWTO, sizeof(bi_howto));
278 
279           if (loadfile_zboot(file, marks, LOAD_KERNEL) == -1)
280                     goto out;
281 
282           /*NOTREACHED*/
283           return 0;
284 
285 out:
286           BI_FREE();
287           bootinfo = 0;
288           return -1;
289 }
290 
291 /*
292  * bootmenu
293  */
294 static char *gettrailer(char *arg);
295 static int parseopts(const char *opts, int *howto);
296 static int parseboot(char *arg, char **filename, int *howto);
297 
298 /* ARGSUSED */
299 static void
bootcmd_help(char * arg)300 bootcmd_help(char *arg)
301 {
302 
303           printf("commands are:\n"
304               "boot [xdNx:][filename] [-1acdqsv]\n"
305               "     (ex. \"boot hd0a:netbsd.old -s\")\n"
306               "     (ex. \"boot path:/mnt/card/netbsd -1\")\n"
307               "ls [path]\n"
308 #ifdef SUPPORT_CONSDEV
309               "consdev {glass|com [speed]}\n"
310 #endif
311               "disk\n"
312               "help|?\n"
313               "quit\n");
314 }
315 
316 /* ARGSUSED */
317 static void
bootcmd_quit(char * arg)318 bootcmd_quit(char *arg)
319 {
320 
321           printf("Exiting...\n");
322           exit(0);
323           /*NOTREACHED*/
324 }
325 
326 static void
bootcmd_ls(char * arg)327 bootcmd_ls(char *arg)
328 {
329           const char *save = default_filename;
330 
331           default_filename = "/";
332           ls(arg);
333           default_filename = save;
334 }
335 
336 static void
bootcmd_boot(char * arg)337 bootcmd_boot(char *arg)
338 {
339           char *filename;
340           int howto;
341 
342           if (parseboot(arg, &filename, &howto)) {
343                     bootit(filename, howto, 1);
344           }
345 }
346 
347 /* ARGSUSED */
348 static void
bootcmd_disk(char * arg)349 bootcmd_disk(char *arg)
350 {
351 
352           printf("disks: %s\n", probed_disks);
353 }
354 
355 #ifdef SUPPORT_CONSDEV
356 static const struct cons_devs {
357           const char          *name;
358           int                 tag;
359 } cons_devs[] = {
360           { "glass",          CONSDEV_GLASS },
361           { "com",  CONSDEV_COM0 },
362           { "com0", CONSDEV_COM0 },
363           { NULL,             0 }
364 };
365 
366 static void
bootcmd_consdev(char * arg)367 bootcmd_consdev(char *arg)
368 {
369           const struct cons_devs *cdp;
370           char *p;
371           int speed = 9600;
372 
373           p = strchr(arg, ' ');
374           if (p != NULL) {
375                     *p++ = '\0';
376                     speed = atoi(p);
377           }
378           for (cdp = cons_devs; cdp->name != NULL; cdp++) {
379                     if (strcmp(arg, cdp->name) == 0) {
380                               consinit(cdp->tag, speed);
381                               print_banner();
382                               return;
383                     }
384           }
385           printf("invalid console device.\n");
386 }
387 #endif
388 
389 void
docommand(char * arg)390 docommand(char *arg)
391 {
392           char *options;
393           int i;
394 
395           options = gettrailer(arg);
396 
397           for (i = 0; bootcmds[i].c_name != NULL; i++) {
398                     if (strcmp(arg, bootcmds[i].c_name) == 0) {
399                               (*bootcmds[i].c_fn)(options);
400                               return;
401                     }
402           }
403 
404           printf("unknown command\n");
405           bootcmd_help(NULL);
406 }
407 
408 void
bootmenu(void)409 bootmenu(void)
410 {
411           char input[256];
412           char *c;
413 
414           for (;;) {
415                     c = input;
416 
417                     input[0] = '\0';
418                     printf("> ");
419                     kgets(input, sizeof(input));
420 
421                     /*
422                      * Skip leading whitespace.
423                      */
424                     while (*c == ' ') {
425                               c++;
426                     }
427                     if (*c != '\0') {
428                               docommand(c);
429                     }
430           }
431 }
432 
433 /*
434  * chops the head from the arguments and returns the arguments if any,
435  * or possibly an empty string.
436  */
437 static char *
gettrailer(char * arg)438 gettrailer(char *arg)
439 {
440           char *options;
441 
442           if ((options = strchr(arg, ' ')) == NULL)
443                     return ("");
444           else
445                     *options++ = '\0';
446 
447           /* trim leading blanks */
448           while (*options == ' ')
449                     options++;
450 
451           return options;
452 }
453 
454 static int
parseopts(const char * opts,int * howto)455 parseopts(const char *opts, int *howto)
456 {
457           int r, tmpopt = 0;
458 
459           opts++;   /* skip - */
460           while (*opts && *opts != ' ') {
461                     r = 0;
462                     BOOT_FLAG(*opts, r);
463                     if (r == 0) {
464                               printf("-%c: unknown flag\n", *opts);
465                               bootcmd_help(NULL);
466                               return 0;
467                     }
468                     tmpopt |= r;
469                     opts++;
470           }
471 
472           *howto = tmpopt;
473           return 1;
474 }
475 
476 static int
parseboot(char * arg,char ** filename,int * howto)477 parseboot(char *arg, char **filename, int *howto)
478 {
479           char *opts = NULL;
480 
481           *filename = 0;
482           *howto = 0;
483 
484           /* if there were no arguments */
485           if (arg == NULL || *arg == '\0')
486                     return 1;
487 
488           /* format is... */
489           /* [[xxNx:]filename] [-adqsv] */
490 
491           /* check for just args */
492           if (arg[0] == '-') {
493                     opts = arg;
494           } else {
495                     /* there's a file name */
496                     *filename = arg;
497 
498                     opts = gettrailer(arg);
499                     if (opts == NULL || *opts == '\0') {
500                               opts = NULL;
501                     } else if (*opts != '-') {
502                               printf("invalid arguments\n");
503                               bootcmd_help(NULL);
504                               return 0;
505                     }
506           }
507 
508           /* at this point, we have dealt with filenames. */
509 
510           /* now, deal with options */
511           if (opts) {
512                     if (parseopts(opts, howto) == 0) {
513                               return 0;
514                     }
515           }
516           return 1;
517 }
518