]> git.proxmox.com Git - efi-boot-shim.git/blame - fallback.c
fix nit
[efi-boot-shim.git] / fallback.c
CommitLineData
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
14EFI_LOADED_IMAGE *this_image = NULL;
15
c0f7d130
PJ
16int
17get_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
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
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 97static EFI_STATUS
d74ab697 98get_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
133EFI_STATUS
134read_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
180EFI_STATUS
181make_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
205CHAR16 *bootorder = NULL;
206int nbootorder = 0;
207
3fa9a534
PJ
208EFI_DEVICE_PATH *first_new_option = NULL;
209VOID *first_new_option_args = NULL;
210UINTN first_new_option_size = 0;
211
3ce517fd 212EFI_STATUS
dfd6c73a
PJ
213add_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
303static EFI_GUID ami_masked_device_path_guid = {
304 0x99e275e7, 0x75a0, 0x4b37,
305 { 0xa2, 0xe6, 0xc5, 0x38, 0x5e, 0x6c, 0x0, 0xcb }
306};
307
308static unsigned int
309calc_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
315static int
316check_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 396EFI_STATUS
0ba09477
GCPL
397find_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
470EFI_STATUS
471set_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
485EFI_STATUS
486update_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
517EFI_STATUS
a8f3dc82 518add_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
592err:
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
602EFI_STATUS
a8f3dc82 603populate_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
632EFI_STATUS
633try_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
692EFI_STATUS
693find_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
817EFI_STATUS
818find_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
930static EFI_STATUS
931try_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
976static UINT32
977get_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
991static EFI_STATUS
992set_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
1004static int
1005draw_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
1016static int
1017get_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
1035extern EFI_STATUS
1036efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *systab);
1037
1038static void
1039__attribute__((__optimize__("0")))
1040debug_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
1065EFI_STATUS
1066efi_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);
1120reset:
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}