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