1 /* $NetBSD: boot.c,v 1.1 2009/03/02 09:33:02 nonaka 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 extern const char bootprog_date[]; 176 extern const char bootprog_maker[]; 177 178 printf("\n"); 179 printf(">> %s, Revision %s\n", bootprog_name, bootprog_rev); 180 printf(">> (%s, %s)\n", bootprog_maker, bootprog_date); 181 } 182 183 void 184 boot(dev_t bootdev) 185 { 186 extern char twiddle_toggle; 187 int currname; 188 int c; 189 190 consinit(CONSDEV_GLASS, -1); 191 192 twiddle_toggle = 1; /* no twiddling until we're ready */ 193 194 /* set default value: hd0a:netbsd */ 195 default_devname = "hd"; 196 default_unit = 0; 197 default_partition = 0; 198 default_filename = names[0][0]; 199 200 diskprobe(probed_disks, sizeof(probed_disks)); 201 202 parsebootconf(_PATH_BOOTCONF); 203 204 #ifdef SUPPORT_CONSDEV 205 /* 206 * If console set in boot.cfg, switch to it. 207 * This will print the banner, so we don't need to explicitly do it 208 */ 209 if (bootconf.consdev) 210 bootcmd_consdev(bootconf.consdev); 211 else 212 #endif 213 print_banner(); 214 215 printf("\ndisks: %s\n", probed_disks); 216 217 /* Display the menu, if applicable */ 218 twiddle_toggle = 0; 219 if (bootconf.nummenu > 0) { 220 /* Does not return */ 221 doboottypemenu(); 222 } 223 224 printf("Press return to boot now, any other key for boot menu\n"); 225 currname = 0; 226 for (currname = 0; currname < __arraycount(names); currname++) { 227 printf("booting %s - starting in ", 228 sprint_bootsel(names[currname][0])); 229 230 c = awaitkey((bootconf.timeout < 0) ? 0 : bootconf.timeout, 1); 231 if ((c != '\r') && (c != '\n') && (c != '\0')) { 232 printf("type \"?\" or \"help\" for help.\n"); 233 bootmenu(); /* does not return */ 234 } 235 236 /* 237 * try pairs of names[] entries, foo and foo.gz 238 */ 239 /* don't print "booting..." again */ 240 bootit(names[currname][0], 0, 0); 241 /* since it failed, try compressed bootfile. */ 242 bootit(names[currname][1], 0, 1); 243 } 244 245 bootmenu(); /* does not return */ 246 } 247 248 void 249 bootit(const char *filename, int howto, int tell) 250 { 251 252 if (tell) { 253 printf("booting %s", sprint_bootsel(filename)); 254 if (howto) 255 printf(" (howto 0x%x)", howto); 256 printf("\n"); 257 } 258 259 if (exec_netbsd(filename, howto) < 0) { 260 printf("boot: %s: %s\n", sprint_bootsel(filename), 261 strerror(errno)); 262 } else { 263 printf("boot returned\n"); 264 } 265 } 266 267 static int 268 exec_netbsd(const char *file, int howto) 269 { 270 u_long marks[MARK_MAX]; 271 272 BI_ALLOC(BTINFO_MAX); 273 274 bi_howto.howto = howto; 275 BI_ADD(&bi_howto, BTINFO_HOWTO, sizeof(bi_howto)); 276 277 if (loadfile_zboot(file, marks, LOAD_KERNEL) == -1) 278 goto out; 279 280 /*NOTREACHED*/ 281 return 0; 282 283 out: 284 BI_FREE(); 285 bootinfo = 0; 286 return -1; 287 } 288 289 /* 290 * bootmenu 291 */ 292 static char *gettrailer(char *arg); 293 static int parseopts(const char *opts, int *howto); 294 static int parseboot(char *arg, char **filename, int *howto); 295 296 /* ARGSUSED */ 297 static void 298 bootcmd_help(char *arg) 299 { 300 301 printf("commands are:\n" 302 "boot [xdNx:][filename] [-acdqsv]\n" 303 " (ex. \"hd0a:netbsd.old -s\"\n" 304 "ls [path]\n" 305 #ifdef SUPPORT_CONSDEV 306 "consdev {glass|com [speed]}\n" 307 #endif 308 "disk\n" 309 "help|?\n" 310 "quit\n"); 311 } 312 313 /* ARGSUSED */ 314 static void 315 bootcmd_quit(char *arg) 316 { 317 318 printf("Exiting...\n"); 319 exit(0); 320 /*NOTREACHED*/ 321 } 322 323 static void 324 bootcmd_ls(char *arg) 325 { 326 const char *save = default_filename; 327 328 default_filename = "/"; 329 ufs_ls(arg); 330 default_filename = save; 331 } 332 333 static void 334 bootcmd_boot(char *arg) 335 { 336 char *filename; 337 int howto; 338 339 if (parseboot(arg, &filename, &howto)) { 340 bootit(filename, howto, 1); 341 } 342 } 343 344 /* ARGSUSED */ 345 static void 346 bootcmd_disk(char *arg) 347 { 348 349 printf("disks: %s\n", probed_disks); 350 } 351 352 #ifdef SUPPORT_CONSDEV 353 static const struct cons_devs { 354 const char *name; 355 int tag; 356 } cons_devs[] = { 357 { "glass", CONSDEV_GLASS }, 358 { "com", CONSDEV_COM0 }, 359 { "com0", CONSDEV_COM0 }, 360 { NULL, 0 } 361 }; 362 363 static void 364 bootcmd_consdev(char *arg) 365 { 366 const struct cons_devs *cdp; 367 char *p; 368 int speed = 9600; 369 370 p = strchr(arg, ' '); 371 if (p != NULL) { 372 *p++ = '\0'; 373 speed = atoi(p); 374 } 375 for (cdp = cons_devs; cdp->name != NULL; cdp++) { 376 if (strcmp(arg, cdp->name) == 0) { 377 consinit(cdp->tag, speed); 378 print_banner(); 379 return; 380 } 381 } 382 printf("invalid console device.\n"); 383 } 384 #endif 385 386 void 387 docommand(char *arg) 388 { 389 char *options; 390 int i; 391 392 options = gettrailer(arg); 393 394 for (i = 0; bootcmds[i].c_name != NULL; i++) { 395 if (strcmp(arg, bootcmds[i].c_name) == 0) { 396 (*bootcmds[i].c_fn)(options); 397 return; 398 } 399 } 400 401 printf("unknown command\n"); 402 bootcmd_help(NULL); 403 } 404 405 void 406 bootmenu(void) 407 { 408 char input[256]; 409 char *c; 410 411 for (;;) { 412 c = input; 413 414 input[0] = '\0'; 415 printf("> "); 416 gets(input); 417 418 /* 419 * Skip leading whitespace. 420 */ 421 while (*c == ' ') { 422 c++; 423 } 424 if (*c != '\0') { 425 docommand(c); 426 } 427 } 428 } 429 430 /* 431 * chops the head from the arguments and returns the arguments if any, 432 * or possibly an empty string. 433 */ 434 static char * 435 gettrailer(char *arg) 436 { 437 char *options; 438 439 if ((options = strchr(arg, ' ')) == NULL) 440 return (""); 441 else 442 *options++ = '\0'; 443 444 /* trim leading blanks */ 445 while (*options && *options == ' ') 446 options++; 447 448 return options; 449 } 450 451 static int 452 parseopts(const char *opts, int *howto) 453 { 454 int r, tmpopt = 0; 455 456 opts++; /* skip - */ 457 while (*opts && *opts != ' ') { 458 r = 0; 459 BOOT_FLAG(*opts, r); 460 if (r == 0) { 461 printf("-%c: unknown flag\n", *opts); 462 bootcmd_help(NULL); 463 return 0; 464 } 465 tmpopt |= r; 466 opts++; 467 } 468 469 *howto = tmpopt; 470 return 1; 471 } 472 473 static int 474 parseboot(char *arg, char **filename, int *howto) 475 { 476 char *opts = NULL; 477 478 *filename = 0; 479 *howto = 0; 480 481 /* if there were no arguments */ 482 if (arg == NULL || *arg == '\0') 483 return 1; 484 485 /* format is... */ 486 /* [[xxNx:]filename] [-adqsv] */ 487 488 /* check for just args */ 489 if (arg[0] == '-') { 490 opts = arg; 491 } else { 492 /* there's a file name */ 493 *filename = arg; 494 495 opts = gettrailer(arg); 496 if (opts == NULL || *opts == '\0') { 497 opts = NULL; 498 } else if (*opts != '-') { 499 printf("invalid arguments\n"); 500 bootcmd_help(NULL); 501 return 0; 502 } 503 } 504 505 /* at this point, we have dealt with filenames. */ 506 507 /* now, deal with options */ 508 if (opts) { 509 if (parseopts(opts, howto) == 0) { 510 return 0; 511 } 512 } 513 return 1; 514 } 515