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