1 /* $NetBSD: boot2.c,v 1.17 2007/12/13 11:52:17 sborrill Exp $ */ 2 3 /* 4 * Copyright (c) 2003 5 * David Laight. All rights reserved 6 * Copyright (c) 1996, 1997, 1999 7 * Matthias Drochner. All rights reserved. 8 * Copyright (c) 1996, 1997 9 * Perry E. Metzger. All rights reserved. 10 * Copyright (c) 1997 11 * Jason R. Thorpe. All rights reserved 12 * 13 * Redistribution and use in source and binary forms, with or without 14 * modification, are permitted provided that the following conditions 15 * are met: 16 * 1. Redistributions of source code must retain the above copyright 17 * notice, this list of conditions and the following disclaimer. 18 * 2. Redistributions in binary form must reproduce the above copyright 19 * notice, this list of conditions and the following disclaimer in the 20 * documentation and/or other materials provided with the distribution. 21 * 3. All advertising materials mentioning features or use of this software 22 * must display the following acknowledgements: 23 * This product includes software developed for the NetBSD Project 24 * by Matthias Drochner. 25 * This product includes software developed for the NetBSD Project 26 * by Perry E. Metzger. 27 * 4. The names of the authors may not be used to endorse or promote products 28 * derived from this software without specific prior written permission. 29 * 30 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 31 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 32 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 33 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 34 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 35 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 36 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 37 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 38 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 39 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 40 */ 41 42 /* Based on stand/biosboot/main.c */ 43 44 #include <sys/types.h> 45 #include <sys/reboot.h> 46 #include <sys/bootblock.h> 47 48 #include <lib/libsa/stand.h> 49 #include <lib/libsa/ufs.h> 50 #include <lib/libkern/libkern.h> 51 52 #include <libi386.h> 53 #include "devopen.h" 54 55 #ifdef SUPPORT_USTARFS 56 #include "ustarfs.h" 57 #endif 58 #ifdef SUPPORT_PS2 59 #include <biosmca.h> 60 #endif 61 62 extern struct x86_boot_params boot_params; 63 64 extern const char bootprog_name[], bootprog_rev[], bootprog_date[], 65 bootprog_maker[]; 66 67 int errno; 68 69 int boot_biosdev; 70 u_int boot_biossector; 71 72 static const char * const names[][2] = { 73 { "netbsd", "netbsd.gz" }, 74 { "onetbsd", "onetbsd.gz" }, 75 { "netbsd.old", "netbsd.old.gz" }, 76 }; 77 78 #define NUMNAMES (sizeof(names)/sizeof(names[0])) 79 #define DEFFILENAME names[0][0] 80 81 #define MAXDEVNAME 16 82 83 #ifndef SMALL 84 #define BOOTCONF "boot.cfg" 85 #define MAXMENU 10 86 #define MAXBANNER 10 87 #endif /* !SMALL */ 88 89 static char *default_devname; 90 static int default_unit, default_partition; 91 static const char *default_filename; 92 93 char *sprint_bootsel(const char *); 94 void bootit(const char *, int, int); 95 void print_banner(void); 96 void boot2(int, u_int); 97 98 #ifndef SMALL 99 void parsebootconf(const char *); 100 void doboottypemenu(void); 101 int atoi(const char *); 102 #endif /* !SMALL */ 103 104 void command_help(char *); 105 void command_ls(char *); 106 void command_quit(char *); 107 void command_boot(char *); 108 void command_dev(char *); 109 void command_consdev(char *); 110 111 const struct bootblk_command commands[] = { 112 { "help", command_help }, 113 { "?", command_help }, 114 { "ls", command_ls }, 115 { "quit", command_quit }, 116 { "boot", command_boot }, 117 { "dev", command_dev }, 118 { "consdev", command_consdev }, 119 { NULL, NULL }, 120 }; 121 122 #ifndef SMALL 123 struct bootconf_def { 124 char *banner[MAXBANNER]; /* Banner text */ 125 char *command[MAXMENU]; /* Menu commands per entry*/ 126 char *consdev; /* Console device */ 127 int def; /* Default menu option */ 128 char *desc[MAXMENU]; /* Menu text per entry */ 129 int nummenu; /* Number of menu items */ 130 int timeout; /* Timeout in seconds */ 131 } bootconf; 132 #endif /* !SMALL */ 133 134 int 135 parsebootfile(const char *fname, char **fsname, char **devname, 136 int *unit, int *partition, const char **file) 137 { 138 const char *col; 139 140 *fsname = "ufs"; 141 *devname = default_devname; 142 *unit = default_unit; 143 *partition = default_partition; 144 *file = default_filename; 145 146 if (fname == NULL) 147 return 0; 148 149 if ((col = strchr(fname, ':')) != NULL) { /* device given */ 150 static char savedevname[MAXDEVNAME+1]; 151 int devlen; 152 int u = 0, p = 0; 153 int i = 0; 154 155 devlen = col - fname; 156 if (devlen > MAXDEVNAME) 157 return EINVAL; 158 159 #define isvalidname(c) ((c) >= 'a' && (c) <= 'z') 160 if (!isvalidname(fname[i])) 161 return EINVAL; 162 do { 163 savedevname[i] = fname[i]; 164 i++; 165 } while (isvalidname(fname[i])); 166 savedevname[i] = '\0'; 167 168 #define isnum(c) ((c) >= '0' && (c) <= '9') 169 if (i < devlen) { 170 if (!isnum(fname[i])) 171 return EUNIT; 172 do { 173 u *= 10; 174 u += fname[i++] - '0'; 175 } while (isnum(fname[i])); 176 } 177 178 #define isvalidpart(c) ((c) >= 'a' && (c) <= 'z') 179 if (i < devlen) { 180 if (!isvalidpart(fname[i])) 181 return EPART; 182 p = fname[i++] - 'a'; 183 } 184 185 if (i != devlen) 186 return ENXIO; 187 188 *devname = savedevname; 189 *unit = u; 190 *partition = p; 191 fname = col + 1; 192 } 193 194 if (*fname) 195 *file = fname; 196 197 return 0; 198 } 199 200 char * 201 sprint_bootsel(const char *filename) 202 { 203 char *fsname, *devname; 204 int unit, partition; 205 const char *file; 206 static char buf[80]; 207 208 if (parsebootfile(filename, &fsname, &devname, &unit, 209 &partition, &file) == 0) { 210 sprintf(buf, "%s%d%c:%s", devname, unit, 'a' + partition, file); 211 return buf; 212 } 213 return "(invalid)"; 214 } 215 216 void 217 bootit(const char *filename, int howto, int tell) 218 { 219 220 if (tell) { 221 printf("booting %s", sprint_bootsel(filename)); 222 if (howto) 223 printf(" (howto 0x%x)", howto); 224 printf("\n"); 225 } 226 227 if (exec_netbsd(filename, 0, howto) < 0) 228 printf("boot: %s: %s\n", sprint_bootsel(filename), 229 strerror(errno)); 230 else 231 printf("boot returned\n"); 232 } 233 234 void 235 print_banner(void) 236 { 237 #ifndef SMALL 238 int n; 239 if (bootconf.banner[0]) { 240 for (n = 0; bootconf.banner[n]; n++) 241 printf("%s\n", bootconf.banner[n]); 242 } else { 243 #endif /* !SMALL */ 244 printf("\n"); 245 printf(">> %s, Revision %s\n", bootprog_name, bootprog_rev); 246 printf(">> (%s, %s)\n", bootprog_maker, bootprog_date); 247 printf(">> Memory: %d/%d k\n", getbasemem(), getextmem()); 248 249 #ifndef SMALL 250 } 251 #endif /* !SMALL */ 252 } 253 254 #ifndef SMALL 255 int 256 atoi(const char *in) 257 { 258 char *c; 259 int ret; 260 261 ret = 0; 262 c = (char *)in; 263 if (*c == '-') 264 c++; 265 for (; isnum(*c); c++) 266 ret = (ret * 10) + (*c - '0'); 267 268 return (*in == '-') ? -ret : ret; 269 } 270 271 /* 272 * This function parses a boot.cnf file in the root of the filesystem 273 * (if present) and populates the global boot configuration. 274 * 275 * The file consists of a number of lines each terminated by \n 276 * The lines are in the format keyword=value. There should be spaces 277 * around the = sign. 278 * 279 * The recognised keywords are: 280 * banner: text displayed instead of the normal welcome text 281 * menu: Descriptive text:command to use 282 * timeout: Timeout in seconds (overrides that set by installboot) 283 * default: the default menu option to use if Return is pressed 284 * consdev: the console device to use 285 * 286 * Example boot.cnf file: 287 * banner=Welcome to NetBSD 288 * banner=Please choose the boot type from the following menu 289 * menu=Boot NetBSD:boot netbsd 290 * menu=Boot into single user mode:boot netbsd -s 291 * menu=Goto boot comand line:prompt 292 * timeout=10 293 * consdev=com0 294 * default=1 295 */ 296 void 297 parsebootconf(const char *conf) 298 { 299 char *bc, *c; 300 int cmenu, cbanner, len; 301 int fd, err, off; 302 struct stat st; 303 char *value, *key; 304 #ifdef SUPPORT_USTARFS 305 void *op_open; 306 #endif 307 308 /* Clear bootconf structure */ 309 bzero((void *)&bootconf, sizeof(bootconf)); 310 311 /* Set timeout to configured */ 312 bootconf.timeout = boot_params.bp_timeout; 313 314 /* don't try to open BOOTCONF if the target fs is ustarfs */ 315 #ifdef SUPPORT_USTARFS 316 #if !defined(LIBSA_SINGLE_FILESYSTEM) 317 fd = open("boot", 0); /* assume we are loaded as "boot" from here */ 318 if (fd < 0) 319 op_open = NULL; /* XXX */ 320 else { 321 op_open = files[fd].f_ops->open; 322 close(fd); 323 } 324 #else 325 op_open = file_system[0].open; 326 #endif /* !LIBSA_SINGLE_FILESYSTEM */ 327 if (op_open == ustarfs_open) 328 return; 329 #endif /* SUPPORT_USTARFS */ 330 331 fd = open(BOOTCONF, 0); 332 if (fd < 0) 333 return; 334 335 err = fstat(fd, &st); 336 if (err == -1) { 337 close(fd); 338 return; 339 } 340 341 bc = alloc(st.st_size + 1); 342 if (bc == NULL) { 343 printf("Could not allocate memory for boot configuration\n"); 344 return; 345 } 346 347 off = 0; 348 do { 349 len = read(fd, bc + off, 1024); 350 if (len <= 0) 351 break; 352 off += len; 353 } while (len > 0); 354 bc[off] = '\0'; 355 356 close(fd); 357 /* bc now contains the whole boot.cnf file */ 358 359 cmenu = 0; 360 cbanner = 0; 361 for(c = bc; *c; c++) { 362 key = c; 363 /* Look for = separator between key and value */ 364 for (; *c && *c != '='; c++) 365 continue; 366 if (*c == '\0') 367 break; /* break if at end of data */ 368 369 /* zero terminate key which points to keyword */ 370 *c++ = 0; 371 value = c; 372 /* Look for end of line (or file) and zero terminate value */ 373 for (; *c && *c != '\n'; c++) 374 continue; 375 *c = 0; 376 377 if (!strncmp(key, "menu", 4)) { 378 if (cmenu >= MAXMENU) 379 continue; 380 bootconf.desc[cmenu] = value; 381 /* Look for : between description and command */ 382 for (; *value && *value != ':'; value++) 383 continue; 384 if(*value) { 385 *value++ = 0; 386 bootconf.command[cmenu] = value; 387 cmenu++; 388 } else { 389 /* No delimiter means invalid line */ 390 bootconf.desc[cmenu] = NULL; 391 } 392 } else if (!strncmp(key, "banner", 6)) { 393 if (cbanner < MAXBANNER) 394 bootconf.banner[cbanner++] = value; 395 } else if (!strncmp(key, "timeout", 7)) { 396 if (!isnum(*value)) 397 bootconf.timeout = -1; 398 else 399 bootconf.timeout = atoi(value); 400 } else if (!strncmp(key, "default", 7)) { 401 bootconf.def = atoi(value) - 1; 402 } else if (!strncmp(key, "consdev", 7)) { 403 bootconf.consdev = value; 404 } 405 } 406 bootconf.nummenu = cmenu; 407 if (bootconf.def < 0) 408 bootconf.def = 0; 409 if (bootconf.def >= cmenu) 410 bootconf.def = cmenu - 1; 411 } 412 413 /* 414 * doboottypemenu will render the menu and parse any user input 415 */ 416 417 void 418 doboottypemenu(void) 419 { 420 int choice; 421 char input[80], c; 422 423 printf("\n"); 424 /* Display menu */ 425 for (choice = 0; bootconf.desc[choice]; choice++) 426 printf(" %d. %s\n", choice+1, bootconf.desc[choice]); 427 428 choice = -1; 429 for(;;) { 430 input[0] = '\0'; 431 432 if (bootconf.timeout < 0) { 433 printf("\nOption: [%d]:", bootconf.def + 1); 434 gets(input); 435 if (input[0] == '\0') choice = bootconf.def; 436 if (input[0] >= '1' && 437 input[0] <= bootconf.nummenu + '0') 438 choice = input[0] - '1'; 439 } else if (bootconf.timeout == 0) 440 choice = bootconf.def; 441 else { 442 printf("\nPress the key for your chosen option or "); 443 printf("Return to choose the default (%d)\n", 444 bootconf.def + 1); 445 printf("Option %d will be chosen in ", 446 bootconf.def + 1); 447 c = awaitkey(bootconf.timeout, 1); 448 if (c >= '1' && c <= bootconf.nummenu + '0') 449 choice = c - '1'; 450 else if (c == '\r' || c == '\n' || c == '\0') 451 /* default if timed out or Return pressed */ 452 choice = bootconf.def; 453 else { 454 /* If any other key pressed, drop to menu */ 455 bootconf.timeout = -1; 456 choice = -1; 457 } 458 } 459 if (choice < 0) 460 continue; 461 if (!strcmp(bootconf.command[choice], "prompt") && 462 ((boot_params.bp_flags & X86_BP_FLAGS_PASSWORD) == 0 || 463 check_password(boot_params.bp_password))) { 464 printf("type \"?\" or \"help\" for help.\n"); 465 bootmenu(); /* does not return */ 466 } else 467 docommand(bootconf.command[choice]); 468 469 } 470 } 471 #endif /* !SMALL */ 472 473 /* 474 * Called from the initial entry point boot_start in biosboot.S 475 * 476 * biosdev: BIOS drive number the system booted from 477 * biossector: Sector number of the NetBSD partition 478 */ 479 void 480 boot2(int biosdev, u_int biossector) 481 { 482 int currname; 483 char c; 484 485 initio(boot_params.bp_consdev); 486 487 #ifdef SUPPORT_PS2 488 biosmca(); 489 #endif 490 gateA20(); 491 492 if (boot_params.bp_flags & X86_BP_FLAGS_RESET_VIDEO) 493 biosvideomode(); 494 495 /* need to remember these */ 496 boot_biosdev = biosdev; 497 boot_biossector = biossector; 498 499 /* try to set default device to what BIOS tells us */ 500 bios2dev(biosdev, biossector, &default_devname, &default_unit, 501 &default_partition); 502 503 /* if the user types "boot" without filename */ 504 default_filename = DEFFILENAME; 505 506 #ifndef SMALL 507 parsebootconf(BOOTCONF); 508 509 /* 510 * If console set in boot.cnf, switch to it. 511 * This will print the banner, so we don't need to explicitly do it 512 */ 513 if (bootconf.consdev) 514 command_consdev(bootconf.consdev); 515 else 516 print_banner(); 517 518 /* Display the menu, if applicable */ 519 if (bootconf.nummenu > 0) { 520 /* Does not return */ 521 doboottypemenu(); 522 } 523 #else 524 print_banner(); 525 #endif 526 527 printf("Press return to boot now, any other key for boot menu\n"); 528 for (currname = 0; currname < NUMNAMES; currname++) { 529 printf("booting %s - starting in ", 530 sprint_bootsel(names[currname][0])); 531 532 #ifdef SMALL 533 c = awaitkey(boot_params.bp_timeout, 1); 534 #else 535 c = awaitkey((bootconf.timeout < 0) ? 0 : bootconf.timeout, 1); 536 #endif 537 if ((c != '\r') && (c != '\n') && (c != '\0') && 538 ((boot_params.bp_flags & X86_BP_FLAGS_PASSWORD) == 0 539 || check_password(boot_params.bp_password))) { 540 printf("type \"?\" or \"help\" for help.\n"); 541 bootmenu(); /* does not return */ 542 } 543 544 /* 545 * try pairs of names[] entries, foo and foo.gz 546 */ 547 /* don't print "booting..." again */ 548 bootit(names[currname][0], 0, 0); 549 /* since it failed, try compressed bootfile. */ 550 bootit(names[currname][1], 0, 1); 551 } 552 553 bootmenu(); /* does not return */ 554 } 555 556 /* ARGSUSED */ 557 void 558 command_help(char *arg) 559 { 560 561 printf("commands are:\n" 562 "boot [xdNx:][filename] [-acdqsv]\n" 563 " (ex. \"hd0a:netbsd.old -s\"\n" 564 "ls [path]\n" 565 "dev xd[N[x]]:\n" 566 "consdev {pc|com[0123]|com[0123]kbd|auto}\n" 567 "help|?\n" 568 "quit\n"); 569 } 570 571 void 572 command_ls(char *arg) 573 { 574 const char *save = default_filename; 575 576 default_filename = "/"; 577 ufs_ls(arg); 578 default_filename = save; 579 } 580 581 /* ARGSUSED */ 582 void 583 command_quit(char *arg) 584 { 585 586 printf("Exiting...\n"); 587 delay(1000000); 588 reboot(); 589 /* Note: we shouldn't get to this point! */ 590 panic("Could not reboot!"); 591 exit(0); 592 } 593 594 void 595 command_boot(char *arg) 596 { 597 char *filename; 598 int howto; 599 600 if (parseboot(arg, &filename, &howto)) 601 bootit(filename, howto, 1); 602 } 603 604 void 605 command_dev(char *arg) 606 { 607 static char savedevname[MAXDEVNAME + 1]; 608 char *fsname, *devname; 609 const char *file; /* dummy */ 610 611 if (*arg == '\0') { 612 printf("%s%d%c:\n", default_devname, default_unit, 613 'a' + default_partition); 614 return; 615 } 616 617 if (strchr(arg, ':') == NULL || 618 parsebootfile(arg, &fsname, &devname, &default_unit, 619 &default_partition, &file)) { 620 command_help(NULL); 621 return; 622 } 623 624 /* put to own static storage */ 625 strncpy(savedevname, devname, MAXDEVNAME + 1); 626 default_devname = savedevname; 627 } 628 629 static const struct cons_devs { 630 const char *name; 631 u_int tag; 632 } cons_devs[] = { 633 { "pc", CONSDEV_PC }, 634 { "com0", CONSDEV_COM0 }, 635 { "com1", CONSDEV_COM1 }, 636 { "com2", CONSDEV_COM2 }, 637 { "com3", CONSDEV_COM3 }, 638 { "com0kbd", CONSDEV_COM0KBD }, 639 { "com1kbd", CONSDEV_COM1KBD }, 640 { "com2kbd", CONSDEV_COM2KBD }, 641 { "com3kbd", CONSDEV_COM3KBD }, 642 { "auto", CONSDEV_AUTO }, 643 { NULL, 0 } 644 }; 645 646 void 647 command_consdev(char *arg) 648 { 649 const struct cons_devs *cdp; 650 651 for (cdp = cons_devs; cdp->name; cdp++) { 652 if (strcmp(arg, cdp->name) == 0) { 653 initio(cdp->tag); 654 print_banner(); 655 return; 656 } 657 } 658 printf("invalid console device.\n"); 659 } 660