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