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