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