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