/* $NetBSD: boot.c,v 1.8 2016/06/11 06:58:42 dholland Exp $ */ /* * Copyright (c) 2009 NONAKA Kimihiro * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include "boot.h" #include "bootinfo.h" #include "bootmenu.h" #include "disk.h" #include "unixdev.h" #include "pathnames.h" #include #include #include "compat_linux.h" static const char * const names[][2] = { { "netbsd", "netbsd.gz" }, { "netbsd.old", "netbsd.old.gz", }, { "onetbsd", "onetbsd.gz" }, }; char *default_devname; uint default_unit, default_partition; const char *default_filename; int default_timeout = 5; static char probed_disks[256]; static char bootconfpath[1024]; static void bootcmd_help(char *); static void bootcmd_ls(char *); static void bootcmd_quit(char *); static void bootcmd_boot(char *); static void bootcmd_disk(char *); #ifdef SUPPORT_CONSDEV static void bootcmd_consdev(char *); #endif static const struct bootblk_command { const char *c_name; void (*c_fn)(char *arg); } bootcmds[] = { { "help", bootcmd_help }, { "?", bootcmd_help }, { "quit", bootcmd_quit }, { "ls", bootcmd_ls }, { "boot", bootcmd_boot }, { "disk", bootcmd_disk }, #ifdef SUPPORT_CONSDEV { "consdev", bootcmd_consdev }, #endif { NULL, NULL }, }; static struct btinfo_howto bi_howto; static void print_banner(void); static int exec_netbsd(const char *file, int howto); int parsebootfile(const char *fname, char **fsname, char **devname, uint *unit, uint *partition, const char **file) { const char *col; *fsname = "ufs"; *devname = default_devname; *unit = default_unit; *partition = default_partition; *file = default_filename; if (fname == NULL) return 0; if ((col = strchr(fname, ':'))) { /* device given */ static char savedevname[MAXDEVNAME+1]; int devlen; unsigned int u = 0, p = 0; int i = 0; devlen = col - fname; if (devlen > MAXDEVNAME) return EINVAL; #define isvalidname(c) ((c) >= 'a' && (c) <= 'z') if (!isvalidname(fname[i])) return EINVAL; do { savedevname[i] = fname[i]; i++; } while (isvalidname(fname[i])); savedevname[i] = '\0'; #define isnum(c) ((c) >= '0' && (c) <= '9') if (i < devlen) { if (!isnum(fname[i])) return (EUNIT); do { u *= 10; u += fname[i++] - '0'; } while (isnum(fname[i])); } #define isvalidpart(c) ((c) >= 'a' && (c) < 'a' + MAXPARTITIONS) if (i < devlen) { if (!isvalidpart(fname[i])) return (EPART); p = fname[i++] - 'a'; } if (i != devlen) return ENXIO; *devname = savedevname; *unit = u; *partition = p; fname = col + 1; } if (*fname) *file = fname; return 0; } char * sprint_bootsel(const char *filename) { static char buf[80]; char *fsname, *devname; uint unit, partition; const char *file; if (parsebootfile(filename, &fsname, &devname, &unit, &partition, &file) == 0) { snprintf(buf, sizeof(buf), "%s%d%c:%s", devname, unit, 'a' + partition, file); return buf; } return "(invalid)"; } static void print_banner(void) { extern const char bootprog_name[]; extern const char bootprog_rev[]; printf("\n"); printf(">> %s, Revision %s\n", bootprog_name, bootprog_rev); } void boot(dev_t bootdev) { extern char twiddle_toggle; int currname; int c; consinit(CONSDEV_GLASS, -1); twiddle_toggle = 1; /* no twiddling until we're ready */ /* set default value: hd0a:netbsd */ default_devname = "hd"; default_unit = 0; default_partition = 0; default_filename = names[0][0]; diskprobe(probed_disks, sizeof(probed_disks)); snprintf(bootconfpath, sizeof(bootconfpath), "%s%d%c:%s", default_devname, default_unit, 'a' + default_partition, BOOTCFG_FILENAME); parsebootconf(bootconfpath); #ifdef SUPPORT_CONSDEV /* * If console set in boot.cfg, switch to it. * This will print the banner, so we don't need to explicitly do it */ if (bootcfg_info.consdev) bootcmd_consdev(bootcfg_info.consdev); else #endif print_banner(); printf("\ndisks: %s\n", probed_disks); /* Display the menu, if applicable */ twiddle_toggle = 0; if (bootcfg_info.nummenu > 0) { /* Does not return */ doboottypemenu(); } printf("Press return to boot now, any other key for boot menu\n"); currname = 0; for (currname = 0; currname < __arraycount(names); currname++) { printf("booting %s - starting in ", sprint_bootsel(names[currname][0])); c = awaitkey((bootcfg_info.timeout < 0) ? 0 : bootcfg_info.timeout, 1); if ((c != '\r') && (c != '\n') && (c != '\0')) { printf("type \"?\" or \"help\" for help.\n"); bootmenu(); /* does not return */ } /* * try pairs of names[] entries, foo and foo.gz */ /* don't print "booting..." again */ bootit(names[currname][0], 0, 0); /* since it failed, try compressed bootfile. */ bootit(names[currname][1], 0, 1); } bootmenu(); /* does not return */ } void bootit(const char *filename, int howto, int tell) { if (tell) { printf("booting %s", sprint_bootsel(filename)); if (howto) printf(" (howto 0x%x)", howto); printf("\n"); } if (exec_netbsd(filename, howto) < 0) { printf("boot: %s: %s\n", sprint_bootsel(filename), strerror(errno)); } else { printf("boot returned\n"); } } static int exec_netbsd(const char *file, int howto) { u_long marks[MARK_MAX]; BI_ALLOC(BTINFO_MAX); bi_howto.howto = howto; BI_ADD(&bi_howto, BTINFO_HOWTO, sizeof(bi_howto)); if (loadfile_zboot(file, marks, LOAD_KERNEL) == -1) goto out; /*NOTREACHED*/ return 0; out: BI_FREE(); bootinfo = 0; return -1; } /* * bootmenu */ static char *gettrailer(char *arg); static int parseopts(const char *opts, int *howto); static int parseboot(char *arg, char **filename, int *howto); /* ARGSUSED */ static void bootcmd_help(char *arg) { printf("commands are:\n" "boot [xdNx:][filename] [-1acdqsv]\n" " (ex. \"boot hd0a:netbsd.old -s\")\n" " (ex. \"boot path:/mnt/card/netbsd -1\")\n" "ls [path]\n" #ifdef SUPPORT_CONSDEV "consdev {glass|com [speed]}\n" #endif "disk\n" "help|?\n" "quit\n"); } /* ARGSUSED */ static void bootcmd_quit(char *arg) { printf("Exiting...\n"); exit(0); /*NOTREACHED*/ } static void bootcmd_ls(char *arg) { const char *save = default_filename; default_filename = "/"; ls(arg); default_filename = save; } static void bootcmd_boot(char *arg) { char *filename; int howto; if (parseboot(arg, &filename, &howto)) { bootit(filename, howto, 1); } } /* ARGSUSED */ static void bootcmd_disk(char *arg) { printf("disks: %s\n", probed_disks); } #ifdef SUPPORT_CONSDEV static const struct cons_devs { const char *name; int tag; } cons_devs[] = { { "glass", CONSDEV_GLASS }, { "com", CONSDEV_COM0 }, { "com0", CONSDEV_COM0 }, { NULL, 0 } }; static void bootcmd_consdev(char *arg) { const struct cons_devs *cdp; char *p; int speed = 9600; p = strchr(arg, ' '); if (p != NULL) { *p++ = '\0'; speed = atoi(p); } for (cdp = cons_devs; cdp->name != NULL; cdp++) { if (strcmp(arg, cdp->name) == 0) { consinit(cdp->tag, speed); print_banner(); return; } } printf("invalid console device.\n"); } #endif void docommand(char *arg) { char *options; int i; options = gettrailer(arg); for (i = 0; bootcmds[i].c_name != NULL; i++) { if (strcmp(arg, bootcmds[i].c_name) == 0) { (*bootcmds[i].c_fn)(options); return; } } printf("unknown command\n"); bootcmd_help(NULL); } void bootmenu(void) { char input[256]; char *c; for (;;) { c = input; input[0] = '\0'; printf("> "); kgets(input, sizeof(input)); /* * Skip leading whitespace. */ while (*c == ' ') { c++; } if (*c != '\0') { docommand(c); } } } /* * chops the head from the arguments and returns the arguments if any, * or possibly an empty string. */ static char * gettrailer(char *arg) { char *options; if ((options = strchr(arg, ' ')) == NULL) return (""); else *options++ = '\0'; /* trim leading blanks */ while (*options == ' ') options++; return options; } static int parseopts(const char *opts, int *howto) { int r, tmpopt = 0; opts++; /* skip - */ while (*opts && *opts != ' ') { r = 0; BOOT_FLAG(*opts, r); if (r == 0) { printf("-%c: unknown flag\n", *opts); bootcmd_help(NULL); return 0; } tmpopt |= r; opts++; } *howto = tmpopt; return 1; } static int parseboot(char *arg, char **filename, int *howto) { char *opts = NULL; *filename = 0; *howto = 0; /* if there were no arguments */ if (arg == NULL || *arg == '\0') return 1; /* format is... */ /* [[xxNx:]filename] [-adqsv] */ /* check for just args */ if (arg[0] == '-') { opts = arg; } else { /* there's a file name */ *filename = arg; opts = gettrailer(arg); if (opts == NULL || *opts == '\0') { opts = NULL; } else if (*opts != '-') { printf("invalid arguments\n"); bootcmd_help(NULL); return 0; } } /* at this point, we have dealt with filenames. */ /* now, deal with options */ if (opts) { if (parseopts(opts, howto) == 0) { return 0; } } return 1; }