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