[SeaBIOS] [PATCH] advanced_bootmenu: up to 35 entries (2 pages if >18), numpad support (console)

Mike Banon posted 1 patch 1 week ago
Failed in applying to current master (apply log)

[SeaBIOS] [PATCH] advanced_bootmenu: up to 35 entries (2 pages if >18), numpad support (console)

Posted by Mike Banon 1 week ago
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
_______________________________________________
SeaBIOS mailing list -- seabios@seabios.org
To unsubscribe send an email to seabios-leave@seabios.org