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