]> code.delx.au - refind/blob - refind/main.c
Version 0.10.0 release.
[refind] / refind / main.c
1 /*
2 * refind/main.c
3 * Main code for the boot menu
4 *
5 * Copyright (c) 2006-2010 Christoph Pfisterer
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions are
10 * met:
11 *
12 * * Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 *
15 * * Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the
18 * distribution.
19 *
20 * * Neither the name of Christoph Pfisterer nor the names of the
21 * contributors may be used to endorse or promote products derived
22 * from this software without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
26 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
27 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
28 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
29 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
30 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
31 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
32 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
33 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
34 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35 */
36 /*
37 * Modifications copyright (c) 2012-2015 Roderick W. Smith
38 *
39 * Modifications distributed under the terms of the GNU General Public
40 * License (GPL) version 3 (GPLv3), or (at your option) any later version.
41 *
42 */
43 /*
44 * This program is free software: you can redistribute it and/or modify
45 * it under the terms of the GNU General Public License as published by
46 * the Free Software Foundation, either version 3 of the License, or
47 * (at your option) any later version.
48 *
49 * This program is distributed in the hope that it will be useful,
50 * but WITHOUT ANY WARRANTY; without even the implied warranty of
51 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
52 * GNU General Public License for more details.
53 *
54 * You should have received a copy of the GNU General Public License
55 * along with this program. If not, see <http://www.gnu.org/licenses/>.
56 */
57
58 #include "global.h"
59 #include "config.h"
60 #include "screen.h"
61 #include "legacy.h"
62 #include "lib.h"
63 #include "icns.h"
64 #include "menu.h"
65 #include "mok.h"
66 #include "gpt.h"
67 #include "apple.h"
68 #include "security_policy.h"
69 #include "driver_support.h"
70 #include "../include/Handle.h"
71 #include "../include/refit_call_wrapper.h"
72 #include "../EfiLib/BdsHelper.h"
73 #include "../EfiLib/legacy.h"
74
75 #ifdef __MAKEWITH_GNUEFI
76 #ifndef EFI_SECURITY_VIOLATION
77 #define EFI_SECURITY_VIOLATION EFIERR (26)
78 #endif
79 #endif
80
81 #ifndef EFI_OS_INDICATIONS_BOOT_TO_FW_UI
82 #define EFI_OS_INDICATIONS_BOOT_TO_FW_UI 0x0000000000000001ULL
83 #endif
84
85 #ifdef __MAKEWITH_TIANO
86 #define LibLocateHandle gBS->LocateHandleBuffer
87 #endif
88
89 //
90 // constants
91
92 #define MACOSX_LOADER_DIR L"System\\Library\\CoreServices"
93 #define MACOSX_LOADER_PATH ( MACOSX_LOADER_DIR L"\\boot.efi" )
94 #if defined (EFIX64)
95 #define SHELL_NAMES L"\\EFI\\tools\\shell.efi,\\EFI\\tools\\shellx64.efi,\\shell.efi,\\shellx64.efi"
96 #define GPTSYNC_NAMES L"\\EFI\\tools\\gptsync.efi,\\EFI\\tools\\gptsync_x64.efi"
97 #define GDISK_NAMES L"\\EFI\\tools\\gdisk.efi,\\EFI\\tools\\gdisk_x64.efi"
98 #define NETBOOT_NAMES L"\\EFI\\tools\\ipxe.efi"
99 #define MEMTEST_NAMES L"memtest86.efi,memtest86_x64.efi,memtest86x64.efi,bootx64.efi"
100 #define DRIVER_DIRS L"drivers,drivers_x64"
101 #define FALLBACK_FULLNAME L"EFI\\BOOT\\bootx64.efi"
102 #define FALLBACK_BASENAME L"bootx64.efi"
103 #define EFI_STUB_ARCH 0x8664
104 EFI_GUID gFreedesktopRootGuid = { 0x4f68bce3, 0xe8cd, 0x4db1, { 0x96, 0xe7, 0xfb, 0xca, 0xf9, 0x84, 0xb7, 0x09 }};
105 #elif defined (EFI32)
106 #define SHELL_NAMES L"\\EFI\\tools\\shell.efi,\\EFI\\tools\\shellia32.efi,\\shell.efi,\\shellia32.efi"
107 #define GPTSYNC_NAMES L"\\EFI\\tools\\gptsync.efi,\\EFI\\tools\\gptsync_ia32.efi"
108 #define GDISK_NAMES L"\\EFI\\tools\\gdisk.efi,\\EFI\\tools\\gdisk_ia32.efi"
109 #define NETBOOT_NAMES L"\\EFI\\tools\\ipxe.efi"
110 #define MEMTEST_NAMES L"memtest86.efi,memtest86_ia32.efi,memtest86ia32.efi,bootia32.efi"
111 #define DRIVER_DIRS L"drivers,drivers_ia32"
112 #define FALLBACK_FULLNAME L"EFI\\BOOT\\bootia32.efi"
113 #define FALLBACK_BASENAME L"bootia32.efi"
114 #define EFI_STUB_ARCH 0x014c
115 EFI_GUID gFreedesktopRootGuid = { 0x44479540, 0xf297, 0x41b2, { 0x9a, 0xf7, 0xd1, 0x31, 0xd5, 0xf0, 0x45, 0x8a }};
116 #else
117 #define SHELL_NAMES L"\\EFI\\tools\\shell.efi,\\shell.efi"
118 #define GPTSYNC_NAMES L"\\EFI\\tools\\gptsync.efi"
119 #define GDISK_NAMES L"\\EFI\\tools\\gdisk.efi"
120 #define NETBOOT_NAMES L"\\EFI\\tools\\ipxe.efi"
121 #define MEMTEST_NAMES L"memtest86.efi"
122 #define DRIVER_DIRS L"drivers"
123 #define FALLBACK_FULLNAME L"EFI\\BOOT\\boot.efi" /* Not really correct */
124 #define FALLBACK_BASENAME L"boot.efi" /* Not really correct */
125 // Below is GUID for ARM64
126 EFI_GUID gFreedesktopRootGuid = { 0xb921b045, 0x1df0, 0x41c3, { 0xaf, 0x44, 0x4c, 0x6f, 0x28, 0x0d, 0x3f, 0xae }};
127 #endif
128 #define FAT_ARCH 0x0ef1fab9 /* ID for Apple "fat" binary */
129
130 #define IPXE_DISCOVER_NAME L"\\efi\\tools\\ipxe_discover.efi"
131 #define IPXE_NAME L"\\efi\\tools\\ipxe.efi"
132
133 // Filename patterns that identify EFI boot loaders. Note that a single case (either L"*.efi" or
134 // L"*.EFI") is fine for most systems; but Gigabyte's buggy Hybrid EFI does a case-sensitive
135 // comparison when it should do a case-insensitive comparison, so I'm doubling this up. It does
136 // no harm on other computers, AFAIK. In theory, every case variation should be done for
137 // completeness, but that's ridiculous....
138 #define LOADER_MATCH_PATTERNS L"*.efi,*.EFI"
139
140 // Patterns that identify Linux kernels. Added to the loader match pattern when the
141 // scan_all_linux_kernels option is set in the configuration file. Causes kernels WITHOUT
142 // a ".efi" extension to be found when scanning for boot loaders.
143 #define LINUX_MATCH_PATTERNS L"vmlinuz*,bzImage*"
144
145 // Maximum length of a text string in certain menus
146 #define MAX_LINE_LENGTH 65
147
148 static REFIT_MENU_ENTRY MenuEntryAbout = { L"About rEFInd", TAG_ABOUT, 1, 0, 'A', NULL, NULL, NULL };
149 static REFIT_MENU_ENTRY MenuEntryReset = { L"Reboot Computer", TAG_REBOOT, 1, 0, 'R', NULL, NULL, NULL };
150 static REFIT_MENU_ENTRY MenuEntryShutdown = { L"Shut Down Computer", TAG_SHUTDOWN, 1, 0, 'U', NULL, NULL, NULL };
151 REFIT_MENU_ENTRY MenuEntryReturn = { L"Return to Main Menu", TAG_RETURN, 1, 0, 0, NULL, NULL, NULL };
152 static REFIT_MENU_ENTRY MenuEntryExit = { L"Exit rEFInd", TAG_EXIT, 1, 0, 0, NULL, NULL, NULL };
153 static REFIT_MENU_ENTRY MenuEntryFirmware = { L"Reboot to Computer Setup Utility", TAG_FIRMWARE, 1, 0, 0, NULL, NULL, NULL };
154 static REFIT_MENU_ENTRY MenuEntryRotateCsr = { L"Change SIP Policy", TAG_CSR_ROTATE, 1, 0, 0, NULL, NULL, NULL };
155
156 REFIT_MENU_SCREEN MainMenu = { L"Main Menu", NULL, 0, NULL, 0, NULL, 0, L"Automatic boot",
157 L"Use arrow keys to move cursor; Enter to boot;",
158 L"Insert or F2 for more options; Esc to refresh" };
159 static REFIT_MENU_SCREEN AboutMenu = { L"About", NULL, 0, NULL, 0, NULL, 0, NULL, L"Press Enter to return to main menu", L"" };
160
161 REFIT_CONFIG GlobalConfig = { FALSE, TRUE, FALSE, FALSE, TRUE, 0, 0, 0, DONT_CHANGE_TEXT_MODE,
162 20, 0, 0, GRAPHICS_FOR_OSX, LEGACY_TYPE_MAC,
163 0, 0, { DEFAULT_BIG_ICON_SIZE / 4, DEFAULT_SMALL_ICON_SIZE, DEFAULT_BIG_ICON_SIZE },
164 BANNER_NOSCALE, NULL, NULL, NULL, NULL, CONFIG_FILE_NAME, NULL, NULL, NULL, NULL,
165 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
166 { TAG_SHELL, TAG_MEMTEST, TAG_GDISK, TAG_APPLE_RECOVERY, TAG_WINDOWS_RECOVERY,
167 TAG_MOK_TOOL, TAG_ABOUT, TAG_SHUTDOWN, TAG_REBOOT, TAG_FIRMWARE,
168 0, 0, 0, 0, 0, 0, 0, 0 }
169 };
170
171 EFI_GUID GlobalGuid = EFI_GLOBAL_VARIABLE;
172 EFI_GUID RefindGuid = REFIND_GUID_VALUE;
173
174 GPT_DATA *gPartitions = NULL;
175
176 // Structure used to hold boot loader filenames and time stamps in
177 // a linked list; used to sort entries within a directory.
178 struct LOADER_LIST {
179 CHAR16 *FileName;
180 EFI_TIME TimeStamp;
181 struct LOADER_LIST *NextEntry;
182 };
183
184 //
185 // misc functions
186 //
187
188 static VOID AboutrEFInd(VOID)
189 {
190 CHAR16 *FirmwareVendor;
191 UINT32 CsrStatus;
192
193 if (AboutMenu.EntryCount == 0) {
194 AboutMenu.TitleImage = BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT);
195 AddMenuInfoLine(&AboutMenu, L"rEFInd Version 0.10.0");
196 AddMenuInfoLine(&AboutMenu, L"");
197 AddMenuInfoLine(&AboutMenu, L"Copyright (c) 2006-2010 Christoph Pfisterer");
198 AddMenuInfoLine(&AboutMenu, L"Copyright (c) 2012-2015 Roderick W. Smith");
199 AddMenuInfoLine(&AboutMenu, L"Portions Copyright (c) Intel Corporation and others");
200 AddMenuInfoLine(&AboutMenu, L"Distributed under the terms of the GNU GPLv3 license");
201 AddMenuInfoLine(&AboutMenu, L"");
202 AddMenuInfoLine(&AboutMenu, L"Running on:");
203 AddMenuInfoLine(&AboutMenu, PoolPrint(L" EFI Revision %d.%02d", ST->Hdr.Revision >> 16, ST->Hdr.Revision & ((1 << 16) - 1)));
204 #if defined(EFI32)
205 AddMenuInfoLine(&AboutMenu, PoolPrint(L" Platform: x86 (32 bit); Secure Boot %s",
206 secure_mode() ? L"active" : L"inactive"));
207 #elif defined(EFIX64)
208 AddMenuInfoLine(&AboutMenu, PoolPrint(L" Platform: x86_64 (64 bit); Secure Boot %s",
209 secure_mode() ? L"active" : L"inactive"));
210 #else
211 AddMenuInfoLine(&AboutMenu, L" Platform: unknown");
212 #endif
213 if (GetCsrStatus(&CsrStatus) == EFI_SUCCESS) {
214 RecordgCsrStatus(CsrStatus, FALSE);
215 AddMenuInfoLine(&AboutMenu, gCsrStatus);
216 }
217 FirmwareVendor = StrDuplicate(ST->FirmwareVendor);
218 LimitStringLength(FirmwareVendor, MAX_LINE_LENGTH); // More than ~65 causes empty info page on 800x600 display
219 AddMenuInfoLine(&AboutMenu, PoolPrint(L" Firmware: %s %d.%02d", FirmwareVendor, ST->FirmwareRevision >> 16,
220 ST->FirmwareRevision & ((1 << 16) - 1)));
221 AddMenuInfoLine(&AboutMenu, PoolPrint(L" Screen Output: %s", egScreenDescription()));
222 AddMenuInfoLine(&AboutMenu, L"");
223 #if defined(__MAKEWITH_GNUEFI)
224 AddMenuInfoLine(&AboutMenu, L"Built with GNU-EFI");
225 #else
226 AddMenuInfoLine(&AboutMenu, L"Built with TianoCore EDK2");
227 #endif
228 AddMenuInfoLine(&AboutMenu, L"");
229 AddMenuInfoLine(&AboutMenu, L"For more information, see the rEFInd Web site:");
230 AddMenuInfoLine(&AboutMenu, L"http://www.rodsbooks.com/refind/");
231 AddMenuEntry(&AboutMenu, &MenuEntryReturn);
232 }
233
234 RunMenu(&AboutMenu, NULL);
235 } /* VOID AboutrEFInd() */
236
237 static VOID WarnSecureBootError(CHAR16 *Name, BOOLEAN Verbose) {
238 if (Name == NULL)
239 Name = L"the loader";
240
241 refit_call2_wrapper(ST->ConOut->SetAttribute, ST->ConOut, ATTR_ERROR);
242 Print(L"Secure Boot validation failure loading %s!\n", Name);
243 refit_call2_wrapper(ST->ConOut->SetAttribute, ST->ConOut, ATTR_BASIC);
244 if (Verbose && secure_mode()) {
245 Print(L"\nThis computer is configured with Secure Boot active, but\n%s has failed validation.\n", Name);
246 Print(L"\nYou can:\n * Launch another boot loader\n");
247 Print(L" * Disable Secure Boot in your firmware\n");
248 Print(L" * Sign %s with a machine owner key (MOK)\n", Name);
249 Print(L" * Use a MOK utility (often present on the second row) to add a MOK with which\n");
250 Print(L" %s has already been signed.\n", Name);
251 Print(L" * Use a MOK utility to register %s (\"enroll its hash\") without\n", Name);
252 Print(L" signing it.\n");
253 Print(L"\nSee http://www.rodsbooks.com/refind/secureboot.html for more information\n");
254 PauseForKey();
255 } // if
256 } // VOID WarnSecureBootError()
257
258 // Returns TRUE if this file is a valid EFI loader file, and is proper ARCH
259 static BOOLEAN IsValidLoader(EFI_FILE *RootDir, CHAR16 *FileName) {
260 BOOLEAN IsValid = TRUE;
261 #if defined (EFIX64) | defined (EFI32)
262 EFI_STATUS Status;
263 EFI_FILE_HANDLE FileHandle;
264 CHAR8 Header[512];
265 UINTN Size = sizeof(Header);
266
267 if ((RootDir == NULL) || (FileName == NULL)) {
268 // Assume valid here, because Macs produce NULL RootDir (& maybe FileName)
269 // when launching from a Firewire drive. This should be handled better, but
270 // fix would have to be in StartEFIImageList() and/or in FindVolumeAndFilename().
271 return TRUE;
272 } // if
273
274 Status = refit_call5_wrapper(RootDir->Open, RootDir, &FileHandle, FileName, EFI_FILE_MODE_READ, 0);
275 if (EFI_ERROR(Status))
276 return FALSE;
277
278 Status = refit_call3_wrapper(FileHandle->Read, FileHandle, &Size, Header);
279 refit_call1_wrapper(FileHandle->Close, FileHandle);
280
281 IsValid = !EFI_ERROR(Status) &&
282 Size == sizeof(Header) &&
283 ((Header[0] == 'M' && Header[1] == 'Z' &&
284 (Size = *(UINT32 *)&Header[0x3c]) < 0x180 &&
285 Header[Size] == 'P' && Header[Size+1] == 'E' &&
286 Header[Size+2] == 0 && Header[Size+3] == 0 &&
287 *(UINT16 *)&Header[Size+4] == EFI_STUB_ARCH) ||
288 (*(UINT32 *)&Header == FAT_ARCH));
289 #endif
290 return IsValid;
291 } // BOOLEAN IsValidLoader()
292
293 // Launch an EFI binary.
294 EFI_STATUS StartEFIImageList(IN EFI_DEVICE_PATH **DevicePaths,
295 IN CHAR16 *LoadOptions, IN UINTN LoaderType,
296 IN CHAR16 *ImageTitle, IN CHAR8 OSType,
297 OUT UINTN *ErrorInStep,
298 IN BOOLEAN Verbose,
299 IN BOOLEAN IsDriver)
300 {
301 EFI_STATUS Status, ReturnStatus;
302 EFI_HANDLE ChildImageHandle, ChildImageHandle2;
303 EFI_LOADED_IMAGE *ChildLoadedImage = NULL;
304 REFIT_VOLUME *Volume = NULL;
305 UINTN DevicePathIndex;
306 CHAR16 ErrorInfo[256];
307 CHAR16 *FullLoadOptions = NULL;
308 CHAR16 *Filename = NULL;
309 CHAR16 *Temp;
310
311 if (ErrorInStep != NULL)
312 *ErrorInStep = 0;
313
314 // set load options
315 if (LoadOptions != NULL) {
316 FullLoadOptions = StrDuplicate(LoadOptions);
317 if ((LoaderType == TYPE_EFI) && (OSType == 'M')) {
318 MergeStrings(&FullLoadOptions, L" ", 0);
319 // NOTE: That last space is also added by the EFI shell and seems to be significant
320 // when passing options to Apple's boot.efi...
321 } // if
322 } // if (LoadOptions != NULL)
323 if (Verbose)
324 Print(L"Starting %s\nUsing load options '%s'\n", ImageTitle, FullLoadOptions ? FullLoadOptions : L"");
325
326 // load the image into memory
327 ReturnStatus = Status = EFI_NOT_FOUND; // in case the list is empty
328 for (DevicePathIndex = 0; DevicePaths[DevicePathIndex] != NULL; DevicePathIndex++) {
329 FindVolumeAndFilename(DevicePaths[DevicePathIndex], &Volume, &Filename);
330 // Some EFIs crash if attempting to load driver for invalid architecture, so
331 // protect for this condition; but sometimes Volume comes back NULL, so provide
332 // an exception. (TODO: Handle this special condition better.)
333 if ((LoaderType == TYPE_LEGACY) || (Volume == NULL) || IsValidLoader(Volume->RootDir, Filename)) {
334 if (Filename && (LoaderType != TYPE_LEGACY)) {
335 Temp = PoolPrint(L"\\%s %s", Filename, FullLoadOptions ? FullLoadOptions : L"");
336 if (Temp != NULL) {
337 MyFreePool(FullLoadOptions);
338 FullLoadOptions = Temp;
339 }
340 } // if (Filename)
341
342 // NOTE: Below commented-out line could be more efficient if file were read ahead of
343 // time and passed as a pre-loaded image to LoadImage(), but it doesn't work on my
344 // 32-bit Mac Mini or my 64-bit Intel box when launching a Linux kernel; the
345 // kernel returns a "Failed to handle fs_proto" error message.
346 // TODO: Track down the cause of this error and fix it, if possible.
347 // ReturnStatus = Status = refit_call6_wrapper(BS->LoadImage, FALSE, SelfImageHandle, DevicePaths[DevicePathIndex],
348 // ImageData, ImageSize, &ChildImageHandle);
349 ReturnStatus = Status = refit_call6_wrapper(BS->LoadImage, FALSE, SelfImageHandle, DevicePaths[DevicePathIndex],
350 NULL, 0, &ChildImageHandle);
351 if (secure_mode() && ShimLoaded()) {
352 // Load ourself into memory. This is a trick to work around a bug in Shim 0.8,
353 // which ties itself into the BS->LoadImage() and BS->StartImage() functions and
354 // then unregisters itself from the EFI system table when its replacement
355 // StartImage() function is called *IF* the previous LoadImage() was for the same
356 // program. The result is that rEFInd can validate only the first program it
357 // launches (often a filesystem driver). Loading a second program (rEFInd itself,
358 // here, to keep it smaller than a kernel) works around this problem. See the
359 // replacements.c file in Shim, and especially its start_image() function, for
360 // the source of the problem.
361 // NOTE: This doesn't check the return status or handle errors. It could
362 // conceivably do weird things if, say, rEFInd were on a USB drive that the
363 // user pulls before launching a program.
364 refit_call6_wrapper(BS->LoadImage, FALSE, SelfImageHandle, GlobalConfig.SelfDevicePath,
365 NULL, 0, &ChildImageHandle2);
366 }
367 } else {
368 Print(L"Invalid loader file!\n");
369 ReturnStatus = EFI_LOAD_ERROR;
370 }
371 if (ReturnStatus != EFI_NOT_FOUND) {
372 break;
373 }
374 } // for
375 if ((Status == EFI_ACCESS_DENIED) || (Status == EFI_SECURITY_VIOLATION)) {
376 WarnSecureBootError(ImageTitle, Verbose);
377 goto bailout;
378 }
379 SPrint(ErrorInfo, 255, L"while loading %s", ImageTitle);
380 if (CheckError(Status, ErrorInfo)) {
381 if (ErrorInStep != NULL)
382 *ErrorInStep = 1;
383 goto bailout;
384 }
385
386 ReturnStatus = Status = refit_call3_wrapper(BS->HandleProtocol, ChildImageHandle, &LoadedImageProtocol,
387 (VOID **) &ChildLoadedImage);
388 if (CheckError(Status, L"while getting a LoadedImageProtocol handle")) {
389 if (ErrorInStep != NULL)
390 *ErrorInStep = 2;
391 goto bailout_unload;
392 }
393 ChildLoadedImage->LoadOptions = (VOID *)FullLoadOptions;
394 ChildLoadedImage->LoadOptionsSize = FullLoadOptions ? ((UINT32)StrLen(FullLoadOptions) + 1) * sizeof(CHAR16) : 0;
395 // turn control over to the image
396 // TODO: (optionally) re-enable the EFI watchdog timer!
397
398 // close open file handles
399 UninitRefitLib();
400 ReturnStatus = Status = refit_call3_wrapper(BS->StartImage, ChildImageHandle, NULL, NULL);
401
402 // control returns here when the child image calls Exit()
403 SPrint(ErrorInfo, 255, L"returned from %s", ImageTitle);
404 if (CheckError(Status, ErrorInfo)) {
405 if (ErrorInStep != NULL)
406 *ErrorInStep = 3;
407 }
408
409 // re-open file handles
410 ReinitRefitLib();
411
412 bailout_unload:
413 // unload the image, we don't care if it works or not...
414 if (!IsDriver)
415 Status = refit_call1_wrapper(BS->UnloadImage, ChildImageHandle);
416
417 bailout:
418 MyFreePool(FullLoadOptions);
419 return ReturnStatus;
420 } /* EFI_STATUS StartEFIImageList() */
421
422 static EFI_STATUS StartEFIImage(IN EFI_DEVICE_PATH *DevicePath,
423 IN CHAR16 *LoadOptions, IN UINTN LoaderType,
424 IN CHAR16 *ImageTitle, IN CHAR8 OSType,
425 OUT UINTN *ErrorInStep,
426 IN BOOLEAN Verbose,
427 IN BOOLEAN IsDriver
428 )
429 {
430 EFI_DEVICE_PATH *DevicePaths[2];
431
432 DevicePaths[0] = DevicePath;
433 DevicePaths[1] = NULL;
434 return StartEFIImageList(DevicePaths, LoadOptions, LoaderType, ImageTitle, OSType, ErrorInStep, Verbose, IsDriver);
435 } /* static EFI_STATUS StartEFIImage() */
436
437 // From gummiboot: Reboot the computer into its built-in user interface
438 static EFI_STATUS RebootIntoFirmware(VOID) {
439 CHAR8 *b;
440 UINTN size;
441 UINT64 osind;
442 EFI_STATUS err;
443
444 osind = EFI_OS_INDICATIONS_BOOT_TO_FW_UI;
445
446 err = EfivarGetRaw(&GlobalGuid, L"OsIndications", &b, &size);
447 if (err == EFI_SUCCESS)
448 osind |= (UINT64)*b;
449 MyFreePool(b);
450
451 err = EfivarSetRaw(&GlobalGuid, L"OsIndications", (CHAR8 *)&osind, sizeof(UINT64), TRUE);
452 if (err != EFI_SUCCESS)
453 return err;
454
455 refit_call4_wrapper(RT->ResetSystem, EfiResetCold, EFI_SUCCESS, 0, NULL);
456 Print(L"Error calling ResetSystem: %r", err);
457 PauseForKey();
458 return err;
459 }
460
461 // Record the value of the loader's name/description in rEFInd's "PreviousBoot" EFI variable,
462 // if it's different from what's already stored there.
463 VOID StoreLoaderName(IN CHAR16 *Name) {
464 EFI_STATUS Status;
465 CHAR16 *OldName = NULL;
466 UINTN Length;
467
468 if (Name) {
469 Status = EfivarGetRaw(&RefindGuid, L"PreviousBoot", (CHAR8**) &OldName, &Length);
470 if ((Status != EFI_SUCCESS) || (StrCmp(OldName, Name) != 0)) {
471 EfivarSetRaw(&RefindGuid, L"PreviousBoot", (CHAR8*) Name, StrLen(Name) * 2 + 2, TRUE);
472 } // if
473 MyFreePool(OldName);
474 } // if
475 } // VOID StoreLoaderName()
476
477 //
478 // EFI OS loader functions
479 //
480
481 // See http://www.thomas-krenn.com/en/wiki/Activating_the_Intel_VT_Virtualization_Feature
482 // for information on Intel VMX features
483 static VOID DoEnableAndLockVMX(VOID)
484 {
485 UINT32 msr = 0x3a;
486 UINT32 low_bits = 0, high_bits = 0;
487
488 // is VMX active ?
489 __asm__ volatile ("rdmsr" : "=a" (low_bits), "=d" (high_bits) : "c" (msr));
490
491 // enable and lock vmx if not locked
492 if ((low_bits & 1) == 0) {
493 high_bits = 0;
494 low_bits = 0x05;
495 msr = 0x3a;
496 __asm__ volatile ("wrmsr" : : "c" (msr), "a" (low_bits), "d" (high_bits));
497 }
498 } // VOID DoEnableAndLockVMX()
499
500 static VOID StartLoader(LOADER_ENTRY *Entry, CHAR16 *SelectionName)
501 {
502 UINTN ErrorInStep = 0;
503
504 if (GlobalConfig.EnableAndLockVMX) {
505 DoEnableAndLockVMX();
506 }
507
508 BeginExternalScreen(Entry->UseGraphicsMode, L"Booting OS");
509 StoreLoaderName(SelectionName);
510 StartEFIImage(Entry->DevicePath, Entry->LoadOptions, TYPE_EFI,
511 Basename(Entry->LoaderPath), Entry->OSType, &ErrorInStep, !Entry->UseGraphicsMode, FALSE);
512 FinishExternalScreen();
513 }
514
515 // Locate an initrd or initramfs file that matches the kernel specified by LoaderPath.
516 // The matching file has a name that begins with "init" and includes the same version
517 // number string as is found in LoaderPath -- but not a longer version number string.
518 // For instance, if LoaderPath is \EFI\kernels\bzImage-3.3.0.efi, and if \EFI\kernels
519 // has a file called initramfs-3.3.0.img, this function will return the string
520 // '\EFI\kernels\initramfs-3.3.0.img'. If the directory ALSO contains the file
521 // initramfs-3.3.0-rc7.img or initramfs-13.3.0.img, those files will NOT match;
522 // however, initmine-3.3.0.img might match. (FindInitrd() returns the first match it
523 // finds.) Thus, care should be taken to avoid placing duplicate matching files in
524 // the kernel's directory.
525 // If no matching init file can be found, returns NULL.
526 static CHAR16 * FindInitrd(IN CHAR16 *LoaderPath, IN REFIT_VOLUME *Volume) {
527 CHAR16 *InitrdName = NULL, *FileName, *KernelVersion, *InitrdVersion, *Path;
528 REFIT_DIR_ITER DirIter;
529 EFI_FILE_INFO *DirEntry;
530
531 FileName = Basename(LoaderPath);
532 KernelVersion = FindNumbers(FileName);
533 Path = FindPath(LoaderPath);
534
535 // Add trailing backslash for root directory; necessary on some systems, but must
536 // NOT be added to all directories, since on other systems, a trailing backslash on
537 // anything but the root directory causes them to flake out!
538 if (StrLen(Path) == 0) {
539 MergeStrings(&Path, L"\\", 0);
540 } // if
541 DirIterOpen(Volume->RootDir, Path, &DirIter);
542 // Now add a trailing backslash if it was NOT added earlier, for consistency in
543 // building the InitrdName later....
544 if ((StrLen(Path) > 0) && (Path[StrLen(Path) - 1] != L'\\'))
545 MergeStrings(&Path, L"\\", 0);
546 while ((DirIterNext(&DirIter, 2, L"init*", &DirEntry)) && (InitrdName == NULL)) {
547 InitrdVersion = FindNumbers(DirEntry->FileName);
548 if (KernelVersion != NULL) {
549 if (MyStriCmp(InitrdVersion, KernelVersion)) {
550 InitrdName = PoolPrint(L"%s%s", Path, DirEntry->FileName);
551 } // if
552 } else {
553 if (InitrdVersion == NULL) {
554 InitrdName = PoolPrint(L"%s%s", Path, DirEntry->FileName);
555 } // if
556 } // if/else
557 MyFreePool(InitrdVersion);
558 } // while
559 DirIterClose(&DirIter);
560
561 // Note: Don't FreePool(FileName), since Basename returns a pointer WITHIN the string it's passed.
562 MyFreePool(KernelVersion);
563 MyFreePool(Path);
564 return (InitrdName);
565 } // static CHAR16 * FindInitrd()
566
567 LOADER_ENTRY * AddPreparedLoaderEntry(LOADER_ENTRY *Entry) {
568 AddMenuEntry(&MainMenu, (REFIT_MENU_ENTRY *)Entry);
569
570 return(Entry);
571 } // LOADER_ENTRY * AddPreparedLoaderEntry()
572
573 // Creates a copy of a menu screen.
574 // Returns a pointer to the copy of the menu screen.
575 static REFIT_MENU_SCREEN* CopyMenuScreen(REFIT_MENU_SCREEN *Entry) {
576 REFIT_MENU_SCREEN *NewEntry;
577 UINTN i;
578
579 NewEntry = AllocateZeroPool(sizeof(REFIT_MENU_SCREEN));
580 if ((Entry != NULL) && (NewEntry != NULL)) {
581 CopyMem(NewEntry, Entry, sizeof(REFIT_MENU_SCREEN));
582 NewEntry->Title = (Entry->Title) ? StrDuplicate(Entry->Title) : NULL;
583 NewEntry->TimeoutText = (Entry->TimeoutText) ? StrDuplicate(Entry->TimeoutText) : NULL;
584 if (Entry->TitleImage != NULL) {
585 NewEntry->TitleImage = AllocatePool(sizeof(EG_IMAGE));
586 if (NewEntry->TitleImage != NULL)
587 CopyMem(NewEntry->TitleImage, Entry->TitleImage, sizeof(EG_IMAGE));
588 } // if
589 NewEntry->InfoLines = (CHAR16**) AllocateZeroPool(Entry->InfoLineCount * (sizeof(CHAR16*)));
590 for (i = 0; i < Entry->InfoLineCount && NewEntry->InfoLines; i++) {
591 NewEntry->InfoLines[i] = (Entry->InfoLines[i]) ? StrDuplicate(Entry->InfoLines[i]) : NULL;
592 } // for
593 NewEntry->Entries = (REFIT_MENU_ENTRY**) AllocateZeroPool(Entry->EntryCount * (sizeof (REFIT_MENU_ENTRY*)));
594 for (i = 0; i < Entry->EntryCount && NewEntry->Entries; i++) {
595 AddMenuEntry(NewEntry, Entry->Entries[i]);
596 } // for
597 NewEntry->Hint1 = (Entry->Hint1) ? StrDuplicate(Entry->Hint1) : NULL;
598 NewEntry->Hint2 = (Entry->Hint2) ? StrDuplicate(Entry->Hint2) : NULL;
599 } // if
600 return (NewEntry);
601 } // static REFIT_MENU_SCREEN* CopyMenuScreen()
602
603 // Creates a copy of a menu entry. Intended to enable moving a stack-based
604 // menu entry (such as the ones for the "reboot" and "exit" functions) to
605 // to the heap. This enables easier deletion of the whole set of menu
606 // entries when re-scanning.
607 // Returns a pointer to the copy of the menu entry.
608 static REFIT_MENU_ENTRY* CopyMenuEntry(REFIT_MENU_ENTRY *Entry) {
609 REFIT_MENU_ENTRY *NewEntry;
610
611 NewEntry = AllocateZeroPool(sizeof(REFIT_MENU_ENTRY));
612 if ((Entry != NULL) && (NewEntry != NULL)) {
613 CopyMem(NewEntry, Entry, sizeof(REFIT_MENU_ENTRY));
614 NewEntry->Title = (Entry->Title) ? StrDuplicate(Entry->Title) : NULL;
615 if (Entry->BadgeImage != NULL) {
616 NewEntry->BadgeImage = AllocatePool(sizeof(EG_IMAGE));
617 if (NewEntry->BadgeImage != NULL)
618 CopyMem(NewEntry->BadgeImage, Entry->BadgeImage, sizeof(EG_IMAGE));
619 }
620 if (Entry->Image != NULL) {
621 NewEntry->Image = AllocatePool(sizeof(EG_IMAGE));
622 if (NewEntry->Image != NULL)
623 CopyMem(NewEntry->Image, Entry->Image, sizeof(EG_IMAGE));
624 }
625 if (Entry->SubScreen != NULL) {
626 NewEntry->SubScreen = CopyMenuScreen(Entry->SubScreen);
627 }
628 } // if
629 return (NewEntry);
630 } // REFIT_MENU_ENTRY* CopyMenuEntry()
631
632 // Creates a new LOADER_ENTRY data structure and populates it with
633 // default values from the specified Entry, or NULL values if Entry
634 // is unspecified (NULL).
635 // Returns a pointer to the new data structure, or NULL if it
636 // couldn't be allocated
637 LOADER_ENTRY *InitializeLoaderEntry(IN LOADER_ENTRY *Entry) {
638 LOADER_ENTRY *NewEntry = NULL;
639
640 NewEntry = AllocateZeroPool(sizeof(LOADER_ENTRY));
641 if (NewEntry != NULL) {
642 NewEntry->me.Title = NULL;
643 NewEntry->me.Tag = TAG_LOADER;
644 NewEntry->Enabled = TRUE;
645 NewEntry->UseGraphicsMode = FALSE;
646 NewEntry->OSType = 0;
647 if (Entry != NULL) {
648 NewEntry->LoaderPath = (Entry->LoaderPath) ? StrDuplicate(Entry->LoaderPath) : NULL;
649 NewEntry->VolName = (Entry->VolName) ? StrDuplicate(Entry->VolName) : NULL;
650 NewEntry->DevicePath = Entry->DevicePath;
651 NewEntry->UseGraphicsMode = Entry->UseGraphicsMode;
652 NewEntry->LoadOptions = (Entry->LoadOptions) ? StrDuplicate(Entry->LoadOptions) : NULL;
653 NewEntry->InitrdPath = (Entry->InitrdPath) ? StrDuplicate(Entry->InitrdPath) : NULL;
654 }
655 } // if
656 return (NewEntry);
657 } // LOADER_ENTRY *InitializeLoaderEntry()
658
659 // Adds InitrdPath to Options, but only if Options doesn't already include an
660 // initrd= line. Done to enable overriding the default initrd selection in a
661 // refind_linux.conf file's options list.
662 // Returns a pointer to a new string. The calling function is responsible for
663 // freeing its memory.
664 static CHAR16 *AddInitrdToOptions(CHAR16 *Options, CHAR16 *InitrdPath) {
665 CHAR16 *NewOptions = NULL;
666
667 if (Options != NULL)
668 NewOptions = StrDuplicate(Options);
669 if ((InitrdPath != NULL) && !StriSubCmp(L"initrd=", Options)) {
670 MergeStrings(&NewOptions, L"initrd=", L' ');
671 MergeStrings(&NewOptions, InitrdPath, 0);
672 }
673 return NewOptions;
674 } // CHAR16 *AddInitrdToOptions()
675
676 // Prepare a REFIT_MENU_SCREEN data structure for a subscreen entry. This sets up
677 // the default entry that launches the boot loader using the same options as the
678 // main Entry does. Subsequent options can be added by the calling function.
679 // If a subscreen already exists in the Entry that's passed to this function,
680 // it's left unchanged and a pointer to it is returned.
681 // Returns a pointer to the new subscreen data structure, or NULL if there
682 // were problems allocating memory.
683 REFIT_MENU_SCREEN *InitializeSubScreen(IN LOADER_ENTRY *Entry) {
684 CHAR16 *FileName, *MainOptions = NULL;
685 REFIT_MENU_SCREEN *SubScreen = NULL;
686 LOADER_ENTRY *SubEntry;
687
688 FileName = Basename(Entry->LoaderPath);
689 if (Entry->me.SubScreen == NULL) { // No subscreen yet; initialize default entry....
690 SubScreen = AllocateZeroPool(sizeof(REFIT_MENU_SCREEN));
691 if (SubScreen != NULL) {
692 SubScreen->Title = AllocateZeroPool(sizeof(CHAR16) * 256);
693 SPrint(SubScreen->Title, 255, L"Boot Options for %s on %s",
694 (Entry->Title != NULL) ? Entry->Title : FileName, Entry->VolName);
695 SubScreen->TitleImage = Entry->me.Image;
696 // default entry
697 SubEntry = InitializeLoaderEntry(Entry);
698 if (SubEntry != NULL) {
699 SubEntry->me.Title = StrDuplicate(L"Boot using default options");
700 MainOptions = SubEntry->LoadOptions;
701 SubEntry->LoadOptions = AddInitrdToOptions(MainOptions, SubEntry->InitrdPath);
702 MyFreePool(MainOptions);
703 AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry);
704 } // if (SubEntry != NULL)
705 SubScreen->Hint1 = StrDuplicate(SUBSCREEN_HINT1);
706 if (GlobalConfig.HideUIFlags & HIDEUI_FLAG_EDITOR) {
707 SubScreen->Hint2 = StrDuplicate(SUBSCREEN_HINT2_NO_EDITOR);
708 } else {
709 SubScreen->Hint2 = StrDuplicate(SUBSCREEN_HINT2);
710 } // if/else
711 } // if (SubScreen != NULL)
712 } else { // existing subscreen; less initialization, and just add new entry later....
713 SubScreen = Entry->me.SubScreen;
714 } // if/else
715 return SubScreen;
716 } // REFIT_MENU_SCREEN *InitializeSubScreen()
717
718 VOID GenerateSubScreen(LOADER_ENTRY *Entry, IN REFIT_VOLUME *Volume, IN BOOLEAN GenerateReturn) {
719 REFIT_MENU_SCREEN *SubScreen;
720 LOADER_ENTRY *SubEntry;
721 CHAR16 *InitrdName;
722 CHAR16 DiagsFileName[256];
723 REFIT_FILE *File;
724 UINTN TokenCount;
725 CHAR16 **TokenList;
726
727 // create the submenu
728 if (StrLen(Entry->Title) == 0) {
729 MyFreePool(Entry->Title);
730 Entry->Title = NULL;
731 }
732 SubScreen = InitializeSubScreen(Entry);
733
734 // loader-specific submenu entries
735 if (Entry->OSType == 'M') { // entries for Mac OS X
736 #if defined(EFIX64)
737 SubEntry = InitializeLoaderEntry(Entry);
738 if (SubEntry != NULL) {
739 SubEntry->me.Title = L"Boot Mac OS X with a 64-bit kernel";
740 SubEntry->LoadOptions = L"arch=x86_64";
741 SubEntry->UseGraphicsMode = GlobalConfig.GraphicsFor & GRAPHICS_FOR_OSX;
742 AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry);
743 } // if
744
745 SubEntry = InitializeLoaderEntry(Entry);
746 if (SubEntry != NULL) {
747 SubEntry->me.Title = L"Boot Mac OS X with a 32-bit kernel";
748 SubEntry->LoadOptions = L"arch=i386";
749 SubEntry->UseGraphicsMode = GlobalConfig.GraphicsFor & GRAPHICS_FOR_OSX;
750 AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry);
751 } // if
752 #endif
753
754 if (!(GlobalConfig.HideUIFlags & HIDEUI_FLAG_SINGLEUSER)) {
755 SubEntry = InitializeLoaderEntry(Entry);
756 if (SubEntry != NULL) {
757 SubEntry->me.Title = L"Boot Mac OS X in verbose mode";
758 SubEntry->UseGraphicsMode = FALSE;
759 SubEntry->LoadOptions = L"-v";
760 AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry);
761 } // if
762
763 #if defined(EFIX64)
764 SubEntry = InitializeLoaderEntry(Entry);
765 if (SubEntry != NULL) {
766 SubEntry->me.Title = L"Boot Mac OS X in verbose mode (64-bit)";
767 SubEntry->UseGraphicsMode = FALSE;
768 SubEntry->LoadOptions = L"-v arch=x86_64";
769 AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry);
770 }
771
772 SubEntry = InitializeLoaderEntry(Entry);
773 if (SubEntry != NULL) {
774 SubEntry->me.Title = L"Boot Mac OS X in verbose mode (32-bit)";
775 SubEntry->UseGraphicsMode = FALSE;
776 SubEntry->LoadOptions = L"-v arch=i386";
777 AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry);
778 }
779 #endif
780
781 SubEntry = InitializeLoaderEntry(Entry);
782 if (SubEntry != NULL) {
783 SubEntry->me.Title = L"Boot Mac OS X in single user mode";
784 SubEntry->UseGraphicsMode = FALSE;
785 SubEntry->LoadOptions = L"-v -s";
786 AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry);
787 } // if
788 } // single-user mode allowed
789
790 if (!(GlobalConfig.HideUIFlags & HIDEUI_FLAG_SAFEMODE)) {
791 SubEntry = InitializeLoaderEntry(Entry);
792 if (SubEntry != NULL) {
793 SubEntry->me.Title = L"Boot Mac OS X in safe mode";
794 SubEntry->UseGraphicsMode = FALSE;
795 SubEntry->LoadOptions = L"-v -x";
796 AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry);
797 } // if
798 } // safe mode allowed
799
800 // check for Apple hardware diagnostics
801 StrCpy(DiagsFileName, L"System\\Library\\CoreServices\\.diagnostics\\diags.efi");
802 if (FileExists(Volume->RootDir, DiagsFileName) && !(GlobalConfig.HideUIFlags & HIDEUI_FLAG_HWTEST)) {
803 SubEntry = InitializeLoaderEntry(Entry);
804 if (SubEntry != NULL) {
805 SubEntry->me.Title = L"Run Apple Hardware Test";
806 MyFreePool(SubEntry->LoaderPath);
807 SubEntry->LoaderPath = StrDuplicate(DiagsFileName);
808 SubEntry->DevicePath = FileDevicePath(Volume->DeviceHandle, SubEntry->LoaderPath);
809 SubEntry->UseGraphicsMode = GlobalConfig.GraphicsFor & GRAPHICS_FOR_OSX;
810 AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry);
811 } // if
812 } // if diagnostics entry found
813
814 } else if (Entry->OSType == 'L') { // entries for Linux kernels with EFI stub loaders
815 File = ReadLinuxOptionsFile(Entry->LoaderPath, Volume);
816 if (File != NULL) {
817 InitrdName = FindInitrd(Entry->LoaderPath, Volume);
818 TokenCount = ReadTokenLine(File, &TokenList);
819 // first entry requires special processing, since it was initially set
820 // up with a default title but correct options by InitializeSubScreen(),
821 // earlier....
822 if ((TokenCount > 1) && (SubScreen->Entries != NULL) && (SubScreen->Entries[0] != NULL)) {
823 MyFreePool(SubScreen->Entries[0]->Title);
824 SubScreen->Entries[0]->Title = TokenList[0] ? StrDuplicate(TokenList[0]) : StrDuplicate(L"Boot Linux");
825 } // if
826 FreeTokenLine(&TokenList, &TokenCount);
827 while ((TokenCount = ReadTokenLine(File, &TokenList)) > 1) {
828 SubEntry = InitializeLoaderEntry(Entry);
829 SubEntry->me.Title = TokenList[0] ? StrDuplicate(TokenList[0]) : StrDuplicate(L"Boot Linux");
830 MyFreePool(SubEntry->LoadOptions);
831 SubEntry->LoadOptions = AddInitrdToOptions(TokenList[1], InitrdName);
832 FreeTokenLine(&TokenList, &TokenCount);
833 SubEntry->UseGraphicsMode = GlobalConfig.GraphicsFor & GRAPHICS_FOR_LINUX;
834 AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry);
835 } // while
836 MyFreePool(InitrdName);
837 MyFreePool(File);
838 } // if
839
840 } else if (Entry->OSType == 'E') { // entries for ELILO
841 SubEntry = InitializeLoaderEntry(Entry);
842 if (SubEntry != NULL) {
843 SubEntry->me.Title = L"Run ELILO in interactive mode";
844 SubEntry->LoadOptions = L"-p";
845 SubEntry->UseGraphicsMode = GlobalConfig.GraphicsFor & GRAPHICS_FOR_ELILO;
846 AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry);
847 }
848
849 SubEntry = InitializeLoaderEntry(Entry);
850 if (SubEntry != NULL) {
851 SubEntry->me.Title = L"Boot Linux for a 17\" iMac or a 15\" MacBook Pro (*)";
852 SubEntry->LoadOptions = L"-d 0 i17";
853 SubEntry->UseGraphicsMode = GlobalConfig.GraphicsFor & GRAPHICS_FOR_ELILO;
854 AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry);
855 }
856
857 SubEntry = InitializeLoaderEntry(Entry);
858 if (SubEntry != NULL) {
859 SubEntry->me.Title = L"Boot Linux for a 20\" iMac (*)";
860 SubEntry->LoadOptions = L"-d 0 i20";
861 SubEntry->UseGraphicsMode = GlobalConfig.GraphicsFor & GRAPHICS_FOR_ELILO;
862 AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry);
863 }
864
865 SubEntry = InitializeLoaderEntry(Entry);
866 if (SubEntry != NULL) {
867 SubEntry->me.Title = L"Boot Linux for a Mac Mini (*)";
868 SubEntry->LoadOptions = L"-d 0 mini";
869 SubEntry->UseGraphicsMode = GlobalConfig.GraphicsFor & GRAPHICS_FOR_ELILO;
870 AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry);
871 }
872
873 AddMenuInfoLine(SubScreen, L"NOTE: This is an example. Entries");
874 AddMenuInfoLine(SubScreen, L"marked with (*) may not work.");
875
876 } else if (Entry->OSType == 'X') { // entries for xom.efi
877 // by default, skip the built-in selection and boot from hard disk only
878 Entry->LoadOptions = L"-s -h";
879
880 SubEntry = InitializeLoaderEntry(Entry);
881 if (SubEntry != NULL) {
882 SubEntry->me.Title = L"Boot Windows from Hard Disk";
883 SubEntry->LoadOptions = L"-s -h";
884 SubEntry->UseGraphicsMode = GlobalConfig.GraphicsFor & GRAPHICS_FOR_WINDOWS;
885 AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry);
886 }
887
888 SubEntry = InitializeLoaderEntry(Entry);
889 if (SubEntry != NULL) {
890 SubEntry->me.Title = L"Boot Windows from CD-ROM";
891 SubEntry->LoadOptions = L"-s -c";
892 SubEntry->UseGraphicsMode = GlobalConfig.GraphicsFor & GRAPHICS_FOR_WINDOWS;
893 AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry);
894 }
895
896 SubEntry = InitializeLoaderEntry(Entry);
897 if (SubEntry != NULL) {
898 SubEntry->me.Title = L"Run XOM in text mode";
899 SubEntry->LoadOptions = L"-v";
900 SubEntry->UseGraphicsMode = GlobalConfig.GraphicsFor & GRAPHICS_FOR_WINDOWS;
901 AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry);
902 }
903 } // entries for xom.efi
904 if (GenerateReturn)
905 AddMenuEntry(SubScreen, &MenuEntryReturn);
906 Entry->me.SubScreen = SubScreen;
907 } // VOID GenerateSubScreen()
908
909 // Returns options for a Linux kernel. Reads them from an options file in the
910 // kernel's directory; and if present, adds an initrd= option for an initial
911 // RAM disk file with the same version number as the kernel file.
912 static CHAR16 * GetMainLinuxOptions(IN CHAR16 * LoaderPath, IN REFIT_VOLUME *Volume) {
913 CHAR16 *Options = NULL, *InitrdName, *FullOptions = NULL;
914
915 Options = GetFirstOptionsFromFile(LoaderPath, Volume);
916 InitrdName = FindInitrd(LoaderPath, Volume);
917 FullOptions = AddInitrdToOptions(Options, InitrdName);
918
919 MyFreePool(Options);
920 MyFreePool(InitrdName);
921 return (FullOptions);
922 } // static CHAR16 * GetMainLinuxOptions()
923
924 // Read the specified file and add values of "ID", "NAME", or "DISTRIB_ID" tokens to
925 // OSIconName list. Intended for adding Linux distribution clues gleaned from
926 // /etc/lsb-release and /etc/os-release files.
927 static VOID ParseReleaseFile(CHAR16 **OSIconName, REFIT_VOLUME *Volume, CHAR16 *FileName) {
928 UINTN FileSize = 0;
929 REFIT_FILE File;
930 CHAR16 **TokenList;
931 UINTN TokenCount = 0;
932
933 if ((Volume == NULL) || (FileName == NULL) || (OSIconName == NULL) || (*OSIconName == NULL))
934 return;
935
936 if (FileExists(Volume->RootDir, FileName) &&
937 (ReadFile(Volume->RootDir, FileName, &File, &FileSize) == EFI_SUCCESS)) {
938 do {
939 TokenCount = ReadTokenLine(&File, &TokenList);
940 if ((TokenCount > 1) && (MyStriCmp(TokenList[0], L"ID") ||
941 MyStriCmp(TokenList[0], L"NAME") ||
942 MyStriCmp(TokenList[0], L"DISTRIB_ID"))) {
943 MergeWords(OSIconName, TokenList[1], L',');
944 } // if
945 FreeTokenLine(&TokenList, &TokenCount);
946 } while (TokenCount > 0);
947 MyFreePool(File.Buffer);
948 } // if
949 } // VOID ParseReleaseFile()
950
951 // Try to guess the name of the Linux distribution & add that name to
952 // OSIconName list.
953 static VOID GuessLinuxDistribution(CHAR16 **OSIconName, REFIT_VOLUME *Volume, CHAR16 *LoaderPath) {
954 // If on Linux root fs, /etc/os-release or /etc/lsb-release file probably has clues....
955 ParseReleaseFile(OSIconName, Volume, L"etc\\lsb-release");
956 ParseReleaseFile(OSIconName, Volume, L"etc\\os-release");
957
958 // Search for clues in the kernel's filename....
959 if (StriSubCmp(L".fc", LoaderPath))
960 MergeStrings(OSIconName, L"fedora", L',');
961 if (StriSubCmp(L".el", LoaderPath))
962 MergeStrings(OSIconName, L"redhat", L',');
963 } // VOID GuessLinuxDistribution()
964
965 // Sets a few defaults for a loader entry -- mainly the icon, but also the OS type
966 // code and shortcut letter. For Linux EFI stub loaders, also sets kernel options
967 // that will (with luck) work fairly automatically.
968 VOID SetLoaderDefaults(LOADER_ENTRY *Entry, CHAR16 *LoaderPath, REFIT_VOLUME *Volume) {
969 CHAR16 *NameClues, *PathOnly, *NoExtension, *OSIconName = NULL, *Temp;
970 CHAR16 ShortcutLetter = 0;
971
972 NameClues = Basename(LoaderPath);
973 PathOnly = FindPath(LoaderPath);
974 NoExtension = StripEfiExtension(NameClues);
975
976 if (Volume->DiskKind == DISK_KIND_NET) {
977 MergeStrings(&NameClues, Entry->me.Title, L' ');
978 } else {
979 // locate a custom icon for the loader
980 // Anything found here takes precedence over the "hints" in the OSIconName variable
981 if (!Entry->me.Image) {
982 Entry->me.Image = egLoadIconAnyType(Volume->RootDir, PathOnly, NoExtension, GlobalConfig.IconSizes[ICON_SIZE_BIG]);
983 }
984 if (!Entry->me.Image) {
985 Entry->me.Image = egCopyImage(Volume->VolIconImage);
986 }
987
988 // Begin creating icon "hints" by using last part of directory path leading
989 // to the loader
990 Temp = FindLastDirName(LoaderPath);
991 MergeStrings(&OSIconName, Temp, L',');
992 MyFreePool(Temp);
993 Temp = NULL;
994 if (OSIconName != NULL) {
995 ShortcutLetter = OSIconName[0];
996 }
997
998 // Add every "word" in the volume label, delimited by spaces, dashes (-), or
999 // underscores (_), to the list of hints to be used in searching for OS
1000 // icons.
1001 MergeWords(&OSIconName, Volume->VolName, L',');
1002 } // if/else network boot
1003
1004 // detect specific loaders
1005 if (StriSubCmp(L"bzImage", NameClues) || StriSubCmp(L"vmlinuz", NameClues)) {
1006 if (Volume->DiskKind != DISK_KIND_NET) {
1007 GuessLinuxDistribution(&OSIconName, Volume, LoaderPath);
1008 Entry->LoadOptions = GetMainLinuxOptions(LoaderPath, Volume);
1009 }
1010 MergeStrings(&OSIconName, L"linux", L',');
1011 Entry->OSType = 'L';
1012 if (ShortcutLetter == 0)
1013 ShortcutLetter = 'L';
1014 Entry->UseGraphicsMode = GlobalConfig.GraphicsFor & GRAPHICS_FOR_LINUX;
1015 } else if (StriSubCmp(L"refit", LoaderPath)) {
1016 MergeStrings(&OSIconName, L"refit", L',');
1017 Entry->OSType = 'R';
1018 ShortcutLetter = 'R';
1019 } else if (StriSubCmp(L"refind", LoaderPath)) {
1020 MergeStrings(&OSIconName, L"refind", L',');
1021 Entry->OSType = 'R';
1022 ShortcutLetter = 'R';
1023 } else if (MyStriCmp(LoaderPath, MACOSX_LOADER_PATH)) {
1024 MergeStrings(&OSIconName, L"mac", L',');
1025 Entry->OSType = 'M';
1026 ShortcutLetter = 'M';
1027 Entry->UseGraphicsMode = GlobalConfig.GraphicsFor & GRAPHICS_FOR_OSX;
1028 } else if (MyStriCmp(NameClues, L"diags.efi")) {
1029 MergeStrings(&OSIconName, L"hwtest", L',');
1030 } else if (MyStriCmp(NameClues, L"e.efi") || MyStriCmp(NameClues, L"elilo.efi") || StriSubCmp(L"elilo", NameClues)) {
1031 MergeStrings(&OSIconName, L"elilo,linux", L',');
1032 Entry->OSType = 'E';
1033 if (ShortcutLetter == 0)
1034 ShortcutLetter = 'L';
1035 Entry->UseGraphicsMode = GlobalConfig.GraphicsFor & GRAPHICS_FOR_ELILO;
1036 } else if (StriSubCmp(L"grub", NameClues)) {
1037 MergeStrings(&OSIconName, L"grub,linux", L',');
1038 Entry->OSType = 'G';
1039 ShortcutLetter = 'G';
1040 Entry->UseGraphicsMode = GlobalConfig.GraphicsFor & GRAPHICS_FOR_GRUB;
1041 } else if (MyStriCmp(NameClues, L"cdboot.efi") ||
1042 MyStriCmp(NameClues, L"bootmgr.efi") ||
1043 MyStriCmp(NameClues, L"bootmgfw.efi") ||
1044 MyStriCmp(NameClues, L"bkpbootmgfw.efi")) {
1045 MergeStrings(&OSIconName, L"win8", L',');
1046 Entry->OSType = 'W';
1047 ShortcutLetter = 'W';
1048 Entry->UseGraphicsMode = GlobalConfig.GraphicsFor & GRAPHICS_FOR_WINDOWS;
1049 } else if (MyStriCmp(NameClues, L"xom.efi")) {
1050 MergeStrings(&OSIconName, L"xom,win,win8", L',');
1051 Entry->OSType = 'X';
1052 ShortcutLetter = 'W';
1053 Entry->UseGraphicsMode = GlobalConfig.GraphicsFor & GRAPHICS_FOR_WINDOWS;
1054 }
1055 else if (StriSubCmp(L"ipxe", NameClues)) {
1056 Entry->OSType = 'N';
1057 ShortcutLetter = 'N';
1058 MergeStrings(&OSIconName, L"network", L',');
1059 }
1060
1061 if ((ShortcutLetter >= 'a') && (ShortcutLetter <= 'z'))
1062 ShortcutLetter = ShortcutLetter - 'a' + 'A'; // convert lowercase to uppercase
1063 Entry->me.ShortcutLetter = ShortcutLetter;
1064 if (Entry->me.Image == NULL)
1065 Entry->me.Image = LoadOSIcon(OSIconName, L"unknown", FALSE);
1066 MyFreePool(PathOnly);
1067 } // VOID SetLoaderDefaults()
1068
1069 // Add a specified EFI boot loader to the list, using automatic settings
1070 // for icons, options, etc.
1071 static LOADER_ENTRY * AddLoaderEntry(IN CHAR16 *LoaderPath, IN CHAR16 *LoaderTitle, IN REFIT_VOLUME *Volume, IN BOOLEAN SubScreenReturn) {
1072 LOADER_ENTRY *Entry;
1073
1074 CleanUpPathNameSlashes(LoaderPath);
1075 Entry = InitializeLoaderEntry(NULL);
1076 if (Entry != NULL) {
1077 Entry->Title = StrDuplicate((LoaderTitle != NULL) ? LoaderTitle : LoaderPath);
1078 Entry->me.Title = AllocateZeroPool(sizeof(CHAR16) * 256);
1079 // Extra space at end of Entry->me.Title enables searching on Volume->VolName even if another volume
1080 // name is identical except for something added to the end (e.g., VolB1 vs. VolB12).
1081 // Note: Volume->VolName will be NULL for network boot programs.
1082 if (Volume->VolName)
1083 SPrint(Entry->me.Title, 255, L"Boot %s from %s ", (LoaderTitle != NULL) ? LoaderTitle : LoaderPath, Volume->VolName);
1084 else
1085 SPrint(Entry->me.Title, 255, L"Boot %s ", (LoaderTitle != NULL) ? LoaderTitle : LoaderPath);
1086 Entry->me.Row = 0;
1087 Entry->me.BadgeImage = Volume->VolBadgeImage;
1088 if ((LoaderPath != NULL) && (LoaderPath[0] != L'\\')) {
1089 Entry->LoaderPath = StrDuplicate(L"\\");
1090 } else {
1091 Entry->LoaderPath = NULL;
1092 }
1093 MergeStrings(&(Entry->LoaderPath), LoaderPath, 0);
1094 Entry->VolName = Volume->VolName;
1095 Entry->DevicePath = FileDevicePath(Volume->DeviceHandle, Entry->LoaderPath);
1096 SetLoaderDefaults(Entry, LoaderPath, Volume);
1097 GenerateSubScreen(Entry, Volume, SubScreenReturn);
1098 AddMenuEntry(&MainMenu, (REFIT_MENU_ENTRY *)Entry);
1099 }
1100
1101 return(Entry);
1102 } // LOADER_ENTRY * AddLoaderEntry()
1103
1104 // Add a Linux kernel as a submenu entry for another (pre-existing) Linux kernel entry.
1105 static VOID AddKernelToSubmenu(LOADER_ENTRY * TargetLoader, CHAR16 *FileName, REFIT_VOLUME *Volume) {
1106 REFIT_FILE *File;
1107 CHAR16 **TokenList = NULL, *InitrdName, *SubmenuName = NULL, *VolName = NULL, *Path = NULL, *Title;
1108 REFIT_MENU_SCREEN *SubScreen;
1109 LOADER_ENTRY *SubEntry;
1110 UINTN TokenCount;
1111
1112 File = ReadLinuxOptionsFile(TargetLoader->LoaderPath, Volume);
1113 if (File != NULL) {
1114 SubScreen = TargetLoader->me.SubScreen;
1115 InitrdName = FindInitrd(FileName, Volume);
1116 while ((TokenCount = ReadTokenLine(File, &TokenList)) > 1) {
1117 SubEntry = InitializeLoaderEntry(TargetLoader);
1118 SplitPathName(FileName, &VolName, &Path, &SubmenuName);
1119 MergeStrings(&SubmenuName, L": ", '\0');
1120 MergeStrings(&SubmenuName, TokenList[0] ? StrDuplicate(TokenList[0]) : StrDuplicate(L"Boot Linux"), '\0');
1121 Title = StrDuplicate(SubmenuName);
1122 LimitStringLength(Title, MAX_LINE_LENGTH);
1123 SubEntry->me.Title = Title;
1124 MyFreePool(SubEntry->LoadOptions);
1125 SubEntry->LoadOptions = AddInitrdToOptions(TokenList[1], InitrdName);
1126 MyFreePool(SubEntry->LoaderPath);
1127 SubEntry->LoaderPath = StrDuplicate(FileName);
1128 CleanUpPathNameSlashes(SubEntry->LoaderPath);
1129 SubEntry->DevicePath = FileDevicePath(Volume->DeviceHandle, SubEntry->LoaderPath);
1130 FreeTokenLine(&TokenList, &TokenCount);
1131 SubEntry->UseGraphicsMode = GlobalConfig.GraphicsFor & GRAPHICS_FOR_LINUX;
1132 AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry);
1133 } // while
1134 MyFreePool(VolName);
1135 MyFreePool(Path);
1136 MyFreePool(SubmenuName);
1137 MyFreePool(InitrdName);
1138 MyFreePool(File);
1139 } // if
1140 } // static VOID AddKernelToSubmenu()
1141
1142 // Returns -1 if (Time1 < Time2), +1 if (Time1 > Time2), or 0 if
1143 // (Time1 == Time2). Precision is only to the nearest second; since
1144 // this is used for sorting boot loader entries, differences smaller
1145 // than this are likely to be meaningless (and unlikely!).
1146 INTN TimeComp(IN EFI_TIME *Time1, IN EFI_TIME *Time2) {
1147 INT64 Time1InSeconds, Time2InSeconds;
1148
1149 // Following values are overestimates; I'm assuming 31 days in every month.
1150 // This is fine for the purpose of this function, which is limited
1151 Time1InSeconds = Time1->Second + (Time1->Minute * 60) + (Time1->Hour * 3600) + (Time1->Day * 86400) +
1152 (Time1->Month * 2678400) + ((Time1->Year - 1998) * 32140800);
1153 Time2InSeconds = Time2->Second + (Time2->Minute * 60) + (Time2->Hour * 3600) + (Time2->Day * 86400) +
1154 (Time2->Month * 2678400) + ((Time2->Year - 1998) * 32140800);
1155 if (Time1InSeconds < Time2InSeconds)
1156 return (-1);
1157 else if (Time1InSeconds > Time2InSeconds)
1158 return (1);
1159
1160 return 0;
1161 } // INTN TimeComp()
1162
1163 // Adds a loader list element, keeping it sorted by date. Returns the new
1164 // first element (the one with the most recent date).
1165 static struct LOADER_LIST * AddLoaderListEntry(struct LOADER_LIST *LoaderList, struct LOADER_LIST *NewEntry) {
1166 struct LOADER_LIST *LatestEntry, *CurrentEntry, *PrevEntry = NULL;
1167
1168 LatestEntry = CurrentEntry = LoaderList;
1169 if (LoaderList == NULL) {
1170 LatestEntry = NewEntry;
1171 } else {
1172 while ((CurrentEntry != NULL) && (TimeComp(&(NewEntry->TimeStamp), &(CurrentEntry->TimeStamp)) < 0)) {
1173 PrevEntry = CurrentEntry;
1174 CurrentEntry = CurrentEntry->NextEntry;
1175 } // while
1176 NewEntry->NextEntry = CurrentEntry;
1177 if (PrevEntry == NULL) {
1178 LatestEntry = NewEntry;
1179 } else {
1180 PrevEntry->NextEntry = NewEntry;
1181 } // if/else
1182 } // if/else
1183 return (LatestEntry);
1184 } // static VOID AddLoaderListEntry()
1185
1186 // Delete the LOADER_LIST linked list
1187 static VOID CleanUpLoaderList(struct LOADER_LIST *LoaderList) {
1188 struct LOADER_LIST *Temp;
1189
1190 while (LoaderList != NULL) {
1191 Temp = LoaderList;
1192 LoaderList = LoaderList->NextEntry;
1193 MyFreePool(Temp->FileName);
1194 MyFreePool(Temp);
1195 } // while
1196 } // static VOID CleanUpLoaderList()
1197
1198 // Returns FALSE if the specified file/volume matches the GlobalConfig.DontScanDirs
1199 // or GlobalConfig.DontScanVolumes specification, or if Path points to a volume
1200 // other than the one specified by Volume, or if the specified path is SelfDir.
1201 // Returns TRUE if none of these conditions is met -- that is, if the path is
1202 // eligible for scanning.
1203 static BOOLEAN ShouldScan(REFIT_VOLUME *Volume, CHAR16 *Path) {
1204 CHAR16 *VolName = NULL, *DontScanDir, *PathCopy = NULL;
1205 UINTN i = 0;
1206 BOOLEAN ScanIt = TRUE;
1207
1208 if ((IsIn(Volume->VolName, GlobalConfig.DontScanVolumes)) || (IsIn(Volume->PartName, GlobalConfig.DontScanVolumes)))
1209 return FALSE;
1210
1211 if (MyStriCmp(Path, SelfDirPath) && (Volume->DeviceHandle == SelfVolume->DeviceHandle))
1212 return FALSE;
1213
1214 // See if Path includes an explicit volume declaration that's NOT Volume....
1215 PathCopy = StrDuplicate(Path);
1216 if (SplitVolumeAndFilename(&PathCopy, &VolName)) {
1217 VolumeNumberToName(Volume, &VolName);
1218 if (VolName && !MyStriCmp(VolName, Volume->VolName)) {
1219 ScanIt = FALSE;
1220 } // if
1221 } // if Path includes volume specification
1222 MyFreePool(PathCopy);
1223 MyFreePool(VolName);
1224 VolName = NULL;
1225
1226 // See if Volume is in GlobalConfig.DontScanDirs....
1227 while (ScanIt && (DontScanDir = FindCommaDelimited(GlobalConfig.DontScanDirs, i++))) {
1228 SplitVolumeAndFilename(&DontScanDir, &VolName);
1229 CleanUpPathNameSlashes(DontScanDir);
1230 VolumeNumberToName(Volume, &VolName);
1231 if (VolName != NULL) {
1232 if (MyStriCmp(VolName, Volume->VolName) && MyStriCmp(DontScanDir, Path))
1233 ScanIt = FALSE;
1234 } else {
1235 if (MyStriCmp(DontScanDir, Path))
1236 ScanIt = FALSE;
1237 }
1238 MyFreePool(DontScanDir);
1239 MyFreePool(VolName);
1240 DontScanDir = NULL;
1241 VolName = NULL;
1242 } // while()
1243
1244 return ScanIt;
1245 } // BOOLEAN ShouldScan()
1246
1247 // Returns TRUE if the file is byte-for-byte identical with the fallback file
1248 // on the volume AND if the file is not itself the fallback file; returns
1249 // FALSE if the file is not identical to the fallback file OR if the file
1250 // IS the fallback file. Intended for use in excluding the fallback boot
1251 // loader when it's a duplicate of another boot loader.
1252 static BOOLEAN DuplicatesFallback(IN REFIT_VOLUME *Volume, IN CHAR16 *FileName) {
1253 CHAR8 *FileContents, *FallbackContents;
1254 EFI_FILE_HANDLE FileHandle, FallbackHandle;
1255 EFI_FILE_INFO *FileInfo, *FallbackInfo;
1256 UINTN FileSize = 0, FallbackSize = 0;
1257 EFI_STATUS Status;
1258 BOOLEAN AreIdentical = FALSE;
1259
1260 if (!FileExists(Volume->RootDir, FileName) || !FileExists(Volume->RootDir, FALLBACK_FULLNAME))
1261 return FALSE;
1262
1263 CleanUpPathNameSlashes(FileName);
1264
1265 if (MyStriCmp(FileName, FALLBACK_FULLNAME))
1266 return FALSE; // identical filenames, so not a duplicate....
1267
1268 Status = refit_call5_wrapper(Volume->RootDir->Open, Volume->RootDir, &FileHandle, FileName, EFI_FILE_MODE_READ, 0);
1269 if (Status == EFI_SUCCESS) {
1270 FileInfo = LibFileInfo(FileHandle);
1271 FileSize = FileInfo->FileSize;
1272 } else {
1273 return FALSE;
1274 }
1275
1276 Status = refit_call5_wrapper(Volume->RootDir->Open, Volume->RootDir, &FallbackHandle, FALLBACK_FULLNAME, EFI_FILE_MODE_READ, 0);
1277 if (Status == EFI_SUCCESS) {
1278 FallbackInfo = LibFileInfo(FallbackHandle);
1279 FallbackSize = FallbackInfo->FileSize;
1280 } else {
1281 refit_call1_wrapper(FileHandle->Close, FileHandle);
1282 return FALSE;
1283 }
1284
1285 if (FallbackSize != FileSize) { // not same size, so can't be identical
1286 AreIdentical = FALSE;
1287 } else { // could be identical; do full check....
1288 FileContents = AllocatePool(FileSize);
1289 FallbackContents = AllocatePool(FallbackSize);
1290 if (FileContents && FallbackContents) {
1291 Status = refit_call3_wrapper(FileHandle->Read, FileHandle, &FileSize, FileContents);
1292 if (Status == EFI_SUCCESS) {
1293 Status = refit_call3_wrapper(FallbackHandle->Read, FallbackHandle, &FallbackSize, FallbackContents);
1294 }
1295 if (Status == EFI_SUCCESS) {
1296 AreIdentical = (CompareMem(FileContents, FallbackContents, FileSize) == 0);
1297 } // if
1298 } // if
1299 MyFreePool(FileContents);
1300 MyFreePool(FallbackContents);
1301 } // if/else
1302
1303 // BUG ALERT: Some systems (e.g., DUET, some Macs with large displays) crash if the
1304 // following two calls are reversed. Go figure....
1305 refit_call1_wrapper(FileHandle->Close, FallbackHandle);
1306 refit_call1_wrapper(FileHandle->Close, FileHandle);
1307 return AreIdentical;
1308 } // BOOLEAN DuplicatesFallback()
1309
1310 // Returns FALSE if two measures of file size are identical for a single file,
1311 // TRUE if not or if the file can't be opened and the other measure is non-0.
1312 // Despite the function's name, this isn't really a direct test of symbolic
1313 // link status, since EFI doesn't officially support symlinks. It does seem
1314 // to be a reliable indicator, though. (OTOH, some disk errors might cause a
1315 // file to fail to open, which would return a false positive -- but as I use
1316 // this function to exclude symbolic links from the list of boot loaders,
1317 // that would be fine, since such boot loaders wouldn't work.)
1318 static BOOLEAN IsSymbolicLink(REFIT_VOLUME *Volume, CHAR16 *Path, EFI_FILE_INFO *DirEntry) {
1319 EFI_FILE_HANDLE FileHandle;
1320 EFI_FILE_INFO *FileInfo = NULL;
1321 EFI_STATUS Status;
1322 UINTN FileSize2 = 0;
1323 CHAR16 *FileName;
1324
1325 FileName = StrDuplicate(Path);
1326 MergeStrings(&FileName, DirEntry->FileName, L'\\');
1327 CleanUpPathNameSlashes(FileName);
1328
1329 Status = refit_call5_wrapper(Volume->RootDir->Open, Volume->RootDir, &FileHandle, FileName, EFI_FILE_MODE_READ, 0);
1330 if (Status == EFI_SUCCESS) {
1331 FileInfo = LibFileInfo(FileHandle);
1332 if (FileInfo != NULL)
1333 FileSize2 = FileInfo->FileSize;
1334 }
1335
1336 MyFreePool(FileName);
1337 MyFreePool(FileInfo);
1338
1339 return (DirEntry->FileSize != FileSize2);
1340 } // BOOLEAN IsSymbolicLink()
1341
1342 // Returns TRUE if a file with the same name as the original but with
1343 // ".efi.signed" is also present in the same directory. Ubuntu is using
1344 // this filename as a signed version of the original unsigned kernel, and
1345 // there's no point in cluttering the display with two kernels that will
1346 // behave identically on non-SB systems, or when one will fail when SB
1347 // is active.
1348 static BOOLEAN HasSignedCounterpart(IN REFIT_VOLUME *Volume, IN CHAR16 *Path, IN CHAR16 *Filename) {
1349 CHAR16 *NewFile = NULL;
1350 BOOLEAN retval = FALSE;
1351
1352 MergeStrings(&NewFile, Path, 0);
1353 MergeStrings(&NewFile, Filename, L'\\');
1354 MergeStrings(&NewFile, L".efi.signed", 0);
1355 if (NewFile != NULL) {
1356 CleanUpPathNameSlashes(NewFile);
1357 if (FileExists(Volume->RootDir, NewFile))
1358 retval = TRUE;
1359 MyFreePool(NewFile);
1360 } // if
1361
1362 return retval;
1363 } // BOOLEAN HasSignedCounterpart()
1364
1365 // Scan an individual directory for EFI boot loader files and, if found,
1366 // add them to the list. Exception: Ignores FALLBACK_FULLNAME, which is picked
1367 // up in ScanEfiFiles(). Sorts the entries within the loader directory so that
1368 // the most recent one appears first in the list.
1369 // Returns TRUE if a duplicate for FALLBACK_FILENAME was found, FALSE if not.
1370 static BOOLEAN ScanLoaderDir(IN REFIT_VOLUME *Volume, IN CHAR16 *Path, IN CHAR16 *Pattern)
1371 {
1372 EFI_STATUS Status;
1373 REFIT_DIR_ITER DirIter;
1374 EFI_FILE_INFO *DirEntry;
1375 CHAR16 FileName[256], *Extension;
1376 struct LOADER_LIST *LoaderList = NULL, *NewLoader;
1377 LOADER_ENTRY *FirstKernel = NULL, *LatestEntry = NULL;
1378 BOOLEAN FoundFallbackDuplicate = FALSE, IsLinux = FALSE, InSelfPath;
1379
1380 InSelfPath = MyStriCmp(Path, SelfDirPath);
1381 if ((!SelfDirPath || !Path || (InSelfPath && (Volume->DeviceHandle != SelfVolume->DeviceHandle)) ||
1382 (!InSelfPath)) && (ShouldScan(Volume, Path))) {
1383 // look through contents of the directory
1384 DirIterOpen(Volume->RootDir, Path, &DirIter);
1385 while (DirIterNext(&DirIter, 2, Pattern, &DirEntry)) {
1386 Extension = FindExtension(DirEntry->FileName);
1387 if (DirEntry->FileName[0] == '.' ||
1388 MyStriCmp(Extension, L".icns") ||
1389 MyStriCmp(Extension, L".png") ||
1390 (MyStriCmp(DirEntry->FileName, FALLBACK_BASENAME) && (MyStriCmp(Path, L"EFI\\BOOT"))) ||
1391 StriSubCmp(L"shell", DirEntry->FileName) ||
1392 IsSymbolicLink(Volume, Path, DirEntry) || /* is symbolic link */
1393 HasSignedCounterpart(Volume, Path, DirEntry->FileName) || /* a file with same name plus ".efi.signed" is present */
1394 FilenameIn(Volume, Path, DirEntry->FileName, GlobalConfig.DontScanFiles))
1395 continue; // skip this
1396
1397 if (Path)
1398 SPrint(FileName, 255, L"\\%s\\%s", Path, DirEntry->FileName);
1399 else
1400 SPrint(FileName, 255, L"\\%s", DirEntry->FileName);
1401 CleanUpPathNameSlashes(FileName);
1402
1403 if(!IsValidLoader(Volume->RootDir, FileName))
1404 continue;
1405
1406 NewLoader = AllocateZeroPool(sizeof(struct LOADER_LIST));
1407 if (NewLoader != NULL) {
1408 NewLoader->FileName = StrDuplicate(FileName);
1409 NewLoader->TimeStamp = DirEntry->ModificationTime;
1410 LoaderList = AddLoaderListEntry(LoaderList, NewLoader);
1411 if (DuplicatesFallback(Volume, FileName))
1412 FoundFallbackDuplicate = TRUE;
1413 } // if
1414 MyFreePool(Extension);
1415 } // while
1416
1417 NewLoader = LoaderList;
1418 while (NewLoader != NULL) {
1419 IsLinux = (StriSubCmp(L"bzImage", NewLoader->FileName) || StriSubCmp(L"vmlinuz", NewLoader->FileName));
1420 if ((FirstKernel != NULL) && IsLinux && GlobalConfig.FoldLinuxKernels) {
1421 AddKernelToSubmenu(FirstKernel, NewLoader->FileName, Volume);
1422 } else {
1423 LatestEntry = AddLoaderEntry(NewLoader->FileName, NULL, Volume, !(IsLinux && GlobalConfig.FoldLinuxKernels));
1424 if (IsLinux && (FirstKernel == NULL))
1425 FirstKernel = LatestEntry;
1426 }
1427 NewLoader = NewLoader->NextEntry;
1428 } // while
1429 if ((NewLoader != NULL) && (FirstKernel != NULL) && IsLinux && GlobalConfig.FoldLinuxKernels)
1430 AddMenuEntry(FirstKernel->me.SubScreen, &MenuEntryReturn);
1431
1432 CleanUpLoaderList(LoaderList);
1433 Status = DirIterClose(&DirIter);
1434 // NOTE: EFI_INVALID_PARAMETER really is an error that should be reported;
1435 // but I've gotten reports from users who are getting this error occasionally
1436 // and I can't find anything wrong or reproduce the problem, so I'm putting
1437 // it down to buggy EFI implementations and ignoring that particular error....
1438 if ((Status != EFI_NOT_FOUND) && (Status != EFI_INVALID_PARAMETER)) {
1439 if (Path)
1440 SPrint(FileName, 255, L"while scanning the %s directory", Path);
1441 else
1442 StrCpy(FileName, L"while scanning the root directory");
1443 CheckError(Status, FileName);
1444 } // if (Status != EFI_NOT_FOUND)
1445 } // if not scanning a blacklisted directory
1446
1447 return FoundFallbackDuplicate;
1448 } /* static VOID ScanLoaderDir() */
1449
1450 // Run the IPXE_DISCOVER_NAME program, which obtains the IP address of the boot
1451 // server and the name of the boot file it delivers.
1452 CHAR16* RuniPXEDiscover(EFI_HANDLE Volume)
1453 {
1454 EFI_STATUS Status;
1455 EFI_DEVICE_PATH *FilePath;
1456 EFI_HANDLE iPXEHandle;
1457 CHAR16 *boot_info = NULL;
1458 UINTN boot_info_size = 0;
1459
1460 FilePath = FileDevicePath (Volume, IPXE_DISCOVER_NAME);
1461 Status = refit_call6_wrapper(BS->LoadImage, FALSE, SelfImageHandle, FilePath, NULL, 0, &iPXEHandle);
1462 if (Status != 0)
1463 return NULL;
1464
1465 Status = refit_call3_wrapper(BS->StartImage, iPXEHandle, &boot_info_size, &boot_info);
1466
1467 return boot_info;
1468 } // RuniPXEDiscover()
1469
1470 // Scan for network (PXE) boot servers. This function relies on the presence
1471 // of the IPXE_DISCOVER_NAME and IPXE_NAME program files on the volume from
1472 // which rEFInd launched. As of December 6, 2014, these tools aren't entirely
1473 // reliable. See BUILDING.txt for information on building them.
1474 static VOID ScanNetboot() {
1475 CHAR16 *iPXEFileName = IPXE_NAME;
1476 CHAR16 *Location;
1477 REFIT_VOLUME *NetVolume;
1478
1479 if (FileExists(SelfVolume->RootDir, IPXE_DISCOVER_NAME) &&
1480 FileExists(SelfVolume->RootDir, IPXE_NAME) &&
1481 IsValidLoader(SelfVolume->RootDir, IPXE_DISCOVER_NAME) &&
1482 IsValidLoader(SelfVolume->RootDir, IPXE_NAME)) {
1483 Location = RuniPXEDiscover(SelfVolume->DeviceHandle);
1484 if (Location != NULL && FileExists(SelfVolume->RootDir, iPXEFileName)) {
1485 NetVolume = AllocatePool(sizeof(REFIT_VOLUME));
1486 CopyMem(NetVolume, SelfVolume, sizeof(REFIT_VOLUME));
1487 NetVolume->DiskKind = DISK_KIND_NET;
1488 NetVolume->VolBadgeImage = BuiltinIcon(BUILTIN_ICON_VOL_NET);
1489 NetVolume->PartName = NetVolume->VolName = NULL;
1490 AddLoaderEntry(iPXEFileName, Location, NetVolume, TRUE);
1491 MyFreePool(NetVolume);
1492 } // if support files exist and are valid
1493 }
1494 } // VOID ScanNetBoot()
1495
1496 static VOID ScanEfiFiles(REFIT_VOLUME *Volume) {
1497 EFI_STATUS Status;
1498 REFIT_DIR_ITER EfiDirIter;
1499 EFI_FILE_INFO *EfiDirEntry;
1500 CHAR16 FileName[256], *Directory = NULL, *MatchPatterns, *VolName = NULL, *SelfPath;
1501 UINTN i, Length;
1502 BOOLEAN ScanFallbackLoader = TRUE;
1503 BOOLEAN FoundBRBackup = FALSE;
1504
1505 if (Volume && (Volume->RootDir != NULL) && (Volume->VolName != NULL) && (Volume->IsReadable)) {
1506 MatchPatterns = StrDuplicate(LOADER_MATCH_PATTERNS);
1507 if (GlobalConfig.ScanAllLinux)
1508 MergeStrings(&MatchPatterns, LINUX_MATCH_PATTERNS, L',');
1509
1510 // check for Mac OS X boot loader
1511 if (ShouldScan(Volume, MACOSX_LOADER_DIR)) {
1512 StrCpy(FileName, MACOSX_LOADER_PATH);
1513 if (FileExists(Volume->RootDir, FileName) && !FilenameIn(Volume, MACOSX_LOADER_DIR, L"boot.efi", GlobalConfig.DontScanFiles)) {
1514 AddLoaderEntry(FileName, L"Mac OS X", Volume, TRUE);
1515 if (DuplicatesFallback(Volume, FileName))
1516 ScanFallbackLoader = FALSE;
1517 }
1518
1519 // check for XOM
1520 StrCpy(FileName, L"System\\Library\\CoreServices\\xom.efi");
1521 if (FileExists(Volume->RootDir, FileName) && !FilenameIn(Volume, MACOSX_LOADER_DIR, L"xom.efi", GlobalConfig.DontScanFiles)) {
1522 AddLoaderEntry(FileName, L"Windows XP (XoM)", Volume, TRUE);
1523 if (DuplicatesFallback(Volume, FileName))
1524 ScanFallbackLoader = FALSE;
1525 }
1526 } // if should scan Mac directory
1527
1528 // check for Microsoft boot loader/menu
1529 if (ShouldScan(Volume, L"EFI\\Microsoft\\Boot")) {
1530 StrCpy(FileName, L"EFI\\Microsoft\\Boot\\bkpbootmgfw.efi");
1531 if (FileExists(Volume->RootDir, FileName) && !FilenameIn(Volume, L"EFI\\Microsoft\\Boot", L"bkpbootmgfw.efi",
1532 GlobalConfig.DontScanFiles)) {
1533 AddLoaderEntry(FileName, L"Microsoft EFI boot (Boot Repair backup)", Volume, TRUE);
1534 FoundBRBackup = TRUE;
1535 if (DuplicatesFallback(Volume, FileName))
1536 ScanFallbackLoader = FALSE;
1537 }
1538 StrCpy(FileName, L"EFI\\Microsoft\\Boot\\bootmgfw.efi");
1539 if (FileExists(Volume->RootDir, FileName) &&
1540 !FilenameIn(Volume, L"EFI\\Microsoft\\Boot", L"bootmgfw.efi", GlobalConfig.DontScanFiles)) {
1541 if (FoundBRBackup)
1542 AddLoaderEntry(FileName, L"Supposed Microsoft EFI boot (probably GRUB)", Volume, TRUE);
1543 else
1544 AddLoaderEntry(FileName, L"Microsoft EFI boot", Volume, TRUE);
1545 if (DuplicatesFallback(Volume, FileName))
1546 ScanFallbackLoader = FALSE;
1547 }
1548 } // if
1549
1550 // scan the root directory for EFI executables
1551 if (ScanLoaderDir(Volume, L"\\", MatchPatterns))
1552 ScanFallbackLoader = FALSE;
1553
1554 // scan subdirectories of the EFI directory (as per the standard)
1555 DirIterOpen(Volume->RootDir, L"EFI", &EfiDirIter);
1556 while (DirIterNext(&EfiDirIter, 1, NULL, &EfiDirEntry)) {
1557 if (MyStriCmp(EfiDirEntry->FileName, L"tools") || EfiDirEntry->FileName[0] == '.')
1558 continue; // skip this, doesn't contain boot loaders or is scanned later
1559 SPrint(FileName, 255, L"EFI\\%s", EfiDirEntry->FileName);
1560 if (ScanLoaderDir(Volume, FileName, MatchPatterns))
1561 ScanFallbackLoader = FALSE;
1562 } // while()
1563 Status = DirIterClose(&EfiDirIter);
1564 if ((Status != EFI_NOT_FOUND) && (Status != EFI_INVALID_PARAMETER))
1565 CheckError(Status, L"while scanning the EFI directory");
1566
1567 // Scan user-specified (or additional default) directories....
1568 i = 0;
1569 while ((Directory = FindCommaDelimited(GlobalConfig.AlsoScan, i++)) != NULL) {
1570 if (ShouldScan(Volume, Directory)) {
1571 SplitVolumeAndFilename(&Directory, &VolName);
1572 CleanUpPathNameSlashes(Directory);
1573 Length = StrLen(Directory);
1574 if ((Length > 0) && ScanLoaderDir(Volume, Directory, MatchPatterns))
1575 ScanFallbackLoader = FALSE;
1576 MyFreePool(VolName);
1577 } // if should scan dir
1578 MyFreePool(Directory);
1579 } // while
1580
1581 // Don't scan the fallback loader if it's on the same volume and a duplicate of rEFInd itself....
1582 SelfPath = DevicePathToStr(SelfLoadedImage->FilePath);
1583 CleanUpPathNameSlashes(SelfPath);
1584 if ((Volume->DeviceHandle == SelfLoadedImage->DeviceHandle) && DuplicatesFallback(Volume, SelfPath))
1585 ScanFallbackLoader = FALSE;
1586
1587 // If not a duplicate & if it exists & if it's not us, create an entry
1588 // for the fallback boot loader
1589 if (ScanFallbackLoader && FileExists(Volume->RootDir, FALLBACK_FULLNAME) && ShouldScan(Volume, L"EFI\\BOOT")) {
1590 AddLoaderEntry(FALLBACK_FULLNAME, L"Fallback boot loader", Volume, TRUE);
1591 }
1592 } // if
1593 } // static VOID ScanEfiFiles()
1594
1595 // Scan internal disks for valid EFI boot loaders....
1596 static VOID ScanInternal(VOID) {
1597 UINTN VolumeIndex;
1598
1599 for (VolumeIndex = 0; VolumeIndex < VolumesCount; VolumeIndex++) {
1600 if (Volumes[VolumeIndex]->DiskKind == DISK_KIND_INTERNAL) {
1601 ScanEfiFiles(Volumes[VolumeIndex]);
1602 }
1603 } // for
1604 } // static VOID ScanInternal()
1605
1606 // Scan external disks for valid EFI boot loaders....
1607 static VOID ScanExternal(VOID) {
1608 UINTN VolumeIndex;
1609
1610 for (VolumeIndex = 0; VolumeIndex < VolumesCount; VolumeIndex++) {
1611 if (Volumes[VolumeIndex]->DiskKind == DISK_KIND_EXTERNAL) {
1612 ScanEfiFiles(Volumes[VolumeIndex]);
1613 }
1614 } // for
1615 } // static VOID ScanExternal()
1616
1617 // Scan internal disks for valid EFI boot loaders....
1618 static VOID ScanOptical(VOID) {
1619 UINTN VolumeIndex;
1620
1621 for (VolumeIndex = 0; VolumeIndex < VolumesCount; VolumeIndex++) {
1622 if (Volumes[VolumeIndex]->DiskKind == DISK_KIND_OPTICAL) {
1623 ScanEfiFiles(Volumes[VolumeIndex]);
1624 }
1625 } // for
1626 } // static VOID ScanOptical()
1627
1628 // default volume badge icon based on disk kind
1629 EG_IMAGE * GetDiskBadge(IN UINTN DiskType) {
1630 EG_IMAGE * Badge = NULL;
1631
1632 switch (DiskType) {
1633 case BBS_HARDDISK:
1634 Badge = BuiltinIcon(BUILTIN_ICON_VOL_INTERNAL);
1635 break;
1636 case BBS_USB:
1637 Badge = BuiltinIcon(BUILTIN_ICON_VOL_EXTERNAL);
1638 break;
1639 case BBS_CDROM:
1640 Badge = BuiltinIcon(BUILTIN_ICON_VOL_OPTICAL);
1641 break;
1642 } // switch()
1643 return Badge;
1644 } // EG_IMAGE * GetDiskBadge()
1645
1646 //
1647 // pre-boot tool functions
1648 //
1649
1650 static VOID StartTool(IN LOADER_ENTRY *Entry)
1651 {
1652 BeginExternalScreen(Entry->UseGraphicsMode, Entry->me.Title + 6); // assumes "Start <title>" as assigned below
1653 StoreLoaderName(Entry->me.Title);
1654 StartEFIImage(Entry->DevicePath, Entry->LoadOptions, TYPE_EFI,
1655 Basename(Entry->LoaderPath), Entry->OSType, NULL, TRUE, FALSE);
1656 FinishExternalScreen();
1657 } /* static VOID StartTool() */
1658
1659 static LOADER_ENTRY * AddToolEntry(EFI_HANDLE DeviceHandle, IN CHAR16 *LoaderPath, IN CHAR16 *LoaderTitle, IN EG_IMAGE *Image,
1660 IN CHAR16 ShortcutLetter, IN BOOLEAN UseGraphicsMode)
1661 {
1662 LOADER_ENTRY *Entry;
1663 CHAR16 *TitleStr = NULL;
1664
1665 Entry = AllocateZeroPool(sizeof(LOADER_ENTRY));
1666
1667 TitleStr = PoolPrint(L"Start %s", LoaderTitle);
1668 Entry->me.Title = TitleStr;
1669 Entry->me.Tag = TAG_TOOL;
1670 Entry->me.Row = 1;
1671 Entry->me.ShortcutLetter = ShortcutLetter;
1672 Entry->me.Image = Image;
1673 Entry->LoaderPath = (LoaderPath) ? StrDuplicate(LoaderPath) : NULL;
1674 Entry->DevicePath = FileDevicePath(DeviceHandle, Entry->LoaderPath);
1675 Entry->UseGraphicsMode = UseGraphicsMode;
1676
1677 AddMenuEntry(&MainMenu, (REFIT_MENU_ENTRY *)Entry);
1678 return Entry;
1679 } /* static LOADER_ENTRY * AddToolEntry() */
1680
1681 //
1682 // pre-boot driver functions
1683 //
1684
1685 static UINTN ScanDriverDir(IN CHAR16 *Path)
1686 {
1687 EFI_STATUS Status;
1688 REFIT_DIR_ITER DirIter;
1689 UINTN NumFound = 0;
1690 EFI_FILE_INFO *DirEntry;
1691 CHAR16 FileName[256];
1692
1693 CleanUpPathNameSlashes(Path);
1694 // look through contents of the directory
1695 DirIterOpen(SelfRootDir, Path, &DirIter);
1696 while (DirIterNext(&DirIter, 2, LOADER_MATCH_PATTERNS, &DirEntry)) {
1697 if (DirEntry->FileName[0] == '.')
1698 continue; // skip this
1699
1700 SPrint(FileName, 255, L"%s\\%s", Path, DirEntry->FileName);
1701 NumFound++;
1702 Status = StartEFIImage(FileDevicePath(SelfLoadedImage->DeviceHandle, FileName),
1703 L"", TYPE_EFI, DirEntry->FileName, 0, NULL, FALSE, TRUE);
1704 }
1705 Status = DirIterClose(&DirIter);
1706 if ((Status != EFI_NOT_FOUND) && (Status != EFI_INVALID_PARAMETER)) {
1707 SPrint(FileName, 255, L"while scanning the %s directory", Path);
1708 CheckError(Status, FileName);
1709 }
1710 return (NumFound);
1711 }
1712
1713 #ifdef __MAKEWITH_GNUEFI
1714 static EFI_STATUS ConnectAllDriversToAllControllers(VOID)
1715 {
1716 EFI_STATUS Status;
1717 UINTN AllHandleCount;
1718 EFI_HANDLE *AllHandleBuffer;
1719 UINTN Index;
1720 UINTN HandleCount;
1721 EFI_HANDLE *HandleBuffer;
1722 UINT32 *HandleType;
1723 UINTN HandleIndex;
1724 BOOLEAN Parent;
1725 BOOLEAN Device;
1726
1727 Status = LibLocateHandle(AllHandles,
1728 NULL,
1729 NULL,
1730 &AllHandleCount,
1731 &AllHandleBuffer);
1732 if (EFI_ERROR(Status))
1733 return Status;
1734
1735 for (Index = 0; Index < AllHandleCount; Index++) {
1736 //
1737 // Scan the handle database
1738 //
1739 Status = LibScanHandleDatabase(NULL,
1740 NULL,
1741 AllHandleBuffer[Index],
1742 NULL,
1743 &HandleCount,
1744 &HandleBuffer,
1745 &HandleType);
1746 if (EFI_ERROR (Status))
1747 goto Done;
1748
1749 Device = TRUE;
1750 if (HandleType[Index] & EFI_HANDLE_TYPE_DRIVER_BINDING_HANDLE)
1751 Device = FALSE;
1752 if (HandleType[Index] & EFI_HANDLE_TYPE_IMAGE_HANDLE)
1753 Device = FALSE;
1754
1755 if (Device) {
1756 Parent = FALSE;
1757 for (HandleIndex = 0; HandleIndex < HandleCount; HandleIndex++) {
1758 if (HandleType[HandleIndex] & EFI_HANDLE_TYPE_PARENT_HANDLE)
1759 Parent = TRUE;
1760 } // for
1761
1762 if (!Parent) {
1763 if (HandleType[Index] & EFI_HANDLE_TYPE_DEVICE_HANDLE) {
1764 Status = refit_call4_wrapper(BS->ConnectController,
1765 AllHandleBuffer[Index],
1766 NULL,
1767 NULL,
1768 TRUE);
1769 }
1770 }
1771 }
1772
1773 MyFreePool (HandleBuffer);
1774 MyFreePool (HandleType);
1775 }
1776
1777 Done:
1778 MyFreePool (AllHandleBuffer);
1779 return Status;
1780 } /* EFI_STATUS ConnectAllDriversToAllControllers() */
1781 #else
1782 static EFI_STATUS ConnectAllDriversToAllControllers(VOID) {
1783 BdsLibConnectAllDriversToAllControllers();
1784 return 0;
1785 }
1786 #endif
1787
1788 // Load all EFI drivers from rEFInd's "drivers" subdirectory and from the
1789 // directories specified by the user in the "scan_driver_dirs" configuration
1790 // file line.
1791 static VOID LoadDrivers(VOID)
1792 {
1793 CHAR16 *Directory, *SelfDirectory;
1794 UINTN i = 0, Length, NumFound = 0;
1795
1796 // load drivers from the subdirectories of rEFInd's home directory specified
1797 // in the DRIVER_DIRS constant.
1798 while ((Directory = FindCommaDelimited(DRIVER_DIRS, i++)) != NULL) {
1799 SelfDirectory = SelfDirPath ? StrDuplicate(SelfDirPath) : NULL;
1800 CleanUpPathNameSlashes(SelfDirectory);
1801 MergeStrings(&SelfDirectory, Directory, L'\\');
1802 NumFound += ScanDriverDir(SelfDirectory);
1803 MyFreePool(Directory);
1804 MyFreePool(SelfDirectory);
1805 }
1806
1807 // Scan additional user-specified driver directories....
1808 i = 0;
1809 while ((Directory = FindCommaDelimited(GlobalConfig.DriverDirs, i++)) != NULL) {
1810 CleanUpPathNameSlashes(Directory);
1811 Length = StrLen(Directory);
1812 if (Length > 0) {
1813 NumFound += ScanDriverDir(Directory);
1814 } // if
1815 MyFreePool(Directory);
1816 } // while
1817
1818 // connect all devices
1819 if (NumFound > 0) {
1820 ConnectAllDriversToAllControllers();
1821 }
1822 } /* static VOID LoadDrivers() */
1823
1824 // Locates boot loaders. NOTE: This assumes that GlobalConfig.LegacyType is set correctly.
1825 static VOID ScanForBootloaders(VOID) {
1826 UINTN i;
1827 CHAR8 s;
1828 BOOLEAN ScanForLegacy = FALSE;
1829
1830 // Determine up-front if we'll be scanning for legacy loaders....
1831 for (i = 0; i < NUM_SCAN_OPTIONS; i++) {
1832 s = GlobalConfig.ScanFor[i];
1833 if ((s == 'c') || (s == 'C') || (s == 'h') || (s == 'H') || (s == 'b') || (s == 'B'))
1834 ScanForLegacy = TRUE;
1835 } // for
1836
1837 // If UEFI & scanning for legacy loaders & deep legacy scan, update NVRAM boot manager list
1838 if ((GlobalConfig.LegacyType == LEGACY_TYPE_UEFI) && ScanForLegacy && GlobalConfig.DeepLegacyScan) {
1839 BdsDeleteAllInvalidLegacyBootOptions();
1840 BdsAddNonExistingLegacyBootOptions();
1841 } // if
1842
1843 // scan for loaders and tools, add them to the menu
1844 for (i = 0; i < NUM_SCAN_OPTIONS; i++) {
1845 switch(GlobalConfig.ScanFor[i]) {
1846 case 'c': case 'C':
1847 ScanLegacyDisc();
1848 break;
1849 case 'h': case 'H':
1850 ScanLegacyInternal();
1851 break;
1852 case 'b': case 'B':
1853 ScanLegacyExternal();
1854 break;
1855 case 'm': case 'M':
1856 ScanUserConfigured(GlobalConfig.ConfigFilename);
1857 break;
1858 case 'e': case 'E':
1859 ScanExternal();
1860 break;
1861 case 'i': case 'I':
1862 ScanInternal();
1863 break;
1864 case 'o': case 'O':
1865 ScanOptical();
1866 break;
1867 case 'n': case 'N':
1868 ScanNetboot();
1869 break;
1870 } // switch()
1871 } // for
1872
1873 // assign shortcut keys
1874 for (i = 0; i < MainMenu.EntryCount && MainMenu.Entries[i]->Row == 0 && i < 9; i++)
1875 MainMenu.Entries[i]->ShortcutDigit = (CHAR16)('1' + i);
1876
1877 // wait for user ACK when there were errors
1878 FinishTextScreen(FALSE);
1879 } // static VOID ScanForBootloaders()
1880
1881 // Locate a single tool from the specified Locations using one of the
1882 // specified Names and add it to the menu.
1883 static VOID FindTool(CHAR16 *Locations, CHAR16 *Names, CHAR16 *Description, UINTN Icon) {
1884 UINTN j = 0, k, VolumeIndex;
1885 CHAR16 *DirName, *FileName, *PathName, FullDescription[256];
1886
1887 while ((DirName = FindCommaDelimited(Locations, j++)) != NULL) {
1888 k = 0;
1889 while ((FileName = FindCommaDelimited(Names, k++)) != NULL) {
1890 PathName = StrDuplicate(DirName);
1891 MergeStrings(&PathName, FileName, MyStriCmp(PathName, L"\\") ? 0 : L'\\');
1892 for (VolumeIndex = 0; VolumeIndex < VolumesCount; VolumeIndex++) {
1893 if ((Volumes[VolumeIndex]->RootDir != NULL) &&
1894 (FileExists(Volumes[VolumeIndex]->RootDir, PathName)) &&
1895 IsValidLoader(Volumes[VolumeIndex]->RootDir, PathName)) {
1896 SPrint(FullDescription, 255, L"%s at %s on %s", Description, PathName, Volumes[VolumeIndex]->VolName);
1897 AddToolEntry(Volumes[VolumeIndex]->DeviceHandle, PathName, FullDescription, BuiltinIcon(Icon), 'S', FALSE);
1898 } // if
1899 } // for
1900 MyFreePool(PathName);
1901 MyFreePool(FileName);
1902 } // while Names
1903 MyFreePool(DirName);
1904 } // while Locations
1905 } // VOID FindTool()
1906
1907 // Add the second-row tags containing built-in and external tools (EFI shell,
1908 // reboot, etc.)
1909 static VOID ScanForTools(VOID) {
1910 CHAR16 *FileName = NULL, *VolName = NULL, *MokLocations, Description[256];
1911 REFIT_MENU_ENTRY *TempMenuEntry;
1912 UINTN i, j, VolumeIndex;
1913 UINT64 osind;
1914 CHAR8 *b = 0;
1915 UINT32 CsrValue;
1916
1917 MokLocations = StrDuplicate(MOK_LOCATIONS);
1918 if (MokLocations != NULL)
1919 MergeStrings(&MokLocations, SelfDirPath, L',');
1920
1921 for (i = 0; i < NUM_TOOLS; i++) {
1922 switch(GlobalConfig.ShowTools[i]) {
1923 // NOTE: Be sure that FileName is NULL at the end of each case.
1924 case TAG_SHUTDOWN:
1925 TempMenuEntry = CopyMenuEntry(&MenuEntryShutdown);
1926 TempMenuEntry->Image = BuiltinIcon(BUILTIN_ICON_FUNC_SHUTDOWN);
1927 AddMenuEntry(&MainMenu, TempMenuEntry);
1928 break;
1929
1930 case TAG_REBOOT:
1931 TempMenuEntry = CopyMenuEntry(&MenuEntryReset);
1932 TempMenuEntry->Image = BuiltinIcon(BUILTIN_ICON_FUNC_RESET);
1933 AddMenuEntry(&MainMenu, TempMenuEntry);
1934 break;
1935
1936 case TAG_ABOUT:
1937 TempMenuEntry = CopyMenuEntry(&MenuEntryAbout);
1938 TempMenuEntry->Image = BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT);
1939 AddMenuEntry(&MainMenu, TempMenuEntry);
1940 break;
1941
1942 case TAG_EXIT:
1943 TempMenuEntry = CopyMenuEntry(&MenuEntryExit);
1944 TempMenuEntry->Image = BuiltinIcon(BUILTIN_ICON_FUNC_EXIT);
1945 AddMenuEntry(&MainMenu, TempMenuEntry);
1946 break;
1947
1948 case TAG_FIRMWARE:
1949 if (EfivarGetRaw(&GlobalGuid, L"OsIndicationsSupported", &b, &j) == EFI_SUCCESS) {
1950 osind = (UINT64)*b;
1951 if (osind & EFI_OS_INDICATIONS_BOOT_TO_FW_UI) {
1952 TempMenuEntry = CopyMenuEntry(&MenuEntryFirmware);
1953 TempMenuEntry->Image = BuiltinIcon(BUILTIN_ICON_FUNC_FIRMWARE);
1954 AddMenuEntry(&MainMenu, TempMenuEntry);
1955 } // if
1956 } // if
1957 break;
1958
1959 case TAG_SHELL:
1960 j = 0;
1961 while ((FileName = FindCommaDelimited(SHELL_NAMES, j++)) != NULL) {
1962 if (FileExists(SelfRootDir, FileName) && IsValidLoader(SelfRootDir, FileName)) {
1963 AddToolEntry(SelfLoadedImage->DeviceHandle, FileName, L"EFI Shell", BuiltinIcon(BUILTIN_ICON_TOOL_SHELL),
1964 'S', FALSE);
1965 }
1966 MyFreePool(FileName);
1967 } // while
1968 break;
1969
1970 case TAG_GPTSYNC:
1971 j = 0;
1972 while ((FileName = FindCommaDelimited(GPTSYNC_NAMES, j++)) != NULL) {
1973 if (FileExists(SelfRootDir, FileName) && IsValidLoader(SelfRootDir, FileName)) {
1974 AddToolEntry(SelfLoadedImage->DeviceHandle, FileName, L"Hybrid MBR tool", BuiltinIcon(BUILTIN_ICON_TOOL_PART),
1975 'P', FALSE);
1976 } // if
1977 MyFreePool(FileName);
1978 } // while
1979 FileName = NULL;
1980 break;
1981
1982 case TAG_GDISK:
1983 j = 0;
1984 while ((FileName = FindCommaDelimited(GDISK_NAMES, j++)) != NULL) {
1985 if (FileExists(SelfRootDir, FileName) && IsValidLoader(SelfRootDir, FileName)) {
1986 AddToolEntry(SelfLoadedImage->DeviceHandle, FileName, L"disk partitioning tool",
1987 BuiltinIcon(BUILTIN_ICON_TOOL_PART), 'G', FALSE);
1988 } // if
1989 MyFreePool(FileName);
1990 } // while
1991 FileName = NULL;
1992 break;
1993
1994 case TAG_NETBOOT:
1995 j = 0;
1996 while ((FileName = FindCommaDelimited(NETBOOT_NAMES, j++)) != NULL) {
1997 if (FileExists(SelfRootDir, FileName) && IsValidLoader(SelfRootDir, FileName)) {
1998 AddToolEntry(SelfLoadedImage->DeviceHandle, FileName, L"Netboot",
1999 BuiltinIcon(BUILTIN_ICON_TOOL_NETBOOT), 'N', FALSE);
2000 } // if
2001 MyFreePool(FileName);
2002 } // while
2003 FileName = NULL;
2004 break;
2005
2006 case TAG_APPLE_RECOVERY:
2007 FileName = StrDuplicate(L"\\com.apple.recovery.boot\\boot.efi");
2008 for (VolumeIndex = 0; VolumeIndex < VolumesCount; VolumeIndex++) {
2009 if ((Volumes[VolumeIndex]->RootDir != NULL) &&
2010 (FileExists(Volumes[VolumeIndex]->RootDir, FileName)) &&
2011 IsValidLoader(Volumes[VolumeIndex]->RootDir, FileName)) {
2012 SPrint(Description, 255, L"Apple Recovery on %s", Volumes[VolumeIndex]->VolName);
2013 AddToolEntry(Volumes[VolumeIndex]->DeviceHandle, FileName, Description,
2014 BuiltinIcon(BUILTIN_ICON_TOOL_APPLE_RESCUE), 'R', TRUE);
2015 } // if
2016 } // for
2017 MyFreePool(FileName);
2018 FileName = NULL;
2019 break;
2020
2021 case TAG_WINDOWS_RECOVERY:
2022 j = 0;
2023 while ((FileName = FindCommaDelimited(GlobalConfig.WindowsRecoveryFiles, j++)) != NULL) {
2024 SplitVolumeAndFilename(&FileName, &VolName);
2025 for (VolumeIndex = 0; VolumeIndex < VolumesCount; VolumeIndex++) {
2026 if ((Volumes[VolumeIndex]->RootDir != NULL) &&
2027 (FileExists(Volumes[VolumeIndex]->RootDir, FileName)) &&
2028 IsValidLoader(Volumes[VolumeIndex]->RootDir, FileName) &&
2029 ((VolName == NULL) || MyStriCmp(VolName, Volumes[VolumeIndex]->VolName))) {
2030 SPrint(Description, 255, L"Microsoft Recovery on %s", Volumes[VolumeIndex]->VolName);
2031 AddToolEntry(Volumes[VolumeIndex]->DeviceHandle, FileName, Description,
2032 BuiltinIcon(BUILTIN_ICON_TOOL_WINDOWS_RESCUE), 'R', TRUE);
2033 } // if
2034 } // for
2035 } // while
2036 MyFreePool(FileName);
2037 FileName = NULL;
2038 MyFreePool(VolName);
2039 VolName = NULL;
2040 break;
2041
2042 case TAG_MOK_TOOL:
2043 FindTool(MokLocations, MOK_NAMES, L"MOK utility", BUILTIN_ICON_TOOL_MOK_TOOL);
2044 break;
2045
2046 case TAG_CSR_ROTATE:
2047 if ((GetCsrStatus(&CsrValue) == EFI_SUCCESS) && (GlobalConfig.CsrValues)) {
2048 TempMenuEntry = CopyMenuEntry(&MenuEntryRotateCsr);
2049 TempMenuEntry->Image = BuiltinIcon(BUILTIN_ICON_FUNC_CSR_ROTATE);
2050 AddMenuEntry(&MainMenu, TempMenuEntry);
2051 } // if
2052 break;
2053
2054 case TAG_MEMTEST:
2055 FindTool(MEMTEST_LOCATIONS, MEMTEST_NAMES, L"Memory test utility", BUILTIN_ICON_TOOL_MEMTEST);
2056 break;
2057
2058 } // switch()
2059 } // for
2060 } // static VOID ScanForTools
2061
2062 // Rescan for boot loaders
2063 static VOID RescanAll(BOOLEAN DisplayMessage) {
2064 EG_PIXEL BGColor;
2065
2066 BGColor.b = 255;
2067 BGColor.g = 175;
2068 BGColor.r = 100;
2069 BGColor.a = 0;
2070 if (DisplayMessage)
2071 egDisplayMessage(L"Scanning for new boot loaders; please wait....", &BGColor);
2072 FreeList((VOID ***) &(MainMenu.Entries), &MainMenu.EntryCount);
2073 MainMenu.Entries = NULL;
2074 MainMenu.EntryCount = 0;
2075 ReadConfig(GlobalConfig.ConfigFilename);
2076 ConnectAllDriversToAllControllers();
2077 ScanVolumes();
2078 ScanForBootloaders();
2079 ScanForTools();
2080 SetupScreen();
2081 } // VOID RescanAll()
2082
2083 #ifdef __MAKEWITH_TIANO
2084
2085 // Minimal initialization function
2086 static VOID InitializeLib(IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable) {
2087 gST = SystemTable;
2088 // gImageHandle = ImageHandle;
2089 gBS = SystemTable->BootServices;
2090 // gRS = SystemTable->RuntimeServices;
2091 gRT = SystemTable->RuntimeServices; // Some BDS functions need gRT to be set
2092 EfiGetSystemConfigurationTable (&gEfiDxeServicesTableGuid, (VOID **) &gDS);
2093 }
2094
2095 #endif
2096
2097 // Set up our own Secure Boot extensions....
2098 // Returns TRUE on success, FALSE otherwise
2099 static BOOLEAN SecureBootSetup(VOID) {
2100 EFI_STATUS Status;
2101 BOOLEAN Success = FALSE;
2102
2103 if (secure_mode() && ShimLoaded()) {
2104 Status = security_policy_install();
2105 if (Status == EFI_SUCCESS) {
2106 Success = TRUE;
2107 } else {
2108 Print(L"Failed to install MOK Secure Boot extensions");
2109 PauseForKey();
2110 }
2111 }
2112 return Success;
2113 } // VOID SecureBootSetup()
2114
2115 // Remove our own Secure Boot extensions....
2116 // Returns TRUE on success, FALSE otherwise
2117 static BOOLEAN SecureBootUninstall(VOID) {
2118 EFI_STATUS Status;
2119 BOOLEAN Success = TRUE;
2120
2121 if (secure_mode()) {
2122 Status = security_policy_uninstall();
2123 if (Status != EFI_SUCCESS) {
2124 Success = FALSE;
2125 BeginTextScreen(L"Secure Boot Policy Failure");
2126 Print(L"Failed to uninstall MOK Secure Boot extensions; forcing a reboot.");
2127 PauseForKey();
2128 refit_call4_wrapper(RT->ResetSystem, EfiResetCold, EFI_SUCCESS, 0, NULL);
2129 }
2130 }
2131 return Success;
2132 } // VOID SecureBootUninstall
2133
2134 // Sets the global configuration filename; will be CONFIG_FILE_NAME unless the
2135 // "-c" command-line option is set, in which case that takes precedence.
2136 // If an error is encountered, leaves the value alone (it should be set to
2137 // CONFIG_FILE_NAME when GlobalConfig is initialized).
2138 static VOID SetConfigFilename(EFI_HANDLE ImageHandle) {
2139 EFI_LOADED_IMAGE *Info;
2140 CHAR16 *Options, *FileName, *SubString;
2141 EFI_STATUS Status;
2142
2143 Status = refit_call3_wrapper(BS->HandleProtocol, ImageHandle, &LoadedImageProtocol, (VOID **) &Info);
2144 if ((Status == EFI_SUCCESS) && (Info->LoadOptionsSize > 0)) {
2145 Options = (CHAR16 *) Info->LoadOptions;
2146 SubString = MyStrStr(Options, L" -c ");
2147 if (SubString) {
2148 FileName = StrDuplicate(&SubString[4]);
2149 if (FileName) {
2150 LimitStringLength(FileName, 256);
2151 }
2152
2153 if (FileExists(SelfDir, FileName)) {
2154 GlobalConfig.ConfigFilename = FileName;
2155 } else {
2156 Print(L"Specified configuration file (%s) doesn't exist; using\n'refind.conf' default\n", FileName);
2157 MyFreePool(FileName);
2158 } // if/else
2159 } // if
2160 } // if
2161 } // VOID SetConfigFilename()
2162
2163 //
2164 // main entry point
2165 //
2166 EFI_STATUS
2167 EFIAPI
2168 efi_main (EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable)
2169 {
2170 EFI_STATUS Status;
2171 BOOLEAN MainLoopRunning = TRUE;
2172 BOOLEAN MokProtocol;
2173 REFIT_MENU_ENTRY *ChosenEntry;
2174 UINTN MenuExit, i;
2175 CHAR16 *SelectionName = NULL;
2176 EG_PIXEL BGColor;
2177
2178 // bootstrap
2179 InitializeLib(ImageHandle, SystemTable);
2180 Status = InitRefitLib(ImageHandle);
2181 if (EFI_ERROR(Status))
2182 return Status;
2183
2184 // read configuration
2185 CopyMem(GlobalConfig.ScanFor, "ieom ", NUM_SCAN_OPTIONS);
2186 FindLegacyBootType();
2187 if (GlobalConfig.LegacyType == LEGACY_TYPE_MAC)
2188 CopyMem(GlobalConfig.ScanFor, "ihebocm ", NUM_SCAN_OPTIONS);
2189 SetConfigFilename(ImageHandle);
2190 ReadConfig(GlobalConfig.ConfigFilename);
2191
2192 if (GlobalConfig.SpoofOSXVersion && GlobalConfig.SpoofOSXVersion[0] != L'\0')
2193 SetAppleOSInfo();
2194
2195 InitScreen();
2196 WarnIfLegacyProblems();
2197 MainMenu.TimeoutSeconds = GlobalConfig.Timeout;
2198
2199 // disable EFI watchdog timer
2200 refit_call4_wrapper(BS->SetWatchdogTimer, 0x0000, 0x0000, 0x0000, NULL);
2201
2202 // further bootstrap (now with config available)
2203 MokProtocol = SecureBootSetup();
2204 LoadDrivers();
2205 ScanVolumes();
2206 ScanForBootloaders();
2207 ScanForTools();
2208 SetupScreen();
2209
2210 if (GlobalConfig.ScanDelay > 0) {
2211 BGColor.b = 255;
2212 BGColor.g = 175;
2213 BGColor.r = 100;
2214 BGColor.a = 0;
2215 if (GlobalConfig.ScanDelay > 1)
2216 egDisplayMessage(L"Pausing before disk scan; please wait....", &BGColor);
2217 for (i = 0; i < GlobalConfig.ScanDelay; i++)
2218 refit_call1_wrapper(BS->Stall, 1000000);
2219 RescanAll(GlobalConfig.ScanDelay > 1);
2220 } // if
2221
2222 if (GlobalConfig.DefaultSelection)
2223 SelectionName = StrDuplicate(GlobalConfig.DefaultSelection);
2224
2225 while (MainLoopRunning) {
2226 MenuExit = RunMainMenu(&MainMenu, &SelectionName, &ChosenEntry);
2227
2228 // The Escape key triggers a re-scan operation....
2229 if (MenuExit == MENU_EXIT_ESCAPE) {
2230 MenuExit = 0;
2231 RescanAll(TRUE);
2232 continue;
2233 }
2234
2235 switch (ChosenEntry->Tag) {
2236
2237 case TAG_REBOOT: // Reboot
2238 TerminateScreen();
2239 refit_call4_wrapper(RT->ResetSystem, EfiResetCold, EFI_SUCCESS, 0, NULL);
2240 MainLoopRunning = FALSE; // just in case we get this far
2241 break;
2242
2243 case TAG_SHUTDOWN: // Shut Down
2244 TerminateScreen();
2245 refit_call4_wrapper(RT->ResetSystem, EfiResetShutdown, EFI_SUCCESS, 0, NULL);
2246 MainLoopRunning = FALSE; // just in case we get this far
2247 break;
2248
2249 case TAG_ABOUT: // About rEFInd
2250 AboutrEFInd();
2251 break;
2252
2253 case TAG_LOADER: // Boot OS via .EFI loader
2254 StartLoader((LOADER_ENTRY *)ChosenEntry, SelectionName);
2255 break;
2256
2257 case TAG_LEGACY: // Boot legacy OS
2258 StartLegacy((LEGACY_ENTRY *)ChosenEntry, SelectionName);
2259 break;
2260
2261 case TAG_LEGACY_UEFI: // Boot a legacy OS on a non-Mac
2262 StartLegacyUEFI((LEGACY_ENTRY *)ChosenEntry, SelectionName);
2263 break;
2264
2265 case TAG_TOOL: // Start a EFI tool
2266 StartTool((LOADER_ENTRY *)ChosenEntry);
2267 break;
2268
2269 case TAG_EXIT: // Terminate rEFInd
2270 if ((MokProtocol) && !SecureBootUninstall()) {
2271 MainLoopRunning = FALSE; // just in case we get this far
2272 } else {
2273 BeginTextScreen(L" ");
2274 return EFI_SUCCESS;
2275 }
2276 break;
2277
2278 case TAG_FIRMWARE: // Reboot into firmware's user interface
2279 RebootIntoFirmware();
2280 break;
2281
2282 case TAG_CSR_ROTATE:
2283 RotateCsrValue();
2284 break;
2285
2286 } // switch()
2287 } // while()
2288
2289 // If we end up here, things have gone wrong. Try to reboot, and if that
2290 // fails, go into an endless loop.
2291 refit_call4_wrapper(RT->ResetSystem, EfiResetCold, EFI_SUCCESS, 0, NULL);
2292 EndlessIdleLoop();
2293
2294 return EFI_SUCCESS;
2295 } /* efi_main() */