1 /* $NetBSD: boot.c,v 1.8 2016/06/11 06:58:42 dholland 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 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 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 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 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 318 bootcmd_quit(char *arg) 319 { 320 321 printf("Exiting...\n"); 322 exit(0); 323 /*NOTREACHED*/ 324 } 325 326 static void 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 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 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 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 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 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 * 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 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 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