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