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