Skip to content

Commit 287f5d0

Browse files
authored
Merge pull request #548 from programmerlexi/bli-entry-control
lib/bli: implement timeout & entry control
2 parents 41e25c0 + 6ae26ba commit 287f5d0

File tree

3 files changed

+223
-42
lines changed

3 files changed

+223
-42
lines changed

common/lib/bli.c

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,28 @@ void uint64_to_decwstr(uint64_t value, wchar_t *buf) {
4141
buf[i] = '\0';
4242
}
4343

44+
bool decwstr_to_size(const wchar_t *buf, size_t buf_size, size_t *value) {
45+
size_t i = 0;
46+
size_t tmp = 0;
47+
48+
if (buf == NULL) {
49+
return false;
50+
}
51+
52+
while (i * 2 < buf_size && buf[i]) {
53+
wchar_t c = buf[i];
54+
if (!(c >= L'0' && c <= L'9')) {
55+
return false;
56+
}
57+
tmp = tmp * 10 + (c - L'0');
58+
i++;
59+
}
60+
61+
*value = tmp;
62+
63+
return true;
64+
}
65+
4466
void bli_set_loader_time(wchar_t *variable, uint64_t time) {
4567
if (time == 0)
4668
return;
@@ -67,6 +89,17 @@ void init_bli(void) {
6789
sizeof(LIMINE_BRAND),
6890
LIMINE_BRAND);
6991

92+
uint64_t features = (1 << 0) | // Timeout control
93+
(1 << 1) | // Oneshot timeout control
94+
(1 << 2) | // Default entry control
95+
(1 << 3) | // Oneshot entry control
96+
(1 << 13); // menu-disabled support
97+
gRT->SetVariable(L"LoaderFeatures",
98+
&bli_vendor_guid,
99+
EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
100+
sizeof(features),
101+
&features);
102+
70103
char part_uuid_str[37];
71104
guid_to_string(&boot_volume->part_guid, part_uuid_str);
72105

@@ -87,4 +120,93 @@ void bli_on_boot(void) {
87120
bli_set_loader_time(L"LoaderTimeExecUSec", rdtsc_usec());
88121
}
89122

123+
static bool handle_timeout(wchar_t *variable, bool erase, size_t *timeout, bool *skip_timeout) {
124+
wchar_t timeout_buf[256];
125+
UINTN getvar_size = sizeof(timeout_buf) - 2;
126+
uint32_t attrs;
127+
if (gRT->GetVariable(variable,
128+
&bli_vendor_guid,
129+
&attrs,
130+
&getvar_size,
131+
timeout_buf) == 0 && getvar_size > 0) {
132+
if (erase) {
133+
gRT->SetVariable(variable, &bli_vendor_guid,
134+
attrs,
135+
0, NULL);
136+
}
137+
if (getvar_size == 24 && memcmp(timeout_buf, L"menu-force",24) == 0) {
138+
*skip_timeout = true;
139+
return true;
140+
}
141+
if ((getvar_size == 24 && memcmp(timeout_buf, L"menu-hidden",24) == 0) || (getvar_size == 28 && memcmp(timeout_buf, L"menu-disabled",28) == 0)) {
142+
// TODO: menu-hidden should enable quiet & set timeout >= 1
143+
*timeout = 0;
144+
return true;
145+
}
146+
size_t t;
147+
if (!decwstr_to_size(timeout_buf, getvar_size, &t)) {
148+
return false;
149+
}
150+
*timeout = t;
151+
return true;
152+
}
153+
return false;
154+
155+
}
156+
157+
bool bli_update_oneshot_timeout(size_t *timeout, bool *skip_timeout) {
158+
return handle_timeout(L"LoaderConfigTimeoutOneShot", true, timeout, skip_timeout);
159+
}
160+
161+
bool bli_update_timeout(size_t *timeout, bool *skip_timeout) {
162+
return handle_timeout(L"LoaderConfigTimeout", false, timeout, skip_timeout);
163+
}
164+
165+
static bool handle_entry(wchar_t *variable, bool erase, char *path, size_t buf_size) {
166+
wchar_t wide_path[256];
167+
UINTN getvar_size = sizeof(wide_path) - 2;
168+
uint32_t attrs;
169+
if (gRT->GetVariable(variable,
170+
&bli_vendor_guid,
171+
&attrs,
172+
&getvar_size,
173+
wide_path) == 0 && getvar_size > 0) {
174+
if (erase) {
175+
gRT->SetVariable(variable, &bli_vendor_guid,
176+
attrs,
177+
0, NULL);
178+
}
179+
180+
size_t i;
181+
for (i = 0; i < buf_size-1 && i * 2 < getvar_size; i++) {
182+
path[i] = wide_path[i] & 0xff; // Assume 0x00 - 0x7f
183+
}
184+
path[i] = 0;
185+
186+
return true;
187+
}
188+
return false;
189+
}
190+
191+
bool bli_get_default_entry(char *path, size_t buf_size) {
192+
return handle_entry(L"LoaderEntryDefault", false, path, buf_size);
193+
}
194+
195+
bool bli_get_oneshot_entry(char *path, size_t buf_size) {
196+
return handle_entry(L"LoaderEntryOneShot", true, path, buf_size);
197+
}
198+
199+
void bli_set_selected_entry(const char *path) {
200+
wchar_t wide_path[256];
201+
size_t pos = 0;
202+
for (; pos < 256 && pos < strlen(path); pos++) {
203+
wide_path[pos] = path[pos];
204+
}
205+
gRT->SetVariable(L"LoaderEntrySelected",
206+
&bli_vendor_guid,
207+
EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
208+
strlen(path)*2 + 1,
209+
wide_path);
210+
}
211+
90212
#endif

common/lib/bli.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,11 @@
55

66
void init_bli(void);
77
void bli_on_boot(void);
8+
bool bli_update_oneshot_timeout(size_t *timeout, bool *skip_timeout);
9+
bool bli_update_timeout(size_t *timeout, bool *skip_timeout);
10+
void bli_set_selected_entry(const char *path);
11+
bool bli_get_default_entry(char *path, size_t buf_size);
12+
bool bli_get_oneshot_entry(char *path, size_t buf_size);
813

914
#endif
1015

common/menu.c

Lines changed: 96 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#include <stdnoreturn.h>
55
#include <config.h>
66
#include <menu.h>
7+
#include <lib/bli.h>
78
#include <lib/print.h>
89
#include <lib/misc.h>
910
#include <lib/libc.h>
@@ -1167,56 +1168,92 @@ noreturn void _menu(bool first_run) {
11671168

11681169
size_t selected_entry = 0;
11691170

1170-
char *default_entry = config_get_value(NULL, 0, "DEFAULT_ENTRY");
1171-
if (default_entry != NULL) {
1172-
bool is_index = true;
1173-
for (const char *p = default_entry; *p != '\0'; p++) {
1174-
if (*p < '0' || *p > '9') {
1175-
is_index = false;
1176-
break;
1177-
}
1178-
}
1179-
if (is_index) {
1180-
selected_entry = strtoui(default_entry, NULL, 10);
1181-
if (selected_entry)
1182-
selected_entry--;
1183-
} else {
1184-
// Copy the path since find_entry_by_path calls config_get_value
1185-
// internally (via should_skip_entry), which clobbers the static buffer.
1186-
char default_entry_path[256];
1187-
size_t len = strlen(default_entry);
1188-
if (len >= sizeof(default_entry_path)) {
1189-
len = sizeof(default_entry_path) - 1;
1190-
}
1191-
memcpy(default_entry_path, default_entry, len);
1192-
default_entry_path[len] = '\0';
1171+
bool has_entry = false;
1172+
1173+
#if defined (UEFI)
1174+
{
1175+
char path[256];
1176+
if (bli_get_oneshot_entry(path, 256)) {
1177+
// Find the entry with this path, expand directories, and get its index.
11931178
struct menu_entry *found_entry = NULL;
11941179
size_t found_index = 0;
1195-
find_entry_by_path(default_entry_path, menu_tree, 0, &found_entry, &found_index, true);
1180+
find_entry_by_path(path, menu_tree, 0, &found_entry, &found_index, true);
11961181
if (found_entry != NULL) {
11971182
selected_entry = found_index;
1183+
has_entry = true;
1184+
}
1185+
}
1186+
}
1187+
#endif
1188+
1189+
if (!has_entry) {
1190+
char *default_entry = config_get_value(NULL, 0, "DEFAULT_ENTRY");
1191+
if (default_entry != NULL) {
1192+
bool is_index = true;
1193+
for (const char *p = default_entry; *p != '\0'; p++) {
1194+
if (*p < '0' || *p > '9') {
1195+
is_index = false;
1196+
break;
1197+
}
1198+
}
1199+
if (is_index) {
1200+
selected_entry = strtoui(default_entry, NULL, 10);
1201+
if (selected_entry)
1202+
selected_entry--;
1203+
} else {
1204+
// Copy the path since find_entry_by_path calls config_get_value
1205+
// internally (via should_skip_entry), which clobbers the static buffer.
1206+
char default_entry_path[256];
1207+
size_t len = strlen(default_entry);
1208+
if (len >= sizeof(default_entry_path)) {
1209+
len = sizeof(default_entry_path) - 1;
1210+
}
1211+
memcpy(default_entry_path, default_entry, len);
1212+
default_entry_path[len] = '\0';
1213+
struct menu_entry *found_entry = NULL;
1214+
size_t found_index = 0;
1215+
find_entry_by_path(default_entry_path, menu_tree, 0, &found_entry, &found_index, true);
1216+
if (found_entry != NULL) {
1217+
selected_entry = found_index;
1218+
}
11981219
}
11991220
}
12001221
}
12011222

12021223
#if defined (UEFI)
1203-
char *remember_last = config_get_value(NULL, 0, "REMEMBER_LAST_ENTRY");
1204-
if (remember_last != NULL && strcasecmp(remember_last, "yes") == 0) {
1205-
char last_entry_path[256];
1206-
UINTN getvar_size = sizeof(last_entry_path);
1207-
if (gRT->GetVariable(L"LimineLastBootedEntry",
1208-
&limine_efi_vendor_guid,
1209-
NULL,
1210-
&getvar_size,
1211-
last_entry_path) == 0 && getvar_size > 0) {
1212-
// Ensure NUL termination
1213-
last_entry_path[getvar_size < sizeof(last_entry_path) ? getvar_size : sizeof(last_entry_path) - 1] = '\0';
1224+
if (!has_entry) {
1225+
char *remember_last = config_get_value(NULL, 0, "REMEMBER_LAST_ENTRY");
1226+
if (remember_last != NULL && strcasecmp(remember_last, "yes") == 0) {
1227+
char last_entry_path[256];
1228+
UINTN getvar_size = sizeof(last_entry_path);
1229+
if (gRT->GetVariable(L"LimineLastBootedEntry",
1230+
&limine_efi_vendor_guid,
1231+
NULL,
1232+
&getvar_size,
1233+
last_entry_path) == 0 && getvar_size > 0) {
1234+
// Ensure NUL termination
1235+
last_entry_path[getvar_size < sizeof(last_entry_path) ? getvar_size : sizeof(last_entry_path) - 1] = '\0';
1236+
// Find the entry with this path, expand directories, and get its index.
1237+
struct menu_entry *found_entry = NULL;
1238+
size_t found_index = 0;
1239+
find_entry_by_path(last_entry_path, menu_tree, 0, &found_entry, &found_index, true);
1240+
if (found_entry != NULL) {
1241+
selected_entry = found_index;
1242+
has_entry = true;
1243+
}
1244+
}
1245+
}
1246+
}
1247+
if (!has_entry) {
1248+
char path[256];
1249+
if (bli_get_default_entry(path, 256)) {
12141250
// Find the entry with this path, expand directories, and get its index.
12151251
struct menu_entry *found_entry = NULL;
12161252
size_t found_index = 0;
1217-
find_entry_by_path(last_entry_path, menu_tree, 0, &found_entry, &found_index, true);
1253+
find_entry_by_path(path, menu_tree, 0, &found_entry, &found_index, true);
12181254
if (found_entry != NULL) {
12191255
selected_entry = found_index;
1256+
has_entry = true;
12201257
}
12211258
}
12221259
}
@@ -1230,14 +1267,30 @@ noreturn void _menu(bool first_run) {
12301267
}
12311268

12321269
size_t timeout = 5;
1233-
char *timeout_config = config_get_value(NULL, 0, "TIMEOUT");
1234-
if (timeout_config != NULL) {
1235-
if (!strcmp(timeout_config, "no"))
1236-
skip_timeout = true;
1237-
else
1238-
timeout = strtoui(timeout_config, NULL, 10);
1270+
1271+
bool has_timeout = false;
1272+
1273+
#if defined (UEFI)
1274+
has_timeout = bli_update_oneshot_timeout(&timeout, &skip_timeout);
1275+
#endif
1276+
1277+
if (!has_timeout) {
1278+
char *timeout_config = config_get_value(NULL, 0, "TIMEOUT");
1279+
if (timeout_config != NULL) {
1280+
has_timeout = true;
1281+
if (!strcmp(timeout_config, "no"))
1282+
skip_timeout = true;
1283+
else
1284+
timeout = strtoui(timeout_config, NULL, 10);
1285+
}
12391286
}
12401287

1288+
#if defined (UEFI)
1289+
if (!has_timeout) {
1290+
has_timeout = bli_update_timeout(&timeout, &skip_timeout);
1291+
}
1292+
#endif
1293+
12411294
#if defined(UEFI)
12421295
bool reboot_to_firmware_supported = reboot_to_fw_ui_supported();
12431296
#endif
@@ -1508,6 +1561,7 @@ noreturn void _menu(bool first_run) {
15081561
EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
15091562
strlen(entry_path) + 1,
15101563
entry_path);
1564+
bli_set_selected_entry(entry_path);
15111565
#endif
15121566

15131567
boot(selected_menu_entry->body);

0 commit comments

Comments
 (0)