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