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