Add support for up to 35 boot menu entries (2 pages if >18). To solve the ">10" problem currently experienced by SeaBIOS users (there are no 11, 12, etc. keys on a keyboard - so impossible to choose the last menu entries if you got
10 entries because of multiple hard drives / secondary payloads / floppies)
- the boot menu has been extended to the letter keys. NOTE: TPM menu has been moved from T to M letter: it is at the end of keyboard's 3rd row of letters and "Trusted" is adjective while "Module" is a noun; alternatively could press '-'.
Also, add support for a numpad. Small USB numpad could be really convenient for choosing the boot entries at coreboot boards used as (maybe headless) servers. '/' char on numpad could be used to open the boot menu or to exit it. If there are >10 boot menu entries - the numpad console interface will be enabled: press one or two digit keys and then ENTER to confirm your choice, or remove a digit by pressing the '.Del' key. Also you could call TPM with '-' key at any moment, or boot with a single key press of your fullsize keyboard.
Signed-off-by: Mike Banon <mikebdp2 at gmail.com> (patch body is after the testing instructions below)
This "advanced_bootmenu" patch is much more useful when used together with my "multiple_floppies" patch: [SeaBIOS] [PATCH v2] ramdisk: search for all available floppy images instead of one https://mail.coreboot.org/pipermail/seabios/2018-December/012670.html
Sadly I haven't done the suggestions by Kevin (I have so many unfinished tasks) so the "multiple_floppies" patch is also not merged yet. But you could install both patches to your coreboot by executing this script while at ./coreboot dir: https://pastebin.com/raw/hv9sSuMU
And here is a coreboot image for QEMU with these two patches applied and my collection of wonderful and useful floppies added to popular the entries list: https://github.com/mikebdp2/floparchive/blob/master/coreboot.rom?raw=true Descriptions of the most prominent floppies could be found here: http://dangerousprototypes.com/docs/Lenovo_G505S_hacking#Useful_floppies
Run this coreboot.rom by executing this QEMU command: (some floppies are 64-bit) qemu-system-x86_64 -L . -m 768 -localtime -vga vmware -net nic,model=rtl8139 \ -net user -soundhw ac97 -bios ./coreboot.rom -boot menu=on -serial stdio
Best regards, Mike Banon
diff --git a/src/boot.c b/src/boot.c index 9f82f3c..f94dd27 100644 --- a/src/boot.c +++ b/src/boot.c @@ -463,6 +463,7 @@ get_keystroke(int msec) * Boot menu and BCV execution ****************************************************************/
+#define BOOTMENU_PAGE_SIZE 18 #define DEFAULT_BOOTMENU_WAIT 2500
// Show IPL option menu. @@ -478,59 +479,282 @@ interactive_bootmenu(void) ;
char *bootmsg = romfile_loadfile("etc/boot-menu-message", NULL); - int menukey = romfile_loadint("etc/boot-menu-key", 1); - printf("%s", bootmsg ?: "\nPress ESC for boot menu.\n\n"); + int menukey = romfile_loadint("etc/boot-menu-key", 1); // custom menukey + printf("%s", bootmsg ?: "\nPress ESC or \ / slash for boot menu.\n\n"); free(bootmsg);
u32 menutime = romfile_loadint("etc/boot-menu-wait", DEFAULT_BOOTMENU_WAIT); enable_bootsplash(); int scan_code = get_keystroke(menutime); disable_bootsplash(); - if (scan_code != menukey) + if (scan_code != menukey && // custom menukey + scan_code != 1 && // ESC + scan_code != 43 && // '' char on keyboard + scan_code != 53 && // '/' char on keyboard + scan_code != 98) { // '/' char on numpad + if (scan_code == -1) + printf("No key pressed.\n"); + else + printf("Not a menukey pressed.\n"); return; + }
while (get_keystroke(0) >= 0) ;
- printf("Select boot device:\n\n"); wait_threads();
- // Show menu items + char keyboard_keys[35] = {'1','2','3','4','5','6','7','8','9','0', + 'q','w','e','r','t','y','u','i','o','p', + 'a','s','d','f','g','h','j','k','l', + 'z','x','c','v','b','n'}; /* m = TPM */ + int numpad_scancodes[10] = { 82, 79, 80, 81, 75, 76, 77, 71, 72, 73 }; + int numpi = 0; // Key index: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9. + int digits = 0; // Numerical length of a current choice number. + int decode = 0; // Decode the current choice number into a letter? + int entry_id = 0; + char desc[77]; + + printf("Select boot device"); + + // Show menu items after counting them and determining a number of pages. + // Only 35 boot menu items (36 if to count a TPM) are supported currently. + int maxmenu = 0; struct bootentry_s *pos; - hlist_for_each_entry(pos, &BootList, node) { - char desc[77]; + hlist_for_each_entry(pos, &BootList, node) maxmenu++; - printf("%d. %s\n", maxmenu + + if (maxmenu > 10) { + if (maxmenu > 35) + maxmenu = 35; + if (maxmenu > BOOTMENU_PAGE_SIZE) + printf(" - page 1 :"); + else + printf(": "); + printf(" // press ENTER after your numpad input"); + if (maxmenu > BOOTMENU_PAGE_SIZE) + printf(" - if any -\n " + " // - or to switch between the pages...\n"); + else + printf(" (if any)\n\n"); + } else { + printf(":\n\n"); + } + + hlist_for_each_entry(pos, &BootList, node) { + if (entry_id == BOOTMENU_PAGE_SIZE) // Show only the first page. + break; + printf("%c. %s\n", keyboard_keys[entry_id] , strtcpy(desc, pos->description, ARRAY_SIZE(desc))); + entry_id++; } + int tpm_cshm = 0; if (tpm_can_show_menu()) { - printf("\nt. TPM Configuration\n"); + tpm_cshm = 1; + printf("\nm-. TPM Configuration"); } - - // Get key press. If the menu key is ESC, do not restart boot unless - // 1.5 seconds have passed. Otherwise users (trained by years of - // repeatedly hitting keys to enter the BIOS) will end up hitting ESC - // multiple times and immediately booting the primary boot device. - int esc_accepted_time = irqtimer_calc(menukey == 1 ? 1500 : 0); + printf("\n> "); + + // Do not restart boot on menukey press, unless DEFAULT_BOOTMENU_WAIT msecs + // have passed. Otherwise users (trained by years of repeatedly hitting keys + // to enter the BIOS) will end up hitting menukey multiple times and + // immediately booting the primary boot device. + int esc_accepted_time = irqtimer_calc(DEFAULT_BOOTMENU_WAIT); + int choice = 0, kb_choice = 0; + int page_num = 1; + int enter = 0; + int backspace = 0; + int tpm_show_menu = 0; for (;;) { scan_code = get_keystroke(1000); - if (scan_code == 1 && !irqtimer_check(esc_accepted_time)) - continue; - if (tpm_can_show_menu() && scan_code == 20 /* t */) { + if (scan_code == menukey || // custom menukey + scan_code == 1 || // ESC + scan_code == 43 || // '' char on keyboard + scan_code == 53 || // '/' char on keyboard + scan_code == 98) { // '/' char on numpad + if (!irqtimer_check(esc_accepted_time)) + continue; + if (digits == 2) // Remove the decoded "(*)" + printf(" \b\b\b"); + /* Remove the existing input before printing a message. */ + for (; digits > 0; digits--) + printf("\b \b"); + printf("Menukey pressed.\n"); + return; + } + kb_choice = 0; + /* 4 rows of keyboard_keys: 1 row with numbers, 3 rows with letters. + Use any of them to select a boot device (except the TPM 'm-' keys) */ + // 1st range: 1-9 and 0 (10) keys <==> 2-11 scan codes <==> 1-10 choice + if (scan_code >= 2 && scan_code <= 11) kb_choice = scan_code - 1; + // 2nd range: Q-P row of letters <==> 16-25 scan codes <==> 11-20 choice + if (scan_code >= 16 && scan_code <= 25) kb_choice = scan_code - 5; + // 3rd range: A-L row of letters <==> 30-38 scan codes <==> 21-29 choice + if (scan_code >= 30 && scan_code <= 38) kb_choice = scan_code - 9; + // 4th range: Z-N row of letters <==> 44-49 scan codes <==> 30-35 choice + if (scan_code >= 44 && scan_code <= 49) kb_choice = scan_code - 14; + // ENTER: (28) on keyboard, (96) on numpad. + if (scan_code == 28 || scan_code == 96) + enter = 1; + // BCKSPC: '<-'(14) and 'Delete'(111) on keyboard, '.Del'(83) on numpad. + if (scan_code == 14 || scan_code == 111 || scan_code == 83) + backspace = 1; + // TPM keys: 'm'(50) and '-'(12) chars on keyboard, '-'(74) on numpad. + if ((scan_code == 50 || scan_code == 12 || scan_code == 74) && tpm_cshm) + tpm_show_menu = 1; + + if (kb_choice != 0 || tpm_show_menu) { + if (kb_choice > maxmenu) { + if (!tpm_show_menu) + continue; + } else { + choice = kb_choice; + } + if (digits == 2) // Remove the decoded "(*)" + printf(" \b\b\b"); + /* Remove the existing input before printing a choice. */ + for (; digits > 0; digits--) + printf("\b \b"); + if (!tpm_show_menu) { + // Choice is any of the detected boot devices ==> lets boot! + break; + } + } else { + // Internal/USB Numpad console interface. + if (digits < 9) { + for (numpi = 0; numpi < 10; numpi++) { + if (scan_code == numpad_scancodes[numpi]) { + if (maxmenu <= 10) { // Console interface is not needed. + if ((numpi != 0 && numpi <= maxmenu) || + (numpi == 0 && 10 <= maxmenu)) { // 10(0) + choice = numpi; + enter = 1; // Fake ENTER to boot this entry now. + } else { // If no such an entry, don't try to boot. + break; + } + } else { + if (digits == 2) { + printf(" \b\b\b"); // Remove the decoded "(*)" + if (choice == 0) { + printf("\b\b \b\b"); // Remove "10". + digits = 0; + } + } + choice = 10 * choice + numpi; + } + if (choice > 0) { + printf("%d", numpi); // Print the entered digit. + digits++; + } else { + if (10 <= maxmenu) + printf("10(0)\b\b\b"); + else + printf("10(?)\b\b\b"); + digits = 2; + } + if (choice > 9 && digits == 2) // Decode into a letter. + decode = 1; + break; + } + } + } + if (backspace && digits > 0) { + backspace = 0; + choice = choice / 10; + if (digits == 2) { + printf(" \b\b\b"); // Remove the decoded "(*)" + // 0 turned into 10: one more Backspace is needed to remove. + if (choice == 0) { + printf("\b \b"); + digits--; + } + } + printf("\b \b"); // Remove the last entered digit. + digits--; + if (choice > 9 && digits == 2) // Decode into a letter. + decode = 1; + } + if (decode) { // Decode the current choice number into a letter. + decode = 0; + if (choice <= maxmenu) { + printf("(%c)", keyboard_keys[choice-1]); + } else { + if (tpm_cshm && choice == 36) + printf("(m)"); // For TPM. + else + printf("(?)"); // No matching letter found. + } + printf("\b\b\b"); // Move a cursor before the "(*)" + } + } + + if (enter) { + enter = 0; + if (choice == 0) { + if (digits == 2) { // for 0 that turned into 10 + if (10 <= maxmenu) + break; + else + continue; + } + // If there are two pages - switch between them. + if (maxmenu > BOOTMENU_PAGE_SIZE) { + entry_id = 0; + page_num = 3 - page_num; // 3 - 1 = 2; 3 - 2 = 1. + printf("\n\nSelect boot device - page %d :" + " // press ENTER after your numpad input" + " - if any -\n " + " // - or to switch between the pages...\n", + page_num); + hlist_for_each_entry(pos, &BootList, node) { + if ((page_num == 1 && entry_id == BOOTMENU_PAGE_SIZE) || + (page_num == 2 && entry_id == 35)) + break; + if (page_num == 1 || entry_id >= BOOTMENU_PAGE_SIZE) + printf("%c. %s\n", keyboard_keys[entry_id], + strtcpy(desc, pos->description, ARRAY_SIZE(desc))); + entry_id++; + } + if (tpm_cshm) + printf("\nm-. TPM Configuration"); + printf("\n> "); + } + } else { + if (choice > maxmenu) { + if (tpm_cshm && choice == 36) + tpm_show_menu = 1; + } else { + // Choice is any of the detected boot devices ==> lets boot! + break; + } + } + } + + if (tpm_show_menu) { + tpm_show_menu = 0; + choice = 0; + if (digits == 0) + printf("TPM key pressed."); + else + digits = 0; printf("\n"); tpm_menu(); + printf("> "); } - if (scan_code >= 1 && scan_code <= maxmenu+1) - break; + } + + if (choice == 0) // 10(0) + choice = 10; + + if (digits == 0 && choice < 36) { + printf("%c", keyboard_keys[choice-1]); + if (choice > 9) // Decode into a number. + printf("(%d)", choice); } printf("\n"); - if (scan_code == 0x01) - // ESC - return;
// Find entry and make top priority. - int choice = scan_code - 1; hlist_for_each_entry(pos, &BootList, node) { if (! --choice) break; diff --git a/src/config.h b/src/config.h index 93c8dbc..f85cc14 100644 --- a/src/config.h +++ b/src/config.h @@ -19,7 +19,7 @@ // Space to reserve in high-memory for tables #define BUILD_MAX_HIGHTABLE (256*1024) // Largest supported externaly facing drive id -#define BUILD_MAX_EXTDRIVE 16 +#define BUILD_MAX_EXTDRIVE 36 // Number of bytes the smbios may be and still live in the f-segment #define BUILD_MAX_SMBIOS_FSEG 600 // Maximum number of bytes the mptable may be and still be copied to f-segment