xref: /netbsd-src/sys/arch/i386/stand/lib/bootmenu.c (revision 4f99b703c0363867a33e95c198aece876a227b07)
1*4f99b703Smartin /*	$NetBSD: bootmenu.c,v 1.20 2024/11/27 17:19:37 martin Exp $	*/
244435f5aSad 
344435f5aSad /*-
444435f5aSad  * Copyright (c) 2008 The NetBSD Foundation, Inc.
544435f5aSad  * All rights reserved.
644435f5aSad  *
744435f5aSad  * Redistribution and use in source and binary forms, with or without
844435f5aSad  * modification, are permitted provided that the following conditions
944435f5aSad  * are met:
1044435f5aSad  * 1. Redistributions of source code must retain the above copyright
1144435f5aSad  *    notice, this list of conditions and the following disclaimer.
1244435f5aSad  * 2. Redistributions in binary form must reproduce the above copyright
1344435f5aSad  *    notice, this list of conditions and the following disclaimer in the
1444435f5aSad  *    documentation and/or other materials provided with the distribution.
1544435f5aSad  *
1644435f5aSad  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
1744435f5aSad  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
1844435f5aSad  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
1944435f5aSad  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
2044435f5aSad  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2144435f5aSad  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2244435f5aSad  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2344435f5aSad  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
2444435f5aSad  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
2544435f5aSad  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
2644435f5aSad  * POSSIBILITY OF SUCH DAMAGE.
2744435f5aSad  */
2844435f5aSad 
2944435f5aSad #ifndef SMALL
3044435f5aSad 
3144435f5aSad #include <sys/types.h>
3244435f5aSad #include <sys/reboot.h>
3344435f5aSad #include <sys/bootblock.h>
3444435f5aSad 
3544435f5aSad #include <lib/libsa/stand.h>
36e9c95bf5Srtr #include <lib/libsa/bootcfg.h>
3744435f5aSad #include <lib/libsa/ufs.h>
3844435f5aSad #include <lib/libkern/libkern.h>
3944435f5aSad 
4044435f5aSad #include <libi386.h>
4144435f5aSad #include <bootmenu.h>
4244435f5aSad 
436a91dd31She static void docommandchoice(int);
446a91dd31She 
4544435f5aSad extern struct x86_boot_params boot_params;
4644435f5aSad extern	const char bootprog_name[], bootprog_rev[], bootprog_kernrev[];
4744435f5aSad 
4844435f5aSad #define MENUFORMAT_AUTO	  0
4944435f5aSad #define MENUFORMAT_NUMBER 1
5044435f5aSad #define MENUFORMAT_LETTER 2
5144435f5aSad 
5244435f5aSad /*
53e9c95bf5Srtr  * XXX
54e9c95bf5Srtr  * if module_add, userconf_add are strictly mi they can be folded back
55e9c95bf5Srtr  * into sys/lib/libsa/bootcfg.c:perform_bootcfg().
5644435f5aSad  */
57e9c95bf5Srtr static void
58e9c95bf5Srtr do_bootcfg_command(const char *cmd, char *arg)
59e9c95bf5Srtr {
60e9c95bf5Srtr 	if (strcmp(cmd, BOOTCFG_CMD_LOAD) == 0)
61e9c95bf5Srtr 		module_add(arg);
626bcd9d5cSmlelstv 	else if (strcmp(cmd, "fs") == 0)
636bcd9d5cSmlelstv 		fs_add(arg);
64e9c95bf5Srtr 	else if (strcmp(cmd, BOOTCFG_CMD_USERCONF) == 0)
65e9c95bf5Srtr 		userconf_add(arg);
66e9c95bf5Srtr }
67e9c95bf5Srtr 
6895e6c117Snonaka int
6944435f5aSad parsebootconf(const char *conf)
7044435f5aSad {
7195e6c117Snonaka 	return perform_bootcfg(conf, &do_bootcfg_command, 32768);
7244435f5aSad }
7344435f5aSad 
7444435f5aSad /*
7544435f5aSad  * doboottypemenu will render the menu and parse any user input
7644435f5aSad  */
7744435f5aSad static int
7844435f5aSad getchoicefrominput(char *input, int def)
7944435f5aSad {
801b3357e5Sjmcneill 	int choice, usedef;
811b3357e5Sjmcneill 
8244435f5aSad 	choice = -1;
831b3357e5Sjmcneill 	usedef = 0;
841b3357e5Sjmcneill 
851b3357e5Sjmcneill 	if (*input == '\0' || *input == '\r' || *input == '\n') {
8644435f5aSad 		choice = def;
871b3357e5Sjmcneill 		usedef = 1;
88e9c95bf5Srtr 	} else if (*input >= 'A' && *input < bootcfg_info.nummenu + 'A')
8944435f5aSad 		choice = (*input) - 'A';
90e9c95bf5Srtr 	else if (*input >= 'a' && *input < bootcfg_info.nummenu + 'a')
9144435f5aSad 		choice = (*input) - 'a';
927b2cf765Sisaki 	else if (isdigit(*input)) {
9344435f5aSad 		choice = atoi(input) - 1;
94e9c95bf5Srtr 		if (choice < 0 || choice >= bootcfg_info.nummenu)
9544435f5aSad 			choice = -1;
9644435f5aSad 	}
97df6bdc8bSjmcneill 
98e9c95bf5Srtr 	if (bootcfg_info.menuformat != MENUFORMAT_LETTER &&
997b2cf765Sisaki 	    !isdigit(*input) && !usedef)
100df6bdc8bSjmcneill 		choice = -1;
101df6bdc8bSjmcneill 
10244435f5aSad 	return choice;
10344435f5aSad }
10444435f5aSad 
1056a91dd31She static void
1066a91dd31She docommandchoice(int choice)
1076a91dd31She {
1086a91dd31She 	char input[80], *ic, *oc;
1096a91dd31She 
110e9c95bf5Srtr 	ic = bootcfg_info.command[choice];
1116a91dd31She 	/* Split command string at ; into separate commands */
1126a91dd31She 	do {
1136a91dd31She 		oc = input;
1146a91dd31She 		/* Look for ; separator */
1156a91dd31She 		for (; *ic && *ic != COMMAND_SEPARATOR; ic++)
1166a91dd31She 			*oc++ = *ic;
1176a91dd31She 		if (*input == '\0')
1186a91dd31She 			continue;
1196a91dd31She 		/* Strip out any trailing spaces */
1206a91dd31She 		oc--;
1216a91dd31She 		for (; *oc == ' ' && oc > input; oc--);
1226a91dd31She 		*++oc = '\0';
1236a91dd31She 		if (*ic == COMMAND_SEPARATOR)
1246a91dd31She 			ic++;
1256a91dd31She 		/* Stop silly command strings like ;;; */
1266a91dd31She 		if (*input != '\0')
1276a91dd31She 			docommand(input);
1286a91dd31She 		/* Skip leading spaces */
1296a91dd31She 		for (; *ic == ' '; ic++);
1306a91dd31She 	} while (*ic);
1316a91dd31She }
1326a91dd31She 
133ee69c9e8Sjakllsch __dead void
13444435f5aSad doboottypemenu(void)
13544435f5aSad {
13644435f5aSad 	int choice;
1376a91dd31She 	char input[80];
13844435f5aSad 
139*4f99b703Smartin 	/*
140*4f99b703Smartin 	 * If we have a single menu entry with empty description and
141*4f99b703Smartin 	 * timeout = 0 we do not display any menu.
142*4f99b703Smartin 	 */
143*4f99b703Smartin 	if ((bootcfg_info.nummenu > 0 &&
144*4f99b703Smartin 	     bootcfg_info.desc[0] != bootcfg_info.command[0] &&
145*4f99b703Smartin 	     bootcfg_info.desc[0][0] != 0) || bootcfg_info.timeout > 0) {
14644435f5aSad 		printf("\n");
147*4f99b703Smartin 
14844435f5aSad 		/* Display menu */
149e9c95bf5Srtr 		if (bootcfg_info.menuformat == MENUFORMAT_LETTER) {
150*4f99b703Smartin 			for (choice = 0; choice < bootcfg_info.nummenu;
151*4f99b703Smartin 			    choice++)
15244435f5aSad 				printf("    %c. %s\n", choice + 'A',
153e9c95bf5Srtr 				    bootcfg_info.desc[choice]);
15444435f5aSad 		} else {
15544435f5aSad 			/* Can't use %2d format string with libsa */
156*4f99b703Smartin 			for (choice = 0; choice < bootcfg_info.nummenu;
157*4f99b703Smartin 			    choice++)
15844435f5aSad 				printf("    %s%d. %s\n",
15944435f5aSad 				    (choice < 9) ?  " " : "",
16044435f5aSad 				    choice + 1,
161e9c95bf5Srtr 				    bootcfg_info.desc[choice]);
16244435f5aSad 		}
163*4f99b703Smartin 	}
16444435f5aSad 	choice = -1;
16544435f5aSad 	for (;;) {
16644435f5aSad 		input[0] = '\0';
16744435f5aSad 
168e9c95bf5Srtr 		if (bootcfg_info.timeout < 0) {
169e9c95bf5Srtr 			if (bootcfg_info.menuformat == MENUFORMAT_LETTER)
17044435f5aSad 				printf("\nOption: [%c]:",
171e9c95bf5Srtr 				    bootcfg_info.def + 'A');
17244435f5aSad 			else
17344435f5aSad 				printf("\nOption: [%d]:",
174e9c95bf5Srtr 				    bootcfg_info.def + 1);
17544435f5aSad 
176979b72c6Sdholland 			kgets(input, sizeof(input));
177e9c95bf5Srtr 			choice = getchoicefrominput(input, bootcfg_info.def);
178e9c95bf5Srtr 		} else if (bootcfg_info.timeout == 0)
179e9c95bf5Srtr 			choice = bootcfg_info.def;
18044435f5aSad 		else  {
18144435f5aSad 			printf("\nChoose an option; RETURN for default; "
18244435f5aSad 			       "SPACE to stop countdown.\n");
183e9c95bf5Srtr 			if (bootcfg_info.menuformat == MENUFORMAT_LETTER)
18444435f5aSad 				printf("Option %c will be chosen in ",
185e9c95bf5Srtr 				    bootcfg_info.def + 'A');
18644435f5aSad 			else
18744435f5aSad 				printf("Option %d will be chosen in ",
188e9c95bf5Srtr 				    bootcfg_info.def + 1);
189e9c95bf5Srtr 			input[0] = awaitkey(bootcfg_info.timeout, 1);
19044435f5aSad 			input[1] = '\0';
191e9c95bf5Srtr 			choice = getchoicefrominput(input, bootcfg_info.def);
19244435f5aSad 			/* If invalid key pressed, drop to menu */
19344435f5aSad 			if (choice == -1)
194e9c95bf5Srtr 				bootcfg_info.timeout = -1;
19544435f5aSad 		}
19644435f5aSad 		if (choice < 0)
19744435f5aSad 			continue;
198e9c95bf5Srtr 		if (!strcmp(bootcfg_info.command[choice], "prompt") &&
19944435f5aSad 		    ((boot_params.bp_flags & X86_BP_FLAGS_PASSWORD) == 0 ||
20044435f5aSad 		    check_password((char *)boot_params.bp_password))) {
20144435f5aSad 			printf("type \"?\" or \"help\" for help.\n");
20244435f5aSad 			bootmenu(); /* does not return */
20344435f5aSad 		} else {
2046a91dd31She 			docommandchoice(choice);
20544435f5aSad 		}
20644435f5aSad 
20744435f5aSad 	}
20844435f5aSad }
20944435f5aSad 
21044435f5aSad #endif	/* !SMALL */
211