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