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