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