:: commit bc5d1e4b8f0278f81da0826b7fd6a274d7ea9b85

programmerlexi <lexi@reyer.name> — 2026-04-11 16:57

parents: 9c3ead9386

lib/bli: implement timeout control

diff --git a/common/lib/bli.c b/common/lib/bli.c
index 4aee0f26..80676fb0 100644
--- a/common/lib/bli.c
+++ b/common/lib/bli.c
@@ -41,6 +41,28 @@ void uint64_to_decwstr(uint64_t value, wchar_t *buf) {
     buf[i] = '\0';
 }
 
+bool decwstr_to_size(const wchar_t *buf, size_t buf_size, size_t *value) {
+    size_t i = 0;
+    size_t tmp = 0;
+
+    if (buf == NULL) {
+        return false;
+    }
+
+    while (i * 2 < buf_size && buf[i]) {
+        wchar_t c = buf[i];
+        if (!(c >= L'0' && c <= L'9')) {
+            return false;
+        }
+        tmp = tmp * 10 + (c - L'0');
+        i++;
+    }
+
+    *value = tmp;
+
+    return true;
+}
+
 void bli_set_loader_time(wchar_t *variable, uint64_t time) {
     if (time == 0)
         return;
@@ -67,6 +89,15 @@ void init_bli(void) {
             sizeof(LIMINE_BRAND),
             LIMINE_BRAND);
 
+    uint64_t features = (1 << 0) | // Timeout control
+                        (1 << 1) | // Oneshot timeout control
+                        (1 << 13); // menu-disabled support
+    gRT->SetVariable(L"LoaderFeatures",
+            &bli_vendor_guid,
+            EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
+            sizeof(features),
+            &features);
+
     char part_uuid_str[37];
     guid_to_string(&boot_volume->part_guid, part_uuid_str);
 
@@ -87,4 +118,46 @@ void bli_on_boot(void) {
     bli_set_loader_time(L"LoaderTimeExecUSec", rdtsc_usec());
 }
 
+static bool handle_timeout(wchar_t *variable, bool erase, size_t *timeout, bool *skip_timeout) {
+    wchar_t timeout_buf[256];
+    UINTN getvar_size = sizeof(timeout_buf) - 2;
+    uint32_t attrs;
+    if (gRT->GetVariable(variable,
+                             &bli_vendor_guid,
+                             &attrs,
+                             &getvar_size,
+                             timeout_buf) == 0 && getvar_size > 0) {
+        if (erase) {
+            gRT->SetVariable(variable, &bli_vendor_guid,
+                attrs,
+                0, NULL);
+        }
+        if (getvar_size == 24 && memcmp(timeout_buf, L"menu-force",24) == 0) {
+            *skip_timeout = true;
+            return true;
+        }
+        if ((getvar_size == 24 && memcmp(timeout_buf, L"menu-hidden",24) == 0) || (getvar_size == 28 && memcmp(timeout_buf, L"menu-disabled",28) == 0)) {
+            // TODO: menu-hidden should enable quiet & set timeout >= 1
+            *timeout = 0;
+            return true;
+        }
+        size_t t;
+        if (!decwstr_to_size(timeout_buf, getvar_size, &t)) {
+            return false;
+        }
+        *timeout = t;
+        return true;
+    }
+    return false;
+
+}
+
+bool bli_update_oneshot_timeout(size_t *timeout, bool *skip_timeout) {
+    return handle_timeout(L"LoaderConfigTimeoutOneShot", true, timeout, skip_timeout);
+}
+
+bool bli_update_timeout(size_t *timeout, bool *skip_timeout) {
+    return handle_timeout(L"LoaderConfigTimeout", false, timeout, skip_timeout);
+}
+
 #endif
diff --git a/common/lib/bli.h b/common/lib/bli.h
index 2e48041d..d970d836 100644
--- a/common/lib/bli.h
+++ b/common/lib/bli.h
@@ -5,6 +5,8 @@
 
 void init_bli(void);
 void bli_on_boot(void);
+bool bli_update_oneshot_timeout(size_t *timeout, bool *skip_timeout);
+bool bli_update_timeout(size_t *timeout, bool *skip_timeout);
 
 #endif
 
diff --git a/common/menu.c b/common/menu.c
index 4f2e73a9..955785d8 100644
--- a/common/menu.c
+++ b/common/menu.c
@@ -4,6 +4,7 @@
 #include <stdnoreturn.h>
 #include <config.h>
 #include <menu.h>
+#include <lib/bli.h>
 #include <lib/print.h>
 #include <lib/misc.h>
 #include <lib/libc.h>
@@ -1187,13 +1188,29 @@ noreturn void _menu(bool first_run) {
     }
 
     size_t timeout = 5;
-    char *timeout_config = config_get_value(NULL, 0, "TIMEOUT");
-    if (timeout_config != NULL) {
-        if (!strcmp(timeout_config, "no"))
-            skip_timeout = true;
-        else
-            timeout = strtoui(timeout_config, NULL, 10);
+
+    bool has_timeout = false;
+
+#if defined (UEFI)
+    has_timeout = bli_update_oneshot_timeout(&timeout, &skip_timeout);
+#endif
+
+    if (!has_timeout) {
+        char *timeout_config = config_get_value(NULL, 0, "TIMEOUT");
+        if (timeout_config != NULL) {
+            has_timeout = true;
+            if (!strcmp(timeout_config, "no"))
+                skip_timeout = true;
+            else
+                timeout = strtoui(timeout_config, NULL, 10);
+        }
+    }
+
+#if defined (UEFI)
+    if (!has_timeout) {
+        has_timeout = bli_update_timeout(&timeout, &skip_timeout);
     }
+#endif
 
 #if defined(UEFI)
     bool reboot_to_firmware_supported = reboot_to_fw_ui_supported();
tab: 248 wrap: offon