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