:: limine / common / protos / efi_boot_entry.c 4.0 KB raw

1
#include <protos/efi_boot_entry.h>
2
3
#if defined(UEFI)
4
5
#include <mm/pmm.h>
6
#include <efi.h>
7
#include <lib/config.h>
8
#include <lib/misc.h>
9
#include <stdbool.h>
10
11
static bool uefi_string_matches(CHAR16 *desc, size_t desc_max_chars, CHAR16 *target) {
12
    while (*target) {
13
        if (desc_max_chars == 0)
14
            return false;
15
        CHAR16 a = *desc >= L'a' && *desc <= L'z' ? *desc - 32 : *desc;
16
        CHAR16 b = *target >= L'a' && *target <= L'z' ? *target - 32 : *target;
17
        if (a != b)
18
            return false;
19
        desc++;
20
        target++;
21
        desc_max_chars--;
22
    }
23
    return desc_max_chars > 0 && *desc == L'\0';
24
}
25
26
static void format_boot_var(CHAR16 *out, UINT16 num) {
27
    out[0] = L'B';
28
    out[1] = L'o';
29
    out[2] = L'o';
30
    out[3] = L't';
31
    out[4] = L"0123456789ABCDEF"[(num >> 12) & 0xF];
32
    out[5] = L"0123456789ABCDEF"[(num >> 8) & 0xF];
33
    out[6] = L"0123456789ABCDEF"[(num >> 4) & 0xF];
34
    out[7] = L"0123456789ABCDEF"[(num >> 0) & 0xF];
35
    out[8] = L'\0';
36
}
37
38
static bool find_boot_entry(CHAR16 *entry, uint16_t *out) {
39
    EFI_STATUS status;
40
    uint16_t boot_order[128];
41
    size_t size = sizeof(boot_order);
42
    EFI_GUID global_variable = EFI_GLOBAL_VARIABLE;
43
44
    status = gRT->GetVariable(L"BootOrder", &global_variable, NULL, &size, boot_order);
45
46
    if (EFI_ERROR(status)) {
47
        panic(true, "efi_boot_entry: Failed to get BootOrder variable (%X)",
48
            (uint64_t)status);
49
    }
50
51
    size_t count = size / sizeof(uint16_t);
52
53
    for (size_t i = 0; i < count; i++) {
54
        CHAR16 var_name[9];
55
56
        format_boot_var(var_name, boot_order[i]);
57
58
        size_t buf_size = 0;
59
        /* Query for buf size */
60
        if (gRT->GetVariable(var_name, &global_variable, NULL, &buf_size, NULL)
61
                != EFI_BUFFER_TOO_SMALL) {
62
            continue;
63
        }
64
        size_t buf_alloc = buf_size;
65
        uint8_t *buf = ext_mem_alloc(buf_alloc);
66
        status = gRT->GetVariable(var_name, &global_variable, NULL, &buf_size, buf);
67
68
        if (EFI_ERROR(status)) {
69
            pmm_free(buf, buf_alloc);
70
            continue;
71
        }
72
        /* Get the description */
73
        if (buf_size < sizeof(uint32_t) + sizeof(uint16_t) + sizeof(CHAR16)) {
74
            pmm_free(buf, buf_alloc);
75
            continue;
76
        }
77
        size_t desc_offset = sizeof(uint32_t) + sizeof(uint16_t);
78
        CHAR16 *desc = (CHAR16 *)(buf + desc_offset);
79
        size_t desc_max_chars = (buf_size - desc_offset) / sizeof(CHAR16);
80
        if (uefi_string_matches(desc, desc_max_chars, entry)) {
81
            *out = boot_order[i];
82
            pmm_free(buf, buf_alloc);
83
            return true;
84
        }
85
        pmm_free(buf, buf_alloc);
86
    }
87
88
    return false;
89
}
90
91
noreturn void efi_boot_entry(char *config) {
92
    char *boot_entry = config_get_value(config, 0, "ENTRY");
93
94
    if (boot_entry == NULL) {
95
        panic(true, "efi_boot_entry: No entry specified");
96
    }
97
98
    /* Convert entry string to UTF-16 */
99
    CHAR16 boot_entry_utf16[128];
100
    size_t i;
101
102
    for (i = 0; i < sizeof(boot_entry_utf16) / sizeof(CHAR16) - 1 && boot_entry[i]; i++) {
103
        boot_entry_utf16[i] = (CHAR16)boot_entry[i];
104
    }
105
106
    boot_entry_utf16[i] = L'\0';
107
108
    /* Find the desired boot entry */
109
    uint16_t boot_next = 0;
110
111
    if (!find_boot_entry(boot_entry_utf16, &boot_next)) {
112
        panic(true, "efi_boot_entry: Failed to find boot entry '%s'", boot_entry);
113
    }
114
115
    EFI_GUID global_variable = EFI_GLOBAL_VARIABLE;
116
117
    /* Set BootNext to it */
118
    EFI_STATUS status = gRT->SetVariable(L"BootNext", &global_variable,
119
                                       EFI_VARIABLE_NON_VOLATILE |
120
                                       EFI_VARIABLE_BOOTSERVICE_ACCESS |
121
                                       EFI_VARIABLE_RUNTIME_ACCESS,
122
                                       sizeof(boot_next), &boot_next);
123
124
    if (status) {
125
        panic(true, "efi_boot_entry: Failed to set BootNext variable (%X)",
126
            (uint64_t)status);
127
    }
128
129
    /* Now reboot */
130
    gRT->ResetSystem(EfiResetWarm, EFI_SUCCESS, 0, NULL);
131
132
    panic(true, "efi_boot_entry: Failed to reboot");
133
}
134
135
#endif
tab: 248 wrap: offon