boot.c revision 1.5 1 /* $NetBSD: boot.c,v 1.5 2012/01/18 23:12:21 nonaka Exp $ */
2
3 /*
4 * Copyright (c) 2009 NONAKA Kimihiro <nonaka (at) 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
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 *
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
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
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 _PATH_BOOTCONF);
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 (bootconf.consdev)
211 bootcmd_consdev(bootconf.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 (bootconf.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((bootconf.timeout < 0) ? 0 : bootconf.timeout, 1);
232 if ((c != '\r') && (c != '\n') && (c != '\0')) {
233 printf("type \"?\" or \"help\" for help.\n");
234 bootmenu(); /* does not return */
235 }
236
237 /*
238 * try pairs of names[] entries, foo and foo.gz
239 */
240 /* don't print "booting..." again */
241 bootit(names[currname][0], 0, 0);
242 /* since it failed, try compressed bootfile. */
243 bootit(names[currname][1], 0, 1);
244 }
245
246 bootmenu(); /* does not return */
247 }
248
249 void
250 bootit(const char *filename, int howto, int tell)
251 {
252
253 if (tell) {
254 printf("booting %s", sprint_bootsel(filename));
255 if (howto)
256 printf(" (howto 0x%x)", howto);
257 printf("\n");
258 }
259
260 if (exec_netbsd(filename, howto) < 0) {
261 printf("boot: %s: %s\n", sprint_bootsel(filename),
262 strerror(errno));
263 } else {
264 printf("boot returned\n");
265 }
266 }
267
268 static int
269 exec_netbsd(const char *file, int howto)
270 {
271 u_long marks[MARK_MAX];
272
273 BI_ALLOC(BTINFO_MAX);
274
275 bi_howto.howto = howto;
276 BI_ADD(&bi_howto, BTINFO_HOWTO, sizeof(bi_howto));
277
278 if (loadfile_zboot(file, marks, LOAD_KERNEL) == -1)
279 goto out;
280
281 /*NOTREACHED*/
282 return 0;
283
284 out:
285 BI_FREE();
286 bootinfo = 0;
287 return -1;
288 }
289
290 /*
291 * bootmenu
292 */
293 static char *gettrailer(char *arg);
294 static int parseopts(const char *opts, int *howto);
295 static int parseboot(char *arg, char **filename, int *howto);
296
297 /* ARGSUSED */
298 static void
299 bootcmd_help(char *arg)
300 {
301
302 printf("commands are:\n"
303 "boot [xdNx:][filename] [-1acdqsv]\n"
304 " (ex. \"boot hd0a:netbsd.old -s\")\n"
305 " (ex. \"boot path:/mnt/card/netbsd -1\")\n"
306 "ls [path]\n"
307 #ifdef SUPPORT_CONSDEV
308 "consdev {glass|com [speed]}\n"
309 #endif
310 "disk\n"
311 "help|?\n"
312 "quit\n");
313 }
314
315 /* ARGSUSED */
316 static void
317 bootcmd_quit(char *arg)
318 {
319
320 printf("Exiting...\n");
321 exit(0);
322 /*NOTREACHED*/
323 }
324
325 static void
326 bootcmd_ls(char *arg)
327 {
328 const char *save = default_filename;
329
330 default_filename = "/";
331 ls(arg);
332 default_filename = save;
333 }
334
335 static void
336 bootcmd_boot(char *arg)
337 {
338 char *filename;
339 int howto;
340
341 if (parseboot(arg, &filename, &howto)) {
342 bootit(filename, howto, 1);
343 }
344 }
345
346 /* ARGSUSED */
347 static void
348 bootcmd_disk(char *arg)
349 {
350
351 printf("disks: %s\n", probed_disks);
352 }
353
354 #ifdef SUPPORT_CONSDEV
355 static const struct cons_devs {
356 const char *name;
357 int tag;
358 } cons_devs[] = {
359 { "glass", CONSDEV_GLASS },
360 { "com", CONSDEV_COM0 },
361 { "com0", CONSDEV_COM0 },
362 { NULL, 0 }
363 };
364
365 static void
366 bootcmd_consdev(char *arg)
367 {
368 const struct cons_devs *cdp;
369 char *p;
370 int speed = 9600;
371
372 p = strchr(arg, ' ');
373 if (p != NULL) {
374 *p++ = '\0';
375 speed = atoi(p);
376 }
377 for (cdp = cons_devs; cdp->name != NULL; cdp++) {
378 if (strcmp(arg, cdp->name) == 0) {
379 consinit(cdp->tag, speed);
380 print_banner();
381 return;
382 }
383 }
384 printf("invalid console device.\n");
385 }
386 #endif
387
388 void
389 docommand(char *arg)
390 {
391 char *options;
392 int i;
393
394 options = gettrailer(arg);
395
396 for (i = 0; bootcmds[i].c_name != NULL; i++) {
397 if (strcmp(arg, bootcmds[i].c_name) == 0) {
398 (*bootcmds[i].c_fn)(options);
399 return;
400 }
401 }
402
403 printf("unknown command\n");
404 bootcmd_help(NULL);
405 }
406
407 void
408 bootmenu(void)
409 {
410 char input[256];
411 char *c;
412
413 for (;;) {
414 c = input;
415
416 input[0] = '\0';
417 printf("> ");
418 gets(input);
419
420 /*
421 * Skip leading whitespace.
422 */
423 while (*c == ' ') {
424 c++;
425 }
426 if (*c != '\0') {
427 docommand(c);
428 }
429 }
430 }
431
432 /*
433 * chops the head from the arguments and returns the arguments if any,
434 * or possibly an empty string.
435 */
436 static char *
437 gettrailer(char *arg)
438 {
439 char *options;
440
441 if ((options = strchr(arg, ' ')) == NULL)
442 return ("");
443 else
444 *options++ = '\0';
445
446 /* trim leading blanks */
447 while (*options && *options == ' ')
448 options++;
449
450 return options;
451 }
452
453 static int
454 parseopts(const char *opts, int *howto)
455 {
456 int r, tmpopt = 0;
457
458 opts++; /* skip - */
459 while (*opts && *opts != ' ') {
460 r = 0;
461 BOOT_FLAG(*opts, r);
462 if (r == 0) {
463 printf("-%c: unknown flag\n", *opts);
464 bootcmd_help(NULL);
465 return 0;
466 }
467 tmpopt |= r;
468 opts++;
469 }
470
471 *howto = tmpopt;
472 return 1;
473 }
474
475 static int
476 parseboot(char *arg, char **filename, int *howto)
477 {
478 char *opts = NULL;
479
480 *filename = 0;
481 *howto = 0;
482
483 /* if there were no arguments */
484 if (arg == NULL || *arg == '\0')
485 return 1;
486
487 /* format is... */
488 /* [[xxNx:]filename] [-adqsv] */
489
490 /* check for just args */
491 if (arg[0] == '-') {
492 opts = arg;
493 } else {
494 /* there's a file name */
495 *filename = arg;
496
497 opts = gettrailer(arg);
498 if (opts == NULL || *opts == '\0') {
499 opts = NULL;
500 } else if (*opts != '-') {
501 printf("invalid arguments\n");
502 bootcmd_help(NULL);
503 return 0;
504 }
505 }
506
507 /* at this point, we have dealt with filenames. */
508
509 /* now, deal with options */
510 if (opts) {
511 if (parseopts(opts, howto) == 0) {
512 return 0;
513 }
514 }
515 return 1;
516 }
517