]> code.delx.au - refind/blob - refind/main.c
New CSM/BIOS/legacy support for UEFI PCs.
[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.4.2");
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)) {
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(IN LEGACY_ENTRY *Entry)
1629 {
1630 BeginExternalScreen(TRUE, L"Booting Legacy OS (UEFI mode)");
1631
1632 BdsDeleteAllInvalidLegacyBootOptions();
1633 BdsAddNonExistingLegacyBootOptions();
1634 // BdsUpdateLegacyDevOrder();
1635
1636 BdsLibConnectDevicePath (Entry->BdsOption->DevicePath);
1637 BdsLibDoLegacyBoot(Entry->BdsOption);
1638
1639 // If we get here, it means that there was a failure....
1640 Print(L"Failure booting legacy (BIOS) OS.");
1641 PauseForKey();
1642 FinishExternalScreen();
1643 } // static VOID StartLegacyUEFI()
1644 #endif // __MAKEWITH_TIANO
1645
1646 static LEGACY_ENTRY * AddLegacyEntry(IN CHAR16 *LoaderTitle, IN REFIT_VOLUME *Volume)
1647 {
1648 LEGACY_ENTRY *Entry, *SubEntry;
1649 REFIT_MENU_SCREEN *SubScreen;
1650 CHAR16 *VolDesc;
1651 CHAR16 ShortcutLetter = 0;
1652
1653 if (LoaderTitle == NULL) {
1654 if (Volume->OSName != NULL) {
1655 LoaderTitle = Volume->OSName;
1656 if (LoaderTitle[0] == 'W' || LoaderTitle[0] == 'L')
1657 ShortcutLetter = LoaderTitle[0];
1658 } else
1659 LoaderTitle = L"Legacy OS";
1660 }
1661 if (Volume->VolName != NULL)
1662 VolDesc = Volume->VolName;
1663 else
1664 VolDesc = (Volume->DiskKind == DISK_KIND_OPTICAL) ? L"CD" : L"HD";
1665
1666 // prepare the menu entry
1667 Entry = AllocateZeroPool(sizeof(LEGACY_ENTRY));
1668 Entry->me.Title = AllocateZeroPool(256 * sizeof(CHAR16));
1669 SPrint(Entry->me.Title, 255, L"Boot %s from %s", LoaderTitle, VolDesc);
1670 Entry->me.Tag = TAG_LEGACY;
1671 Entry->me.Row = 0;
1672 Entry->me.ShortcutLetter = ShortcutLetter;
1673 Entry->me.Image = LoadOSIcon(Volume->OSIconName, L"legacy", FALSE);
1674 Entry->me.BadgeImage = Volume->VolBadgeImage;
1675 Entry->Volume = Volume;
1676 Entry->LoadOptions = (Volume->DiskKind == DISK_KIND_OPTICAL) ? L"CD" :
1677 ((Volume->DiskKind == DISK_KIND_EXTERNAL) ? L"USB" : L"HD");
1678 Entry->Enabled = TRUE;
1679
1680 // create the submenu
1681 SubScreen = AllocateZeroPool(sizeof(REFIT_MENU_SCREEN));
1682 SubScreen->Title = AllocateZeroPool(256 * sizeof(CHAR16));
1683 SPrint(SubScreen->Title, 255, L"Boot Options for %s on %s", LoaderTitle, VolDesc);
1684 SubScreen->TitleImage = Entry->me.Image;
1685 SubScreen->Hint1 = StrDuplicate(SUBSCREEN_HINT1);
1686 if (GlobalConfig.HideUIFlags & HIDEUI_FLAG_EDITOR) {
1687 SubScreen->Hint2 = StrDuplicate(SUBSCREEN_HINT2_NO_EDITOR);
1688 } else {
1689 SubScreen->Hint2 = StrDuplicate(SUBSCREEN_HINT2);
1690 } // if/else
1691
1692 // default entry
1693 SubEntry = AllocateZeroPool(sizeof(LEGACY_ENTRY));
1694 SubEntry->me.Title = AllocateZeroPool(256 * sizeof(CHAR16));
1695 SPrint(SubEntry->me.Title, 255, L"Boot %s", LoaderTitle);
1696 SubEntry->me.Tag = TAG_LEGACY;
1697 SubEntry->Volume = Entry->Volume;
1698 SubEntry->LoadOptions = Entry->LoadOptions;
1699 AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry);
1700
1701 AddMenuEntry(SubScreen, &MenuEntryReturn);
1702 Entry->me.SubScreen = SubScreen;
1703 AddMenuEntry(&MainMenu, (REFIT_MENU_ENTRY *)Entry);
1704 return Entry;
1705 } /* static LEGACY_ENTRY * AddLegacyEntry() */
1706
1707
1708 #ifdef __MAKEWITH_GNUEFI
1709 static VOID ScanLegacyUEFI(IN UINTN DiskType){}
1710 #else
1711 // default volume badge icon based on disk kind
1712 static EG_IMAGE * GetDiskBadge(IN UINTN DiskType) {
1713 EG_IMAGE * Badge = NULL;
1714
1715 switch (DiskType) {
1716 case BBS_HARDDISK:
1717 Badge = BuiltinIcon(BUILTIN_ICON_VOL_INTERNAL);
1718 break;
1719 case BBS_USB:
1720 Badge = BuiltinIcon(BUILTIN_ICON_VOL_EXTERNAL);
1721 break;
1722 case BBS_CDROM:
1723 Badge = BuiltinIcon(BUILTIN_ICON_VOL_OPTICAL);
1724 break;
1725 } // switch()
1726 return Badge;
1727 } // static EG_IMAGE * GetDiskBadge()
1728
1729 /**
1730 Create a rEFInd boot option from a Legacy BIOS protocol option.
1731 */
1732 static LEGACY_ENTRY * AddLegacyEntryUEFI(BDS_COMMON_OPTION *BdsOption, IN UINT16 DiskType)
1733 {
1734 LEGACY_ENTRY *Entry, *SubEntry;
1735 REFIT_MENU_SCREEN *SubScreen;
1736 CHAR16 ShortcutLetter = 0;
1737 CHAR16 *LegacyDescription = BdsOption->Description;
1738
1739 // prepare the menu entry
1740 Entry = AllocateZeroPool(sizeof(LEGACY_ENTRY));
1741 Entry->me.Title = AllocateZeroPool(256 * sizeof(CHAR16));
1742 SPrint(Entry->me.Title, 255, L"Boot legacy target %s", LegacyDescription);
1743 Entry->me.Tag = TAG_LEGACY_UEFI;
1744 Entry->me.Row = 0;
1745 Entry->me.ShortcutLetter = ShortcutLetter;
1746 Entry->me.Image = LoadOSIcon(L"legacy", L"legacy", TRUE);
1747 Entry->LoadOptions = (DiskType == BBS_CDROM) ? L"CD" :
1748 ((DiskType == BBS_USB) ? L"USB" : L"HD");
1749 Entry->me.BadgeImage = GetDiskBadge(DiskType);
1750 Entry->BdsOption = BdsOption;
1751 Entry->Enabled = TRUE;
1752
1753 // create the submenu
1754 SubScreen = AllocateZeroPool(sizeof(REFIT_MENU_SCREEN));
1755 SubScreen->Title = AllocateZeroPool(256 * sizeof(CHAR16));
1756 SPrint(SubScreen->Title, 255, L"No boot options for legacy target");
1757 SubScreen->TitleImage = Entry->me.Image;
1758 SubScreen->Hint1 = StrDuplicate(SUBSCREEN_HINT1);
1759 if (GlobalConfig.HideUIFlags & HIDEUI_FLAG_EDITOR) {
1760 SubScreen->Hint2 = StrDuplicate(SUBSCREEN_HINT2_NO_EDITOR);
1761 } else {
1762 SubScreen->Hint2 = StrDuplicate(SUBSCREEN_HINT2);
1763 } // if/else
1764
1765 // default entry
1766 SubEntry = AllocateZeroPool(sizeof(LEGACY_ENTRY));
1767 SubEntry->me.Title = AllocateZeroPool(256 * sizeof(CHAR16));
1768 SPrint(SubEntry->me.Title, 255, L"Boot %s", LegacyDescription);
1769 SubEntry->me.Tag = TAG_LEGACY_UEFI;
1770 Entry->BdsOption = BdsOption;
1771 AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry);
1772
1773 AddMenuEntry(SubScreen, &MenuEntryReturn);
1774 Entry->me.SubScreen = SubScreen;
1775 AddMenuEntry(&MainMenu, (REFIT_MENU_ENTRY *)Entry);
1776 return Entry;
1777 } /* static LEGACY_ENTRY * AddLegacyEntryUEFI() */
1778
1779 /**
1780 Scan for legacy BIOS targets on machines that implement EFI_LEGACY_BIOS_PROTOCOL.
1781 In testing, protocol has not been implemented on Macs but has been
1782 implemented on several Dell PCs and an ASUS motherboard.
1783 Restricts output to disks of the specified DiskType.
1784 */
1785 static VOID ScanLegacyUEFI(IN UINTN DiskType)
1786 {
1787 EFI_STATUS Status;
1788 EFI_LEGACY_BIOS_PROTOCOL *LegacyBios;
1789 UINT16 *BootOrder = NULL;
1790 UINTN Index = 0;
1791 CHAR16 BootOption[10];
1792 UINTN BootOrderSize = 0;
1793 CHAR16 Buffer[20];
1794 BDS_COMMON_OPTION *BdsOption;
1795 LIST_ENTRY TempList;
1796 BBS_BBS_DEVICE_PATH * BbsDevicePath = NULL;
1797
1798 InitializeListHead (&TempList);
1799 ZeroMem (Buffer, sizeof (Buffer));
1800
1801 // If LegacyBios protocol is not implemented on this platform, then
1802 //we do not support this type of legacy boot on this machine.
1803 Status = gBS->LocateProtocol(&gEfiLegacyBootProtocolGuid, NULL, (VOID **) &LegacyBios);
1804 if (EFI_ERROR (Status))
1805 return;
1806
1807 // Grab the boot order
1808 BootOrder = BdsLibGetVariableAndSize(L"BootOrder", &gEfiGlobalVariableGuid, &BootOrderSize);
1809 if (BootOrder == NULL) {
1810 BootOrderSize = 0;
1811 }
1812
1813 Index = 0;
1814 while (Index < BootOrderSize / sizeof (UINT16))
1815 {
1816 // Grab each boot option variable from the boot order, and convert
1817 // the variable into a BDS boot option
1818 UnicodeSPrint (BootOption, sizeof (BootOption), L"Boot%04x", BootOrder[Index]);
1819 BdsOption = BdsLibVariableToOption (&TempList, BootOption);
1820
1821 if (BdsOption != NULL) {
1822 BbsDevicePath = (BBS_BBS_DEVICE_PATH *)BdsOption->DevicePath;
1823
1824 // Only add the entry if it is of a requested type (e.g. USB, HD)
1825
1826 // Two checks necessary because some systems return EFI boot loaders
1827 // with a DeviceType value that would inappropriately include them
1828 // as legacy loaders....
1829 if ((BbsDevicePath->DeviceType == DiskType) && (BdsOption->DevicePath->Type == DEVICE_TYPE_BIOS)) {
1830 AddLegacyEntryUEFI(BdsOption, BbsDevicePath->DeviceType);
1831 }
1832 }
1833 Index++;
1834 }
1835 } /* static VOID ScanLegacyUEFI() */
1836 #endif // __MAKEWITH_GNUEFI
1837
1838 static VOID ScanLegacyVolume(REFIT_VOLUME *Volume, UINTN VolumeIndex) {
1839 UINTN VolumeIndex2;
1840 BOOLEAN ShowVolume, HideIfOthersFound;
1841
1842 ShowVolume = FALSE;
1843 HideIfOthersFound = FALSE;
1844 if (Volume->IsAppleLegacy) {
1845 ShowVolume = TRUE;
1846 HideIfOthersFound = TRUE;
1847 } else if (Volume->HasBootCode) {
1848 ShowVolume = TRUE;
1849 if (Volume->BlockIO == Volume->WholeDiskBlockIO &&
1850 Volume->BlockIOOffset == 0 &&
1851 Volume->OSName == NULL)
1852 // this is a whole disk (MBR) entry; hide if we have entries for partitions
1853 HideIfOthersFound = TRUE;
1854 }
1855 if (HideIfOthersFound) {
1856 // check for other bootable entries on the same disk
1857 for (VolumeIndex2 = 0; VolumeIndex2 < VolumesCount; VolumeIndex2++) {
1858 if (VolumeIndex2 != VolumeIndex && Volumes[VolumeIndex2]->HasBootCode &&
1859 Volumes[VolumeIndex2]->WholeDiskBlockIO == Volume->WholeDiskBlockIO)
1860 ShowVolume = FALSE;
1861 }
1862 }
1863
1864 if (ShowVolume)
1865 AddLegacyEntry(NULL, Volume);
1866 } // static VOID ScanLegacyVolume()
1867
1868 // Scan attached optical discs for legacy (BIOS) boot code
1869 // and add anything found to the list....
1870 static VOID ScanLegacyDisc(VOID)
1871 {
1872 UINTN VolumeIndex;
1873 REFIT_VOLUME *Volume;
1874
1875 if (GlobalConfig.LegacyType == LEGACY_TYPE_MAC) {
1876 for (VolumeIndex = 0; VolumeIndex < VolumesCount; VolumeIndex++) {
1877 Volume = Volumes[VolumeIndex];
1878 if (Volume->DiskKind == DISK_KIND_OPTICAL)
1879 ScanLegacyVolume(Volume, VolumeIndex);
1880 } // for
1881 } else if (GlobalConfig.LegacyType == LEGACY_TYPE_UEFI) {
1882 ScanLegacyUEFI(BBS_CDROM);
1883 }
1884 } /* static VOID ScanLegacyDisc() */
1885
1886 // Scan internal hard disks for legacy (BIOS) boot code
1887 // and add anything found to the list....
1888 static VOID ScanLegacyInternal(VOID)
1889 {
1890 UINTN VolumeIndex;
1891 REFIT_VOLUME *Volume;
1892
1893 if (GlobalConfig.LegacyType == LEGACY_TYPE_MAC) {
1894 for (VolumeIndex = 0; VolumeIndex < VolumesCount; VolumeIndex++) {
1895 Volume = Volumes[VolumeIndex];
1896 if (Volume->DiskKind == DISK_KIND_INTERNAL)
1897 ScanLegacyVolume(Volume, VolumeIndex);
1898 } // for
1899 } else if (GlobalConfig.LegacyType == LEGACY_TYPE_UEFI) {
1900 ScanLegacyUEFI(BBS_HARDDISK);
1901 }
1902 } /* static VOID ScanLegacyInternal() */
1903
1904 // Scan external disks for legacy (BIOS) boot code
1905 // and add anything found to the list....
1906 static VOID ScanLegacyExternal(VOID)
1907 {
1908 UINTN VolumeIndex;
1909 REFIT_VOLUME *Volume;
1910
1911 if (GlobalConfig.LegacyType == LEGACY_TYPE_MAC) {
1912 for (VolumeIndex = 0; VolumeIndex < VolumesCount; VolumeIndex++) {
1913 Volume = Volumes[VolumeIndex];
1914 if (Volume->DiskKind == DISK_KIND_EXTERNAL)
1915 ScanLegacyVolume(Volume, VolumeIndex);
1916 } // for
1917 } else if (GlobalConfig.LegacyType == LEGACY_TYPE_UEFI) {
1918 ScanLegacyUEFI(BBS_USB);
1919 }
1920 } /* static VOID ScanLegacyExternal() */
1921
1922 //
1923 // pre-boot tool functions
1924 //
1925
1926 static VOID StartTool(IN LOADER_ENTRY *Entry)
1927 {
1928 BeginExternalScreen(Entry->UseGraphicsMode, Entry->me.Title + 6); // assumes "Start <title>" as assigned below
1929 StartEFIImage(Entry->DevicePath, Entry->LoadOptions, TYPE_EFI,
1930 Basename(Entry->LoaderPath), Entry->OSType, NULL, TRUE);
1931 FinishExternalScreen();
1932 } /* static VOID StartTool() */
1933
1934 static LOADER_ENTRY * AddToolEntry(EFI_HANDLE DeviceHandle, IN CHAR16 *LoaderPath, IN CHAR16 *LoaderTitle, IN EG_IMAGE *Image,
1935 IN CHAR16 ShortcutLetter, IN BOOLEAN UseGraphicsMode)
1936 {
1937 LOADER_ENTRY *Entry;
1938 CHAR16 *TitleStr = NULL;
1939
1940 Entry = AllocateZeroPool(sizeof(LOADER_ENTRY));
1941
1942 TitleStr = PoolPrint(L"Start %s", LoaderTitle);
1943 Entry->me.Title = TitleStr;
1944 Entry->me.Tag = TAG_TOOL;
1945 Entry->me.Row = 1;
1946 Entry->me.ShortcutLetter = ShortcutLetter;
1947 Entry->me.Image = Image;
1948 Entry->LoaderPath = (LoaderPath) ? StrDuplicate(LoaderPath) : NULL;
1949 Entry->DevicePath = FileDevicePath(DeviceHandle, Entry->LoaderPath);
1950 Entry->UseGraphicsMode = UseGraphicsMode;
1951
1952 AddMenuEntry(&MainMenu, (REFIT_MENU_ENTRY *)Entry);
1953 return Entry;
1954 } /* static LOADER_ENTRY * AddToolEntry() */
1955
1956 //
1957 // pre-boot driver functions
1958 //
1959
1960 static UINTN ScanDriverDir(IN CHAR16 *Path)
1961 {
1962 EFI_STATUS Status;
1963 REFIT_DIR_ITER DirIter;
1964 UINTN NumFound = 0;
1965 EFI_FILE_INFO *DirEntry;
1966 CHAR16 FileName[256];
1967
1968 CleanUpPathNameSlashes(Path);
1969 // look through contents of the directory
1970 DirIterOpen(SelfRootDir, Path, &DirIter);
1971 while (DirIterNext(&DirIter, 2, LOADER_MATCH_PATTERNS, &DirEntry)) {
1972 if (DirEntry->FileName[0] == '.')
1973 continue; // skip this
1974
1975 SPrint(FileName, 255, L"%s\\%s", Path, DirEntry->FileName);
1976 NumFound++;
1977 Status = StartEFIImage(FileDevicePath(SelfLoadedImage->DeviceHandle, FileName),
1978 L"", TYPE_EFI, DirEntry->FileName, 0, NULL, FALSE);
1979 }
1980 Status = DirIterClose(&DirIter);
1981 if (Status != EFI_NOT_FOUND) {
1982 SPrint(FileName, 255, L"while scanning the %s directory", Path);
1983 CheckError(Status, FileName);
1984 }
1985 return (NumFound);
1986 }
1987
1988 #ifdef __MAKEWITH_GNUEFI
1989 static EFI_STATUS ConnectAllDriversToAllControllers(VOID)
1990 {
1991 EFI_STATUS Status;
1992 UINTN AllHandleCount;
1993 EFI_HANDLE *AllHandleBuffer;
1994 UINTN Index;
1995 UINTN HandleCount;
1996 EFI_HANDLE *HandleBuffer;
1997 UINT32 *HandleType;
1998 UINTN HandleIndex;
1999 BOOLEAN Parent;
2000 BOOLEAN Device;
2001
2002 Status = LibLocateHandle(AllHandles,
2003 NULL,
2004 NULL,
2005 &AllHandleCount,
2006 &AllHandleBuffer);
2007 if (EFI_ERROR(Status))
2008 return Status;
2009
2010 for (Index = 0; Index < AllHandleCount; Index++) {
2011 //
2012 // Scan the handle database
2013 //
2014 Status = LibScanHandleDatabase(NULL,
2015 NULL,
2016 AllHandleBuffer[Index],
2017 NULL,
2018 &HandleCount,
2019 &HandleBuffer,
2020 &HandleType);
2021 if (EFI_ERROR (Status))
2022 goto Done;
2023
2024 Device = TRUE;
2025 if (HandleType[Index] & EFI_HANDLE_TYPE_DRIVER_BINDING_HANDLE)
2026 Device = FALSE;
2027 if (HandleType[Index] & EFI_HANDLE_TYPE_IMAGE_HANDLE)
2028 Device = FALSE;
2029
2030 if (Device) {
2031 Parent = FALSE;
2032 for (HandleIndex = 0; HandleIndex < HandleCount; HandleIndex++) {
2033 if (HandleType[HandleIndex] & EFI_HANDLE_TYPE_PARENT_HANDLE)
2034 Parent = TRUE;
2035 } // for
2036
2037 if (!Parent) {
2038 if (HandleType[Index] & EFI_HANDLE_TYPE_DEVICE_HANDLE) {
2039 Status = refit_call4_wrapper(BS->ConnectController,
2040 AllHandleBuffer[Index],
2041 NULL,
2042 NULL,
2043 TRUE);
2044 }
2045 }
2046 }
2047
2048 MyFreePool (HandleBuffer);
2049 MyFreePool (HandleType);
2050 }
2051
2052 Done:
2053 MyFreePool (AllHandleBuffer);
2054 return Status;
2055 } /* EFI_STATUS ConnectAllDriversToAllControllers() */
2056 #else
2057 static EFI_STATUS ConnectAllDriversToAllControllers(VOID) {
2058 BdsLibConnectAllDriversToAllControllers();
2059 return 0;
2060 }
2061 #endif
2062
2063 // Load all EFI drivers from rEFInd's "drivers" subdirectory and from the
2064 // directories specified by the user in the "scan_driver_dirs" configuration
2065 // file line.
2066 static VOID LoadDrivers(VOID)
2067 {
2068 CHAR16 *Directory, *SelfDirectory;
2069 UINTN i = 0, Length, NumFound = 0;
2070
2071 // load drivers from the subdirectories of rEFInd's home directory specified
2072 // in the DRIVER_DIRS constant.
2073 while ((Directory = FindCommaDelimited(DRIVER_DIRS, i++)) != NULL) {
2074 SelfDirectory = SelfDirPath ? StrDuplicate(SelfDirPath) : NULL;
2075 CleanUpPathNameSlashes(SelfDirectory);
2076 MergeStrings(&SelfDirectory, Directory, L'\\');
2077 NumFound += ScanDriverDir(SelfDirectory);
2078 MyFreePool(Directory);
2079 MyFreePool(SelfDirectory);
2080 }
2081
2082 // Scan additional user-specified driver directories....
2083 i = 0;
2084 while ((Directory = FindCommaDelimited(GlobalConfig.DriverDirs, i++)) != NULL) {
2085 CleanUpPathNameSlashes(Directory);
2086 Length = StrLen(Directory);
2087 if (Length > 0) {
2088 NumFound += ScanDriverDir(Directory);
2089 } // if
2090 MyFreePool(Directory);
2091 } // while
2092
2093 // connect all devices
2094 if (NumFound > 0)
2095 ConnectAllDriversToAllControllers();
2096 } /* static VOID LoadDrivers() */
2097
2098 // Determine what (if any) type of legacy (BIOS) boot support is available
2099 static VOID FindLegacyBootType(VOID) {
2100 #ifdef __MAKEWITH_TIANO
2101 EFI_STATUS Status;
2102 EFI_LEGACY_BIOS_PROTOCOL *LegacyBios;
2103 #endif
2104
2105 GlobalConfig.LegacyType = LEGACY_TYPE_NONE;
2106
2107 // UEFI-style legacy BIOS support is available only with the TianoCore EDK2
2108 // build environment, and then only with some EFI implementations....
2109 #ifdef __MAKEWITH_TIANO
2110 Status = gBS->LocateProtocol (&gEfiLegacyBootProtocolGuid, NULL, (VOID **) &LegacyBios);
2111 if (!EFI_ERROR (Status))
2112 GlobalConfig.LegacyType = LEGACY_TYPE_UEFI;
2113 #endif
2114
2115 // Macs have their own system. If the firmware vendor code contains the
2116 // string "Apple", assume it's available. Note that this overrides the
2117 // UEFI type, and might yield false positives if the vendor string
2118 // contains "Apple" as part of something bigger, so this isn't 100%
2119 // perfect.
2120 if (StriSubCmp(L"Apple", ST->FirmwareVendor))
2121 GlobalConfig.LegacyType = LEGACY_TYPE_MAC;
2122 } // static VOID FindLegacyBootType
2123
2124 // Warn the user if legacy OS scans are enabled but the firmware or this
2125 // application can't support them....
2126 static VOID WarnIfLegacyProblems() {
2127 BOOLEAN found = FALSE;
2128 UINTN i = 0;
2129
2130 if (GlobalConfig.LegacyType == LEGACY_TYPE_NONE) {
2131 do {
2132 if (GlobalConfig.ScanFor[i] == 'h' || GlobalConfig.ScanFor[i] == 'b' || GlobalConfig.ScanFor[i] == 'c')
2133 found = TRUE;
2134 i++;
2135 } while ((i < NUM_SCAN_OPTIONS) && (!found));
2136 if (found) {
2137 Print(L"NOTE: refind.conf's 'scanfor' line specifies scanning for one or more legacy\n");
2138 Print(L"(BIOS) boot options; however, this is not possible because ");
2139 #ifdef __MAKEWITH_TIANO
2140 Print(L"your computer lacks\n");
2141 Print(L"the necessary Compatibility Support Module (CSM) support.\n");
2142 #else
2143 Print(L"this program was\n");
2144 Print(L"compiled without the necessary support. Please visit\n");
2145 Print(L"http://www.rodsbooks.com/refind/getting.html and download and install a rEFInd\n");
2146 Print(L"binary built with the TianoCore EDK2 to enable legacy boot support.\n");
2147 #endif
2148 PauseForKey();
2149 } // if (found)
2150 } // if no legacy support
2151 } // static VOID WarnIfLegacyProblems()
2152
2153 // Locates boot loaders. NOTE: This assumes that GlobalConfig.LegacyType is set correctly.
2154 static VOID ScanForBootloaders(VOID) {
2155 UINTN i;
2156
2157 ScanVolumes();
2158
2159 // scan for loaders and tools, add them to the menu
2160 for (i = 0; i < NUM_SCAN_OPTIONS; i++) {
2161 switch(GlobalConfig.ScanFor[i]) {
2162 case 'c': case 'C':
2163 ScanLegacyDisc();
2164 break;
2165 case 'h': case 'H':
2166 ScanLegacyInternal();
2167 break;
2168 case 'b': case 'B':
2169 ScanLegacyExternal();
2170 break;
2171 case 'm': case 'M':
2172 ScanUserConfigured(CONFIG_FILE_NAME);
2173 break;
2174 case 'e': case 'E':
2175 ScanExternal();
2176 break;
2177 case 'i': case 'I':
2178 ScanInternal();
2179 break;
2180 case 'o': case 'O':
2181 ScanOptical();
2182 break;
2183 } // switch()
2184 } // for
2185
2186 // assign shortcut keys
2187 for (i = 0; i < MainMenu.EntryCount && MainMenu.Entries[i]->Row == 0 && i < 9; i++)
2188 MainMenu.Entries[i]->ShortcutDigit = (CHAR16)('1' + i);
2189
2190 // wait for user ACK when there were errors
2191 FinishTextScreen(FALSE);
2192 } // static VOID ScanForBootloaders()
2193
2194 // Locate a single tool from the specified Locations using one of the
2195 // specified Names and add it to the menu.
2196 static VOID FindTool(CHAR16 *Locations, CHAR16 *Names, CHAR16 *Description, UINTN Icon) {
2197 UINTN j = 0, k, VolumeIndex;
2198 CHAR16 *DirName, *FileName, *PathName, FullDescription[256];
2199
2200 while ((DirName = FindCommaDelimited(Locations, j++)) != NULL) {
2201 k = 0;
2202 while ((FileName = FindCommaDelimited(Names, k++)) != NULL) {
2203 PathName = StrDuplicate(DirName);
2204 MergeStrings(&PathName, FileName, (StriCmp(PathName, L"\\") == 0) ? 0 : L'\\');
2205 for (VolumeIndex = 0; VolumeIndex < VolumesCount; VolumeIndex++) {
2206 if ((Volumes[VolumeIndex]->RootDir != NULL) && (FileExists(Volumes[VolumeIndex]->RootDir, PathName))) {
2207 SPrint(FullDescription, 255, L"%s at %s on %s", Description, PathName, Volumes[VolumeIndex]->VolName);
2208 AddToolEntry(Volumes[VolumeIndex]->DeviceHandle, PathName, FullDescription, BuiltinIcon(Icon), 'S', FALSE);
2209 } // if
2210 } // for
2211 MyFreePool(PathName);
2212 MyFreePool(FileName);
2213 } // while Names
2214 MyFreePool(DirName);
2215 } // while Locations
2216 } // VOID FindTool()
2217
2218 // Add the second-row tags containing built-in and external tools (EFI shell,
2219 // reboot, etc.)
2220 static VOID ScanForTools(VOID) {
2221 CHAR16 *FileName = NULL, *MokLocations, Description[256];
2222 REFIT_MENU_ENTRY *TempMenuEntry;
2223 UINTN i, j, VolumeIndex;
2224 UINT64 osind;
2225 CHAR8 *b = 0;
2226
2227 MokLocations = StrDuplicate(MOK_LOCATIONS);
2228 if (MokLocations != NULL)
2229 MergeStrings(&MokLocations, SelfDirPath, L',');
2230
2231 for (i = 0; i < NUM_TOOLS; i++) {
2232 switch(GlobalConfig.ShowTools[i]) {
2233 // NOTE: Be sure that FileName is NULL at the end of each case.
2234 case TAG_SHUTDOWN:
2235 TempMenuEntry = CopyMenuEntry(&MenuEntryShutdown);
2236 TempMenuEntry->Image = BuiltinIcon(BUILTIN_ICON_FUNC_SHUTDOWN);
2237 AddMenuEntry(&MainMenu, TempMenuEntry);
2238 break;
2239
2240 case TAG_REBOOT:
2241 TempMenuEntry = CopyMenuEntry(&MenuEntryReset);
2242 TempMenuEntry->Image = BuiltinIcon(BUILTIN_ICON_FUNC_RESET);
2243 AddMenuEntry(&MainMenu, TempMenuEntry);
2244 break;
2245
2246 case TAG_ABOUT:
2247 TempMenuEntry = CopyMenuEntry(&MenuEntryAbout);
2248 TempMenuEntry->Image = BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT);
2249 AddMenuEntry(&MainMenu, TempMenuEntry);
2250 break;
2251
2252 case TAG_EXIT:
2253 TempMenuEntry = CopyMenuEntry(&MenuEntryExit);
2254 TempMenuEntry->Image = BuiltinIcon(BUILTIN_ICON_FUNC_EXIT);
2255 AddMenuEntry(&MainMenu, TempMenuEntry);
2256 break;
2257
2258 case TAG_FIRMWARE:
2259 if (EfivarGetRaw(&GlobalGuid, L"OsIndicationsSupported", &b, &j) == EFI_SUCCESS) {
2260 osind = (UINT64)*b;
2261 if (osind & EFI_OS_INDICATIONS_BOOT_TO_FW_UI) {
2262 TempMenuEntry = CopyMenuEntry(&MenuEntryFirmware);
2263 TempMenuEntry->Image = BuiltinIcon(BUILTIN_ICON_FUNC_FIRMWARE);
2264 AddMenuEntry(&MainMenu, TempMenuEntry);
2265 } // if
2266 } // if
2267 break;
2268
2269 case TAG_SHELL:
2270 j = 0;
2271 while ((FileName = FindCommaDelimited(SHELL_NAMES, j++)) != NULL) {
2272 if (FileExists(SelfRootDir, FileName)) {
2273 AddToolEntry(SelfLoadedImage->DeviceHandle, FileName, L"EFI Shell", BuiltinIcon(BUILTIN_ICON_TOOL_SHELL),
2274 'S', FALSE);
2275 }
2276 MyFreePool(FileName);
2277 } // while
2278 break;
2279
2280 case TAG_GPTSYNC:
2281 j = 0;
2282 while ((FileName = FindCommaDelimited(GPTSYNC_NAMES, j++)) != NULL) {
2283 if (FileExists(SelfRootDir, FileName)) {
2284 AddToolEntry(SelfLoadedImage->DeviceHandle, FileName, L"Hybrid MBR tool", BuiltinIcon(BUILTIN_ICON_TOOL_PART),
2285 'P', FALSE);
2286 } // if
2287 MyFreePool(FileName);
2288 } // while
2289 FileName = NULL;
2290 break;
2291
2292 case TAG_APPLE_RECOVERY:
2293 FileName = StrDuplicate(L"\\com.apple.recovery.boot\\boot.efi");
2294 for (VolumeIndex = 0; VolumeIndex < VolumesCount; VolumeIndex++) {
2295 if ((Volumes[VolumeIndex]->RootDir != NULL) && (FileExists(Volumes[VolumeIndex]->RootDir, FileName))) {
2296 SPrint(Description, 255, L"Apple Recovery on %s", Volumes[VolumeIndex]->VolName);
2297 AddToolEntry(Volumes[VolumeIndex]->DeviceHandle, FileName, Description,
2298 BuiltinIcon(BUILTIN_ICON_TOOL_APPLE_RESCUE), 'R', TRUE);
2299 }
2300 } // for
2301 MyFreePool(FileName);
2302 FileName = NULL;
2303 break;
2304
2305 case TAG_MOK_TOOL:
2306 FindTool(MokLocations, MOK_NAMES, L"MOK utility utility", BUILTIN_ICON_TOOL_MOK_TOOL);
2307 break;
2308
2309 case TAG_MEMTEST:
2310 FindTool(MEMTEST_LOCATIONS, MEMTEST_NAMES, L"Memory test utility", BUILTIN_ICON_TOOL_MEMTEST);
2311 break;
2312
2313 } // switch()
2314 } // for
2315 } // static VOID ScanForTools
2316
2317 // Rescan for boot loaders
2318 VOID RescanAll(VOID) {
2319 EG_PIXEL BGColor;
2320
2321 BGColor.b = 255;
2322 BGColor.g = 175;
2323 BGColor.r = 100;
2324 BGColor.a = 0;
2325 egDisplayMessage(L"Scanning for new boot loaders; please wait....", &BGColor);
2326 FreeList((VOID ***) &(MainMenu.Entries), &MainMenu.EntryCount);
2327 MainMenu.Entries = NULL;
2328 MainMenu.EntryCount = 0;
2329 ReadConfig(CONFIG_FILE_NAME);
2330 ConnectAllDriversToAllControllers();
2331 ScanVolumes();
2332 ScanForBootloaders();
2333 ScanForTools();
2334 SetupScreen();
2335 } // VOID RescanAll()
2336
2337 #ifdef __MAKEWITH_TIANO
2338
2339 // Minimal initialization function
2340 static VOID InitializeLib(IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable) {
2341 gST = SystemTable;
2342 // gImageHandle = ImageHandle;
2343 gBS = SystemTable->BootServices;
2344 // gRS = SystemTable->RuntimeServices;
2345 gRT = SystemTable->RuntimeServices; // Some BDS functions need gRT to be set
2346 EfiGetSystemConfigurationTable (&gEfiDxeServicesTableGuid, (VOID **) &gDS);
2347
2348 InitializeConsoleSim();
2349 }
2350
2351 #endif
2352
2353 // Set up our own Secure Boot extensions....
2354 // Returns TRUE on success, FALSE otherwise
2355 static BOOLEAN SecureBootSetup(VOID) {
2356 EFI_STATUS Status;
2357 BOOLEAN Success = FALSE;
2358
2359 if (secure_mode() && ShimLoaded()) {
2360 Status = security_policy_install();
2361 if (Status == EFI_SUCCESS) {
2362 Success = TRUE;
2363 } else {
2364 Print(L"Failed to install MOK Secure Boot extensions");
2365 }
2366 }
2367 return Success;
2368 } // VOID SecureBootSetup()
2369
2370 // Remove our own Secure Boot extensions....
2371 // Returns TRUE on success, FALSE otherwise
2372 static BOOLEAN SecureBootUninstall(VOID) {
2373 EFI_STATUS Status;
2374 BOOLEAN Success = TRUE;
2375
2376 if (secure_mode()) {
2377 Status = security_policy_uninstall();
2378 if (Status != EFI_SUCCESS) {
2379 Success = FALSE;
2380 BeginTextScreen(L"Secure Boot Policy Failure");
2381 Print(L"Failed to uninstall MOK Secure Boot extensions; forcing a reboot.");
2382 PauseForKey();
2383 refit_call4_wrapper(RT->ResetSystem, EfiResetCold, EFI_SUCCESS, 0, NULL);
2384 }
2385 }
2386 return Success;
2387 } // VOID SecureBootUninstall
2388
2389 //
2390 // main entry point
2391 //
2392 EFI_STATUS
2393 EFIAPI
2394 efi_main (EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable)
2395 {
2396 EFI_STATUS Status;
2397 BOOLEAN MainLoopRunning = TRUE;
2398 BOOLEAN MokProtocol;
2399 REFIT_MENU_ENTRY *ChosenEntry;
2400 UINTN MenuExit, i;
2401 CHAR16 *Selection = NULL;
2402 EG_PIXEL BGColor;
2403
2404 // bootstrap
2405 InitializeLib(ImageHandle, SystemTable);
2406 Status = InitRefitLib(ImageHandle);
2407 if (EFI_ERROR(Status))
2408 return Status;
2409
2410 // read configuration
2411 CopyMem(GlobalConfig.ScanFor, "ieom ", NUM_SCAN_OPTIONS);
2412 FindLegacyBootType();
2413 if (GlobalConfig.LegacyType == LEGACY_TYPE_MAC)
2414 CopyMem(GlobalConfig.ScanFor, "ihebocm ", NUM_SCAN_OPTIONS);
2415 ReadConfig(CONFIG_FILE_NAME);
2416 ScanVolumes();
2417
2418 InitScreen();
2419 WarnIfLegacyProblems();
2420 MainMenu.TimeoutSeconds = GlobalConfig.Timeout;
2421
2422 // disable EFI watchdog timer
2423 refit_call4_wrapper(BS->SetWatchdogTimer, 0x0000, 0x0000, 0x0000, NULL);
2424
2425 // further bootstrap (now with config available)
2426 MokProtocol = SecureBootSetup();
2427 LoadDrivers();
2428 ScanForBootloaders();
2429 ScanForTools();
2430 SetupScreen();
2431
2432 if (GlobalConfig.ScanDelay > 0) {
2433 BGColor.b = 255;
2434 BGColor.g = 175;
2435 BGColor.r = 100;
2436 BGColor.a = 0;
2437 egDisplayMessage(L"Pausing before disk scan; please wait....", &BGColor);
2438 for (i = 0; i < GlobalConfig.ScanDelay; i++)
2439 refit_call1_wrapper(BS->Stall, 1000000);
2440 RescanAll();
2441 } // if
2442
2443 if (GlobalConfig.DefaultSelection)
2444 Selection = StrDuplicate(GlobalConfig.DefaultSelection);
2445
2446 while (MainLoopRunning) {
2447 MenuExit = RunMainMenu(&MainMenu, Selection, &ChosenEntry);
2448
2449 // The Escape key triggers a re-scan operation....
2450 if (MenuExit == MENU_EXIT_ESCAPE) {
2451 RescanAll();
2452 continue;
2453 }
2454
2455 switch (ChosenEntry->Tag) {
2456
2457 case TAG_REBOOT: // Reboot
2458 TerminateScreen();
2459 refit_call4_wrapper(RT->ResetSystem, EfiResetCold, EFI_SUCCESS, 0, NULL);
2460 MainLoopRunning = FALSE; // just in case we get this far
2461 break;
2462
2463 case TAG_SHUTDOWN: // Shut Down
2464 TerminateScreen();
2465 refit_call4_wrapper(RT->ResetSystem, EfiResetShutdown, EFI_SUCCESS, 0, NULL);
2466 MainLoopRunning = FALSE; // just in case we get this far
2467 break;
2468
2469 case TAG_ABOUT: // About rEFInd
2470 AboutrEFInd();
2471 break;
2472
2473 case TAG_LOADER: // Boot OS via .EFI loader
2474 StartLoader((LOADER_ENTRY *)ChosenEntry);
2475 break;
2476
2477 case TAG_LEGACY: // Boot legacy OS
2478 StartLegacy((LEGACY_ENTRY *)ChosenEntry);
2479 break;
2480
2481 #ifdef __MAKEWITH_TIANO
2482 case TAG_LEGACY_UEFI: // Boot a legacy OS on a non-Mac
2483 StartLegacyUEFI((LEGACY_ENTRY *)ChosenEntry);
2484 break;
2485 #endif
2486
2487 case TAG_TOOL: // Start a EFI tool
2488 StartTool((LOADER_ENTRY *)ChosenEntry);
2489 break;
2490
2491 case TAG_EXIT: // Terminate rEFInd
2492 if ((MokProtocol) && !SecureBootUninstall()) {
2493 MainLoopRunning = FALSE; // just in case we get this far
2494 } else {
2495 BeginTextScreen(L" ");
2496 return EFI_SUCCESS;
2497 }
2498 break;
2499
2500 case TAG_FIRMWARE: // Reboot into firmware's user interface
2501 RebootIntoFirmware();
2502 break;
2503
2504 } // switch()
2505 MyFreePool(Selection);
2506 Selection = (ChosenEntry->Title) ? StrDuplicate(ChosenEntry->Title) : NULL;
2507 } // while()
2508
2509 // If we end up here, things have gone wrong. Try to reboot, and if that
2510 // fails, go into an endless loop.
2511 refit_call4_wrapper(RT->ResetSystem, EfiResetCold, EFI_SUCCESS, 0, NULL);
2512 EndlessIdleLoop();
2513
2514 return EFI_SUCCESS;
2515 } /* efi_main() */