| 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 |