1/* $NetBSD: boot.c,v 1.4 2011/12/25 06:09:10 tsutsui 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 44static const char * const names[][2] = { 45 { "netbsd", "netbsd.gz" }, 46 { "netbsd.old", "netbsd.old.gz", }, 47 { "onetbsd", "onetbsd.gz" }, 48}; 49 50char *default_devname; 51uint default_unit, default_partition; 52const char *default_filename; 53int default_timeout = 5; 54 55static char probed_disks[256]; 56static char bootconfpath[1024]; 57 58static void bootcmd_help(char *); 59static void bootcmd_ls(char *); 60static void bootcmd_quit(char *); 61static void bootcmd_boot(char *); 62static void bootcmd_disk(char *); 63#ifdef SUPPORT_CONSDEV 64static void bootcmd_consdev(char *); 65#endif 66 67static 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 83static struct btinfo_howto bi_howto; 84 85static void print_banner(void); 86static int exec_netbsd(const char *file, int howto); 87 88int 89parsebootfile(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 154char * 155sprint_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 171static void 172print_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 181void 182boot(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 249void 250bootit(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 268static int 269exec_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 284out: 285 BI_FREE(); 286 bootinfo = 0; 287 return -1; 288} 289 290/* 291 * bootmenu 292 */ 293static char *gettrailer(char *arg); 294static int parseopts(const char *opts, int *howto); 295static int parseboot(char *arg, char **filename, int *howto); 296 297/* ARGSUSED */ 298static void 299bootcmd_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 */ 316static void 317bootcmd_quit(char *arg) 318{ 319 320 printf("Exiting...\n"); 321 exit(0); 322 /*NOTREACHED*/ 323} 324 325static void 326bootcmd_ls(char *arg) 327{ 328 const char *save = default_filename; 329 330 default_filename = "/"; 331 ls(arg); 332 default_filename = save; 333} 334 335static void 336bootcmd_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 */ 347static void 348bootcmd_disk(char *arg) 349{ 350 351 printf("disks: %s\n", probed_disks); 352} 353 354#ifdef SUPPORT_CONSDEV 355static 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 365static void 366bootcmd_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 388void 389docommand(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 407void 408bootmenu(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 */ 436static char * 437gettrailer(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 453static int 454parseopts(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 475static int 476parseboot(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