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