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