:: limine / common / lib / bli.c 6.4 KB raw

1
#if defined (UEFI)
2
3
#include <stdint.h>
4
#include <stddef.h>
5
#include <config.h>
6
#include <sys/cpu.h>
7
#include <efi.h>
8
#include <lib/bli.h>
9
#include <lib/guid.h>
10
#include <lib/misc.h>
11
#include <menu.h>
12
13
#define LIMINE_BRAND L"Limine " LIMINE_VERSION
14
15
static EFI_GUID bli_vendor_guid = { 0x4a67b082, 0x0a4c, 0x41cf, { 0xb6, 0xc7, 0x44, 0x0b, 0x29, 0xbb, 0x8c, 0x4f } };
16
17
// The buffer must be at least 21 bytes long
18
void uint64_to_decwstr(uint64_t value, wchar_t *buf) {
19
    wchar_t tmp[21];
20
    size_t i = 0;
21
22
    if (buf == NULL) {
23
        return;
24
    }
25
26
    if (value == 0) {
27
        buf[0] = '0';
28
        buf[1] = '\0';
29
        return;
30
    }
31
32
    // Convert digits in reverse order
33
    while (value > 0) {
34
        tmp[i++] = '0' + (value % 10);
35
        value /= 10;
36
    }
37
38
    // Reverse the string into the buffer
39
    for (size_t j = 0; j < i; j++) {
40
        buf[j] = tmp[i - j - 1];
41
    }
42
    buf[i] = '\0';
43
}
44
45
bool decwstr_to_size(const wchar_t *buf, size_t buf_size, size_t *value) {
46
    size_t i = 0;
47
    size_t tmp = 0;
48
49
    if (buf == NULL) {
50
        return false;
51
    }
52
53
    if (buf_size == 0 || buf[0] == L'\0') {
54
        return false;
55
    }
56
57
    while (i * 2 < buf_size && buf[i]) {
58
        wchar_t c = buf[i];
59
        if (!(c >= L'0' && c <= L'9')) {
60
            return false;
61
        }
62
        tmp = CHECKED_MUL(tmp, (size_t)10, return false);
63
        tmp = CHECKED_ADD(tmp, (size_t)(c - L'0'), return false);
64
        i++;
65
    }
66
67
    *value = tmp;
68
69
    return true;
70
}
71
72
void bli_set_loader_time(wchar_t *variable, uint64_t time) {
73
    if (time == 0)
74
        return;
75
76
    wchar_t time_wstr[21];
77
    uint64_to_decwstr(time, time_wstr);
78
79
    size_t len = 0;
80
    while (time_wstr[len] != L'\0') len++;
81
82
    gRT->SetVariable(variable,
83
            &bli_vendor_guid,
84
            EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
85
            (len + 1) * sizeof(wchar_t),
86
            time_wstr);
87
}
88
89
void init_bli(void) {
90
    bli_set_loader_time(L"LoaderTimeInitUSec", usec_at_bootloader_entry);
91
92
    gRT->SetVariable(L"LoaderInfo",
93
            &bli_vendor_guid,
94
            EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
95
            sizeof(LIMINE_BRAND),
96
            LIMINE_BRAND);
97
98
    uint64_t features = (1 << 0) | // Timeout control
99
                        (1 << 1) | // Oneshot timeout control
100
                        (1 << 2) | // Default entry control
101
                        (1 << 3) | // Oneshot entry control
102
                        (1 << 13); // menu-disabled support
103
    gRT->SetVariable(L"LoaderFeatures",
104
            &bli_vendor_guid,
105
            EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
106
            sizeof(features),
107
            &features);
108
109
    char part_uuid_str[37];
110
    guid_to_string(&boot_volume->part_guid, part_uuid_str);
111
112
    // Convert part_uuid_str to a wide-char string
113
    wchar_t part_uuid[37];
114
    for (size_t i = 0; i < 37; i++) {
115
        part_uuid[i] = (wchar_t) part_uuid_str[i];
116
    }
117
118
    gRT->SetVariable(L"LoaderDevicePartUUID",
119
            &bli_vendor_guid,
120
            EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
121
            sizeof(part_uuid),
122
            part_uuid);
123
}
124
125
void bli_on_boot(void) {
126
    bli_set_loader_time(L"LoaderTimeExecUSec", rdtsc_usec());
127
}
128
129
static bool handle_timeout(wchar_t *variable, bool erase, size_t *timeout, bool *skip_timeout) {
130
    wchar_t timeout_buf[256];
131
    UINTN getvar_size = sizeof(timeout_buf) - 2;
132
    uint32_t attrs;
133
    if (gRT->GetVariable(variable,
134
                             &bli_vendor_guid,
135
                             &attrs,
136
                             &getvar_size,
137
                             timeout_buf) == 0 && getvar_size > 0) {
138
        if (erase) {
139
            gRT->SetVariable(variable, &bli_vendor_guid,
140
                attrs,
141
                0, NULL);
142
        }
143
        if (getvar_size == 22 && memcmp(timeout_buf, L"menu-force", 22) == 0) {
144
            *skip_timeout = true;
145
            return true;
146
        }
147
        if ((getvar_size == 24 && memcmp(timeout_buf, L"menu-hidden",24) == 0) || (getvar_size == 28 && memcmp(timeout_buf, L"menu-disabled",28) == 0)) {
148
            // TODO: menu-hidden should enable quiet & set timeout >= 1
149
            *timeout = 0;
150
            return true;
151
        }
152
        size_t t;
153
        if (!decwstr_to_size(timeout_buf, getvar_size, &t)) {
154
            return false;
155
        }
156
        // For LoaderConfigTimeoutOneShot, "0" means show menu indefinitely.
157
        if (erase && t == 0) {
158
            *skip_timeout = true;
159
            return true;
160
        }
161
        *timeout = t;
162
        return true;
163
    }
164
    return false;
165
166
}
167
168
bool bli_update_oneshot_timeout(size_t *timeout, bool *skip_timeout) {
169
    return handle_timeout(L"LoaderConfigTimeoutOneShot", true, timeout, skip_timeout);
170
}
171
172
bool bli_update_timeout(size_t *timeout, bool *skip_timeout) {
173
    return handle_timeout(L"LoaderConfigTimeout", false, timeout, skip_timeout);
174
}
175
176
static bool handle_entry(wchar_t *variable, bool erase, char *path, size_t buf_size) {
177
    wchar_t wide_path[256];
178
    UINTN getvar_size = sizeof(wide_path) - 2;
179
    uint32_t attrs;
180
    if (gRT->GetVariable(variable,
181
                             &bli_vendor_guid,
182
                             &attrs,
183
                             &getvar_size,
184
                             wide_path) == 0 && getvar_size > 0) {
185
        if (erase) {
186
            gRT->SetVariable(variable, &bli_vendor_guid,
187
                attrs,
188
                0, NULL);
189
        }
190
191
        size_t i;
192
        for (i = 0; i < buf_size-1 && i * 2 < getvar_size; i++) {
193
            if (wide_path[i] > 0x7f) {
194
                return false;
195
            }
196
            path[i] = wide_path[i] & 0x7f;
197
        }
198
        path[i] = 0;
199
200
        return true;
201
    }
202
    return false;
203
}
204
205
bool bli_get_default_entry(char *path, size_t buf_size) {
206
    return handle_entry(L"LoaderEntryDefault", false, path, buf_size);
207
}
208
209
bool bli_get_oneshot_entry(char *path, size_t buf_size) {
210
    return handle_entry(L"LoaderEntryOneShot", true, path, buf_size);
211
}
212
213
void bli_set_selected_entry(const char *path) {
214
    wchar_t wide_path[MENU_PATH_MAX];
215
    size_t len = strlen(path);
216
    if (len > MENU_PATH_MAX - 1) {
217
        len = MENU_PATH_MAX - 1;
218
    }
219
    for (size_t pos = 0; pos < len; pos++) {
220
        wide_path[pos] = path[pos];
221
    }
222
    wide_path[len] = L'\0';
223
    gRT->SetVariable(L"LoaderEntrySelected",
224
            &bli_vendor_guid,
225
            EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
226
            (len + 1) * sizeof(wchar_t),
227
            wide_path);
228
}
229
230
#endif
tab: 248 wrap: offon