]>
Commit | Line | Data |
---|---|---|
f739fcd8 | 1 | // SPDX-License-Identifier: GPL-2.0+ |
9975fe96 | 2 | /* |
e1fec152 | 3 | * EFI boot manager |
9975fe96 RC |
4 | * |
5 | * Copyright (c) 2017 Rob Clark | |
9975fe96 RC |
6 | */ |
7 | ||
7a373e54 HS |
8 | #define LOG_CATEGORY LOGC_EFI |
9 | ||
9975fe96 RC |
10 | #include <common.h> |
11 | #include <charset.h> | |
f7ae49fc | 12 | #include <log.h> |
9975fe96 RC |
13 | #include <malloc.h> |
14 | #include <efi_loader.h> | |
dda8c716 | 15 | #include <efi_variable.h> |
1a82b341 | 16 | #include <asm/unaligned.h> |
9975fe96 RC |
17 | |
18 | static const struct efi_boot_services *bs; | |
19 | static const struct efi_runtime_services *rs; | |
20 | ||
9975fe96 RC |
21 | /* |
22 | * bootmgr implements the logic of trying to find a payload to boot | |
23 | * based on the BootOrder + BootXXXX variables, and then loading it. | |
24 | * | |
25 | * TODO detecting a special key held (f9?) and displaying a boot menu | |
26 | * like you would get on a PC would be clever. | |
27 | * | |
28 | * TODO if we had a way to write and persist variables after the OS | |
29 | * has started, we'd also want to check OsIndications to see if we | |
30 | * should do normal or recovery boot. | |
31 | */ | |
32 | ||
15621aa2 HS |
33 | /** |
34 | * get_var() - get UEFI variable | |
35 | * | |
36 | * It is the caller's duty to free the returned buffer. | |
37 | * | |
38 | * @name: name of variable | |
39 | * @vendor: vendor GUID of variable | |
40 | * @size: size of allocated buffer | |
41 | * Return: buffer with variable data or NULL | |
42 | */ | |
9975fe96 | 43 | static void *get_var(u16 *name, const efi_guid_t *vendor, |
45c66f9c | 44 | efi_uintn_t *size) |
9975fe96 | 45 | { |
9975fe96 RC |
46 | efi_status_t ret; |
47 | void *buf = NULL; | |
48 | ||
49 | *size = 0; | |
dda8c716 | 50 | ret = efi_get_variable_int(name, vendor, NULL, size, buf, NULL); |
9975fe96 RC |
51 | if (ret == EFI_BUFFER_TOO_SMALL) { |
52 | buf = malloc(*size); | |
dda8c716 | 53 | ret = efi_get_variable_int(name, vendor, NULL, size, buf, NULL); |
9975fe96 RC |
54 | } |
55 | ||
56 | if (ret != EFI_SUCCESS) { | |
57 | free(buf); | |
58 | *size = 0; | |
59 | return NULL; | |
60 | } | |
61 | ||
62 | return buf; | |
63 | } | |
64 | ||
15621aa2 HS |
65 | /** |
66 | * try_load_entry() - try to load image for boot option | |
67 | * | |
9975fe96 | 68 | * Attempt to load load-option number 'n', returning device_path and file_path |
15621aa2 | 69 | * if successful. This checks that the EFI_LOAD_OPTION is active (enabled) |
9975fe96 | 70 | * and that the specified file to boot exists. |
15621aa2 | 71 | * |
0ad64007 HS |
72 | * @n: number of the boot option, e.g. 0x0a13 for Boot0A13 |
73 | * @handle: on return handle for the newly installed image | |
74 | * @load_options: load options set on the loaded image protocol | |
75 | * Return: status code | |
9975fe96 | 76 | */ |
0ad64007 HS |
77 | static efi_status_t try_load_entry(u16 n, efi_handle_t *handle, |
78 | void **load_options) | |
9975fe96 | 79 | { |
1a82b341 | 80 | struct efi_load_option lo; |
9975fe96 RC |
81 | u16 varname[] = L"Boot0000"; |
82 | u16 hexmap[] = L"0123456789ABCDEF"; | |
6b95b38c | 83 | void *load_option; |
45c66f9c | 84 | efi_uintn_t size; |
6b95b38c | 85 | efi_status_t ret; |
9975fe96 RC |
86 | |
87 | varname[4] = hexmap[(n & 0xf000) >> 12]; | |
88 | varname[5] = hexmap[(n & 0x0f00) >> 8]; | |
89 | varname[6] = hexmap[(n & 0x00f0) >> 4]; | |
90 | varname[7] = hexmap[(n & 0x000f) >> 0]; | |
91 | ||
92 | load_option = get_var(varname, &efi_global_variable_guid, &size); | |
93 | if (!load_option) | |
6b95b38c | 94 | return EFI_LOAD_ERROR; |
9975fe96 | 95 | |
0e69bcfb HS |
96 | ret = efi_deserialize_load_option(&lo, load_option, &size); |
97 | if (ret != EFI_SUCCESS) { | |
98 | log_warning("Invalid load option for %ls\n", varname); | |
99 | goto error; | |
100 | } | |
9975fe96 RC |
101 | |
102 | if (lo.attributes & LOAD_OPTION_ACTIVE) { | |
37279ad3 | 103 | u32 attributes; |
9975fe96 | 104 | |
7a373e54 HS |
105 | log_debug("%s: trying to load \"%ls\" from %pD\n", |
106 | __func__, lo.label, lo.file_path); | |
9975fe96 | 107 | |
6b95b38c AT |
108 | ret = EFI_CALL(efi_load_image(true, efi_root, lo.file_path, |
109 | NULL, 0, handle)); | |
94e6e550 | 110 | if (ret != EFI_SUCCESS) { |
7a373e54 HS |
111 | log_warning("Loading %ls '%ls' failed\n", |
112 | varname, lo.label); | |
9975fe96 | 113 | goto error; |
94e6e550 | 114 | } |
9975fe96 | 115 | |
37279ad3 AT |
116 | attributes = EFI_VARIABLE_BOOTSERVICE_ACCESS | |
117 | EFI_VARIABLE_RUNTIME_ACCESS; | |
dda8c716 HS |
118 | ret = efi_set_variable_int(L"BootCurrent", |
119 | &efi_global_variable_guid, | |
0ad64007 | 120 | attributes, sizeof(n), &n, false); |
53f6a5aa IA |
121 | if (ret != EFI_SUCCESS) |
122 | goto unload; | |
123 | /* try to register load file2 for initrd's */ | |
124 | if (IS_ENABLED(CONFIG_EFI_LOAD_FILE2_INITRD)) { | |
125 | ret = efi_initrd_register(); | |
126 | if (ret != EFI_SUCCESS) | |
127 | goto unload; | |
6b95b38c | 128 | } |
37279ad3 | 129 | |
7a373e54 | 130 | log_info("Booting: %ls\n", lo.label); |
6b95b38c AT |
131 | } else { |
132 | ret = EFI_LOAD_ERROR; | |
9975fe96 RC |
133 | } |
134 | ||
0ad64007 HS |
135 | /* Set load options */ |
136 | if (size) { | |
137 | *load_options = malloc(size); | |
138 | if (!*load_options) { | |
139 | ret = EFI_OUT_OF_RESOURCES; | |
140 | goto error; | |
141 | } | |
142 | memcpy(*load_options, lo.optional_data, size); | |
143 | ret = efi_set_load_options(*handle, size, *load_options); | |
144 | } else { | |
e434311d | 145 | *load_options = NULL; |
0ad64007 HS |
146 | } |
147 | ||
9975fe96 RC |
148 | error: |
149 | free(load_option); | |
150 | ||
53f6a5aa IA |
151 | return ret; |
152 | ||
153 | unload: | |
154 | if (EFI_CALL(efi_unload_image(*handle)) != EFI_SUCCESS) | |
155 | log_err("Unloading image failed\n"); | |
156 | free(load_option); | |
157 | ||
6b95b38c | 158 | return ret; |
9975fe96 RC |
159 | } |
160 | ||
15621aa2 HS |
161 | /** |
162 | * efi_bootmgr_load() - try to load from BootNext or BootOrder | |
163 | * | |
37279ad3 AT |
164 | * Attempt to load from BootNext or in the order specified by BootOrder |
165 | * EFI variable, the available load-options, finding and returning | |
166 | * the first one that can be loaded successfully. | |
15621aa2 | 167 | * |
0ad64007 HS |
168 | * @handle: on return handle for the newly installed image |
169 | * @load_options: load options set on the loaded image protocol | |
170 | * Return: status code | |
9975fe96 | 171 | */ |
0ad64007 | 172 | efi_status_t efi_bootmgr_load(efi_handle_t *handle, void **load_options) |
9975fe96 | 173 | { |
37279ad3 | 174 | u16 bootnext, *bootorder; |
45c66f9c | 175 | efi_uintn_t size; |
9975fe96 | 176 | int i, num; |
37279ad3 | 177 | efi_status_t ret; |
9975fe96 | 178 | |
9975fe96 RC |
179 | bs = systab.boottime; |
180 | rs = systab.runtime; | |
181 | ||
37279ad3 | 182 | /* BootNext */ |
37279ad3 | 183 | size = sizeof(bootnext); |
dda8c716 HS |
184 | ret = efi_get_variable_int(L"BootNext", |
185 | &efi_global_variable_guid, | |
186 | NULL, &size, &bootnext, NULL); | |
37279ad3 AT |
187 | if (ret == EFI_SUCCESS || ret == EFI_BUFFER_TOO_SMALL) { |
188 | /* BootNext does exist here */ | |
189 | if (ret == EFI_BUFFER_TOO_SMALL || size != sizeof(u16)) | |
7a373e54 | 190 | log_err("BootNext must be 16-bit integer\n"); |
37279ad3 AT |
191 | |
192 | /* delete BootNext */ | |
dda8c716 HS |
193 | ret = efi_set_variable_int(L"BootNext", |
194 | &efi_global_variable_guid, | |
195 | 0, 0, NULL, false); | |
37279ad3 AT |
196 | |
197 | /* load BootNext */ | |
198 | if (ret == EFI_SUCCESS) { | |
199 | if (size == sizeof(u16)) { | |
0ad64007 HS |
200 | ret = try_load_entry(bootnext, handle, |
201 | load_options); | |
6b95b38c AT |
202 | if (ret == EFI_SUCCESS) |
203 | return ret; | |
7a373e54 HS |
204 | log_warning( |
205 | "Loading from BootNext failed, falling back to BootOrder\n"); | |
37279ad3 AT |
206 | } |
207 | } else { | |
7a373e54 | 208 | log_err("Deleting BootNext failed\n"); |
37279ad3 AT |
209 | } |
210 | } | |
211 | ||
212 | /* BootOrder */ | |
9975fe96 | 213 | bootorder = get_var(L"BootOrder", &efi_global_variable_guid, &size); |
33e44971 | 214 | if (!bootorder) { |
7a373e54 | 215 | log_info("BootOrder not defined\n"); |
6b95b38c | 216 | ret = EFI_NOT_FOUND; |
9975fe96 | 217 | goto error; |
33e44971 | 218 | } |
9975fe96 RC |
219 | |
220 | num = size / sizeof(uint16_t); | |
221 | for (i = 0; i < num; i++) { | |
7a373e54 HS |
222 | log_debug("%s trying to load Boot%04X\n", __func__, |
223 | bootorder[i]); | |
0ad64007 | 224 | ret = try_load_entry(bootorder[i], handle, load_options); |
6b95b38c | 225 | if (ret == EFI_SUCCESS) |
9975fe96 RC |
226 | break; |
227 | } | |
228 | ||
229 | free(bootorder); | |
230 | ||
231 | error: | |
6b95b38c | 232 | return ret; |
9975fe96 | 233 | } |