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