]>
Commit | Line | Data |
---|---|---|
aedb8470 | 1 | // SPDX-License-Identifier: BSD-2-Clause-Patent |
3ce517fd | 2 | /* |
aedb8470 PJ |
3 | * Copyright Red Hat, Inc. |
4 | * Copyright Peter Jones <pjones@redhat.com> | |
3ce517fd PJ |
5 | */ |
6 | ||
7 | #include <efi.h> | |
8 | #include <efilib.h> | |
9 | ||
b953468e | 10 | #include "shim.h" |
3ce517fd | 11 | |
a5db51a5 GL |
12 | #define NO_REBOOT L"FB_NO_REBOOT" |
13 | ||
3ce517fd PJ |
14 | EFI_LOADED_IMAGE *this_image = NULL; |
15 | ||
c0f7d130 PJ |
16 | int |
17 | get_fallback_verbose(void) | |
18 | { | |
c0f7d130 PJ |
19 | UINT8 *data = NULL; |
20 | UINTN dataSize = 0; | |
21 | EFI_STATUS efi_status; | |
22 | unsigned int i; | |
23 | static int state = -1; | |
24 | ||
25 | if (state != -1) | |
26 | return state; | |
27 | ||
28 | efi_status = get_variable(L"FALLBACK_VERBOSE", | |
b953468e | 29 | &data, &dataSize, SHIM_LOCK_GUID); |
c0f7d130 PJ |
30 | if (EFI_ERROR(efi_status)) { |
31 | state = 0; | |
32 | return state; | |
33 | } | |
34 | ||
77ebb3d6 | 35 | state = 0; |
c0f7d130 PJ |
36 | for (i = 0; i < dataSize; i++) { |
37 | if (data[i]) { | |
38 | state = 1; | |
77ebb3d6 | 39 | break; |
c0f7d130 PJ |
40 | } |
41 | } | |
42 | ||
77ebb3d6 PJ |
43 | if (data) |
44 | FreePool(data); | |
c0f7d130 PJ |
45 | return state; |
46 | } | |
47 | ||
48 | #define VerbosePrintUnprefixed(fmt, ...) \ | |
49 | ({ \ | |
50 | UINTN ret_ = 0; \ | |
51 | if (get_fallback_verbose()) \ | |
1fe31ee1 | 52 | ret_ = console_print((fmt), ##__VA_ARGS__); \ |
c0f7d130 PJ |
53 | ret_; \ |
54 | }) | |
55 | ||
0172d435 PJ |
56 | #define VerbosePrint(fmt, ...) \ |
57 | ({ \ | |
58 | UINTN line_ = __LINE__ - 2; \ | |
59 | UINTN ret_ = 0; \ | |
60 | if (get_fallback_verbose()) { \ | |
61 | console_print(L"%a:%d: ", __func__, line_); \ | |
62 | ret_ = console_print((fmt), ##__VA_ARGS__); \ | |
63 | } \ | |
64 | ret_; \ | |
c0f7d130 PJ |
65 | }) |
66 | ||
dfd6c73a PJ |
67 | static EFI_STATUS |
68 | FindSubDevicePath(EFI_DEVICE_PATH *In, UINT8 Type, UINT8 SubType, | |
69 | EFI_DEVICE_PATH **Out) | |
70 | { | |
71 | EFI_DEVICE_PATH *dp = In; | |
72 | if (!In || !Out) | |
73 | return EFI_INVALID_PARAMETER; | |
74 | ||
c0f7d130 PJ |
75 | CHAR16 *dps = DevicePathToStr(In); |
76 | VerbosePrint(L"input device path: \"%s\"\n", dps); | |
77 | FreePool(dps); | |
78 | ||
dfd6c73a PJ |
79 | for (dp = In; !IsDevicePathEnd(dp); dp = NextDevicePathNode(dp)) { |
80 | if (DevicePathType(dp) == Type && | |
81 | DevicePathSubType(dp) == SubType) { | |
c0f7d130 PJ |
82 | dps = DevicePathToStr(dp); |
83 | VerbosePrint(L"sub-path (%hhd,%hhd): \"%s\"\n", | |
84 | Type, SubType, dps); | |
85 | FreePool(dps); | |
86 | ||
dfd6c73a PJ |
87 | *Out = DuplicateDevicePath(dp); |
88 | if (!*Out) | |
89 | return EFI_OUT_OF_RESOURCES; | |
90 | return EFI_SUCCESS; | |
91 | } | |
92 | } | |
93 | *Out = NULL; | |
94 | return EFI_NOT_FOUND; | |
95 | } | |
96 | ||
3ce517fd | 97 | static EFI_STATUS |
d74ab697 | 98 | get_file_size(EFI_FILE_HANDLE fh, UINTN *retsize) |
3ce517fd | 99 | { |
802221cf | 100 | EFI_STATUS efi_status; |
3ce517fd PJ |
101 | void *buffer = NULL; |
102 | UINTN bs = 0; | |
3ce517fd PJ |
103 | |
104 | /* The API here is "Call it once with bs=0, it fills in bs, | |
105 | * then allocate a buffer and ask again to get it filled. */ | |
9fdca5bb | 106 | efi_status = fh->GetInfo(fh, &EFI_FILE_INFO_GUID, &bs, NULL); |
802221cf PJ |
107 | if (EFI_ERROR(efi_status) && efi_status != EFI_BUFFER_TOO_SMALL) |
108 | return efi_status; | |
05458d22 PJ |
109 | if (bs == 0) |
110 | return EFI_SUCCESS; | |
111 | ||
112 | buffer = AllocateZeroPool(bs); | |
113 | if (!buffer) { | |
1fe31ee1 | 114 | console_print(L"Could not allocate memory\n"); |
05458d22 | 115 | return EFI_OUT_OF_RESOURCES; |
3ce517fd | 116 | } |
9fdca5bb | 117 | efi_status = fh->GetInfo(fh, &EFI_FILE_INFO_GUID, &bs, buffer); |
3ce517fd | 118 | /* This checks *either* the error from the first GetInfo, if it isn't |
05458d22 PJ |
119 | * the EFI_BUFFER_TOO_SMALL we're expecting, or the second GetInfo |
120 | * call in *any* case. */ | |
802221cf | 121 | if (EFI_ERROR(efi_status)) { |
1fe31ee1 | 122 | console_print(L"Could not get file info: %r\n", efi_status); |
3ce517fd PJ |
123 | if (buffer) |
124 | FreePool(buffer); | |
802221cf | 125 | return efi_status; |
3ce517fd PJ |
126 | } |
127 | EFI_FILE_INFO *fi = buffer; | |
128 | *retsize = fi->FileSize; | |
129 | FreePool(buffer); | |
130 | return EFI_SUCCESS; | |
131 | } | |
132 | ||
133 | EFI_STATUS | |
134 | read_file(EFI_FILE_HANDLE fh, CHAR16 *fullpath, CHAR16 **buffer, UINT64 *bs) | |
135 | { | |
136 | EFI_FILE_HANDLE fh2; | |
802221cf PJ |
137 | EFI_STATUS efi_status; |
138 | ||
9fdca5bb | 139 | efi_status = fh->Open(fh, &fh2, fullpath, EFI_FILE_READ_ONLY, 0); |
802221cf | 140 | if (EFI_ERROR(efi_status)) { |
1fe31ee1 | 141 | console_print(L"Couldn't open \"%s\": %r\n", fullpath, efi_status); |
802221cf | 142 | return efi_status; |
3ce517fd PJ |
143 | } |
144 | ||
d74ab697 | 145 | UINTN len = 0; |
3ce517fd | 146 | CHAR16 *b = NULL; |
802221cf PJ |
147 | efi_status = get_file_size(fh2, &len); |
148 | if (EFI_ERROR(efi_status)) { | |
1fe31ee1 HG |
149 | console_print(L"Could not get file size for \"%s\": %r\n", |
150 | fullpath, efi_status); | |
9fdca5bb | 151 | fh2->Close(fh2); |
802221cf | 152 | return efi_status; |
3ce517fd PJ |
153 | } |
154 | ||
809dc7a1 | 155 | if (len > 1024 * PAGE_SIZE) { |
9fdca5bb | 156 | fh2->Close(fh2); |
809dc7a1 PJ |
157 | return EFI_BAD_BUFFER_SIZE; |
158 | } | |
159 | ||
3ce517fd PJ |
160 | b = AllocateZeroPool(len + 2); |
161 | if (!buffer) { | |
1fe31ee1 | 162 | console_print(L"Could not allocate memory\n"); |
9fdca5bb | 163 | fh2->Close(fh2); |
3ce517fd PJ |
164 | return EFI_OUT_OF_RESOURCES; |
165 | } | |
166 | ||
9fdca5bb | 167 | efi_status = fh->Read(fh, &len, b); |
802221cf | 168 | if (EFI_ERROR(efi_status)) { |
3ce517fd | 169 | FreePool(buffer); |
9fdca5bb | 170 | fh2->Close(fh2); |
1fe31ee1 | 171 | console_print(L"Could not read file: %r\n", efi_status); |
802221cf | 172 | return efi_status; |
3ce517fd PJ |
173 | } |
174 | *buffer = b; | |
175 | *bs = len; | |
9fdca5bb | 176 | fh2->Close(fh2); |
3ce517fd PJ |
177 | return EFI_SUCCESS; |
178 | } | |
179 | ||
180 | EFI_STATUS | |
181 | make_full_path(CHAR16 *dirname, CHAR16 *filename, CHAR16 **out, UINT64 *outlen) | |
182 | { | |
183 | UINT64 len; | |
87c8f07e | 184 | |
dfd6c73a PJ |
185 | len = StrLen(L"\\EFI\\") + StrLen(dirname) |
186 | + StrLen(L"\\") + StrLen(filename) | |
187 | + 2; | |
3ce517fd | 188 | |
4665fcab | 189 | CHAR16 *fullpath = AllocateZeroPool(len*sizeof(CHAR16)); |
3ce517fd | 190 | if (!fullpath) { |
1fe31ee1 | 191 | console_print(L"Could not allocate memory\n"); |
3ce517fd PJ |
192 | return EFI_OUT_OF_RESOURCES; |
193 | } | |
194 | ||
195 | StrCat(fullpath, L"\\EFI\\"); | |
196 | StrCat(fullpath, dirname); | |
197 | StrCat(fullpath, L"\\"); | |
198 | StrCat(fullpath, filename); | |
199 | ||
200 | *out = fullpath; | |
201 | *outlen = len; | |
202 | return EFI_SUCCESS; | |
203 | } | |
204 | ||
205 | CHAR16 *bootorder = NULL; | |
206 | int nbootorder = 0; | |
207 | ||
3fa9a534 PJ |
208 | EFI_DEVICE_PATH *first_new_option = NULL; |
209 | VOID *first_new_option_args = NULL; | |
210 | UINTN first_new_option_size = 0; | |
211 | ||
3ce517fd | 212 | EFI_STATUS |
dfd6c73a PJ |
213 | add_boot_option(EFI_DEVICE_PATH *hddp, EFI_DEVICE_PATH *fulldp, |
214 | CHAR16 *filename, CHAR16 *label, CHAR16 *arguments) | |
3ce517fd PJ |
215 | { |
216 | static int i = 0; | |
217 | CHAR16 varname[] = L"Boot0000"; | |
218 | CHAR16 hexmap[] = L"0123456789ABCDEF"; | |
802221cf | 219 | EFI_STATUS efi_status; |
3ce517fd PJ |
220 | |
221 | for(; i <= 0xffff; i++) { | |
222 | varname[4] = hexmap[(i & 0xf000) >> 12]; | |
223 | varname[5] = hexmap[(i & 0x0f00) >> 8]; | |
224 | varname[6] = hexmap[(i & 0x00f0) >> 4]; | |
225 | varname[7] = hexmap[(i & 0x000f) >> 0]; | |
226 | ||
b953468e | 227 | void *var = LibGetVariable(varname, &GV_GUID); |
3ce517fd PJ |
228 | if (!var) { |
229 | int size = sizeof(UINT32) + sizeof (UINT16) + | |
dfd6c73a PJ |
230 | StrLen(label)*2 + 2 + DevicePathSize(hddp) + |
231 | StrLen(arguments) * 2; | |
3ce517fd | 232 | |
6b251052 | 233 | CHAR8 *data = AllocateZeroPool(size + 2); |
3ce517fd PJ |
234 | CHAR8 *cursor = data; |
235 | *(UINT32 *)cursor = LOAD_OPTION_ACTIVE; | |
236 | cursor += sizeof (UINT32); | |
dfd6c73a | 237 | *(UINT16 *)cursor = DevicePathSize(hddp); |
3ce517fd PJ |
238 | cursor += sizeof (UINT16); |
239 | StrCpy((CHAR16 *)cursor, label); | |
240 | cursor += StrLen(label)*2 + 2; | |
dfd6c73a PJ |
241 | CopyMem(cursor, hddp, DevicePathSize(hddp)); |
242 | cursor += DevicePathSize(hddp); | |
3ce517fd PJ |
243 | StrCpy((CHAR16 *)cursor, arguments); |
244 | ||
1fe31ee1 HG |
245 | console_print(L"Creating boot entry \"%s\" with label \"%s\" " |
246 | L"for file \"%s\"\n", | |
247 | varname, label, filename); | |
dfd6c73a PJ |
248 | |
249 | if (!first_new_option) { | |
250 | first_new_option = DuplicateDevicePath(fulldp); | |
251 | first_new_option_args = arguments; | |
252 | first_new_option_size = StrLen(arguments) * sizeof (CHAR16); | |
253 | } | |
254 | ||
9fdca5bb PJ |
255 | efi_status = gRT->SetVariable(varname, &GV_GUID, |
256 | EFI_VARIABLE_NON_VOLATILE | | |
257 | EFI_VARIABLE_BOOTSERVICE_ACCESS | | |
258 | EFI_VARIABLE_RUNTIME_ACCESS, | |
259 | size, data); | |
3ce517fd PJ |
260 | |
261 | FreePool(data); | |
262 | ||
802221cf | 263 | if (EFI_ERROR(efi_status)) { |
1fe31ee1 HG |
264 | console_print(L"Could not create variable: %r\n", |
265 | efi_status); | |
802221cf | 266 | return efi_status; |
3ce517fd PJ |
267 | } |
268 | ||
269 | CHAR16 *newbootorder = AllocateZeroPool(sizeof (CHAR16) | |
270 | * (nbootorder + 1)); | |
271 | if (!newbootorder) | |
272 | return EFI_OUT_OF_RESOURCES; | |
273 | ||
274 | int j = 0; | |
382a0b66 | 275 | newbootorder[0] = i & 0xffff; |
3ce517fd PJ |
276 | if (nbootorder) { |
277 | for (j = 0; j < nbootorder; j++) | |
382a0b66 | 278 | newbootorder[j+1] = bootorder[j]; |
3ce517fd PJ |
279 | FreePool(bootorder); |
280 | } | |
3ce517fd PJ |
281 | bootorder = newbootorder; |
282 | nbootorder += 1; | |
283 | #ifdef DEBUG_FALLBACK | |
1fe31ee1 HG |
284 | console_print(L"nbootorder: %d\nBootOrder: ", |
285 | nbootorder); | |
3ce517fd | 286 | for (j = 0 ; j < nbootorder ; j++) |
1fe31ee1 HG |
287 | console_print(L"%04x ", bootorder[j]); |
288 | console_print(L"\n"); | |
3ce517fd PJ |
289 | #endif |
290 | ||
291 | return EFI_SUCCESS; | |
292 | } | |
293 | } | |
294 | return EFI_OUT_OF_RESOURCES; | |
295 | } | |
296 | ||
0cc030c2 LZ |
297 | /* |
298 | * AMI BIOS (e.g, Intel NUC5i3MYHE) may automatically hide and patch BootXXXX | |
299 | * variables with ami_masked_device_path_guid. We can get the valid device path | |
300 | * if just skipping it and its next end path. | |
301 | */ | |
302 | ||
303 | static EFI_GUID ami_masked_device_path_guid = { | |
304 | 0x99e275e7, 0x75a0, 0x4b37, | |
305 | { 0xa2, 0xe6, 0xc5, 0x38, 0x5e, 0x6c, 0x0, 0xcb } | |
306 | }; | |
307 | ||
308 | static unsigned int | |
309 | calc_masked_boot_option_size(unsigned int size) | |
310 | { | |
311 | return size + sizeof(EFI_DEVICE_PATH) + | |
312 | sizeof(ami_masked_device_path_guid) + sizeof(EFI_DEVICE_PATH); | |
313 | } | |
314 | ||
315 | static int | |
316 | check_masked_boot_option(CHAR8 *candidate, unsigned int candidate_size, | |
317 | CHAR8 *data, unsigned int data_size) | |
318 | { | |
319 | /* | |
320 | * The patched BootXXXX variables contain a hardware device path and | |
321 | * an end path, preceding the real device path. | |
322 | */ | |
323 | if (calc_masked_boot_option_size(data_size) != candidate_size) | |
324 | return 1; | |
325 | ||
326 | CHAR8 *cursor = candidate; | |
327 | ||
328 | /* Check whether the BootXXXX is patched */ | |
329 | cursor += sizeof(UINT32) + sizeof(UINT16); | |
330 | cursor += StrSize((CHAR16 *)cursor); | |
331 | ||
332 | unsigned int min_valid_size = cursor - candidate + sizeof(EFI_DEVICE_PATH); | |
333 | ||
334 | if (candidate_size <= min_valid_size) | |
335 | return 1; | |
336 | ||
337 | EFI_DEVICE_PATH *dp = (EFI_DEVICE_PATH *)cursor; | |
338 | unsigned int node_size = DevicePathNodeLength(dp) - sizeof(EFI_DEVICE_PATH); | |
339 | ||
340 | min_valid_size += node_size; | |
341 | if (candidate_size <= min_valid_size || | |
342 | DevicePathType(dp) != HARDWARE_DEVICE_PATH || | |
343 | DevicePathSubType(dp) != HW_VENDOR_DP || | |
344 | node_size != sizeof(ami_masked_device_path_guid) || | |
345 | CompareGuid((EFI_GUID *)(cursor + sizeof(EFI_DEVICE_PATH)), | |
346 | &ami_masked_device_path_guid)) | |
347 | return 1; | |
348 | ||
349 | /* Check whether the patched guid is followed by an end path */ | |
350 | min_valid_size += sizeof(EFI_DEVICE_PATH); | |
351 | if (candidate_size <= min_valid_size) | |
352 | return 1; | |
353 | ||
354 | dp = NextDevicePathNode(dp); | |
355 | if (!IsDevicePathEnd(dp)) | |
356 | return 1; | |
357 | ||
358 | /* | |
359 | * OK. We may really get a masked BootXXXX variable. The next | |
360 | * step is to test whether it is hidden. | |
361 | */ | |
362 | UINT32 attrs = *(UINT32 *)candidate; | |
363 | #ifndef LOAD_OPTION_HIDDEN | |
364 | # define LOAD_OPTION_HIDDEN 0x00000008 | |
365 | #endif | |
366 | if (!(attrs & LOAD_OPTION_HIDDEN)) | |
367 | return 1; | |
368 | ||
369 | attrs &= ~LOAD_OPTION_HIDDEN; | |
370 | ||
371 | /* Compare the field Attributes */ | |
372 | if (attrs != *(UINT32 *)data) | |
373 | return 1; | |
374 | ||
375 | /* Compare the field FilePathListLength */ | |
376 | data += sizeof(UINT32); | |
377 | candidate += sizeof(UINT32); | |
378 | if (calc_masked_boot_option_size(*(UINT16 *)data) != | |
379 | *(UINT16 *)candidate) | |
380 | return 1; | |
381 | ||
382 | /* Compare the field Description */ | |
383 | data += sizeof(UINT16); | |
384 | candidate += sizeof(UINT16); | |
385 | if (CompareMem(candidate, data, cursor - candidate)) | |
386 | return 1; | |
387 | ||
388 | /* Compare the filed FilePathList */ | |
389 | cursor = (CHAR8 *)NextDevicePathNode(dp); | |
390 | data += sizeof(UINT16); | |
391 | data += StrSize((CHAR16 *)data); | |
392 | ||
393 | return CompareMem(cursor, data, candidate_size - min_valid_size); | |
394 | } | |
395 | ||
894a2738 | 396 | EFI_STATUS |
0ba09477 GCPL |
397 | find_boot_option(EFI_DEVICE_PATH *dp, EFI_DEVICE_PATH *fulldp, |
398 | CHAR16 *filename, CHAR16 *label, CHAR16 *arguments, | |
399 | UINT16 *optnum) | |
894a2738 | 400 | { |
5495694c | 401 | unsigned int size = sizeof(UINT32) + sizeof (UINT16) + |
894a2738 | 402 | StrLen(label)*2 + 2 + DevicePathSize(dp) + |
4aac8a11 | 403 | StrLen(arguments) * 2; |
894a2738 | 404 | |
6b251052 | 405 | CHAR8 *data = AllocateZeroPool(size + 2); |
894a2738 PJ |
406 | if (!data) |
407 | return EFI_OUT_OF_RESOURCES; | |
408 | CHAR8 *cursor = data; | |
409 | *(UINT32 *)cursor = LOAD_OPTION_ACTIVE; | |
410 | cursor += sizeof (UINT32); | |
411 | *(UINT16 *)cursor = DevicePathSize(dp); | |
412 | cursor += sizeof (UINT16); | |
413 | StrCpy((CHAR16 *)cursor, label); | |
414 | cursor += StrLen(label)*2 + 2; | |
415 | CopyMem(cursor, dp, DevicePathSize(dp)); | |
416 | cursor += DevicePathSize(dp); | |
417 | StrCpy((CHAR16 *)cursor, arguments); | |
418 | ||
419 | int i = 0; | |
420 | CHAR16 varname[] = L"Boot0000"; | |
421 | CHAR16 hexmap[] = L"0123456789ABCDEF"; | |
802221cf | 422 | EFI_STATUS efi_status; |
894a2738 | 423 | |
0cc030c2 LZ |
424 | UINTN max_candidate_size = calc_masked_boot_option_size(size); |
425 | CHAR8 *candidate = AllocateZeroPool(max_candidate_size); | |
894a2738 PJ |
426 | if (!candidate) { |
427 | FreePool(data); | |
428 | return EFI_OUT_OF_RESOURCES; | |
429 | } | |
430 | ||
431 | for(i = 0; i < nbootorder && i < 0x10000; i++) { | |
432 | varname[4] = hexmap[(bootorder[i] & 0xf000) >> 12]; | |
433 | varname[5] = hexmap[(bootorder[i] & 0x0f00) >> 8]; | |
434 | varname[6] = hexmap[(bootorder[i] & 0x00f0) >> 4]; | |
435 | varname[7] = hexmap[(bootorder[i] & 0x000f) >> 0]; | |
436 | ||
0cc030c2 | 437 | UINTN candidate_size = max_candidate_size; |
9fdca5bb PJ |
438 | efi_status = gRT->GetVariable(varname, &GV_GUID, NULL, |
439 | &candidate_size, candidate); | |
802221cf | 440 | if (EFI_ERROR(efi_status)) |
894a2738 PJ |
441 | continue; |
442 | ||
0cc030c2 LZ |
443 | if (candidate_size != size) { |
444 | if (check_masked_boot_option(candidate, candidate_size, | |
445 | data, size)) | |
446 | continue; | |
447 | } else if (CompareMem(candidate, data, size)) | |
894a2738 PJ |
448 | continue; |
449 | ||
0cc030c2 LZ |
450 | VerbosePrint(L"Found boot entry \"%s\" with label \"%s\" " |
451 | L"for file \"%s\"\n", varname, label, filename); | |
894a2738 PJ |
452 | |
453 | /* at this point, we have duplicate data. */ | |
0ba09477 GCPL |
454 | if (!first_new_option) { |
455 | first_new_option = DuplicateDevicePath(fulldp); | |
456 | first_new_option_args = arguments; | |
457 | first_new_option_size = StrLen(arguments) * sizeof (CHAR16); | |
458 | } | |
459 | ||
894a2738 PJ |
460 | *optnum = i; |
461 | FreePool(candidate); | |
462 | FreePool(data); | |
463 | return EFI_SUCCESS; | |
464 | } | |
465 | FreePool(candidate); | |
466 | FreePool(data); | |
467 | return EFI_NOT_FOUND; | |
468 | } | |
469 | ||
470 | EFI_STATUS | |
471 | set_boot_order(void) | |
472 | { | |
473 | CHAR16 *oldbootorder; | |
474 | UINTN size; | |
894a2738 | 475 | |
b953468e | 476 | oldbootorder = LibGetVariableAndSize(L"BootOrder", &GV_GUID, &size); |
894a2738 PJ |
477 | if (oldbootorder) { |
478 | nbootorder = size / sizeof (CHAR16); | |
479 | bootorder = oldbootorder; | |
480 | } | |
481 | return EFI_SUCCESS; | |
482 | ||
483 | } | |
484 | ||
3ce517fd PJ |
485 | EFI_STATUS |
486 | update_boot_order(void) | |
487 | { | |
3ce517fd | 488 | UINTN size; |
382a0b66 | 489 | UINTN len = 0; |
3ce517fd | 490 | CHAR16 *newbootorder = NULL; |
802221cf | 491 | EFI_STATUS efi_status; |
3ce517fd | 492 | |
382a0b66 GCPL |
493 | size = nbootorder * sizeof(CHAR16); |
494 | newbootorder = AllocateZeroPool(size); | |
495 | if (!newbootorder) | |
496 | return EFI_OUT_OF_RESOURCES; | |
497 | CopyMem(newbootorder, bootorder, size); | |
3ce517fd | 498 | |
c0f7d130 | 499 | VerbosePrint(L"nbootorder: %d\nBootOrder: ", size / sizeof (CHAR16)); |
90c65f72 | 500 | UINTN j; |
3ce517fd | 501 | for (j = 0 ; j < size / sizeof (CHAR16); j++) |
c0f7d130 | 502 | VerbosePrintUnprefixed(L"%04x ", newbootorder[j]); |
1fe31ee1 | 503 | console_print(L"\n"); |
9fdca5bb | 504 | efi_status = gRT->GetVariable(L"BootOrder", &GV_GUID, NULL, &len, NULL); |
802221cf | 505 | if (efi_status == EFI_BUFFER_TOO_SMALL) |
b953468e | 506 | LibDeleteVariable(L"BootOrder", &GV_GUID); |
3ce517fd | 507 | |
9fdca5bb PJ |
508 | efi_status = gRT->SetVariable(L"BootOrder", &GV_GUID, |
509 | EFI_VARIABLE_NON_VOLATILE | | |
510 | EFI_VARIABLE_BOOTSERVICE_ACCESS | | |
511 | EFI_VARIABLE_RUNTIME_ACCESS, | |
512 | size, newbootorder); | |
3ce517fd | 513 | FreePool(newbootorder); |
802221cf | 514 | return efi_status; |
3ce517fd PJ |
515 | } |
516 | ||
517 | EFI_STATUS | |
a8f3dc82 | 518 | add_to_boot_list(CHAR16 *dirname, CHAR16 *filename, CHAR16 *label, CHAR16 *arguments) |
3ce517fd PJ |
519 | { |
520 | CHAR16 *fullpath = NULL; | |
521 | UINT64 pathlen = 0; | |
802221cf | 522 | EFI_STATUS efi_status; |
3ce517fd | 523 | |
802221cf PJ |
524 | efi_status = make_full_path(dirname, filename, &fullpath, &pathlen); |
525 | if (EFI_ERROR(efi_status)) | |
526 | return efi_status; | |
87c8f07e | 527 | |
dfd6c73a PJ |
528 | EFI_DEVICE_PATH *full_device_path = NULL; |
529 | EFI_DEVICE_PATH *dp = NULL; | |
c0f7d130 | 530 | CHAR16 *dps; |
87c8f07e | 531 | |
a8f3dc82 | 532 | full_device_path = FileDevicePath(this_image->DeviceHandle, fullpath); |
dfd6c73a | 533 | if (!full_device_path) { |
802221cf | 534 | efi_status = EFI_OUT_OF_RESOURCES; |
3ce517fd PJ |
535 | goto err; |
536 | } | |
c0f7d130 PJ |
537 | dps = DevicePathToStr(full_device_path); |
538 | VerbosePrint(L"file DP: %s\n", dps); | |
539 | FreePool(dps); | |
3ce517fd | 540 | |
802221cf PJ |
541 | efi_status = FindSubDevicePath(full_device_path, |
542 | MEDIA_DEVICE_PATH, MEDIA_HARDDRIVE_DP, | |
543 | &dp); | |
544 | if (EFI_ERROR(efi_status)) { | |
545 | if (efi_status == EFI_NOT_FOUND) { | |
dfd6c73a PJ |
546 | dp = full_device_path; |
547 | } else { | |
802221cf | 548 | efi_status = EFI_OUT_OF_RESOURCES; |
dfd6c73a PJ |
549 | goto err; |
550 | } | |
551 | } | |
552 | ||
dfd6c73a | 553 | { |
c0f7d130 PJ |
554 | UINTN s = DevicePathSize(dp); |
555 | UINTN i; | |
556 | UINT8 *dpv = (void *)dp; | |
557 | for (i = 0; i < s; i++) { | |
558 | if (i % 16 == 0) { | |
559 | if (i > 0) | |
560 | VerbosePrintUnprefixed(L"\n"); | |
561 | VerbosePrint(L""); | |
562 | } | |
563 | VerbosePrintUnprefixed(L"%02x ", dpv[i]); | |
564 | } | |
565 | VerbosePrintUnprefixed(L"\n"); | |
3ce517fd | 566 | |
c0f7d130 PJ |
567 | CHAR16 *dps = DevicePathToStr(dp); |
568 | VerbosePrint(L"device path: \"%s\"\n", dps); | |
569 | FreePool(dps); | |
3fa9a534 | 570 | } |
3ce517fd | 571 | |
894a2738 | 572 | UINT16 option; |
802221cf PJ |
573 | efi_status = find_boot_option(dp, full_device_path, fullpath, label, |
574 | arguments, &option); | |
575 | if (EFI_ERROR(efi_status)) { | |
576 | add_boot_option(dp, full_device_path, fullpath, label, | |
577 | arguments); | |
894a2738 PJ |
578 | } else if (option != 0) { |
579 | CHAR16 *newbootorder; | |
580 | newbootorder = AllocateZeroPool(sizeof (CHAR16) * nbootorder); | |
581 | if (!newbootorder) | |
582 | return EFI_OUT_OF_RESOURCES; | |
583 | ||
584 | newbootorder[0] = bootorder[option]; | |
585 | CopyMem(newbootorder + 1, bootorder, sizeof (CHAR16) * option); | |
586 | CopyMem(newbootorder + option + 1, bootorder + option + 1, | |
587 | sizeof (CHAR16) * (nbootorder - option - 1)); | |
588 | FreePool(bootorder); | |
589 | bootorder = newbootorder; | |
590 | } | |
3ce517fd PJ |
591 | |
592 | err: | |
dfd6c73a PJ |
593 | if (full_device_path) |
594 | FreePool(full_device_path); | |
e6f3a6ec | 595 | if (dp && dp != full_device_path) |
3ce517fd PJ |
596 | FreePool(dp); |
597 | if (fullpath) | |
598 | FreePool(fullpath); | |
802221cf | 599 | return efi_status; |
3ce517fd PJ |
600 | } |
601 | ||
602 | EFI_STATUS | |
a8f3dc82 | 603 | populate_stanza(CHAR16 *dirname, CHAR16 *filename, CHAR16 *csv) |
3ce517fd | 604 | { |
3ce517fd | 605 | CHAR16 *file = csv; |
c0f7d130 | 606 | VerbosePrint(L"CSV data: \"%s\"\n", csv); |
3ce517fd PJ |
607 | |
608 | UINTN comma0 = StrCSpn(csv, L","); | |
609 | if (comma0 == 0) | |
610 | return EFI_INVALID_PARAMETER; | |
611 | file[comma0] = L'\0'; | |
c0f7d130 | 612 | VerbosePrint(L"filename: \"%s\"\n", file); |
3ce517fd PJ |
613 | |
614 | CHAR16 *label = csv + comma0 + 1; | |
615 | UINTN comma1 = StrCSpn(label, L","); | |
616 | if (comma1 == 0) | |
617 | return EFI_INVALID_PARAMETER; | |
618 | label[comma1] = L'\0'; | |
c0f7d130 | 619 | VerbosePrint(L"label: \"%s\"\n", label); |
3ce517fd PJ |
620 | |
621 | CHAR16 *arguments = csv + comma0 +1 + comma1 +1; | |
622 | UINTN comma2 = StrCSpn(arguments, L","); | |
623 | arguments[comma2] = L'\0'; | |
624 | /* This one is optional, so don't check if comma2 is 0 */ | |
c0f7d130 | 625 | VerbosePrint(L"arguments: \"%s\"\n", arguments); |
3ce517fd | 626 | |
a8f3dc82 | 627 | add_to_boot_list(dirname, file, label, arguments); |
3ce517fd PJ |
628 | |
629 | return EFI_SUCCESS; | |
630 | } | |
631 | ||
632 | EFI_STATUS | |
633 | try_boot_csv(EFI_FILE_HANDLE fh, CHAR16 *dirname, CHAR16 *filename) | |
634 | { | |
635 | CHAR16 *fullpath = NULL; | |
636 | UINT64 pathlen = 0; | |
802221cf | 637 | EFI_STATUS efi_status; |
3ce517fd | 638 | |
802221cf PJ |
639 | efi_status = make_full_path(dirname, filename, &fullpath, &pathlen); |
640 | if (EFI_ERROR(efi_status)) | |
641 | return efi_status; | |
3ce517fd | 642 | |
c0f7d130 | 643 | VerbosePrint(L"Found file \"%s\"\n", fullpath); |
3ce517fd PJ |
644 | |
645 | CHAR16 *buffer; | |
646 | UINT64 bs; | |
802221cf PJ |
647 | efi_status = read_file(fh, fullpath, &buffer, &bs); |
648 | if (EFI_ERROR(efi_status)) { | |
1fe31ee1 HG |
649 | console_print(L"Could not read file \"%s\": %r\n", |
650 | fullpath, efi_status); | |
3ce517fd | 651 | FreePool(fullpath); |
802221cf | 652 | return efi_status; |
3ce517fd PJ |
653 | } |
654 | FreePool(fullpath); | |
655 | ||
c0f7d130 | 656 | VerbosePrint(L"File looks like:\n%s\n", buffer); |
3ce517fd PJ |
657 | |
658 | CHAR16 *start = buffer; | |
f0e4df7d PJ |
659 | /* The file may or may not start with the Unicode byte order marker. |
660 | * Sadness ensues. Since UEFI is defined as LE, I'm going to decree | |
661 | * that these files must also be LE. | |
662 | * | |
663 | * IT IS THUS SO. | |
664 | * | |
665 | * But if we find the LE byte order marker, just skip it. | |
3ce517fd PJ |
666 | */ |
667 | if (*start == 0xfeff) | |
668 | start++; | |
669 | while (*start) { | |
82a9c9fd | 670 | while (*start == L'\r' || *start == L'\n') |
3ce517fd | 671 | start++; |
3ce517fd PJ |
672 | UINTN l = StrCSpn(start, L"\r\n"); |
673 | if (l == 0) { | |
674 | if (start[l] == L'\0') | |
675 | break; | |
676 | start++; | |
677 | continue; | |
678 | } | |
679 | CHAR16 c = start[l]; | |
680 | start[l] = L'\0'; | |
681 | ||
a8f3dc82 | 682 | populate_stanza(dirname, filename, start); |
3ce517fd PJ |
683 | |
684 | start[l] = c; | |
685 | start += l; | |
686 | } | |
687 | ||
688 | FreePool(buffer); | |
689 | return EFI_SUCCESS; | |
690 | } | |
691 | ||
692 | EFI_STATUS | |
693 | find_boot_csv(EFI_FILE_HANDLE fh, CHAR16 *dirname) | |
694 | { | |
802221cf | 695 | EFI_STATUS efi_status; |
3ce517fd PJ |
696 | void *buffer = NULL; |
697 | UINTN bs = 0; | |
3ce517fd PJ |
698 | |
699 | /* The API here is "Call it once with bs=0, it fills in bs, | |
700 | * then allocate a buffer and ask again to get it filled. */ | |
9fdca5bb | 701 | efi_status = fh->GetInfo(fh, &EFI_FILE_INFO_GUID, &bs, NULL); |
802221cf | 702 | if (EFI_ERROR(efi_status) && efi_status != EFI_BUFFER_TOO_SMALL) { |
1fe31ee1 HG |
703 | console_print(L"Could not get directory info for \\EFI\\%s\\: %r\n", |
704 | dirname, efi_status); | |
802221cf | 705 | return efi_status; |
05458d22 PJ |
706 | } |
707 | if (bs == 0) | |
708 | return EFI_SUCCESS; | |
709 | ||
710 | buffer = AllocateZeroPool(bs); | |
711 | if (!buffer) { | |
1fe31ee1 | 712 | console_print(L"Could not allocate memory\n"); |
05458d22 | 713 | return EFI_OUT_OF_RESOURCES; |
3ce517fd | 714 | } |
05458d22 | 715 | |
9fdca5bb | 716 | efi_status = fh->GetInfo(fh, &EFI_FILE_INFO_GUID, &bs, buffer); |
3ce517fd | 717 | /* This checks *either* the error from the first GetInfo, if it isn't |
ea1d6905 PJ |
718 | * the EFI_BUFFER_TOO_SMALL we're expecting, or the second GetInfo |
719 | * call in *any* case. */ | |
802221cf | 720 | if (EFI_ERROR(efi_status)) { |
1fe31ee1 HG |
721 | console_print(L"Could not get info for \"%s\": %r\n", dirname, |
722 | efi_status); | |
3ce517fd PJ |
723 | if (buffer) |
724 | FreePool(buffer); | |
802221cf | 725 | return efi_status; |
3ce517fd PJ |
726 | } |
727 | ||
728 | EFI_FILE_INFO *fi = buffer; | |
729 | if (!(fi->Attribute & EFI_FILE_DIRECTORY)) { | |
730 | FreePool(buffer); | |
731 | return EFI_SUCCESS; | |
732 | } | |
733 | FreePool(buffer); | |
3a7feeff | 734 | buffer = NULL; |
3ce517fd | 735 | |
47f3a65e PJ |
736 | CHAR16 *bootcsv=NULL, *bootarchcsv=NULL; |
737 | ||
3ce517fd PJ |
738 | bs = 0; |
739 | do { | |
740 | bs = 0; | |
9fdca5bb | 741 | efi_status = fh->Read(fh, &bs, NULL); |
802221cf PJ |
742 | if (EFI_ERROR(efi_status) && |
743 | efi_status != EFI_BUFFER_TOO_SMALL) { | |
1fe31ee1 HG |
744 | console_print(L"Could not read \\EFI\\%s\\: %r\n", |
745 | dirname, efi_status); | |
802221cf | 746 | return efi_status; |
3a7feeff | 747 | } |
d89b722e PJ |
748 | /* If there's no data to read, don't try to allocate 0 bytes |
749 | * and read the data... */ | |
750 | if (bs == 0) | |
751 | break; | |
3ce517fd | 752 | |
3a7feeff PJ |
753 | buffer = AllocateZeroPool(bs); |
754 | if (!buffer) { | |
1fe31ee1 | 755 | console_print(L"Could not allocate memory\n"); |
3a7feeff | 756 | return EFI_OUT_OF_RESOURCES; |
3ce517fd | 757 | } |
3a7feeff | 758 | |
9fdca5bb | 759 | efi_status = fh->Read(fh, &bs, buffer); |
802221cf | 760 | if (EFI_ERROR(efi_status)) { |
1fe31ee1 HG |
761 | console_print(L"Could not read \\EFI\\%s\\: %r\n", |
762 | dirname, efi_status); | |
3ce517fd | 763 | FreePool(buffer); |
802221cf | 764 | return efi_status; |
3ce517fd | 765 | } |
3a7feeff | 766 | |
3ce517fd PJ |
767 | if (bs == 0) |
768 | break; | |
769 | ||
770 | fi = buffer; | |
771 | ||
47f3a65e PJ |
772 | if (!bootcsv && !StrCaseCmp(fi->FileName, L"boot.csv")) |
773 | bootcsv = StrDuplicate(fi->FileName); | |
774 | ||
775 | if (!bootarchcsv && | |
776 | !StrCaseCmp(fi->FileName, L"boot" EFI_ARCH L".csv")) | |
777 | bootarchcsv = StrDuplicate(fi->FileName); | |
3ce517fd PJ |
778 | |
779 | FreePool(buffer); | |
780 | buffer = NULL; | |
781 | } while (bs != 0); | |
782 | ||
802221cf | 783 | efi_status = EFI_SUCCESS; |
47f3a65e PJ |
784 | if (bootarchcsv) { |
785 | EFI_FILE_HANDLE fh2; | |
9fdca5bb PJ |
786 | efi_status = fh->Open(fh, &fh2, bootarchcsv, |
787 | EFI_FILE_READ_ONLY, 0); | |
802221cf | 788 | if (EFI_ERROR(efi_status) || fh2 == NULL) { |
1fe31ee1 HG |
789 | console_print(L"Couldn't open \\EFI\\%s\\%s: %r\n", |
790 | dirname, bootarchcsv, efi_status); | |
47f3a65e | 791 | } else { |
802221cf | 792 | efi_status = try_boot_csv(fh2, dirname, bootarchcsv); |
9fdca5bb | 793 | fh2->Close(fh2); |
802221cf | 794 | if (EFI_ERROR(efi_status)) |
1fe31ee1 HG |
795 | console_print(L"Could not process \\EFI\\%s\\%s: %r\n", |
796 | dirname, bootarchcsv, efi_status); | |
47f3a65e PJ |
797 | } |
798 | } | |
802221cf | 799 | if ((EFI_ERROR(efi_status) || !bootarchcsv) && bootcsv) { |
47f3a65e | 800 | EFI_FILE_HANDLE fh2; |
9fdca5bb PJ |
801 | efi_status = fh->Open(fh, &fh2, bootcsv, |
802 | EFI_FILE_READ_ONLY, 0); | |
802221cf | 803 | if (EFI_ERROR(efi_status) || fh2 == NULL) { |
1fe31ee1 HG |
804 | console_print(L"Couldn't open \\EFI\\%s\\%s: %r\n", |
805 | dirname, bootcsv, efi_status); | |
47f3a65e | 806 | } else { |
802221cf | 807 | efi_status = try_boot_csv(fh2, dirname, bootcsv); |
9fdca5bb | 808 | fh2->Close(fh2); |
802221cf | 809 | if (EFI_ERROR(efi_status)) |
1fe31ee1 HG |
810 | console_print(L"Could not process \\EFI\\%s\\%s: %r\n", |
811 | dirname, bootarchcsv, efi_status); | |
47f3a65e PJ |
812 | } |
813 | } | |
802221cf | 814 | return EFI_SUCCESS; |
3ce517fd PJ |
815 | } |
816 | ||
817 | EFI_STATUS | |
818 | find_boot_options(EFI_HANDLE device) | |
819 | { | |
802221cf | 820 | EFI_STATUS efi_status; |
3ce517fd | 821 | EFI_FILE_IO_INTERFACE *fio = NULL; |
802221cf | 822 | |
9fdca5bb PJ |
823 | efi_status = gBS->HandleProtocol(device, &FileSystemProtocol, |
824 | (void **) &fio); | |
802221cf | 825 | if (EFI_ERROR(efi_status)) { |
1fe31ee1 | 826 | console_print(L"Couldn't find file system: %r\n", efi_status); |
802221cf | 827 | return efi_status; |
3ce517fd PJ |
828 | } |
829 | ||
830 | /* EFI_FILE_HANDLE is a pointer to an EFI_FILE, and I have | |
831 | * *no idea* what frees the memory allocated here. Hopefully | |
832 | * Close() does. */ | |
833 | EFI_FILE_HANDLE fh = NULL; | |
9fdca5bb | 834 | efi_status = fio->OpenVolume(fio, &fh); |
802221cf | 835 | if (EFI_ERROR(efi_status) || fh == NULL) { |
1fe31ee1 | 836 | console_print(L"Couldn't open file system: %r\n", efi_status); |
802221cf | 837 | return efi_status; |
3ce517fd PJ |
838 | } |
839 | ||
840 | EFI_FILE_HANDLE fh2 = NULL; | |
9fdca5bb | 841 | efi_status = fh->Open(fh, &fh2, L"EFI", EFI_FILE_READ_ONLY, 0); |
802221cf | 842 | if (EFI_ERROR(efi_status) || fh2 == NULL) { |
1fe31ee1 | 843 | console_print(L"Couldn't open EFI: %r\n", efi_status); |
9fdca5bb | 844 | fh->Close(fh); |
802221cf | 845 | return efi_status; |
3ce517fd | 846 | } |
9fdca5bb | 847 | efi_status = fh2->SetPosition(fh2, 0); |
802221cf | 848 | if (EFI_ERROR(efi_status)) { |
1fe31ee1 | 849 | console_print(L"Couldn't set file position: %r\n", efi_status); |
9fdca5bb PJ |
850 | fh2->Close(fh2); |
851 | fh->Close(fh); | |
802221cf | 852 | return efi_status; |
3ce517fd PJ |
853 | } |
854 | ||
855 | void *buffer; | |
856 | UINTN bs; | |
857 | do { | |
858 | bs = 0; | |
9fdca5bb | 859 | efi_status = fh2->Read(fh2, &bs, NULL); |
802221cf | 860 | if (EFI_ERROR(efi_status) && efi_status != EFI_BUFFER_TOO_SMALL) { |
1fe31ee1 | 861 | console_print(L"Could not read \\EFI\\: %r\n", efi_status); |
802221cf | 862 | return efi_status; |
3ce517fd PJ |
863 | } |
864 | if (bs == 0) | |
865 | break; | |
866 | ||
89126c1a PJ |
867 | buffer = AllocateZeroPool(bs); |
868 | if (!buffer) { | |
1fe31ee1 | 869 | console_print(L"Could not allocate memory\n"); |
89126c1a | 870 | /* sure, this might work, why not? */ |
9fdca5bb PJ |
871 | fh2->Close(fh2); |
872 | fh->Close(fh); | |
89126c1a PJ |
873 | return EFI_OUT_OF_RESOURCES; |
874 | } | |
875 | ||
9fdca5bb | 876 | efi_status = fh2->Read(fh2, &bs, buffer); |
802221cf | 877 | if (EFI_ERROR(efi_status)) { |
3ce517fd PJ |
878 | if (buffer) { |
879 | FreePool(buffer); | |
880 | buffer = NULL; | |
881 | } | |
9fdca5bb PJ |
882 | fh2->Close(fh2); |
883 | fh->Close(fh); | |
802221cf | 884 | return efi_status; |
3ce517fd PJ |
885 | } |
886 | EFI_FILE_INFO *fi = buffer; | |
887 | ||
888 | if (!(fi->Attribute & EFI_FILE_DIRECTORY)) { | |
889 | FreePool(buffer); | |
890 | buffer = NULL; | |
891 | continue; | |
892 | } | |
893 | if (!StrCmp(fi->FileName, L".") || | |
894 | !StrCmp(fi->FileName, L"..") || | |
895 | !StrCaseCmp(fi->FileName, L"BOOT")) { | |
896 | FreePool(buffer); | |
897 | buffer = NULL; | |
898 | continue; | |
899 | } | |
c0f7d130 | 900 | VerbosePrint(L"Found directory named \"%s\"\n", fi->FileName); |
3ce517fd PJ |
901 | |
902 | EFI_FILE_HANDLE fh3; | |
9fdca5bb PJ |
903 | efi_status = fh2->Open(fh2, &fh3, fi->FileName, |
904 | EFI_FILE_READ_ONLY, 0); | |
802221cf | 905 | if (EFI_ERROR(efi_status)) { |
1fe31ee1 HG |
906 | console_print(L"%d Couldn't open %s: %r\n", __LINE__, |
907 | fi->FileName, efi_status); | |
3ce517fd PJ |
908 | FreePool(buffer); |
909 | buffer = NULL; | |
910 | continue; | |
911 | } | |
912 | ||
802221cf | 913 | efi_status = find_boot_csv(fh3, fi->FileName); |
9fdca5bb | 914 | fh3->Close(fh3); |
3ce517fd PJ |
915 | FreePool(buffer); |
916 | buffer = NULL; | |
802221cf | 917 | if (efi_status == EFI_OUT_OF_RESOURCES) |
3ce517fd PJ |
918 | break; |
919 | ||
920 | } while (1); | |
921 | ||
802221cf PJ |
922 | if (!EFI_ERROR(efi_status) && nbootorder > 0) |
923 | efi_status = update_boot_order(); | |
8adfd201 | 924 | |
9fdca5bb PJ |
925 | fh2->Close(fh2); |
926 | fh->Close(fh); | |
802221cf | 927 | return efi_status; |
3ce517fd | 928 | } |
3fa9a534 PJ |
929 | |
930 | static EFI_STATUS | |
931 | try_start_first_option(EFI_HANDLE parent_image_handle) | |
932 | { | |
802221cf | 933 | EFI_STATUS efi_status; |
3fa9a534 PJ |
934 | EFI_HANDLE image_handle; |
935 | ||
936 | if (!first_new_option) { | |
937 | return EFI_SUCCESS; | |
938 | } | |
939 | ||
9fdca5bb PJ |
940 | efi_status = gBS->LoadImage(0, parent_image_handle, first_new_option, |
941 | NULL, 0, &image_handle); | |
802221cf | 942 | if (EFI_ERROR(efi_status)) { |
dfd6c73a PJ |
943 | CHAR16 *dps = DevicePathToStr(first_new_option); |
944 | UINTN s = DevicePathSize(first_new_option); | |
5495694c | 945 | unsigned int i; |
dfd6c73a | 946 | UINT8 *dpv = (void *)first_new_option; |
1fe31ee1 HG |
947 | console_print(L"LoadImage failed: %r\nDevice path: \"%s\"\n", |
948 | efi_status, dps); | |
dfd6c73a PJ |
949 | for (i = 0; i < s; i++) { |
950 | if (i > 0 && i % 16 == 0) | |
1fe31ee1 HG |
951 | console_print(L"\n"); |
952 | console_print(L"%02x ", dpv[i]); | |
dfd6c73a | 953 | } |
1fe31ee1 | 954 | console_print(L"\n"); |
dfd6c73a | 955 | |
9fdca5bb | 956 | msleep(500000000); |
802221cf | 957 | return efi_status; |
3fa9a534 | 958 | } |
a41306e8 PJ |
959 | |
960 | EFI_LOADED_IMAGE *image; | |
9fdca5bb PJ |
961 | efi_status = gBS->HandleProtocol(image_handle, &LoadedImageProtocol, |
962 | (void *) &image); | |
802221cf | 963 | if (!EFI_ERROR(efi_status)) { |
a41306e8 PJ |
964 | image->LoadOptions = first_new_option_args; |
965 | image->LoadOptionsSize = first_new_option_size; | |
966 | } | |
967 | ||
9fdca5bb | 968 | efi_status = gBS->StartImage(image_handle, NULL, NULL); |
802221cf | 969 | if (EFI_ERROR(efi_status)) { |
1fe31ee1 | 970 | console_print(L"StartImage failed: %r\n", efi_status); |
9fdca5bb | 971 | msleep(500000000); |
3fa9a534 | 972 | } |
802221cf | 973 | return efi_status; |
3fa9a534 PJ |
974 | } |
975 | ||
a5db51a5 GL |
976 | static UINT32 |
977 | get_fallback_no_reboot(void) | |
978 | { | |
979 | EFI_STATUS efi_status; | |
980 | UINT32 no_reboot; | |
981 | UINTN size = sizeof(UINT32); | |
982 | ||
983 | efi_status = gRT->GetVariable(NO_REBOOT, &SHIM_LOCK_GUID, | |
984 | NULL, &size, &no_reboot); | |
985 | if (!EFI_ERROR(efi_status)) { | |
986 | return no_reboot; | |
987 | } | |
988 | return 0; | |
989 | } | |
990 | ||
991 | static EFI_STATUS | |
992 | set_fallback_no_reboot(void) | |
993 | { | |
994 | EFI_STATUS efi_status; | |
995 | UINT32 no_reboot = 1; | |
996 | efi_status = gRT->SetVariable(NO_REBOOT, &SHIM_LOCK_GUID, | |
997 | EFI_VARIABLE_NON_VOLATILE | |
998 | | EFI_VARIABLE_BOOTSERVICE_ACCESS | |
999 | | EFI_VARIABLE_RUNTIME_ACCESS, | |
1000 | sizeof(UINT32), &no_reboot); | |
1001 | return efi_status; | |
1002 | } | |
1003 | ||
1004 | static int | |
1005 | draw_countdown(void) | |
1006 | { | |
1007 | CHAR16 *title = L"Boot Option Restoration"; | |
1008 | CHAR16 *message = L"Press any key to stop system reset"; | |
1009 | int timeout; | |
1010 | ||
1011 | timeout = console_countdown(title, message, 5); | |
1012 | ||
1013 | return timeout; | |
1014 | } | |
1015 | ||
1016 | static int | |
1017 | get_user_choice(void) | |
1018 | { | |
1019 | int choice; | |
1020 | CHAR16 *title[] = {L"Boot Option Restored", NULL}; | |
1021 | CHAR16 *menu_strings[] = { | |
1022 | L"Reset system", | |
1023 | L"Continue boot", | |
1024 | L"Always continue boot", | |
1025 | NULL | |
1026 | }; | |
1027 | ||
1028 | do { | |
1029 | choice = console_select(title, menu_strings, 0); | |
1030 | } while (choice < 0 || choice > 2); | |
1031 | ||
1032 | return choice; | |
1033 | } | |
1034 | ||
a0319607 PJ |
1035 | extern EFI_STATUS |
1036 | efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *systab); | |
1037 | ||
1038 | static void | |
1039 | __attribute__((__optimize__("0"))) | |
1040 | debug_hook(void) | |
1041 | { | |
a0319607 PJ |
1042 | UINT8 *data = NULL; |
1043 | UINTN dataSize = 0; | |
1044 | EFI_STATUS efi_status; | |
d3b7dc54 | 1045 | register volatile int x = 0; |
a0319607 PJ |
1046 | extern char _etext, _edata; |
1047 | ||
b953468e PJ |
1048 | efi_status = get_variable(L"SHIM_DEBUG", &data, &dataSize, |
1049 | SHIM_LOCK_GUID); | |
a0319607 PJ |
1050 | if (EFI_ERROR(efi_status)) { |
1051 | return; | |
1052 | } | |
1053 | ||
77ebb3d6 PJ |
1054 | if (data) |
1055 | FreePool(data); | |
a0319607 PJ |
1056 | if (x) |
1057 | return; | |
1058 | ||
1059 | x = 1; | |
1fe31ee1 HG |
1060 | console_print(L"add-symbol-file "DEBUGDIR |
1061 | L"fb" EFI_ARCH L".efi.debug %p -s .data %p\n", | |
1062 | &_etext, &_edata); | |
a0319607 PJ |
1063 | } |
1064 | ||
3ce517fd PJ |
1065 | EFI_STATUS |
1066 | efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *systab) | |
1067 | { | |
802221cf | 1068 | EFI_STATUS efi_status; |
3ce517fd PJ |
1069 | |
1070 | InitializeLib(image, systab); | |
1071 | ||
a0319607 PJ |
1072 | /* |
1073 | * if SHIM_DEBUG is set, wait for a debugger to attach. | |
1074 | */ | |
1075 | debug_hook(); | |
1076 | ||
9fdca5bb PJ |
1077 | efi_status = gBS->HandleProtocol(image, &LoadedImageProtocol, |
1078 | (void *) &this_image); | |
802221cf | 1079 | if (EFI_ERROR(efi_status)) { |
1fe31ee1 HG |
1080 | console_print(L"Error: could not find loaded image: %r\n", |
1081 | efi_status); | |
802221cf | 1082 | return efi_status; |
3ce517fd PJ |
1083 | } |
1084 | ||
1fe31ee1 | 1085 | console_print(L"System BootOrder not found. Initializing defaults.\n"); |
3ce517fd | 1086 | |
894a2738 PJ |
1087 | set_boot_order(); |
1088 | ||
802221cf PJ |
1089 | efi_status = find_boot_options(this_image->DeviceHandle); |
1090 | if (EFI_ERROR(efi_status)) { | |
1fe31ee1 HG |
1091 | console_print(L"Error: could not find boot options: %r\n", |
1092 | efi_status); | |
802221cf | 1093 | return efi_status; |
3ce517fd PJ |
1094 | } |
1095 | ||
802221cf PJ |
1096 | efi_status = fallback_should_prefer_reset(); |
1097 | if (EFI_ERROR(efi_status)) { | |
431b8a2e PJ |
1098 | VerbosePrint(L"tpm not present, starting the first image\n"); |
1099 | try_start_first_option(image); | |
1100 | } else { | |
a5db51a5 GL |
1101 | if (get_fallback_no_reboot() == 1) { |
1102 | VerbosePrint(L"NO_REBOOT is set, starting the first image\n"); | |
1103 | try_start_first_option(image); | |
1104 | } | |
1105 | ||
1106 | int timeout = draw_countdown(); | |
1107 | if (timeout == 0) | |
1108 | goto reset; | |
1109 | ||
1110 | int choice = get_user_choice(); | |
1111 | if (choice == 0) { | |
1112 | goto reset; | |
1113 | } else if (choice == 2) { | |
1114 | efi_status = set_fallback_no_reboot(); | |
1115 | if (EFI_ERROR(efi_status)) | |
1116 | goto reset; | |
1117 | } | |
1118 | VerbosePrint(L"tpm present, starting the first image\n"); | |
1119 | try_start_first_option(image); | |
1120 | reset: | |
431b8a2e PJ |
1121 | VerbosePrint(L"tpm present, resetting system\n"); |
1122 | } | |
3fa9a534 | 1123 | |
1fe31ee1 | 1124 | console_print(L"Reset System\n"); |
c0f7d130 PJ |
1125 | |
1126 | if (get_fallback_verbose()) { | |
1fe31ee1 | 1127 | console_print(L"Verbose enabled, sleeping for half a second\n"); |
9fdca5bb | 1128 | msleep(500000); |
c0f7d130 PJ |
1129 | } |
1130 | ||
9fdca5bb | 1131 | gRT->ResetSystem(EfiResetCold, EFI_SUCCESS, 0, NULL); |
f7fbcdce | 1132 | |
3ce517fd PJ |
1133 | return EFI_SUCCESS; |
1134 | } |