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