1 /* $NetBSD: boot.c,v 1.2 2011/01/22 19:19:25 joerg 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 57 static void bootcmd_help(char *); 58 static void bootcmd_ls(char *); 59 static void bootcmd_quit(char *); 60 static void bootcmd_boot(char *); 61 static void bootcmd_disk(char *); 62 #ifdef SUPPORT_CONSDEV 63 static void bootcmd_consdev(char *); 64 #endif 65 66 static const struct bootblk_command { 67 const char *c_name; 68 void (*c_fn)(char *arg); 69 } bootcmds[] = { 70 { "help", bootcmd_help }, 71 { "?", bootcmd_help }, 72 { "quit", bootcmd_quit }, 73 { "ls", bootcmd_ls }, 74 { "boot", bootcmd_boot }, 75 { "disk", bootcmd_disk }, 76 #ifdef SUPPORT_CONSDEV 77 { "consdev", bootcmd_consdev }, 78 #endif 79 { NULL, NULL }, 80 }; 81 82 static struct btinfo_howto bi_howto; 83 84 static void print_banner(void); 85 static int exec_netbsd(const char *file, int howto); 86 87 int 88 parsebootfile(const char *fname, char **fsname, char **devname, 89 uint *unit, uint *partition, const char **file) 90 { 91 const char *col; 92 93 *fsname = "ufs"; 94 *devname = default_devname; 95 *unit = default_unit; 96 *partition = default_partition; 97 *file = default_filename; 98 99 if (fname == NULL) 100 return 0; 101 102 if ((col = strchr(fname, ':'))) { /* device given */ 103 static char savedevname[MAXDEVNAME+1]; 104 int devlen; 105 unsigned int u = 0, p = 0; 106 int i = 0; 107 108 devlen = col - fname; 109 if (devlen > MAXDEVNAME) 110 return EINVAL; 111 112 #define isvalidname(c) ((c) >= 'a' && (c) <= 'z') 113 if (!isvalidname(fname[i])) 114 return EINVAL; 115 do { 116 savedevname[i] = fname[i]; 117 i++; 118 } while (isvalidname(fname[i])); 119 savedevname[i] = '\0'; 120 121 #define isnum(c) ((c) >= '0' && (c) <= '9') 122 if (i < devlen) { 123 if (!isnum(fname[i])) 124 return (EUNIT); 125 do { 126 u *= 10; 127 u += fname[i++] - '0'; 128 } while (isnum(fname[i])); 129 } 130 131 #define isvalidpart(c) ((c) >= 'a' && (c) <= 'a' + MAXPARTITIONS) 132 if (i < devlen) { 133 if (!isvalidpart(fname[i])) 134 return (EPART); 135 p = fname[i++] - 'a'; 136 } 137 138 if (i != devlen) 139 return ENXIO; 140 141 *devname = savedevname; 142 *unit = u; 143 *partition = p; 144 fname = col + 1; 145 } 146 147 if (*fname) 148 *file = fname; 149 150 return 0; 151 } 152 153 char * 154 sprint_bootsel(const char *filename) 155 { 156 static char buf[80]; 157 char *fsname, *devname; 158 uint unit, partition; 159 const char *file; 160 161 if (parsebootfile(filename, &fsname, &devname, &unit, &partition, 162 &file) == 0) { 163 snprintf(buf, sizeof(buf), "%s%d%c:%s", devname, unit, 164 'a' + partition, file); 165 return buf; 166 } 167 return "(invalid)"; 168 } 169 170 static void 171 print_banner(void) 172 { 173 extern const char bootprog_name[]; 174 extern const char bootprog_rev[]; 175 176 printf("\n"); 177 printf(">> %s, Revision %s\n", bootprog_name, bootprog_rev); 178 } 179 180 void 181 boot(dev_t bootdev) 182 { 183 extern char twiddle_toggle; 184 int currname; 185 int c; 186 187 consinit(CONSDEV_GLASS, -1); 188 189 twiddle_toggle = 1; /* no twiddling until we're ready */ 190 191 /* set default value: hd0a:netbsd */ 192 default_devname = "hd"; 193 default_unit = 0; 194 default_partition = 0; 195 default_filename = names[0][0]; 196 197 diskprobe(probed_disks, sizeof(probed_disks)); 198 199 parsebootconf(_PATH_BOOTCONF); 200 201 #ifdef SUPPORT_CONSDEV 202 /* 203 * If console set in boot.cfg, switch to it. 204 * This will print the banner, so we don't need to explicitly do it 205 */ 206 if (bootconf.consdev) 207 bootcmd_consdev(bootconf.consdev); 208 else 209 #endif 210 print_banner(); 211 212 printf("\ndisks: %s\n", probed_disks); 213 214 /* Display the menu, if applicable */ 215 twiddle_toggle = 0; 216 if (bootconf.nummenu > 0) { 217 /* Does not return */ 218 doboottypemenu(); 219 } 220 221 printf("Press return to boot now, any other key for boot menu\n"); 222 currname = 0; 223 for (currname = 0; currname < __arraycount(names); currname++) { 224 printf("booting %s - starting in ", 225 sprint_bootsel(names[currname][0])); 226 227 c = awaitkey((bootconf.timeout < 0) ? 0 : bootconf.timeout, 1); 228 if ((c != '\r') && (c != '\n') && (c != '\0')) { 229 printf("type \"?\" or \"help\" for help.\n"); 230 bootmenu(); /* does not return */ 231 } 232 233 /* 234 * try pairs of names[] entries, foo and foo.gz 235 */ 236 /* don't print "booting..." again */ 237 bootit(names[currname][0], 0, 0); 238 /* since it failed, try compressed bootfile. */ 239 bootit(names[currname][1], 0, 1); 240 } 241 242 bootmenu(); /* does not return */ 243 } 244 245 void 246 bootit(const char *filename, int howto, int tell) 247 { 248 249 if (tell) { 250 printf("booting %s", sprint_bootsel(filename)); 251 if (howto) 252 printf(" (howto 0x%x)", howto); 253 printf("\n"); 254 } 255 256 if (exec_netbsd(filename, howto) < 0) { 257 printf("boot: %s: %s\n", sprint_bootsel(filename), 258 strerror(errno)); 259 } else { 260 printf("boot returned\n"); 261 } 262 } 263 264 static int 265 exec_netbsd(const char *file, int howto) 266 { 267 u_long marks[MARK_MAX]; 268 269 BI_ALLOC(BTINFO_MAX); 270 271 bi_howto.howto = howto; 272 BI_ADD(&bi_howto, BTINFO_HOWTO, sizeof(bi_howto)); 273 274 if (loadfile_zboot(file, marks, LOAD_KERNEL) == -1) 275 goto out; 276 277 /*NOTREACHED*/ 278 return 0; 279 280 out: 281 BI_FREE(); 282 bootinfo = 0; 283 return -1; 284 } 285 286 /* 287 * bootmenu 288 */ 289 static char *gettrailer(char *arg); 290 static int parseopts(const char *opts, int *howto); 291 static int parseboot(char *arg, char **filename, int *howto); 292 293 /* ARGSUSED */ 294 static void 295 bootcmd_help(char *arg) 296 { 297 298 printf("commands are:\n" 299 "boot [xdNx:][filename] [-acdqsv]\n" 300 " (ex. \"hd0a:netbsd.old -s\"\n" 301 "ls [path]\n" 302 #ifdef SUPPORT_CONSDEV 303 "consdev {glass|com [speed]}\n" 304 #endif 305 "disk\n" 306 "help|?\n" 307 "quit\n"); 308 } 309 310 /* ARGSUSED */ 311 static void 312 bootcmd_quit(char *arg) 313 { 314 315 printf("Exiting...\n"); 316 exit(0); 317 /*NOTREACHED*/ 318 } 319 320 static void 321 bootcmd_ls(char *arg) 322 { 323 const char *save = default_filename; 324 325 default_filename = "/"; 326 ufs_ls(arg); 327 default_filename = save; 328 } 329 330 static void 331 bootcmd_boot(char *arg) 332 { 333 char *filename; 334 int howto; 335 336 if (parseboot(arg, &filename, &howto)) { 337 bootit(filename, howto, 1); 338 } 339 } 340 341 /* ARGSUSED */ 342 static void 343 bootcmd_disk(char *arg) 344 { 345 346 printf("disks: %s\n", probed_disks); 347 } 348 349 #ifdef SUPPORT_CONSDEV 350 static const struct cons_devs { 351 const char *name; 352 int tag; 353 } cons_devs[] = { 354 { "glass", CONSDEV_GLASS }, 355 { "com", CONSDEV_COM0 }, 356 { "com0", CONSDEV_COM0 }, 357 { NULL, 0 } 358 }; 359 360 static void 361 bootcmd_consdev(char *arg) 362 { 363 const struct cons_devs *cdp; 364 char *p; 365 int speed = 9600; 366 367 p = strchr(arg, ' '); 368 if (p != NULL) { 369 *p++ = '\0'; 370 speed = atoi(p); 371 } 372 for (cdp = cons_devs; cdp->name != NULL; cdp++) { 373 if (strcmp(arg, cdp->name) == 0) { 374 consinit(cdp->tag, speed); 375 print_banner(); 376 return; 377 } 378 } 379 printf("invalid console device.\n"); 380 } 381 #endif 382 383 void 384 docommand(char *arg) 385 { 386 char *options; 387 int i; 388 389 options = gettrailer(arg); 390 391 for (i = 0; bootcmds[i].c_name != NULL; i++) { 392 if (strcmp(arg, bootcmds[i].c_name) == 0) { 393 (*bootcmds[i].c_fn)(options); 394 return; 395 } 396 } 397 398 printf("unknown command\n"); 399 bootcmd_help(NULL); 400 } 401 402 void 403 bootmenu(void) 404 { 405 char input[256]; 406 char *c; 407 408 for (;;) { 409 c = input; 410 411 input[0] = '\0'; 412 printf("> "); 413 gets(input); 414 415 /* 416 * Skip leading whitespace. 417 */ 418 while (*c == ' ') { 419 c++; 420 } 421 if (*c != '\0') { 422 docommand(c); 423 } 424 } 425 } 426 427 /* 428 * chops the head from the arguments and returns the arguments if any, 429 * or possibly an empty string. 430 */ 431 static char * 432 gettrailer(char *arg) 433 { 434 char *options; 435 436 if ((options = strchr(arg, ' ')) == NULL) 437 return (""); 438 else 439 *options++ = '\0'; 440 441 /* trim leading blanks */ 442 while (*options && *options == ' ') 443 options++; 444 445 return options; 446 } 447 448 static int 449 parseopts(const char *opts, int *howto) 450 { 451 int r, tmpopt = 0; 452 453 opts++; /* skip - */ 454 while (*opts && *opts != ' ') { 455 r = 0; 456 BOOT_FLAG(*opts, r); 457 if (r == 0) { 458 printf("-%c: unknown flag\n", *opts); 459 bootcmd_help(NULL); 460 return 0; 461 } 462 tmpopt |= r; 463 opts++; 464 } 465 466 *howto = tmpopt; 467 return 1; 468 } 469 470 static int 471 parseboot(char *arg, char **filename, int *howto) 472 { 473 char *opts = NULL; 474 475 *filename = 0; 476 *howto = 0; 477 478 /* if there were no arguments */ 479 if (arg == NULL || *arg == '\0') 480 return 1; 481 482 /* format is... */ 483 /* [[xxNx:]filename] [-adqsv] */ 484 485 /* check for just args */ 486 if (arg[0] == '-') { 487 opts = arg; 488 } else { 489 /* there's a file name */ 490 *filename = arg; 491 492 opts = gettrailer(arg); 493 if (opts == NULL || *opts == '\0') { 494 opts = NULL; 495 } else if (*opts != '-') { 496 printf("invalid arguments\n"); 497 bootcmd_help(NULL); 498 return 0; 499 } 500 } 501 502 /* at this point, we have dealt with filenames. */ 503 504 /* now, deal with options */ 505 if (opts) { 506 if (parseopts(opts, howto) == 0) { 507 return 0; 508 } 509 } 510 return 1; 511 } 512