1 /* $NetBSD: boot2.c,v 1.90 2025/07/31 02:59:13 pgoyette Exp $ */ 2 3 /*- 4 * Copyright (c) 2008, 2009 The NetBSD Foundation, Inc. 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 NETBSD FOUNDATION, INC. AND CONTRIBUTORS 17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 /* 30 * Copyright (c) 2003 31 * David Laight. All rights reserved 32 * Copyright (c) 1996, 1997, 1999 33 * Matthias Drochner. All rights reserved. 34 * Copyright (c) 1996, 1997 35 * Perry E. Metzger. All rights reserved. 36 * Copyright (c) 1997 37 * Jason R. Thorpe. All rights reserved 38 * 39 * Redistribution and use in source and binary forms, with or without 40 * modification, are permitted provided that the following conditions 41 * are met: 42 * 1. Redistributions of source code must retain the above copyright 43 * notice, this list of conditions and the following disclaimer. 44 * 2. Redistributions in binary form must reproduce the above copyright 45 * notice, this list of conditions and the following disclaimer in the 46 * documentation and/or other materials provided with the distribution. 47 * 3. All advertising materials mentioning features or use of this software 48 * must display the following acknowledgements: 49 * This product includes software developed for the NetBSD Project 50 * by Matthias Drochner. 51 * This product includes software developed for the NetBSD Project 52 * by Perry E. Metzger. 53 * 4. The names of the authors may not be used to endorse or promote products 54 * derived from this software without specific prior written permission. 55 * 56 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 57 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 58 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 59 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 60 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 61 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 62 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 63 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 64 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 65 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 66 */ 67 68 /* Based on stand/biosboot/main.c */ 69 70 #include <sys/types.h> 71 #include <sys/reboot.h> 72 #include <sys/bootblock.h> 73 74 #include <lib/libsa/stand.h> 75 #include <lib/libsa/bootcfg.h> 76 #include <lib/libsa/ufs.h> 77 #include <lib/libkern/libkern.h> 78 79 #include <libi386.h> 80 #include <bootmod.h> 81 #include <bootmenu.h> 82 #include <biosdisk.h> 83 #include <vbe.h> 84 #include "devopen.h" 85 86 #ifdef _STANDALONE 87 #include <bootinfo.h> 88 #endif 89 #ifdef SUPPORT_PS2 90 #include <biosmca.h> 91 #endif 92 93 extern struct x86_boot_params boot_params; 94 95 extern const char bootprog_name[], bootprog_rev[], bootprog_kernrev[]; 96 97 int errno; 98 99 int boot_biosdev; 100 daddr_t boot_biossector; 101 102 static const char * const names[][2] = { 103 { "netbsd", "netbsd.gz" }, 104 { "onetbsd", "onetbsd.gz" }, 105 { "netbsd.old", "netbsd.old.gz" }, 106 { "netbsd/kernel", "netbsd/kernel.gz" }, 107 { "onetbsd/kernel", "onetbsd/kernel.gz" }, 108 { "netbsd.old/kernel", "netbsd.old/kernel.gz" }, 109 }; 110 111 #define NUMNAMES (sizeof(names)/sizeof(names[0])) 112 #define DEFFILENAME names[0][0] 113 114 #ifndef NO_GPT 115 #define MAXDEVNAME 39 /* "NAME=" + 34 char part_name */ 116 #else 117 #define MAXDEVNAME 16 118 #endif 119 120 static char *default_devname; 121 static int default_unit, default_partition; 122 static const char *default_filename; 123 static const char *default_part_name; 124 125 char *sprint_bootsel(const char *); 126 static void bootit(const char *, int); 127 static void bootit2(char *, size_t, int); 128 void boot2(int, uint64_t); 129 130 void command_help(char *); 131 #if LIBSA_ENABLE_LS_OP 132 void command_ls(char *); 133 #endif 134 void command_quit(char *); 135 void command_boot(char *); 136 void command_pkboot(char *); 137 void command_consdev(char *); 138 void command_root(char *); 139 #ifndef SMALL 140 void command_menu(char *); 141 #endif 142 void command_modules(char *); 143 void command_multiboot(char *); 144 145 const struct bootblk_command commands[] = { 146 { "help", command_help }, 147 { "?", command_help }, 148 #if LIBSA_ENABLE_LS_OP 149 { "ls", command_ls }, 150 #endif 151 { "quit", command_quit }, 152 { "boot", command_boot }, 153 { "pkboot", command_pkboot }, 154 { "dev", command_dev }, 155 { "consdev", command_consdev }, 156 { "root", command_root }, 157 #ifndef SMALL 158 { "menu", command_menu }, 159 #endif 160 { "modules", command_modules }, 161 { "load", module_add }, 162 { "multiboot", command_multiboot }, 163 { "vesa", command_vesa }, 164 { "splash", splash_add }, 165 { "rndseed", rnd_add }, 166 { "fs", fs_add }, 167 { "userconf", userconf_add }, 168 { NULL, NULL }, 169 }; 170 171 int 172 parsebootfile(const char *fname, char **fsname, char **devname, 173 int *unit, int *partition, const char **file) 174 { 175 const char *col; 176 static char savedevname[MAXDEVNAME+1]; 177 178 *fsname = "ufs"; 179 if (default_part_name == NULL) { 180 *devname = default_devname; 181 } else { 182 snprintf(savedevname, sizeof(savedevname), 183 "NAME=%s", default_part_name); 184 *devname = savedevname; 185 } 186 *unit = default_unit; 187 *partition = default_partition; 188 *file = default_filename; 189 190 if (fname == NULL) 191 return 0; 192 193 if ((col = strchr(fname, ':')) != NULL) { /* device given */ 194 int devlen; 195 int u = 0, p = 0; 196 int i = 0; 197 198 devlen = col - fname; 199 if (devlen > MAXDEVNAME) 200 return EINVAL; 201 202 #ifndef NO_GPT 203 if (strstr(fname, "NAME=") == fname) { 204 strlcpy(savedevname, fname, devlen + 1); 205 *devname = savedevname; 206 *unit = -1; 207 *partition = -1; 208 fname = col + 1; 209 goto out; 210 } 211 #endif 212 213 #define isvalidname(c) ((c) >= 'a' && (c) <= 'z') 214 if (!isvalidname(fname[i])) 215 return EINVAL; 216 do { 217 savedevname[i] = fname[i]; 218 i++; 219 } while (isvalidname(fname[i])); 220 savedevname[i] = '\0'; 221 222 #define isnum(c) ((c) >= '0' && (c) <= '9') 223 if (i < devlen) { 224 if (!isnum(fname[i])) 225 return EUNIT; 226 do { 227 u *= 10; 228 u += fname[i++] - '0'; 229 } while (isnum(fname[i])); 230 } 231 232 #define isvalidpart(c) ((c) >= 'a' && (c) <= 'z') 233 if (i < devlen) { 234 if (!isvalidpart(fname[i])) 235 return EPART; 236 p = fname[i++] - 'a'; 237 } 238 239 if (i != devlen) 240 return ENXIO; 241 242 *devname = savedevname; 243 *unit = u; 244 *partition = p; 245 fname = col + 1; 246 } 247 248 out: 249 if (*fname) 250 *file = fname; 251 252 return 0; 253 } 254 255 char * 256 sprint_bootsel(const char *filename) 257 { 258 char *fsname, *devname; 259 int unit, partition; 260 const char *file; 261 static char buf[80]; 262 263 if (parsebootfile(filename, &fsname, &devname, &unit, 264 &partition, &file) == 0) { 265 if (strstr(devname, "NAME=") == devname) 266 snprintf(buf, sizeof(buf), "%s:%s", devname, file); 267 else 268 snprintf(buf, sizeof(buf), "%s%d%c:%s", devname, unit, 269 'a' + partition, file); 270 return buf; 271 } 272 return "(invalid)"; 273 } 274 275 static void 276 clearit(void) 277 { 278 279 if (bootcfg_info.clear) 280 clear_pc_screen(); 281 } 282 283 static void 284 bootit(const char *filename, int howto) 285 { 286 if (howto & AB_VERBOSE) 287 printf("booting %s (howto 0x%x)\n", sprint_bootsel(filename), 288 howto); 289 290 if (exec_netbsd(filename, 0, howto, boot_biosdev < 0x80, clearit) < 0) 291 printf("boot: %s: %s\n", sprint_bootsel(filename), 292 strerror(errno)); 293 else 294 printf("boot returned\n"); 295 } 296 297 /* 298 * Called from the initial entry point boot_start in biosboot.S 299 * 300 * biosdev: BIOS drive number the system booted from 301 * biossector: Sector number of the NetBSD partition 302 */ 303 void 304 boot2(int biosdev, uint64_t biossector) 305 { 306 extern char twiddle_toggle; 307 int currname; 308 char c; 309 310 twiddle_toggle = 1; /* no twiddling until we're ready */ 311 312 initio(boot_params.bp_consdev); 313 314 #ifdef SUPPORT_PS2 315 biosmca(); 316 #endif 317 gateA20(); 318 319 boot_modules_enabled = !(boot_params.bp_flags 320 & X86_BP_FLAGS_NOMODULES); 321 if (boot_params.bp_flags & X86_BP_FLAGS_RESET_VIDEO) 322 biosvideomode(); 323 324 vbe_init(); 325 326 /* need to remember these */ 327 boot_biosdev = biosdev; 328 boot_biossector = biossector; 329 330 /* try to set default device to what BIOS tells us */ 331 bios2dev(biosdev, biossector, &default_devname, &default_unit, 332 &default_partition, &default_part_name); 333 334 /* if the user types "boot" without filename */ 335 default_filename = DEFFILENAME; 336 337 #ifndef SMALL 338 if (!(boot_params.bp_flags & X86_BP_FLAGS_NOBOOTCONF)) { 339 parsebootconf(BOOTCFG_FILENAME); 340 } else { 341 bootcfg_info.timeout = boot_params.bp_timeout; 342 } 343 344 345 /* 346 * If console set in boot.cfg, switch to it. 347 * This will print the banner, so we don't need to explicitly do it 348 */ 349 if (bootcfg_info.consdev) { 350 command_consdev(bootcfg_info.consdev); 351 } else { 352 clearit(); 353 print_bootcfg_banner(bootprog_name, bootprog_rev); 354 } 355 356 /* Display the menu, if applicable */ 357 twiddle_toggle = 0; 358 if (bootcfg_info.nummenu > 0) { 359 /* Does not return */ 360 doboottypemenu(); 361 } 362 363 #else 364 twiddle_toggle = 0; 365 clearit(); 366 print_bootcfg_banner(bootprog_name, bootprog_rev); 367 #endif 368 369 printf("Press return to boot now, any other key for boot menu\n"); 370 for (currname = 0; currname < NUMNAMES; currname++) { 371 printf("booting %s - starting in ", 372 sprint_bootsel(names[currname][0])); 373 374 #ifdef SMALL 375 c = awaitkey(boot_params.bp_timeout, 1); 376 #else 377 c = awaitkey((bootcfg_info.timeout < 0) ? 0 378 : bootcfg_info.timeout, 1); 379 #endif 380 if ((c != '\r') && (c != '\n') && (c != '\0')) { 381 if ((boot_params.bp_flags & X86_BP_FLAGS_PASSWORD) == 0) { 382 /* do NOT ask for password */ 383 bootmenu(); /* does not return */ 384 } else { 385 /* DO ask for password */ 386 if (check_password((char *)boot_params.bp_password)) { 387 /* password ok */ 388 printf("type \"?\" or \"help\" for help.\n"); 389 bootmenu(); /* does not return */ 390 } else { 391 /* bad password */ 392 printf("Wrong password.\n"); 393 currname = 0; 394 continue; 395 } 396 } 397 } 398 399 /* 400 * try pairs of names[] entries, foo and foo.gz 401 */ 402 /* don't print "booting..." again */ 403 bootit(names[currname][0], 0); 404 /* since it failed, try compressed bootfile. */ 405 bootit(names[currname][1], AB_VERBOSE); 406 } 407 408 bootmenu(); /* does not return */ 409 } 410 411 /* ARGSUSED */ 412 void 413 command_help(char *arg) 414 { 415 416 printf("commands are:\n" 417 "boot [dev:][filename] [-12acdqsvxz]\n" 418 #ifndef NO_RAIDFRAME 419 " dev syntax is (hd|fd|cd|raid)[N[x]]\n" 420 #else 421 " dev syntax is (hd|fd|cd)[N[x]]n" 422 #endif 423 #ifndef NO_GPT 424 " or NAME=gpt_label\n" 425 #endif 426 " (ex. \"hd0a:netbsd.old -s\")\n" 427 "pkboot [dev:][filename] [-12acdqsvxz]\n" 428 #if LIBSA_ENABLE_LS_OP 429 "ls [dev:][path]\n" 430 #endif 431 "dev [dev:]\n" 432 "consdev {pc|{com[0123]|com[0123]kbd|auto}[,{speed}]}\n" 433 "root {spec}\n" 434 " spec can be disk, e.g. wd0, sd0\n" 435 " or string like wedge:name\n" 436 "vesa {modenum|on|off|enabled|disabled|list}\n" 437 #ifndef SMALL 438 "menu (reenters boot menu, if defined in boot.cfg)\n" 439 #endif 440 "modules {on|off|enabled|disabled}\n" 441 "load {path_to_module}\n" 442 "multiboot [dev:][filename] [<args>]\n" 443 "splash {path_to_image_file}\n" 444 "userconf {command}\n" 445 "rndseed {path_to_rndseed_file}\n" 446 "help|?\n" 447 "quit\n"); 448 } 449 450 #if LIBSA_ENABLE_LS_OP 451 void 452 command_ls(char *arg) 453 { 454 const char *save = default_filename; 455 456 default_filename = "/"; 457 ls(arg); 458 default_filename = save; 459 } 460 #endif 461 462 /* ARGSUSED */ 463 void 464 command_quit(char *arg) 465 { 466 467 printf("Exiting...\n"); 468 delay(1000000); 469 reboot(); 470 /* Note: we shouldn't get to this point! */ 471 panic("Could not reboot!"); 472 } 473 474 static void 475 bootit2(char *path, size_t plen, int howto) 476 { 477 bootit(path, howto); 478 snprintf(path, plen, "%s.gz", path); 479 bootit(path, howto | AB_VERBOSE); 480 } 481 482 void 483 command_boot(char *arg) 484 { 485 char *filename; 486 char path[512]; 487 int howto; 488 489 if (!parseboot(arg, &filename, &howto)) 490 return; 491 492 if (filename != NULL && filename[0] != '\0') { 493 /* try old locations first to assist atf test beds */ 494 snprintf(path, sizeof(path) - 4, "%s", filename); 495 bootit2(path, sizeof(path), howto); 496 497 /* 498 * now treat given filename as a directory unless there 499 * is already an embedded path-name separator '/' present 500 */ 501 if (strchr(filename + 1, '/') == NULL) { 502 snprintf(path, sizeof(path) - 4, "%s/kernel", 503 filename); 504 bootit2(path, sizeof(path), howto); 505 } 506 } else { 507 int i; 508 509 for (i = 0; i < NUMNAMES; i++) { 510 bootit(names[i][0], howto); 511 bootit(names[i][1], howto); 512 } 513 } 514 } 515 516 void 517 command_pkboot(char *arg) 518 { 519 extern int has_prekern; 520 has_prekern = 1; 521 command_boot(arg); 522 has_prekern = 0; 523 } 524 525 void 526 command_dev(char *arg) 527 { 528 static char savedevname[MAXDEVNAME + 1]; 529 char *fsname, *devname; 530 const char *file; /* dummy */ 531 532 if (*arg == '\0') { 533 biosdisk_probe(); 534 535 #ifndef NO_GPT 536 if (default_part_name) 537 printf("default NAME=%s on %s%d\n", default_part_name, 538 default_devname, default_unit); 539 else 540 #endif 541 printf("default %s%d%c\n", 542 default_devname, default_unit, 543 'a' + default_partition); 544 return; 545 } 546 547 if (strchr(arg, ':') == NULL || 548 parsebootfile(arg, &fsname, &devname, &default_unit, 549 &default_partition, &file)) { 550 command_help(NULL); 551 return; 552 } 553 554 /* put to own static storage */ 555 strncpy(savedevname, devname, MAXDEVNAME + 1); 556 default_devname = savedevname; 557 558 /* +5 to skip leading NAME= */ 559 if (strstr(devname, "NAME=") == devname) 560 default_part_name = default_devname + 5; 561 } 562 563 static const struct cons_devs { 564 const char *name; 565 u_int tag; 566 } cons_devs[] = { 567 { "pc", CONSDEV_PC }, 568 { "com0", CONSDEV_COM0 }, 569 { "com1", CONSDEV_COM1 }, 570 { "com2", CONSDEV_COM2 }, 571 { "com3", CONSDEV_COM3 }, 572 { "com0kbd", CONSDEV_COM0KBD }, 573 { "com1kbd", CONSDEV_COM1KBD }, 574 { "com2kbd", CONSDEV_COM2KBD }, 575 { "com3kbd", CONSDEV_COM3KBD }, 576 { "auto", CONSDEV_AUTO }, 577 { NULL, 0 } 578 }; 579 580 void 581 command_consdev(char *arg) 582 { 583 const struct cons_devs *cdp; 584 char *sep; 585 int speed; 586 587 sep = strchr(arg, ','); 588 if (sep != NULL) 589 *sep++ = '\0'; 590 591 for (cdp = cons_devs; cdp->name; cdp++) { 592 if (strcmp(arg, cdp->name) != 0) 593 continue; 594 595 if (sep != NULL) { 596 if (cdp->tag == CONSDEV_PC) 597 goto error; 598 599 speed = atoi(sep); 600 if (speed < 0) 601 goto error; 602 boot_params.bp_conspeed = speed; 603 } 604 605 initio(cdp->tag); 606 clearit(); 607 print_bootcfg_banner(bootprog_name, bootprog_rev); 608 return; 609 } 610 error: 611 printf("invalid console device.\n"); 612 } 613 614 void 615 command_root(char *arg) 616 { 617 struct btinfo_rootdevice *biv = &bi_root; 618 619 strncpy(biv->devname, arg, sizeof(biv->devname)); 620 if (biv->devname[sizeof(biv->devname)-1] != '\0') { 621 biv->devname[sizeof(biv->devname)-1] = '\0'; 622 printf("truncated to %s\n",biv->devname); 623 } 624 } 625 626 #ifndef SMALL 627 /* ARGSUSED */ 628 void 629 command_menu(char *arg) 630 { 631 632 if (bootcfg_info.nummenu > 0) { 633 /* Does not return */ 634 doboottypemenu(); 635 } else { 636 printf("No menu defined in boot.cfg\n"); 637 } 638 } 639 #endif /* !SMALL */ 640 641 void 642 command_modules(char *arg) 643 { 644 645 if (strcmp(arg, "enabled") == 0 || 646 strcmp(arg, "on") == 0) 647 boot_modules_enabled = true; 648 else if (strcmp(arg, "disabled") == 0 || 649 strcmp(arg, "off") == 0) 650 boot_modules_enabled = false; 651 else 652 printf("invalid flag, must be 'enabled' or 'disabled'.\n"); 653 } 654 655 void 656 command_multiboot(char *arg) 657 { 658 char *filename; 659 660 filename = arg; 661 if (exec_multiboot(filename, gettrailer(arg)) < 0) 662 printf("multiboot: %s: %s\n", sprint_bootsel(filename), 663 strerror(errno)); 664 else 665 printf("boot returned\n"); 666 } 667 668