3 * Main code for the boot menu
5 * Copyright (c) 2006-2010 Christoph Pfisterer
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions are
12 * * Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
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
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.
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.
37 * Modifications copyright (c) 2012-2013 Roderick W. Smith
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.
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"
58 #ifdef __MAKEWITH_GNUEFI
59 #define EFI_SECURITY_VIOLATION EFIERR (26)
61 #include "../EfiLib/BdsHelper.h"
62 #include "../EfiLib/legacy.h"
63 #endif // __MAKEWITH_GNUEFI
65 #ifndef EFI_OS_INDICATIONS_BOOT_TO_FW_UI
66 #define EFI_OS_INDICATIONS_BOOT_TO_FW_UI 0x0000000000000001ULL
72 #define MACOSX_LOADER_PATH L"System\\Library\\CoreServices\\boot.efi"
74 #define SHELL_NAMES L"\\EFI\\tools\\shell.efi,\\EFI\\tools\\shellx64.efi,\\shell.efi,\\shellx64.efi"
75 #define GPTSYNC_NAMES L"\\EFI\\tools\\gptsync.efi,\\EFI\\tools\\gptsync_x64.efi"
76 #define MEMTEST_NAMES L"memtest86.efi,memtest86_x64.efi,memtest86x64.efi,bootx64.efi"
77 #define DRIVER_DIRS L"drivers,drivers_x64"
78 #define FALLBACK_FULLNAME L"EFI\\BOOT\\bootx64.efi"
79 #define FALLBACK_BASENAME L"bootx64.efi"
80 #define EFI_STUB_ARCH 0x8664
82 #define SHELL_NAMES L"\\EFI\\tools\\shell.efi,\\EFI\\tools\\shellia32.efi,\\shell.efi,\\shellia32.efi"
83 #define GPTSYNC_NAMES L"\\EFI\\tools\\gptsync.efi,\\EFI\\tools\\gptsync_ia32.efi"
84 #define MEMTEST_NAMES L"memtest86.efi,memtest86_ia32.efi,memtest86ia32.efi,bootia32.efi"
85 #define DRIVER_DIRS L"drivers,drivers_ia32"
86 #define FALLBACK_FULLNAME L"EFI\\BOOT\\bootia32.efi"
87 #define FALLBACK_BASENAME L"bootia32.efi"
88 #define EFI_STUB_ARCH 0x014c
90 #define SHELL_NAMES L"\\EFI\\tools\\shell.efi,\\shell.efi"
91 #define GPTSYNC_NAMES L"\\EFI\\tools\\gptsync.efi"
92 #define MEMTEST_NAMES L"memtest86.efi"
93 #define DRIVER_DIRS L"drivers"
94 #define FALLBACK_FULLNAME L"EFI\\BOOT\\boot.efi" /* Not really correct */
95 #define FALLBACK_BASENAME L"boot.efi" /* Not really correct */
97 #define FAT_ARCH 0x0ef1fab9 /* ID for Apple "fat" binary */
99 // Filename patterns that identify EFI boot loaders. Note that a single case (either L"*.efi" or
100 // L"*.EFI") is fine for most systems; but Gigabyte's buggy Hybrid EFI does a case-sensitive
101 // comparison when it should do a case-insensitive comparison, so I'm doubling this up. It does
102 // no harm on other computers, AFAIK. In theory, every case variation should be done for
103 // completeness, but that's ridiculous....
104 #define LOADER_MATCH_PATTERNS L"*.efi,*.EFI"
106 // Patterns that identify Linux kernels. Added to the loader match pattern when the
107 // scan_all_linux_kernels option is set in the configuration file. Causes kernels WITHOUT
108 // a ".efi" extension to be found when scanning for boot loaders.
109 #define LINUX_MATCH_PATTERNS L"vmlinuz*,bzImage*"
111 // Default hint text for program-launch submenus
112 #define SUBSCREEN_HINT1 L"Use arrow keys to move cursor; Enter to boot;"
113 #define SUBSCREEN_HINT2 L"Insert or F2 to edit options; Esc to return to main menu"
114 #define SUBSCREEN_HINT2_NO_EDITOR L"Esc to return to main menu"
118 #define TYPE_LEGACY 2
120 static REFIT_MENU_ENTRY MenuEntryAbout
= { L
"About rEFInd", TAG_ABOUT
, 1, 0, 'A', NULL
, NULL
, NULL
};
121 static REFIT_MENU_ENTRY MenuEntryReset
= { L
"Reboot Computer", TAG_REBOOT
, 1, 0, 'R', NULL
, NULL
, NULL
};
122 static REFIT_MENU_ENTRY MenuEntryShutdown
= { L
"Shut Down Computer", TAG_SHUTDOWN
, 1, 0, 'U', NULL
, NULL
, NULL
};
123 static REFIT_MENU_ENTRY MenuEntryReturn
= { L
"Return to Main Menu", TAG_RETURN
, 1, 0, 0, NULL
, NULL
, NULL
};
124 static REFIT_MENU_ENTRY MenuEntryExit
= { L
"Exit rEFInd", TAG_EXIT
, 1, 0, 0, NULL
, NULL
, NULL
};
125 static REFIT_MENU_ENTRY MenuEntryFirmware
= { L
"Reboot to Computer Setup Utility", TAG_FIRMWARE
, 1, 0, 0, NULL
, NULL
, NULL
};
127 static REFIT_MENU_SCREEN MainMenu
= { L
"Main Menu", NULL
, 0, NULL
, 0, NULL
, 0, L
"Automatic boot",
128 L
"Use arrow keys to move cursor; Enter to boot;",
129 L
"Insert or F2 for more options; Esc to refresh" };
130 static REFIT_MENU_SCREEN AboutMenu
= { L
"About", NULL
, 0, NULL
, 0, NULL
, 0, NULL
, L
"Press Enter to return to main menu", L
"" };
132 REFIT_CONFIG GlobalConfig
= { FALSE
, FALSE
, 0, 0, 0, DONT_CHANGE_TEXT_MODE
, 20, 0, 0, GRAPHICS_FOR_OSX
, LEGACY_TYPE_MAC
, 0, 0,
133 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
134 { TAG_SHELL
, TAG_MEMTEST
, TAG_APPLE_RECOVERY
, TAG_MOK_TOOL
, TAG_ABOUT
, TAG_SHUTDOWN
,
135 TAG_REBOOT
, TAG_FIRMWARE
, 0, 0, 0, 0, 0, 0 }
138 EFI_GUID GlobalGuid
= EFI_GLOBAL_VARIABLE
;
140 // Structure used to hold boot loader filenames and time stamps in
141 // a linked list; used to sort entries within a directory.
145 struct LOADER_LIST
*NextEntry
;
152 static VOID
AboutrEFInd(VOID
)
154 if (AboutMenu
.EntryCount
== 0) {
155 AboutMenu
.TitleImage
= BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT
);
156 AddMenuInfoLine(&AboutMenu
, L
"rEFInd Version 0.7.4.2");
157 AddMenuInfoLine(&AboutMenu
, L
"");
158 AddMenuInfoLine(&AboutMenu
, L
"Copyright (c) 2006-2010 Christoph Pfisterer");
159 AddMenuInfoLine(&AboutMenu
, L
"Copyright (c) 2012-2013 Roderick W. Smith");
160 AddMenuInfoLine(&AboutMenu
, L
"Portions Copyright (c) Intel Corporation and others");
161 AddMenuInfoLine(&AboutMenu
, L
"Distributed under the terms of the GNU GPLv3 license");
162 AddMenuInfoLine(&AboutMenu
, L
"");
163 AddMenuInfoLine(&AboutMenu
, L
"Running on:");
164 AddMenuInfoLine(&AboutMenu
, PoolPrint(L
" EFI Revision %d.%02d", ST
->Hdr
.Revision
>> 16, ST
->Hdr
.Revision
& ((1 << 16) - 1)));
166 AddMenuInfoLine(&AboutMenu
, L
" Platform: x86 (32 bit)");
167 #elif defined(EFIX64)
168 AddMenuInfoLine(&AboutMenu
, PoolPrint(L
" Platform: x86_64 (64 bit); Secure Boot %s",
169 secure_mode() ? L
"active" : L
"inactive"));
171 AddMenuInfoLine(&AboutMenu
, L
" Platform: unknown");
173 AddMenuInfoLine(&AboutMenu
, PoolPrint(L
" Firmware: %s %d.%02d", ST
->FirmwareVendor
, ST
->FirmwareRevision
>> 16,
174 ST
->FirmwareRevision
& ((1 << 16) - 1)));
175 AddMenuInfoLine(&AboutMenu
, PoolPrint(L
" Screen Output: %s", egScreenDescription()));
176 AddMenuInfoLine(&AboutMenu
, L
"");
177 #if defined(__MAKEWITH_GNUEFI)
178 AddMenuInfoLine(&AboutMenu
, L
"Built with GNU-EFI");
180 AddMenuInfoLine(&AboutMenu
, L
"Built with TianoCore EDK2");
182 AddMenuInfoLine(&AboutMenu
, L
"");
183 AddMenuInfoLine(&AboutMenu
, L
"For more information, see the rEFInd Web site:");
184 AddMenuInfoLine(&AboutMenu
, L
"http://www.rodsbooks.com/refind/");
185 AddMenuEntry(&AboutMenu
, &MenuEntryReturn
);
188 RunMenu(&AboutMenu
, NULL
);
189 } /* VOID AboutrEFInd() */
191 static VOID
WarnSecureBootError(CHAR16
*Name
, BOOLEAN Verbose
) {
193 Name
= L
"the loader";
195 refit_call2_wrapper(ST
->ConOut
->SetAttribute
, ST
->ConOut
, ATTR_ERROR
);
196 Print(L
"Secure Boot validation failure loading %s!\n", Name
);
197 refit_call2_wrapper(ST
->ConOut
->SetAttribute
, ST
->ConOut
, ATTR_BASIC
);
198 if (Verbose
&& secure_mode()) {
199 Print(L
"\nThis computer is configured with Secure Boot active, but\n%s has failed validation.\n", Name
);
200 Print(L
"\nYou can:\n * Launch another boot loader\n");
201 Print(L
" * Disable Secure Boot in your firmware\n");
202 Print(L
" * Sign %s with a machine owner key (MOK)\n", Name
);
203 Print(L
" * Use a MOK utility (often present on the second row) to add a MOK with which\n");
204 Print(L
" %s has already been signed.\n", Name
);
205 Print(L
" * Use a MOK utility to register %s (\"enroll its hash\") without\n", Name
);
206 Print(L
" signing it.\n");
207 Print(L
"\nSee http://www.rodsbooks.com/refind/secureboot.html for more information\n");
210 } // VOID WarnSecureBootError()
212 // Returns TRUE if this file is a valid EFI loader file, and is proper ARCH
213 static BOOLEAN
IsValidLoader(EFI_FILE
*RootDir
, CHAR16
*FileName
) {
214 BOOLEAN IsValid
= TRUE
;
215 #if defined (EFIX64) | defined (EFI32)
217 EFI_FILE_HANDLE FileHandle
;
219 UINTN Size
= sizeof(Header
);
221 if ((RootDir
== NULL
) || (FileName
== NULL
)) {
222 // Assume valid here, because Macs produce NULL RootDir (& maybe FileName)
223 // when launching from a Firewire drive. This should be handled better, but
224 // fix would have to be in StartEFIImageList() and/or in FindVolumeAndFilename().
228 Status
= refit_call5_wrapper(RootDir
->Open
, RootDir
, &FileHandle
, FileName
, EFI_FILE_MODE_READ
, 0);
229 if (EFI_ERROR(Status
))
232 Status
= refit_call3_wrapper(FileHandle
->Read
, FileHandle
, &Size
, Header
);
233 refit_call1_wrapper(FileHandle
->Close
, FileHandle
);
235 IsValid
= !EFI_ERROR(Status
) &&
236 Size
== sizeof(Header
) &&
237 ((Header
[0] == 'M' && Header
[1] == 'Z' &&
238 (Size
= *(UINT32
*)&Header
[0x3c]) < 0x180 &&
239 Header
[Size
] == 'P' && Header
[Size
+1] == 'E' &&
240 Header
[Size
+2] == 0 && Header
[Size
+3] == 0 &&
241 *(UINT16
*)&Header
[Size
+4] == EFI_STUB_ARCH
) ||
242 (*(UINT32
*)&Header
== FAT_ARCH
));
245 } // BOOLEAN IsValidLoader()
247 // Launch an EFI binary.
248 static EFI_STATUS
StartEFIImageList(IN EFI_DEVICE_PATH
**DevicePaths
,
249 IN CHAR16
*LoadOptions
, IN UINTN LoaderType
,
250 IN CHAR16
*ImageTitle
, IN CHAR8 OSType
,
251 OUT UINTN
*ErrorInStep
,
254 EFI_STATUS Status
, ReturnStatus
;
255 EFI_HANDLE ChildImageHandle
;
256 EFI_LOADED_IMAGE
*ChildLoadedImage
= NULL
;
257 REFIT_VOLUME
*Volume
= NULL
;
258 UINTN DevicePathIndex
;
259 CHAR16 ErrorInfo
[256];
260 CHAR16
*FullLoadOptions
= NULL
;
261 CHAR16
*Filename
= NULL
;
264 if (ErrorInStep
!= NULL
)
268 if (LoadOptions
!= NULL
) {
269 FullLoadOptions
= StrDuplicate(LoadOptions
);
270 if ((LoaderType
== TYPE_EFI
) && (OSType
== 'M')) {
271 MergeStrings(&FullLoadOptions
, L
" ", 0);
272 // NOTE: That last space is also added by the EFI shell and seems to be significant
273 // when passing options to Apple's boot.efi...
275 } // if (LoadOptions != NULL)
277 Print(L
"Starting %s\nUsing load options '%s'\n", ImageTitle
, FullLoadOptions
? FullLoadOptions
: L
"");
279 // load the image into memory (and execute it, in the case of a shim/MOK image).
280 ReturnStatus
= Status
= EFI_NOT_FOUND
; // in case the list is empty
281 for (DevicePathIndex
= 0; DevicePaths
[DevicePathIndex
] != NULL
; DevicePathIndex
++) {
282 FindVolumeAndFilename(DevicePaths
[DevicePathIndex
], &Volume
, &Filename
);
283 // Some EFIs crash if attempting to load driver for invalid architecture, so
284 // protect for this condition; but sometimes Volume comes back NULL, so provide
285 // an exception. (TODO: Handle this special condition better.)
286 if ((LoaderType
== TYPE_LEGACY
) || (Volume
== NULL
) || IsValidLoader(Volume
->RootDir
, Filename
)) {
287 if (Filename
&& (LoaderType
!= TYPE_LEGACY
)) {
288 Temp
= PoolPrint(L
"\\%s %s", Filename
, FullLoadOptions
? FullLoadOptions
: L
"");
290 MyFreePool(FullLoadOptions
);
291 FullLoadOptions
= Temp
;
295 // NOTE: Below commented-out line could be more efficient if file were read ahead of
296 // time and passed as a pre-loaded image to LoadImage(), but it doesn't work on my
297 // 32-bit Mac Mini or my 64-bit Intel box when launching a Linux kernel; the
298 // kernel returns a "Failed to handle fs_proto" error message.
299 // TODO: Track down the cause of this error and fix it, if possible.
300 // ReturnStatus = Status = refit_call6_wrapper(BS->LoadImage, FALSE, SelfImageHandle, DevicePaths[DevicePathIndex],
301 // ImageData, ImageSize, &ChildImageHandle);
302 ReturnStatus
= Status
= refit_call6_wrapper(BS
->LoadImage
, FALSE
, SelfImageHandle
, DevicePaths
[DevicePathIndex
],
303 NULL
, 0, &ChildImageHandle
);
305 Print(L
"Invalid loader file!\n");
306 ReturnStatus
= EFI_LOAD_ERROR
;
308 if (ReturnStatus
!= EFI_NOT_FOUND
) {
312 if ((Status
== EFI_ACCESS_DENIED
) || (Status
== EFI_SECURITY_VIOLATION
)) {
313 WarnSecureBootError(ImageTitle
, Verbose
);
316 SPrint(ErrorInfo
, 255, L
"while loading %s", ImageTitle
);
317 if (CheckError(Status
, ErrorInfo
)) {
318 if (ErrorInStep
!= NULL
)
323 ReturnStatus
= Status
= refit_call3_wrapper(BS
->HandleProtocol
, ChildImageHandle
, &LoadedImageProtocol
,
324 (VOID
**) &ChildLoadedImage
);
325 if (CheckError(Status
, L
"while getting a LoadedImageProtocol handle")) {
326 if (ErrorInStep
!= NULL
)
330 ChildLoadedImage
->LoadOptions
= (VOID
*)FullLoadOptions
;
331 ChildLoadedImage
->LoadOptionsSize
= FullLoadOptions
? ((UINT32
)StrLen(FullLoadOptions
) + 1) * sizeof(CHAR16
) : 0;
332 // turn control over to the image
333 // TODO: (optionally) re-enable the EFI watchdog timer!
335 // close open file handles
337 ReturnStatus
= Status
= refit_call3_wrapper(BS
->StartImage
, ChildImageHandle
, NULL
, NULL
);
339 // control returns here when the child image calls Exit()
340 SPrint(ErrorInfo
, 255, L
"returned from %s", ImageTitle
);
341 if (CheckError(Status
, ErrorInfo
)) {
342 if (ErrorInStep
!= NULL
)
346 // re-open file handles
350 // unload the image, we don't care if it works or not...
351 Status
= refit_call1_wrapper(BS
->UnloadImage
, ChildImageHandle
);
354 MyFreePool(FullLoadOptions
);
356 } /* static EFI_STATUS StartEFIImageList() */
358 static EFI_STATUS
StartEFIImage(IN EFI_DEVICE_PATH
*DevicePath
,
359 IN CHAR16
*LoadOptions
, IN UINTN LoaderType
,
360 IN CHAR16
*ImageTitle
, IN CHAR8 OSType
,
361 OUT UINTN
*ErrorInStep
,
364 EFI_DEVICE_PATH
*DevicePaths
[2];
366 DevicePaths
[0] = DevicePath
;
367 DevicePaths
[1] = NULL
;
368 return StartEFIImageList(DevicePaths
, LoadOptions
, LoaderType
, ImageTitle
, OSType
, ErrorInStep
, Verbose
);
369 } /* static EFI_STATUS StartEFIImage() */
371 // From gummiboot: Retrieve a raw EFI variable.
372 // Returns EFI status
373 static EFI_STATUS
EfivarGetRaw(EFI_GUID
*vendor
, CHAR16
*name
, CHAR8
**buffer
, UINTN
*size
) {
378 l
= sizeof(CHAR16
*) * EFI_MAXIMUM_VARIABLE_SIZE
;
379 buf
= AllocatePool(l
);
381 return EFI_OUT_OF_RESOURCES
;
383 err
= refit_call5_wrapper(RT
->GetVariable
, name
, vendor
, NULL
, &l
, buf
);
384 if (EFI_ERROR(err
) == EFI_SUCCESS
) {
391 } // EFI_STATUS EfivarGetRaw()
393 // From gummiboot: Set an EFI variable
394 static EFI_STATUS
EfivarSetRaw(EFI_GUID
*vendor
, CHAR16
*name
, CHAR8
*buf
, UINTN size
, BOOLEAN persistent
) {
397 flags
= EFI_VARIABLE_BOOTSERVICE_ACCESS
|EFI_VARIABLE_RUNTIME_ACCESS
;
399 flags
|= EFI_VARIABLE_NON_VOLATILE
;
401 return refit_call5_wrapper(RT
->SetVariable
, name
, vendor
, flags
, size
, buf
);
402 } // EFI_STATUS EfivarSetRaw()
404 // From gummiboot: Reboot the computer into its built-in user interface
405 static EFI_STATUS
RebootIntoFirmware(VOID
) {
411 osind
= EFI_OS_INDICATIONS_BOOT_TO_FW_UI
;
413 err
= EfivarGetRaw(&GlobalGuid
, L
"OsIndications", &b
, &size
);
414 if (err
== EFI_SUCCESS
)
418 err
= EfivarSetRaw(&GlobalGuid
, L
"OsIndications", (CHAR8
*)&osind
, sizeof(UINT64
), TRUE
);
419 if (err
!= EFI_SUCCESS
)
422 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);
423 Print(L
"Error calling ResetSystem: %r", err
);
430 // EFI OS loader functions
433 static VOID
StartLoader(LOADER_ENTRY
*Entry
)
435 UINTN ErrorInStep
= 0;
437 BeginExternalScreen(Entry
->UseGraphicsMode
, L
"Booting OS");
438 StartEFIImage(Entry
->DevicePath
, Entry
->LoadOptions
, TYPE_EFI
,
439 Basename(Entry
->LoaderPath
), Entry
->OSType
, &ErrorInStep
, !Entry
->UseGraphicsMode
);
440 FinishExternalScreen();
443 // Locate an initrd or initramfs file that matches the kernel specified by LoaderPath.
444 // The matching file has a name that begins with "init" and includes the same version
445 // number string as is found in LoaderPath -- but not a longer version number string.
446 // For instance, if LoaderPath is \EFI\kernels\bzImage-3.3.0.efi, and if \EFI\kernels
447 // has a file called initramfs-3.3.0.img, this function will return the string
448 // '\EFI\kernels\initramfs-3.3.0.img'. If the directory ALSO contains the file
449 // initramfs-3.3.0-rc7.img or initramfs-13.3.0.img, those files will NOT match;
450 // however, initmine-3.3.0.img might match. (FindInitrd() returns the first match it
451 // finds). Thus, care should be taken to avoid placing duplicate matching files in
452 // the kernel's directory.
453 // If no matching init file can be found, returns NULL.
454 static CHAR16
* FindInitrd(IN CHAR16
*LoaderPath
, IN REFIT_VOLUME
*Volume
) {
455 CHAR16
*InitrdName
= NULL
, *FileName
, *KernelVersion
, *InitrdVersion
, *Path
;
456 REFIT_DIR_ITER DirIter
;
457 EFI_FILE_INFO
*DirEntry
;
459 FileName
= Basename(LoaderPath
);
460 KernelVersion
= FindNumbers(FileName
);
461 Path
= FindPath(LoaderPath
);
463 // Add trailing backslash for root directory; necessary on some systems, but must
464 // NOT be added to all directories, since on other systems, a trailing backslash on
465 // anything but the root directory causes them to flake out!
466 if (StrLen(Path
) == 0) {
467 MergeStrings(&Path
, L
"\\", 0);
469 DirIterOpen(Volume
->RootDir
, Path
, &DirIter
);
470 // Now add a trailing backslash if it was NOT added earlier, for consistency in
471 // building the InitrdName later....
472 if ((StrLen(Path
) > 0) && (Path
[StrLen(Path
) - 1] != L
'\\'))
473 MergeStrings(&Path
, L
"\\", 0);
474 while ((DirIterNext(&DirIter
, 2, L
"init*", &DirEntry
)) && (InitrdName
== NULL
)) {
475 InitrdVersion
= FindNumbers(DirEntry
->FileName
);
476 if (KernelVersion
!= NULL
) {
477 if (StriCmp(InitrdVersion
, KernelVersion
) == 0) {
478 InitrdName
= PoolPrint(L
"%s%s", Path
, DirEntry
->FileName
);
481 if (InitrdVersion
== NULL
) {
482 InitrdName
= PoolPrint(L
"%s%s", Path
, DirEntry
->FileName
);
485 MyFreePool(InitrdVersion
);
487 DirIterClose(&DirIter
);
489 // Note: Don't FreePool(FileName), since Basename returns a pointer WITHIN the string it's passed.
490 MyFreePool(KernelVersion
);
493 } // static CHAR16 * FindInitrd()
495 LOADER_ENTRY
* AddPreparedLoaderEntry(LOADER_ENTRY
*Entry
) {
496 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
499 } // LOADER_ENTRY * AddPreparedLoaderEntry()
501 // Creates a copy of a menu screen.
502 // Returns a pointer to the copy of the menu screen.
503 static REFIT_MENU_SCREEN
* CopyMenuScreen(REFIT_MENU_SCREEN
*Entry
) {
504 REFIT_MENU_SCREEN
*NewEntry
;
507 NewEntry
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
508 if ((Entry
!= NULL
) && (NewEntry
!= NULL
)) {
509 CopyMem(NewEntry
, Entry
, sizeof(REFIT_MENU_SCREEN
));
510 NewEntry
->Title
= (Entry
->Title
) ? StrDuplicate(Entry
->Title
) : NULL
;
511 NewEntry
->TimeoutText
= (Entry
->TimeoutText
) ? StrDuplicate(Entry
->TimeoutText
) : NULL
;
512 if (Entry
->TitleImage
!= NULL
) {
513 NewEntry
->TitleImage
= AllocatePool(sizeof(EG_IMAGE
));
514 if (NewEntry
->TitleImage
!= NULL
)
515 CopyMem(NewEntry
->TitleImage
, Entry
->TitleImage
, sizeof(EG_IMAGE
));
517 NewEntry
->InfoLines
= (CHAR16
**) AllocateZeroPool(Entry
->InfoLineCount
* (sizeof(CHAR16
*)));
518 for (i
= 0; i
< Entry
->InfoLineCount
&& NewEntry
->InfoLines
; i
++) {
519 NewEntry
->InfoLines
[i
] = (Entry
->InfoLines
[i
]) ? StrDuplicate(Entry
->InfoLines
[i
]) : NULL
;
521 NewEntry
->Entries
= (REFIT_MENU_ENTRY
**) AllocateZeroPool(Entry
->EntryCount
* (sizeof (REFIT_MENU_ENTRY
*)));
522 for (i
= 0; i
< Entry
->EntryCount
&& NewEntry
->Entries
; i
++) {
523 AddMenuEntry(NewEntry
, Entry
->Entries
[i
]);
525 NewEntry
->Hint1
= (Entry
->Hint1
) ? StrDuplicate(Entry
->Hint1
) : NULL
;
526 NewEntry
->Hint2
= (Entry
->Hint2
) ? StrDuplicate(Entry
->Hint2
) : NULL
;
529 } // static REFIT_MENU_SCREEN* CopyMenuScreen()
531 // Creates a copy of a menu entry. Intended to enable moving a stack-based
532 // menu entry (such as the ones for the "reboot" and "exit" functions) to
533 // to the heap. This enables easier deletion of the whole set of menu
534 // entries when re-scanning.
535 // Returns a pointer to the copy of the menu entry.
536 static REFIT_MENU_ENTRY
* CopyMenuEntry(REFIT_MENU_ENTRY
*Entry
) {
537 REFIT_MENU_ENTRY
*NewEntry
;
539 NewEntry
= AllocateZeroPool(sizeof(REFIT_MENU_ENTRY
));
540 if ((Entry
!= NULL
) && (NewEntry
!= NULL
)) {
541 CopyMem(NewEntry
, Entry
, sizeof(REFIT_MENU_ENTRY
));
542 NewEntry
->Title
= (Entry
->Title
) ? StrDuplicate(Entry
->Title
) : NULL
;
543 if (Entry
->BadgeImage
!= NULL
) {
544 NewEntry
->BadgeImage
= AllocatePool(sizeof(EG_IMAGE
));
545 if (NewEntry
->BadgeImage
!= NULL
)
546 CopyMem(NewEntry
->BadgeImage
, Entry
->BadgeImage
, sizeof(EG_IMAGE
));
548 if (Entry
->Image
!= NULL
) {
549 NewEntry
->Image
= AllocatePool(sizeof(EG_IMAGE
));
550 if (NewEntry
->Image
!= NULL
)
551 CopyMem(NewEntry
->Image
, Entry
->Image
, sizeof(EG_IMAGE
));
553 if (Entry
->SubScreen
!= NULL
) {
554 NewEntry
->SubScreen
= CopyMenuScreen(Entry
->SubScreen
);
558 } // REFIT_MENU_ENTRY* CopyMenuEntry()
560 // Creates a new LOADER_ENTRY data structure and populates it with
561 // default values from the specified Entry, or NULL values if Entry
562 // is unspecified (NULL).
563 // Returns a pointer to the new data structure, or NULL if it
564 // couldn't be allocated
565 LOADER_ENTRY
*InitializeLoaderEntry(IN LOADER_ENTRY
*Entry
) {
566 LOADER_ENTRY
*NewEntry
= NULL
;
568 NewEntry
= AllocateZeroPool(sizeof(LOADER_ENTRY
));
569 if (NewEntry
!= NULL
) {
570 NewEntry
->me
.Title
= NULL
;
571 NewEntry
->me
.Tag
= TAG_LOADER
;
572 NewEntry
->Enabled
= TRUE
;
573 NewEntry
->UseGraphicsMode
= FALSE
;
574 NewEntry
->OSType
= 0;
576 NewEntry
->LoaderPath
= (Entry
->LoaderPath
) ? StrDuplicate(Entry
->LoaderPath
) : NULL
;
577 NewEntry
->VolName
= (Entry
->VolName
) ? StrDuplicate(Entry
->VolName
) : NULL
;
578 NewEntry
->DevicePath
= Entry
->DevicePath
;
579 NewEntry
->UseGraphicsMode
= Entry
->UseGraphicsMode
;
580 NewEntry
->LoadOptions
= (Entry
->LoadOptions
) ? StrDuplicate(Entry
->LoadOptions
) : NULL
;
581 NewEntry
->InitrdPath
= (Entry
->InitrdPath
) ? StrDuplicate(Entry
->InitrdPath
) : NULL
;
585 } // LOADER_ENTRY *InitializeLoaderEntry()
587 // Adds InitrdPath to Options, but only if Options doesn't already include an
588 // initrd= line. Done to enable overriding the default initrd selection in a
589 // refind_linux.conf file's options list.
590 // Returns a pointer to a new string. The calling function is responsible for
591 // freeing its memory.
592 static CHAR16
*AddInitrdToOptions(CHAR16
*Options
, CHAR16
*InitrdPath
) {
593 CHAR16
*NewOptions
= NULL
;
596 NewOptions
= StrDuplicate(Options
);
597 if ((InitrdPath
!= NULL
) && !StriSubCmp(L
"initrd=", Options
)) {
598 MergeStrings(&NewOptions
, L
"initrd=", L
' ');
599 MergeStrings(&NewOptions
, InitrdPath
, 0);
602 } // CHAR16 *AddInitrdToOptions()
604 // Prepare a REFIT_MENU_SCREEN data structure for a subscreen entry. This sets up
605 // the default entry that launches the boot loader using the same options as the
606 // main Entry does. Subsequent options can be added by the calling function.
607 // If a subscreen already exists in the Entry that's passed to this function,
608 // it's left unchanged and a pointer to it is returned.
609 // Returns a pointer to the new subscreen data structure, or NULL if there
610 // were problems allocating memory.
611 REFIT_MENU_SCREEN
*InitializeSubScreen(IN LOADER_ENTRY
*Entry
) {
612 CHAR16
*FileName
, *MainOptions
= NULL
;
613 REFIT_MENU_SCREEN
*SubScreen
= NULL
;
614 LOADER_ENTRY
*SubEntry
;
616 FileName
= Basename(Entry
->LoaderPath
);
617 if (Entry
->me
.SubScreen
== NULL
) { // No subscreen yet; initialize default entry....
618 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
619 if (SubScreen
!= NULL
) {
620 SubScreen
->Title
= AllocateZeroPool(sizeof(CHAR16
) * 256);
621 SPrint(SubScreen
->Title
, 255, L
"Boot Options for %s on %s",
622 (Entry
->Title
!= NULL
) ? Entry
->Title
: FileName
, Entry
->VolName
);
623 SubScreen
->TitleImage
= Entry
->me
.Image
;
625 SubEntry
= InitializeLoaderEntry(Entry
);
626 if (SubEntry
!= NULL
) {
627 SubEntry
->me
.Title
= StrDuplicate(L
"Boot using default options");
628 MainOptions
= SubEntry
->LoadOptions
;
629 SubEntry
->LoadOptions
= AddInitrdToOptions(MainOptions
, SubEntry
->InitrdPath
);
630 MyFreePool(MainOptions
);
631 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
632 } // if (SubEntry != NULL)
633 SubScreen
->Hint1
= StrDuplicate(SUBSCREEN_HINT1
);
634 if (GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_EDITOR
) {
635 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2_NO_EDITOR
);
637 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2
);
639 } // if (SubScreen != NULL)
640 } else { // existing subscreen; less initialization, and just add new entry later....
641 SubScreen
= Entry
->me
.SubScreen
;
644 } // REFIT_MENU_SCREEN *InitializeSubScreen()
646 VOID
GenerateSubScreen(LOADER_ENTRY
*Entry
, IN REFIT_VOLUME
*Volume
) {
647 REFIT_MENU_SCREEN
*SubScreen
;
648 LOADER_ENTRY
*SubEntry
;
650 CHAR16 DiagsFileName
[256];
655 // create the submenu
656 if (StrLen(Entry
->Title
) == 0) {
657 MyFreePool(Entry
->Title
);
660 SubScreen
= InitializeSubScreen(Entry
);
662 // loader-specific submenu entries
663 if (Entry
->OSType
== 'M') { // entries for Mac OS X
665 SubEntry
= InitializeLoaderEntry(Entry
);
666 if (SubEntry
!= NULL
) {
667 SubEntry
->me
.Title
= L
"Boot Mac OS X with a 64-bit kernel";
668 SubEntry
->LoadOptions
= L
"arch=x86_64";
669 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
670 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
673 SubEntry
= InitializeLoaderEntry(Entry
);
674 if (SubEntry
!= NULL
) {
675 SubEntry
->me
.Title
= L
"Boot Mac OS X with a 32-bit kernel";
676 SubEntry
->LoadOptions
= L
"arch=i386";
677 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
678 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
682 if (!(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_SINGLEUSER
)) {
683 SubEntry
= InitializeLoaderEntry(Entry
);
684 if (SubEntry
!= NULL
) {
685 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode";
686 SubEntry
->UseGraphicsMode
= FALSE
;
687 SubEntry
->LoadOptions
= L
"-v";
688 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
692 SubEntry
= InitializeLoaderEntry(Entry
);
693 if (SubEntry
!= NULL
) {
694 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode (64-bit)";
695 SubEntry
->UseGraphicsMode
= FALSE
;
696 SubEntry
->LoadOptions
= L
"-v arch=x86_64";
697 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
700 SubEntry
= InitializeLoaderEntry(Entry
);
701 if (SubEntry
!= NULL
) {
702 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode (32-bit)";
703 SubEntry
->UseGraphicsMode
= FALSE
;
704 SubEntry
->LoadOptions
= L
"-v arch=i386";
705 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
709 SubEntry
= InitializeLoaderEntry(Entry
);
710 if (SubEntry
!= NULL
) {
711 SubEntry
->me
.Title
= L
"Boot Mac OS X in single user mode";
712 SubEntry
->UseGraphicsMode
= FALSE
;
713 SubEntry
->LoadOptions
= L
"-v -s";
714 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
716 } // single-user mode allowed
718 if (!(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_SAFEMODE
)) {
719 SubEntry
= InitializeLoaderEntry(Entry
);
720 if (SubEntry
!= NULL
) {
721 SubEntry
->me
.Title
= L
"Boot Mac OS X in safe mode";
722 SubEntry
->UseGraphicsMode
= FALSE
;
723 SubEntry
->LoadOptions
= L
"-v -x";
724 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
726 } // safe mode allowed
728 // check for Apple hardware diagnostics
729 StrCpy(DiagsFileName
, L
"System\\Library\\CoreServices\\.diagnostics\\diags.efi");
730 if (FileExists(Volume
->RootDir
, DiagsFileName
) && !(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_HWTEST
)) {
731 SubEntry
= InitializeLoaderEntry(Entry
);
732 if (SubEntry
!= NULL
) {
733 SubEntry
->me
.Title
= L
"Run Apple Hardware Test";
734 MyFreePool(SubEntry
->LoaderPath
);
735 SubEntry
->LoaderPath
= StrDuplicate(DiagsFileName
);
736 SubEntry
->DevicePath
= FileDevicePath(Volume
->DeviceHandle
, SubEntry
->LoaderPath
);
737 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
738 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
740 } // if diagnostics entry found
742 } else if (Entry
->OSType
== 'L') { // entries for Linux kernels with EFI stub loaders
743 File
= ReadLinuxOptionsFile(Entry
->LoaderPath
, Volume
);
745 InitrdName
= FindInitrd(Entry
->LoaderPath
, Volume
);
746 TokenCount
= ReadTokenLine(File
, &TokenList
);
747 // first entry requires special processing, since it was initially set
748 // up with a default title but correct options by InitializeSubScreen(),
750 if ((SubScreen
->Entries
!= NULL
) && (SubScreen
->Entries
[0] != NULL
)) {
751 MyFreePool(SubScreen
->Entries
[0]->Title
);
752 SubScreen
->Entries
[0]->Title
= TokenList
[0] ? StrDuplicate(TokenList
[0]) : StrDuplicate(L
"Boot Linux");
754 FreeTokenLine(&TokenList
, &TokenCount
);
755 while ((TokenCount
= ReadTokenLine(File
, &TokenList
)) > 1) {
756 SubEntry
= InitializeLoaderEntry(Entry
);
757 SubEntry
->me
.Title
= TokenList
[0] ? StrDuplicate(TokenList
[0]) : StrDuplicate(L
"Boot Linux");
758 MyFreePool(SubEntry
->LoadOptions
);
759 SubEntry
->LoadOptions
= AddInitrdToOptions(TokenList
[1], InitrdName
);
760 FreeTokenLine(&TokenList
, &TokenCount
);
761 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_LINUX
;
762 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
764 MyFreePool(InitrdName
);
768 } else if (Entry
->OSType
== 'E') { // entries for ELILO
769 SubEntry
= InitializeLoaderEntry(Entry
);
770 if (SubEntry
!= NULL
) {
771 SubEntry
->me
.Title
= L
"Run ELILO in interactive mode";
772 SubEntry
->LoadOptions
= L
"-p";
773 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
774 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
777 SubEntry
= InitializeLoaderEntry(Entry
);
778 if (SubEntry
!= NULL
) {
779 SubEntry
->me
.Title
= L
"Boot Linux for a 17\" iMac or a 15\" MacBook Pro (*)";
780 SubEntry
->UseGraphicsMode
= TRUE
;
781 SubEntry
->LoadOptions
= L
"-d 0 i17";
782 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
783 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
786 SubEntry
= InitializeLoaderEntry(Entry
);
787 if (SubEntry
!= NULL
) {
788 SubEntry
->me
.Title
= L
"Boot Linux for a 20\" iMac (*)";
789 SubEntry
->UseGraphicsMode
= TRUE
;
790 SubEntry
->LoadOptions
= L
"-d 0 i20";
791 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
792 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
795 SubEntry
= InitializeLoaderEntry(Entry
);
796 if (SubEntry
!= NULL
) {
797 SubEntry
->me
.Title
= L
"Boot Linux for a Mac Mini (*)";
798 SubEntry
->UseGraphicsMode
= TRUE
;
799 SubEntry
->LoadOptions
= L
"-d 0 mini";
800 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
801 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
804 AddMenuInfoLine(SubScreen
, L
"NOTE: This is an example. Entries");
805 AddMenuInfoLine(SubScreen
, L
"marked with (*) may not work.");
807 } else if (Entry
->OSType
== 'X') { // entries for xom.efi
808 // by default, skip the built-in selection and boot from hard disk only
809 Entry
->LoadOptions
= L
"-s -h";
811 SubEntry
= InitializeLoaderEntry(Entry
);
812 if (SubEntry
!= NULL
) {
813 SubEntry
->me
.Title
= L
"Boot Windows from Hard Disk";
814 SubEntry
->LoadOptions
= L
"-s -h";
815 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
816 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
819 SubEntry
= InitializeLoaderEntry(Entry
);
820 if (SubEntry
!= NULL
) {
821 SubEntry
->me
.Title
= L
"Boot Windows from CD-ROM";
822 SubEntry
->LoadOptions
= L
"-s -c";
823 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
824 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
827 SubEntry
= InitializeLoaderEntry(Entry
);
828 if (SubEntry
!= NULL
) {
829 SubEntry
->me
.Title
= L
"Run XOM in text mode";
830 SubEntry
->UseGraphicsMode
= FALSE
;
831 SubEntry
->LoadOptions
= L
"-v";
832 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
833 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
835 } // entries for xom.efi
836 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
837 Entry
->me
.SubScreen
= SubScreen
;
838 } // VOID GenerateSubScreen()
840 // Returns options for a Linux kernel. Reads them from an options file in the
841 // kernel's directory; and if present, adds an initrd= option for an initial
842 // RAM disk file with the same version number as the kernel file.
843 static CHAR16
* GetMainLinuxOptions(IN CHAR16
* LoaderPath
, IN REFIT_VOLUME
*Volume
) {
844 CHAR16
*Options
= NULL
, *InitrdName
, *FullOptions
= NULL
;
846 Options
= GetFirstOptionsFromFile(LoaderPath
, Volume
);
847 InitrdName
= FindInitrd(LoaderPath
, Volume
);
848 FullOptions
= AddInitrdToOptions(Options
, InitrdName
);
851 MyFreePool(InitrdName
);
852 return (FullOptions
);
853 } // static CHAR16 * GetMainLinuxOptions()
855 // Try to guess the name of the Linux distribution & add that name to
857 static VOID
GuessLinuxDistribution(CHAR16
**OSIconName
, REFIT_VOLUME
*Volume
, CHAR16
*LoaderPath
) {
861 UINTN TokenCount
= 0;
863 // If on Linux root fs, /etc/os-release file probably has clues....
864 if (FileExists(Volume
->RootDir
, L
"etc\\os-release") &&
865 (ReadFile(Volume
->RootDir
, L
"etc\\os-release", &File
, &FileSize
) == EFI_SUCCESS
)) {
867 TokenCount
= ReadTokenLine(&File
, &TokenList
);
868 if ((TokenCount
> 1) && ((StriCmp(TokenList
[0], L
"ID") == 0) || (StriCmp(TokenList
[0], L
"NAME") == 0))) {
869 MergeStrings(OSIconName
, TokenList
[1], L
',');
871 FreeTokenLine(&TokenList
, &TokenCount
);
872 } while (TokenCount
> 0);
873 MyFreePool(File
.Buffer
);
876 // Search for clues in the kernel's filename....
877 if (StriSubCmp(L
".fc", LoaderPath
))
878 MergeStrings(OSIconName
, L
"fedora", L
',');
879 if (StriSubCmp(L
".el", LoaderPath
))
880 MergeStrings(OSIconName
, L
"redhat", L
',');
881 } // VOID GuessLinuxDistribution()
883 // Sets a few defaults for a loader entry -- mainly the icon, but also the OS type
884 // code and shortcut letter. For Linux EFI stub loaders, also sets kernel options
885 // that will (with luck) work fairly automatically.
886 VOID
SetLoaderDefaults(LOADER_ENTRY
*Entry
, CHAR16
*LoaderPath
, REFIT_VOLUME
*Volume
) {
887 CHAR16
*FileName
, *PathOnly
, *NoExtension
, *OSIconName
= NULL
, *Temp
, *SubString
;
888 CHAR16 ShortcutLetter
= 0;
891 FileName
= Basename(LoaderPath
);
892 PathOnly
= FindPath(LoaderPath
);
893 NoExtension
= StripEfiExtension(FileName
);
895 // locate a custom icon for the loader
896 // Anything found here takes precedence over the "hints" in the OSIconName variable
897 if (!Entry
->me
.Image
)
898 Entry
->me
.Image
= egLoadIconAnyType(Volume
->RootDir
, PathOnly
, NoExtension
, 128);
899 if (!Entry
->me
.Image
)
900 Entry
->me
.Image
= egCopyImage(Volume
->VolIconImage
);
902 // Begin creating icon "hints" by using last part of directory path leading
904 Temp
= FindLastDirName(LoaderPath
);
905 MergeStrings(&OSIconName
, Temp
, L
',');
908 if (OSIconName
!= NULL
) {
909 ShortcutLetter
= OSIconName
[0];
912 // Add every "word" in the volume label, delimited by spaces, dashes (-), or
913 // underscores (_), to the list of hints to be used in searching for OS
915 if ((Volume
->VolName
) && (StrLen(Volume
->VolName
) > 0)) {
916 Temp
= SubString
= StrDuplicate(Volume
->VolName
);
918 Length
= StrLen(Temp
);
919 for (i
= 0; i
< Length
; i
++) {
920 if ((Temp
[i
] == L
' ') || (Temp
[i
] == L
'_') || (Temp
[i
] == L
'-')) {
922 if (StrLen(SubString
) > 0)
923 MergeStrings(&OSIconName
, SubString
, L
',');
924 SubString
= Temp
+ i
+ 1;
927 MergeStrings(&OSIconName
, SubString
, L
',');
932 // detect specific loaders
933 if (StriSubCmp(L
"bzImage", LoaderPath
) || StriSubCmp(L
"vmlinuz", LoaderPath
)) {
934 GuessLinuxDistribution(&OSIconName
, Volume
, LoaderPath
);
935 MergeStrings(&OSIconName
, L
"linux", L
',');
937 if (ShortcutLetter
== 0)
938 ShortcutLetter
= 'L';
939 Entry
->LoadOptions
= GetMainLinuxOptions(LoaderPath
, Volume
);
940 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_LINUX
;
941 } else if (StriSubCmp(L
"refit", LoaderPath
)) {
942 MergeStrings(&OSIconName
, L
"refit", L
',');
944 ShortcutLetter
= 'R';
945 } else if (StriSubCmp(L
"refind", LoaderPath
)) {
946 MergeStrings(&OSIconName
, L
"refind", L
',');
948 ShortcutLetter
= 'R';
949 } else if (StriCmp(LoaderPath
, MACOSX_LOADER_PATH
) == 0) {
950 if (Volume
->VolIconImage
!= NULL
) { // custom icon file found
951 Entry
->me
.Image
= Volume
->VolIconImage
;
953 MergeStrings(&OSIconName
, L
"mac", L
',');
955 ShortcutLetter
= 'M';
956 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
957 } else if (StriCmp(FileName
, L
"diags.efi") == 0) {
958 MergeStrings(&OSIconName
, L
"hwtest", L
',');
959 } else if (StriCmp(FileName
, L
"e.efi") == 0 || StriCmp(FileName
, L
"elilo.efi") == 0 || StriSubCmp(L
"elilo", FileName
)) {
960 MergeStrings(&OSIconName
, L
"elilo,linux", L
',');
962 if (ShortcutLetter
== 0)
963 ShortcutLetter
= 'L';
964 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
965 } else if (StriSubCmp(L
"grub", FileName
)) {
967 ShortcutLetter
= 'G';
968 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_GRUB
;
969 } else if (StriCmp(FileName
, L
"cdboot.efi") == 0 ||
970 StriCmp(FileName
, L
"bootmgr.efi") == 0 ||
971 StriCmp(FileName
, L
"bootmgfw.efi") == 0 ||
972 StriCmp(FileName
, L
"bkpbootmgfw.efi") == 0) {
973 MergeStrings(&OSIconName
, L
"win", L
',');
975 ShortcutLetter
= 'W';
976 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
977 } else if (StriCmp(FileName
, L
"xom.efi") == 0) {
978 MergeStrings(&OSIconName
, L
"xom,win", L
',');
979 Entry
->UseGraphicsMode
= TRUE
;
981 ShortcutLetter
= 'W';
982 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
985 if ((ShortcutLetter
>= 'a') && (ShortcutLetter
<= 'z'))
986 ShortcutLetter
= ShortcutLetter
- 'a' + 'A'; // convert lowercase to uppercase
987 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
988 if (Entry
->me
.Image
== NULL
)
989 Entry
->me
.Image
= LoadOSIcon(OSIconName
, L
"unknown", FALSE
);
990 MyFreePool(PathOnly
);
991 } // VOID SetLoaderDefaults()
993 // Add a specified EFI boot loader to the list, using automatic settings
994 // for icons, options, etc.
995 LOADER_ENTRY
* AddLoaderEntry(IN CHAR16
*LoaderPath
, IN CHAR16
*LoaderTitle
, IN REFIT_VOLUME
*Volume
) {
998 CleanUpPathNameSlashes(LoaderPath
);
999 Entry
= InitializeLoaderEntry(NULL
);
1000 if (Entry
!= NULL
) {
1001 Entry
->Title
= StrDuplicate((LoaderTitle
!= NULL
) ? LoaderTitle
: LoaderPath
);
1002 Entry
->me
.Title
= AllocateZeroPool(sizeof(CHAR16
) * 256);
1003 SPrint(Entry
->me
.Title
, 255, L
"Boot %s from %s ", (LoaderTitle
!= NULL
) ? LoaderTitle
: LoaderPath
, Volume
->VolName
);
1005 Entry
->me
.BadgeImage
= Volume
->VolBadgeImage
;
1006 if ((LoaderPath
!= NULL
) && (LoaderPath
[0] != L
'\\')) {
1007 Entry
->LoaderPath
= StrDuplicate(L
"\\");
1009 Entry
->LoaderPath
= NULL
;
1011 MergeStrings(&(Entry
->LoaderPath
), LoaderPath
, 0);
1012 Entry
->VolName
= Volume
->VolName
;
1013 Entry
->DevicePath
= FileDevicePath(Volume
->DeviceHandle
, Entry
->LoaderPath
);
1014 SetLoaderDefaults(Entry
, LoaderPath
, Volume
);
1015 GenerateSubScreen(Entry
, Volume
);
1016 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1020 } // LOADER_ENTRY * AddLoaderEntry()
1022 // Returns -1 if (Time1 < Time2), +1 if (Time1 > Time2), or 0 if
1023 // (Time1 == Time2). Precision is only to the nearest second; since
1024 // this is used for sorting boot loader entries, differences smaller
1025 // than this are likely to be meaningless (and unlikely!).
1026 INTN
TimeComp(IN EFI_TIME
*Time1
, IN EFI_TIME
*Time2
) {
1027 INT64 Time1InSeconds
, Time2InSeconds
;
1029 // Following values are overestimates; I'm assuming 31 days in every month.
1030 // This is fine for the purpose of this function, which is limited
1031 Time1InSeconds
= Time1
->Second
+ (Time1
->Minute
* 60) + (Time1
->Hour
* 3600) + (Time1
->Day
* 86400) +
1032 (Time1
->Month
* 2678400) + ((Time1
->Year
- 1998) * 32140800);
1033 Time2InSeconds
= Time2
->Second
+ (Time2
->Minute
* 60) + (Time2
->Hour
* 3600) + (Time2
->Day
* 86400) +
1034 (Time2
->Month
* 2678400) + ((Time2
->Year
- 1998) * 32140800);
1035 if (Time1InSeconds
< Time2InSeconds
)
1037 else if (Time1InSeconds
> Time2InSeconds
)
1041 } // INTN TimeComp()
1043 // Adds a loader list element, keeping it sorted by date. Returns the new
1044 // first element (the one with the most recent date).
1045 static struct LOADER_LIST
* AddLoaderListEntry(struct LOADER_LIST
*LoaderList
, struct LOADER_LIST
*NewEntry
) {
1046 struct LOADER_LIST
*LatestEntry
, *CurrentEntry
, *PrevEntry
= NULL
;
1048 LatestEntry
= CurrentEntry
= LoaderList
;
1049 if (LoaderList
== NULL
) {
1050 LatestEntry
= NewEntry
;
1052 while ((CurrentEntry
!= NULL
) && (TimeComp(&(NewEntry
->TimeStamp
), &(CurrentEntry
->TimeStamp
)) < 0)) {
1053 PrevEntry
= CurrentEntry
;
1054 CurrentEntry
= CurrentEntry
->NextEntry
;
1056 NewEntry
->NextEntry
= CurrentEntry
;
1057 if (PrevEntry
== NULL
) {
1058 LatestEntry
= NewEntry
;
1060 PrevEntry
->NextEntry
= NewEntry
;
1063 return (LatestEntry
);
1064 } // static VOID AddLoaderListEntry()
1066 // Delete the LOADER_LIST linked list
1067 static VOID
CleanUpLoaderList(struct LOADER_LIST
*LoaderList
) {
1068 struct LOADER_LIST
*Temp
;
1070 while (LoaderList
!= NULL
) {
1072 LoaderList
= LoaderList
->NextEntry
;
1073 MyFreePool(Temp
->FileName
);
1076 } // static VOID CleanUpLoaderList()
1078 // Returns FALSE if the specified file/volume matches the GlobalConfig.DontScanDirs
1079 // or GlobalConfig.DontScanVolumes specification, or if Path points to a volume
1080 // other than the one specified by Volume, or if the specified path is SelfDir.
1081 // Returns TRUE if none of these conditions is met -- that is, if the path is
1082 // eligible for scanning.
1083 static BOOLEAN
ShouldScan(REFIT_VOLUME
*Volume
, CHAR16
*Path
) {
1084 CHAR16
*VolName
= NULL
, *DontScanDir
, *PathCopy
= NULL
;
1085 UINTN i
= 0, VolNum
;
1086 BOOLEAN ScanIt
= TRUE
;
1088 if (IsIn(Volume
->VolName
, GlobalConfig
.DontScanVolumes
))
1091 if ((StriCmp(Path
, SelfDirPath
) == 0) && (Volume
->DeviceHandle
== SelfVolume
->DeviceHandle
))
1094 // See if Path includes an explicit volume declaration that's NOT Volume....
1095 PathCopy
= StrDuplicate(Path
);
1096 if (SplitVolumeAndFilename(&PathCopy
, &VolName
)) {
1097 if (StriCmp(VolName
, Volume
->VolName
) != 0) {
1098 if ((StrLen(VolName
) > 2) && (VolName
[0] == L
'f') && (VolName
[1] == L
's') && (VolName
[2] >= L
'0') && (VolName
[2] <= L
'9')) {
1099 VolNum
= Atoi(VolName
+ 2);
1100 if (VolNum
!= Volume
->VolNumber
) {
1107 } // if Path includes volume specification
1108 MyFreePool(PathCopy
);
1109 MyFreePool(VolName
);
1112 // See if Volume is in GlobalConfig.DontScanDirs....
1113 while ((DontScanDir
= FindCommaDelimited(GlobalConfig
.DontScanDirs
, i
++)) && ScanIt
) {
1114 SplitVolumeAndFilename(&DontScanDir
, &VolName
);
1115 CleanUpPathNameSlashes(DontScanDir
);
1116 if (VolName
!= NULL
) {
1117 if ((StriCmp(VolName
, Volume
->VolName
) == 0) && (StriCmp(DontScanDir
, Path
) == 0))
1119 if ((StrLen(VolName
) > 2) && (VolName
[0] == L
'f') && (VolName
[1] == L
's') && (VolName
[2] >= L
'0') && (VolName
[2] <= L
'9')) {
1120 VolNum
= Atoi(VolName
+ 2);
1121 if ((VolNum
== Volume
->VolNumber
) && (StriCmp(DontScanDir
, Path
) == 0))
1125 if (StriCmp(DontScanDir
, Path
) == 0)
1128 MyFreePool(DontScanDir
);
1129 MyFreePool(VolName
);
1134 } // BOOLEAN ShouldScan()
1136 // Returns TRUE if the file is byte-for-byte identical with the fallback file
1137 // on the volume AND if the file is not itself the fallback file; returns
1138 // FALSE if the file is not identical to the fallback file OR if the file
1139 // IS the fallback file. Intended for use in excluding the fallback boot
1140 // loader when it's a duplicate of another boot loader.
1141 static BOOLEAN
DuplicatesFallback(IN REFIT_VOLUME
*Volume
, IN CHAR16
*FileName
) {
1142 CHAR8
*FileContents
, *FallbackContents
;
1143 EFI_FILE_HANDLE FileHandle
, FallbackHandle
;
1144 EFI_FILE_INFO
*FileInfo
, *FallbackInfo
;
1145 UINTN FileSize
= 0, FallbackSize
= 0;
1147 BOOLEAN AreIdentical
= FALSE
;
1149 if (!FileExists(Volume
->RootDir
, FileName
) || !FileExists(Volume
->RootDir
, FALLBACK_FULLNAME
))
1152 CleanUpPathNameSlashes(FileName
);
1154 if (StriCmp(FileName
, FALLBACK_FULLNAME
) == 0)
1155 return FALSE
; // identical filenames, so not a duplicate....
1157 Status
= refit_call5_wrapper(Volume
->RootDir
->Open
, Volume
->RootDir
, &FileHandle
, FileName
, EFI_FILE_MODE_READ
, 0);
1158 if (Status
== EFI_SUCCESS
) {
1159 FileInfo
= LibFileInfo(FileHandle
);
1160 FileSize
= FileInfo
->FileSize
;
1165 Status
= refit_call5_wrapper(Volume
->RootDir
->Open
, Volume
->RootDir
, &FallbackHandle
, FALLBACK_FULLNAME
, EFI_FILE_MODE_READ
, 0);
1166 if (Status
== EFI_SUCCESS
) {
1167 FallbackInfo
= LibFileInfo(FallbackHandle
);
1168 FallbackSize
= FallbackInfo
->FileSize
;
1170 refit_call1_wrapper(FileHandle
->Close
, FileHandle
);
1174 if (FallbackSize
!= FileSize
) { // not same size, so can't be identical
1175 AreIdentical
= FALSE
;
1176 } else { // could be identical; do full check....
1177 FileContents
= AllocatePool(FileSize
);
1178 FallbackContents
= AllocatePool(FallbackSize
);
1179 if (FileContents
&& FallbackContents
) {
1180 Status
= refit_call3_wrapper(FileHandle
->Read
, FileHandle
, &FileSize
, FileContents
);
1181 if (Status
== EFI_SUCCESS
) {
1182 Status
= refit_call3_wrapper(FallbackHandle
->Read
, FallbackHandle
, &FallbackSize
, FallbackContents
);
1184 if (Status
== EFI_SUCCESS
) {
1185 AreIdentical
= (CompareMem(FileContents
, FallbackContents
, FileSize
) == 0);
1188 MyFreePool(FileContents
);
1189 MyFreePool(FallbackContents
);
1192 // BUG ALERT: Some systems (e.g., DUET, some Macs with large displays) crash if the
1193 // following two calls are reversed. Go figure....
1194 refit_call1_wrapper(FileHandle
->Close
, FallbackHandle
);
1195 refit_call1_wrapper(FileHandle
->Close
, FileHandle
);
1196 return AreIdentical
;
1197 } // BOOLEAN DuplicatesFallback()
1199 // Returns FALSE if two measures of file size are identical for a single file,
1200 // TRUE if not or if the file can't be opened and the other measure is non-0.
1201 // Despite the function's name, this isn't really a direct test of symbolic
1202 // link status, since EFI doesn't officially support symlinks. It does seem
1203 // to be a reliable indicator, though. (OTOH, some disk errors might cause a
1204 // file to fail to open, which would return a false positive -- but as I use
1205 // this function to exclude symbolic links from the list of boot loaders,
1206 // that would be fine, since such boot loaders wouldn't work.)
1207 static BOOLEAN
IsSymbolicLink(REFIT_VOLUME
*Volume
, CHAR16
*Path
, EFI_FILE_INFO
*DirEntry
) {
1208 EFI_FILE_HANDLE FileHandle
;
1209 EFI_FILE_INFO
*FileInfo
= NULL
;
1211 UINTN FileSize2
= 0;
1214 FileName
= StrDuplicate(Path
);
1215 MergeStrings(&FileName
, DirEntry
->FileName
, L
'\\');
1216 CleanUpPathNameSlashes(FileName
);
1218 Status
= refit_call5_wrapper(Volume
->RootDir
->Open
, Volume
->RootDir
, &FileHandle
, FileName
, EFI_FILE_MODE_READ
, 0);
1219 if (Status
== EFI_SUCCESS
) {
1220 FileInfo
= LibFileInfo(FileHandle
);
1221 if (FileInfo
!= NULL
)
1222 FileSize2
= FileInfo
->FileSize
;
1225 MyFreePool(FileName
);
1226 MyFreePool(FileInfo
);
1228 return (DirEntry
->FileSize
!= FileSize2
);
1229 } // BOOLEAN IsSymbolicLink()
1231 // Scan an individual directory for EFI boot loader files and, if found,
1232 // add them to the list. Exception: Ignores FALLBACK_FULLNAME, which is picked
1233 // up in ScanEfiFiles(). Sorts the entries within the loader directory so that
1234 // the most recent one appears first in the list.
1235 // Returns TRUE if a duplicate for FALLBACK_FILENAME was found, FALSE if not.
1236 static BOOLEAN
ScanLoaderDir(IN REFIT_VOLUME
*Volume
, IN CHAR16
*Path
, IN CHAR16
*Pattern
)
1239 REFIT_DIR_ITER DirIter
;
1240 EFI_FILE_INFO
*DirEntry
;
1241 CHAR16 FileName
[256], *Extension
;
1242 struct LOADER_LIST
*LoaderList
= NULL
, *NewLoader
;
1243 BOOLEAN FoundFallbackDuplicate
= FALSE
;
1245 if ((!SelfDirPath
|| !Path
|| ((StriCmp(Path
, SelfDirPath
) == 0) && (Volume
->DeviceHandle
!= SelfVolume
->DeviceHandle
)) ||
1246 (StriCmp(Path
, SelfDirPath
) != 0)) &&
1247 (ShouldScan(Volume
, Path
))) {
1248 // look through contents of the directory
1249 DirIterOpen(Volume
->RootDir
, Path
, &DirIter
);
1250 while (DirIterNext(&DirIter
, 2, Pattern
, &DirEntry
)) {
1251 Extension
= FindExtension(DirEntry
->FileName
);
1252 if (DirEntry
->FileName
[0] == '.' ||
1253 StriCmp(Extension
, L
".icns") == 0 ||
1254 StriCmp(Extension
, L
".png") == 0 ||
1255 (StriCmp(DirEntry
->FileName
, FALLBACK_BASENAME
) == 0 && (StriCmp(Path
, L
"EFI\\BOOT") == 0)) ||
1256 StriSubCmp(L
"shell", DirEntry
->FileName
) ||
1257 IsSymbolicLink(Volume
, Path
, DirEntry
) || /* is symbolic link */
1258 IsIn(DirEntry
->FileName
, GlobalConfig
.DontScanFiles
))
1259 continue; // skip this
1262 SPrint(FileName
, 255, L
"\\%s\\%s", Path
, DirEntry
->FileName
);
1264 SPrint(FileName
, 255, L
"\\%s", DirEntry
->FileName
);
1265 CleanUpPathNameSlashes(FileName
);
1267 if(!IsValidLoader(Volume
->RootDir
, FileName
))
1270 NewLoader
= AllocateZeroPool(sizeof(struct LOADER_LIST
));
1271 if (NewLoader
!= NULL
) {
1272 NewLoader
->FileName
= StrDuplicate(FileName
);
1273 NewLoader
->TimeStamp
= DirEntry
->ModificationTime
;
1274 LoaderList
= AddLoaderListEntry(LoaderList
, NewLoader
);
1275 if (DuplicatesFallback(Volume
, FileName
))
1276 FoundFallbackDuplicate
= TRUE
;
1278 MyFreePool(Extension
);
1281 NewLoader
= LoaderList
;
1282 while (NewLoader
!= NULL
) {
1283 AddLoaderEntry(NewLoader
->FileName
, NULL
, Volume
);
1284 NewLoader
= NewLoader
->NextEntry
;
1287 CleanUpLoaderList(LoaderList
);
1288 Status
= DirIterClose(&DirIter
);
1289 // NOTE: EFI_INVALID_PARAMETER really is an error that should be reported;
1290 // but I've gotten reports from users who are getting this error occasionally
1291 // and I can't find anything wrong or reproduce the problem, so I'm putting
1292 // it down to buggy EFI implementations and ignoring that particular error....
1293 if ((Status
!= EFI_NOT_FOUND
) && (Status
!= EFI_INVALID_PARAMETER
)) {
1295 SPrint(FileName
, 255, L
"while scanning the %s directory", Path
);
1297 StrCpy(FileName
, L
"while scanning the root directory");
1298 CheckError(Status
, FileName
);
1299 } // if (Status != EFI_NOT_FOUND)
1300 } // if not scanning a blacklisted directory
1302 return FoundFallbackDuplicate
;
1303 } /* static VOID ScanLoaderDir() */
1305 static VOID
ScanEfiFiles(REFIT_VOLUME
*Volume
) {
1307 REFIT_DIR_ITER EfiDirIter
;
1308 EFI_FILE_INFO
*EfiDirEntry
;
1309 CHAR16 FileName
[256], *Directory
, *MatchPatterns
, *VolName
= NULL
, *SelfPath
;
1311 BOOLEAN ScanFallbackLoader
= TRUE
;
1312 BOOLEAN FoundBRBackup
= FALSE
;
1314 MatchPatterns
= StrDuplicate(LOADER_MATCH_PATTERNS
);
1315 if (GlobalConfig
.ScanAllLinux
)
1316 MergeStrings(&MatchPatterns
, LINUX_MATCH_PATTERNS
, L
',');
1318 if ((Volume
->RootDir
!= NULL
) && (Volume
->VolName
!= NULL
)) {
1319 // check for Mac OS X boot loader
1320 if (ShouldScan(Volume
, L
"System\\Library\\CoreServices")) {
1321 StrCpy(FileName
, MACOSX_LOADER_PATH
);
1322 if (FileExists(Volume
->RootDir
, FileName
) && !IsIn(L
"boot.efi", GlobalConfig
.DontScanFiles
)) {
1323 AddLoaderEntry(FileName
, L
"Mac OS X", Volume
);
1324 if (DuplicatesFallback(Volume
, FileName
))
1325 ScanFallbackLoader
= FALSE
;
1329 StrCpy(FileName
, L
"System\\Library\\CoreServices\\xom.efi");
1330 if (FileExists(Volume
->RootDir
, FileName
) && !IsIn(L
"boot.efi", GlobalConfig
.DontScanFiles
)) {
1331 AddLoaderEntry(FileName
, L
"Windows XP (XoM)", Volume
);
1332 if (DuplicatesFallback(Volume
, FileName
))
1333 ScanFallbackLoader
= FALSE
;
1335 } // if should scan Mac directory
1337 // check for Microsoft boot loader/menu
1338 if (ShouldScan(Volume
, L
"EFI\\Microsoft\\Boot")) {
1339 StrCpy(FileName
, L
"EFI\\Microsoft\\Boot\\bkpbootmgfw.efi");
1340 if (FileExists(Volume
->RootDir
, FileName
) && !IsIn(L
"bkpbootmgfw.efi", GlobalConfig
.DontScanFiles
)) {
1341 AddLoaderEntry(FileName
, L
"Microsoft EFI boot (Boot Repair backup)", Volume
);
1342 FoundBRBackup
= TRUE
;
1343 if (DuplicatesFallback(Volume
, FileName
))
1344 ScanFallbackLoader
= FALSE
;
1346 StrCpy(FileName
, L
"EFI\\Microsoft\\Boot\\bootmgfw.efi");
1347 if (FileExists(Volume
->RootDir
, FileName
) && !IsIn(L
"bootmgfw.efi", GlobalConfig
.DontScanFiles
)) {
1349 AddLoaderEntry(FileName
, L
"Supposed Microsoft EFI boot (probably GRUB)", Volume
);
1351 AddLoaderEntry(FileName
, L
"Microsoft EFI boot", Volume
);
1352 if (DuplicatesFallback(Volume
, FileName
))
1353 ScanFallbackLoader
= FALSE
;
1357 // scan the root directory for EFI executables
1358 if (ScanLoaderDir(Volume
, L
"\\", MatchPatterns
))
1359 ScanFallbackLoader
= FALSE
;
1361 // scan subdirectories of the EFI directory (as per the standard)
1362 DirIterOpen(Volume
->RootDir
, L
"EFI", &EfiDirIter
);
1363 while (DirIterNext(&EfiDirIter
, 1, NULL
, &EfiDirEntry
)) {
1364 if (StriCmp(EfiDirEntry
->FileName
, L
"tools") == 0 || EfiDirEntry
->FileName
[0] == '.')
1365 continue; // skip this, doesn't contain boot loaders or is scanned later
1366 SPrint(FileName
, 255, L
"EFI\\%s", EfiDirEntry
->FileName
);
1367 if (ScanLoaderDir(Volume
, FileName
, MatchPatterns
))
1368 ScanFallbackLoader
= FALSE
;
1370 Status
= DirIterClose(&EfiDirIter
);
1371 if (Status
!= EFI_NOT_FOUND
)
1372 CheckError(Status
, L
"while scanning the EFI directory");
1374 // Scan user-specified (or additional default) directories....
1376 while ((Directory
= FindCommaDelimited(GlobalConfig
.AlsoScan
, i
++)) != NULL
) {
1377 if (ShouldScan(Volume
, Directory
)) {
1378 SplitVolumeAndFilename(&Directory
, &VolName
);
1379 CleanUpPathNameSlashes(Directory
);
1380 Length
= StrLen(Directory
);
1381 if ((Length
> 0) && ScanLoaderDir(Volume
, Directory
, MatchPatterns
))
1382 ScanFallbackLoader
= FALSE
;
1383 MyFreePool(VolName
);
1384 } // if should scan dir
1385 MyFreePool(Directory
);
1388 // Don't scan the fallback loader if it's on the same volume and a duplicate of rEFInd itself....
1389 SelfPath
= DevicePathToStr(SelfLoadedImage
->FilePath
);
1390 CleanUpPathNameSlashes(SelfPath
);
1391 if ((Volume
->DeviceHandle
== SelfLoadedImage
->DeviceHandle
) && DuplicatesFallback(Volume
, SelfPath
))
1392 ScanFallbackLoader
= FALSE
;
1394 // If not a duplicate & if it exists & if it's not us, create an entry
1395 // for the fallback boot loader
1396 if (ScanFallbackLoader
&& FileExists(Volume
->RootDir
, FALLBACK_FULLNAME
) && ShouldScan(Volume
, L
"EFI\\BOOT"))
1397 AddLoaderEntry(FALLBACK_FULLNAME
, L
"Fallback boot loader", Volume
);
1399 } // static VOID ScanEfiFiles()
1401 // Scan internal disks for valid EFI boot loaders....
1402 static VOID
ScanInternal(VOID
) {
1405 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1406 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_INTERNAL
) {
1407 ScanEfiFiles(Volumes
[VolumeIndex
]);
1410 } // static VOID ScanInternal()
1412 // Scan external disks for valid EFI boot loaders....
1413 static VOID
ScanExternal(VOID
) {
1416 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1417 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_EXTERNAL
) {
1418 ScanEfiFiles(Volumes
[VolumeIndex
]);
1421 } // static VOID ScanExternal()
1423 // Scan internal disks for valid EFI boot loaders....
1424 static VOID
ScanOptical(VOID
) {
1427 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1428 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_OPTICAL
) {
1429 ScanEfiFiles(Volumes
[VolumeIndex
]);
1432 } // static VOID ScanOptical()
1435 // legacy boot functions
1438 static EFI_STATUS
ActivateMbrPartition(IN EFI_BLOCK_IO
*BlockIO
, IN UINTN PartitionIndex
)
1441 UINT8 SectorBuffer
[512];
1442 MBR_PARTITION_INFO
*MbrTable
, *EMbrTable
;
1443 UINT32 ExtBase
, ExtCurrent
, NextExtCurrent
;
1444 UINTN LogicalPartitionIndex
= 4;
1446 BOOLEAN HaveBootCode
;
1449 Status
= refit_call5_wrapper(BlockIO
->ReadBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, 0, 512, SectorBuffer
);
1450 if (EFI_ERROR(Status
))
1452 if (*((UINT16
*)(SectorBuffer
+ 510)) != 0xaa55)
1453 return EFI_NOT_FOUND
; // safety measure #1
1455 // add boot code if necessary
1456 HaveBootCode
= FALSE
;
1457 for (i
= 0; i
< MBR_BOOTCODE_SIZE
; i
++) {
1458 if (SectorBuffer
[i
] != 0) {
1459 HaveBootCode
= TRUE
;
1463 if (!HaveBootCode
) {
1464 // no boot code found in the MBR, add the syslinux MBR code
1465 SetMem(SectorBuffer
, MBR_BOOTCODE_SIZE
, 0);
1466 CopyMem(SectorBuffer
, syslinux_mbr
, SYSLINUX_MBR_SIZE
);
1469 // set the partition active
1470 MbrTable
= (MBR_PARTITION_INFO
*)(SectorBuffer
+ 446);
1472 for (i
= 0; i
< 4; i
++) {
1473 if (MbrTable
[i
].Flags
!= 0x00 && MbrTable
[i
].Flags
!= 0x80)
1474 return EFI_NOT_FOUND
; // safety measure #2
1475 if (i
== PartitionIndex
)
1476 MbrTable
[i
].Flags
= 0x80;
1477 else if (PartitionIndex
>= 4 && IS_EXTENDED_PART_TYPE(MbrTable
[i
].Type
)) {
1478 MbrTable
[i
].Flags
= 0x80;
1479 ExtBase
= MbrTable
[i
].StartLBA
;
1481 MbrTable
[i
].Flags
= 0x00;
1485 Status
= refit_call5_wrapper(BlockIO
->WriteBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, 0, 512, SectorBuffer
);
1486 if (EFI_ERROR(Status
))
1489 if (PartitionIndex
>= 4) {
1490 // we have to activate a logical partition, so walk the EMBR chain
1492 // NOTE: ExtBase was set above while looking at the MBR table
1493 for (ExtCurrent
= ExtBase
; ExtCurrent
; ExtCurrent
= NextExtCurrent
) {
1494 // read current EMBR
1495 Status
= refit_call5_wrapper(BlockIO
->ReadBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, ExtCurrent
, 512, SectorBuffer
);
1496 if (EFI_ERROR(Status
))
1498 if (*((UINT16
*)(SectorBuffer
+ 510)) != 0xaa55)
1499 return EFI_NOT_FOUND
; // safety measure #3
1501 // scan EMBR, set appropriate partition active
1502 EMbrTable
= (MBR_PARTITION_INFO
*)(SectorBuffer
+ 446);
1504 for (i
= 0; i
< 4; i
++) {
1505 if (EMbrTable
[i
].Flags
!= 0x00 && EMbrTable
[i
].Flags
!= 0x80)
1506 return EFI_NOT_FOUND
; // safety measure #4
1507 if (EMbrTable
[i
].StartLBA
== 0 || EMbrTable
[i
].Size
== 0)
1509 if (IS_EXTENDED_PART_TYPE(EMbrTable
[i
].Type
)) {
1510 // link to next EMBR
1511 NextExtCurrent
= ExtBase
+ EMbrTable
[i
].StartLBA
;
1512 EMbrTable
[i
].Flags
= (PartitionIndex
>= LogicalPartitionIndex
) ? 0x80 : 0x00;
1515 // logical partition
1516 EMbrTable
[i
].Flags
= (PartitionIndex
== LogicalPartitionIndex
) ? 0x80 : 0x00;
1517 LogicalPartitionIndex
++;
1521 // write current EMBR
1522 Status
= refit_call5_wrapper(BlockIO
->WriteBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, ExtCurrent
, 512, SectorBuffer
);
1523 if (EFI_ERROR(Status
))
1526 if (PartitionIndex
< LogicalPartitionIndex
)
1527 break; // stop the loop, no need to touch further EMBRs
1533 } /* static EFI_STATUS ActivateMbrPartition() */
1535 // early 2006 Core Duo / Core Solo models
1536 static UINT8 LegacyLoaderDevicePath1Data
[] = {
1537 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1538 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1539 0xFF, 0xFF, 0xF9, 0xFF, 0x00, 0x00, 0x00, 0x00,
1540 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1541 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1542 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1544 // mid-2006 Mac Pro (and probably other Core 2 models)
1545 static UINT8 LegacyLoaderDevicePath2Data
[] = {
1546 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1547 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1548 0xFF, 0xFF, 0xF7, 0xFF, 0x00, 0x00, 0x00, 0x00,
1549 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1550 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1551 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1553 // mid-2007 MBP ("Santa Rosa" based models)
1554 static UINT8 LegacyLoaderDevicePath3Data
[] = {
1555 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1556 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1557 0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
1558 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1559 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1560 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1563 static UINT8 LegacyLoaderDevicePath4Data
[] = {
1564 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1565 0x00, 0x00, 0xC0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1566 0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
1567 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1568 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1569 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1571 // late-2008 MB/MBP (NVidia chipset)
1572 static UINT8 LegacyLoaderDevicePath5Data
[] = {
1573 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1574 0x00, 0x40, 0xCB, 0xFF, 0x00, 0x00, 0x00, 0x00,
1575 0xFF, 0xBF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
1576 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1577 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1578 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1581 static EFI_DEVICE_PATH
*LegacyLoaderList
[] = {
1582 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath1Data
,
1583 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath2Data
,
1584 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath3Data
,
1585 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath4Data
,
1586 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath5Data
,
1590 #define MAX_DISCOVERED_PATHS (16)
1592 static VOID
StartLegacy(IN LEGACY_ENTRY
*Entry
)
1595 EG_IMAGE
*BootLogoImage
;
1596 UINTN ErrorInStep
= 0;
1597 EFI_DEVICE_PATH
*DiscoveredPathList
[MAX_DISCOVERED_PATHS
];
1599 BeginExternalScreen(TRUE
, L
"Booting Legacy OS (Mac mode)");
1601 BootLogoImage
= LoadOSIcon(Entry
->Volume
->OSIconName
, L
"legacy", TRUE
);
1602 if (BootLogoImage
!= NULL
)
1603 BltImageAlpha(BootLogoImage
,
1604 (UGAWidth
- BootLogoImage
->Width
) >> 1,
1605 (UGAHeight
- BootLogoImage
->Height
) >> 1,
1606 &StdBackgroundPixel
);
1608 if (Entry
->Volume
->IsMbrPartition
) {
1609 ActivateMbrPartition(Entry
->Volume
->WholeDiskBlockIO
, Entry
->Volume
->MbrPartitionIndex
);
1612 ExtractLegacyLoaderPaths(DiscoveredPathList
, MAX_DISCOVERED_PATHS
, LegacyLoaderList
);
1614 Status
= StartEFIImageList(DiscoveredPathList
, Entry
->LoadOptions
, TYPE_LEGACY
, L
"legacy loader", 0, &ErrorInStep
, TRUE
);
1615 if (Status
== EFI_NOT_FOUND
) {
1616 if (ErrorInStep
== 1) {
1617 Print(L
"\nPlease make sure that you have the latest firmware update installed.\n");
1618 } else if (ErrorInStep
== 3) {
1619 Print(L
"\nThe firmware refused to boot from the selected volume. Note that external\n"
1620 L
"hard drives are not well-supported by Apple's firmware for legacy OS booting.\n");
1623 FinishExternalScreen();
1624 } /* static VOID StartLegacy() */
1626 // Start a device on a non-Mac using the EFI_LEGACY_BIOS_PROTOCOL
1627 #ifdef __MAKEWITH_TIANO
1628 static VOID
StartLegacyUEFI(IN LEGACY_ENTRY
*Entry
)
1630 BeginExternalScreen(TRUE
, L
"Booting Legacy OS (UEFI mode)");
1632 BdsDeleteAllInvalidLegacyBootOptions();
1633 BdsAddNonExistingLegacyBootOptions();
1634 // BdsUpdateLegacyDevOrder();
1636 BdsLibConnectDevicePath (Entry
->BdsOption
->DevicePath
);
1637 BdsLibDoLegacyBoot(Entry
->BdsOption
);
1639 // If we get here, it means that there was a failure....
1640 Print(L
"Failure booting legacy (BIOS) OS.");
1642 FinishExternalScreen();
1643 } // static VOID StartLegacyUEFI()
1644 #endif // __MAKEWITH_TIANO
1646 static LEGACY_ENTRY
* AddLegacyEntry(IN CHAR16
*LoaderTitle
, IN REFIT_VOLUME
*Volume
)
1648 LEGACY_ENTRY
*Entry
, *SubEntry
;
1649 REFIT_MENU_SCREEN
*SubScreen
;
1651 CHAR16 ShortcutLetter
= 0;
1653 if (LoaderTitle
== NULL
) {
1654 if (Volume
->OSName
!= NULL
) {
1655 LoaderTitle
= Volume
->OSName
;
1656 if (LoaderTitle
[0] == 'W' || LoaderTitle
[0] == 'L')
1657 ShortcutLetter
= LoaderTitle
[0];
1659 LoaderTitle
= L
"Legacy OS";
1661 if (Volume
->VolName
!= NULL
)
1662 VolDesc
= Volume
->VolName
;
1664 VolDesc
= (Volume
->DiskKind
== DISK_KIND_OPTICAL
) ? L
"CD" : L
"HD";
1666 // prepare the menu entry
1667 Entry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1668 Entry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1669 SPrint(Entry
->me
.Title
, 255, L
"Boot %s from %s", LoaderTitle
, VolDesc
);
1670 Entry
->me
.Tag
= TAG_LEGACY
;
1672 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1673 Entry
->me
.Image
= LoadOSIcon(Volume
->OSIconName
, L
"legacy", FALSE
);
1674 Entry
->me
.BadgeImage
= Volume
->VolBadgeImage
;
1675 Entry
->Volume
= Volume
;
1676 Entry
->LoadOptions
= (Volume
->DiskKind
== DISK_KIND_OPTICAL
) ? L
"CD" :
1677 ((Volume
->DiskKind
== DISK_KIND_EXTERNAL
) ? L
"USB" : L
"HD");
1678 Entry
->Enabled
= TRUE
;
1680 // create the submenu
1681 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
1682 SubScreen
->Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1683 SPrint(SubScreen
->Title
, 255, L
"Boot Options for %s on %s", LoaderTitle
, VolDesc
);
1684 SubScreen
->TitleImage
= Entry
->me
.Image
;
1685 SubScreen
->Hint1
= StrDuplicate(SUBSCREEN_HINT1
);
1686 if (GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_EDITOR
) {
1687 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2_NO_EDITOR
);
1689 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2
);
1693 SubEntry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1694 SubEntry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1695 SPrint(SubEntry
->me
.Title
, 255, L
"Boot %s", LoaderTitle
);
1696 SubEntry
->me
.Tag
= TAG_LEGACY
;
1697 SubEntry
->Volume
= Entry
->Volume
;
1698 SubEntry
->LoadOptions
= Entry
->LoadOptions
;
1699 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
1701 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
1702 Entry
->me
.SubScreen
= SubScreen
;
1703 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1705 } /* static LEGACY_ENTRY * AddLegacyEntry() */
1708 #ifdef __MAKEWITH_GNUEFI
1709 static VOID
ScanLegacyUEFI(IN UINTN DiskType
){}
1711 // default volume badge icon based on disk kind
1712 static EG_IMAGE
* GetDiskBadge(IN UINTN DiskType
) {
1713 EG_IMAGE
* Badge
= NULL
;
1717 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_INTERNAL
);
1720 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_EXTERNAL
);
1723 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_OPTICAL
);
1727 } // static EG_IMAGE * GetDiskBadge()
1730 Create a rEFInd boot option from a Legacy BIOS protocol option.
1732 static LEGACY_ENTRY
* AddLegacyEntryUEFI(BDS_COMMON_OPTION
*BdsOption
, IN UINT16 DiskType
)
1734 LEGACY_ENTRY
*Entry
, *SubEntry
;
1735 REFIT_MENU_SCREEN
*SubScreen
;
1736 CHAR16 ShortcutLetter
= 0;
1737 CHAR16
*LegacyDescription
= BdsOption
->Description
;
1739 // prepare the menu entry
1740 Entry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1741 Entry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1742 SPrint(Entry
->me
.Title
, 255, L
"Boot legacy target %s", LegacyDescription
);
1743 Entry
->me
.Tag
= TAG_LEGACY_UEFI
;
1745 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1746 Entry
->me
.Image
= LoadOSIcon(L
"legacy", L
"legacy", TRUE
);
1747 Entry
->LoadOptions
= (DiskType
== BBS_CDROM
) ? L
"CD" :
1748 ((DiskType
== BBS_USB
) ? L
"USB" : L
"HD");
1749 Entry
->me
.BadgeImage
= GetDiskBadge(DiskType
);
1750 Entry
->BdsOption
= BdsOption
;
1751 Entry
->Enabled
= TRUE
;
1753 // create the submenu
1754 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
1755 SubScreen
->Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1756 SPrint(SubScreen
->Title
, 255, L
"No boot options for legacy target");
1757 SubScreen
->TitleImage
= Entry
->me
.Image
;
1758 SubScreen
->Hint1
= StrDuplicate(SUBSCREEN_HINT1
);
1759 if (GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_EDITOR
) {
1760 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2_NO_EDITOR
);
1762 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2
);
1766 SubEntry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1767 SubEntry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1768 SPrint(SubEntry
->me
.Title
, 255, L
"Boot %s", LegacyDescription
);
1769 SubEntry
->me
.Tag
= TAG_LEGACY_UEFI
;
1770 Entry
->BdsOption
= BdsOption
;
1771 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
1773 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
1774 Entry
->me
.SubScreen
= SubScreen
;
1775 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1777 } /* static LEGACY_ENTRY * AddLegacyEntryUEFI() */
1780 Scan for legacy BIOS targets on machines that implement EFI_LEGACY_BIOS_PROTOCOL.
1781 In testing, protocol has not been implemented on Macs but has been
1782 implemented on several Dell PCs and an ASUS motherboard.
1783 Restricts output to disks of the specified DiskType.
1785 static VOID
ScanLegacyUEFI(IN UINTN DiskType
)
1788 EFI_LEGACY_BIOS_PROTOCOL
*LegacyBios
;
1789 UINT16
*BootOrder
= NULL
;
1791 CHAR16 BootOption
[10];
1792 UINTN BootOrderSize
= 0;
1794 BDS_COMMON_OPTION
*BdsOption
;
1795 LIST_ENTRY TempList
;
1796 BBS_BBS_DEVICE_PATH
* BbsDevicePath
= NULL
;
1798 InitializeListHead (&TempList
);
1799 ZeroMem (Buffer
, sizeof (Buffer
));
1801 // If LegacyBios protocol is not implemented on this platform, then
1802 //we do not support this type of legacy boot on this machine.
1803 Status
= gBS
->LocateProtocol(&gEfiLegacyBootProtocolGuid
, NULL
, (VOID
**) &LegacyBios
);
1804 if (EFI_ERROR (Status
))
1807 // Grab the boot order
1808 BootOrder
= BdsLibGetVariableAndSize(L
"BootOrder", &gEfiGlobalVariableGuid
, &BootOrderSize
);
1809 if (BootOrder
== NULL
) {
1814 while (Index
< BootOrderSize
/ sizeof (UINT16
))
1816 // Grab each boot option variable from the boot order, and convert
1817 // the variable into a BDS boot option
1818 UnicodeSPrint (BootOption
, sizeof (BootOption
), L
"Boot%04x", BootOrder
[Index
]);
1819 BdsOption
= BdsLibVariableToOption (&TempList
, BootOption
);
1821 if (BdsOption
!= NULL
) {
1822 BbsDevicePath
= (BBS_BBS_DEVICE_PATH
*)BdsOption
->DevicePath
;
1824 // Only add the entry if it is of a requested type (e.g. USB, HD)
1826 // Two checks necessary because some systems return EFI boot loaders
1827 // with a DeviceType value that would inappropriately include them
1828 // as legacy loaders....
1829 if ((BbsDevicePath
->DeviceType
== DiskType
) && (BdsOption
->DevicePath
->Type
== DEVICE_TYPE_BIOS
)) {
1830 AddLegacyEntryUEFI(BdsOption
, BbsDevicePath
->DeviceType
);
1835 } /* static VOID ScanLegacyUEFI() */
1836 #endif // __MAKEWITH_GNUEFI
1838 static VOID
ScanLegacyVolume(REFIT_VOLUME
*Volume
, UINTN VolumeIndex
) {
1840 BOOLEAN ShowVolume
, HideIfOthersFound
;
1843 HideIfOthersFound
= FALSE
;
1844 if (Volume
->IsAppleLegacy
) {
1846 HideIfOthersFound
= TRUE
;
1847 } else if (Volume
->HasBootCode
) {
1849 if (Volume
->BlockIO
== Volume
->WholeDiskBlockIO
&&
1850 Volume
->BlockIOOffset
== 0 &&
1851 Volume
->OSName
== NULL
)
1852 // this is a whole disk (MBR) entry; hide if we have entries for partitions
1853 HideIfOthersFound
= TRUE
;
1855 if (HideIfOthersFound
) {
1856 // check for other bootable entries on the same disk
1857 for (VolumeIndex2
= 0; VolumeIndex2
< VolumesCount
; VolumeIndex2
++) {
1858 if (VolumeIndex2
!= VolumeIndex
&& Volumes
[VolumeIndex2
]->HasBootCode
&&
1859 Volumes
[VolumeIndex2
]->WholeDiskBlockIO
== Volume
->WholeDiskBlockIO
)
1865 AddLegacyEntry(NULL
, Volume
);
1866 } // static VOID ScanLegacyVolume()
1868 // Scan attached optical discs for legacy (BIOS) boot code
1869 // and add anything found to the list....
1870 static VOID
ScanLegacyDisc(VOID
)
1873 REFIT_VOLUME
*Volume
;
1875 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
) {
1876 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1877 Volume
= Volumes
[VolumeIndex
];
1878 if (Volume
->DiskKind
== DISK_KIND_OPTICAL
)
1879 ScanLegacyVolume(Volume
, VolumeIndex
);
1881 } else if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
1882 ScanLegacyUEFI(BBS_CDROM
);
1884 } /* static VOID ScanLegacyDisc() */
1886 // Scan internal hard disks for legacy (BIOS) boot code
1887 // and add anything found to the list....
1888 static VOID
ScanLegacyInternal(VOID
)
1891 REFIT_VOLUME
*Volume
;
1893 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
) {
1894 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1895 Volume
= Volumes
[VolumeIndex
];
1896 if (Volume
->DiskKind
== DISK_KIND_INTERNAL
)
1897 ScanLegacyVolume(Volume
, VolumeIndex
);
1899 } else if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
1900 ScanLegacyUEFI(BBS_HARDDISK
);
1902 } /* static VOID ScanLegacyInternal() */
1904 // Scan external disks for legacy (BIOS) boot code
1905 // and add anything found to the list....
1906 static VOID
ScanLegacyExternal(VOID
)
1909 REFIT_VOLUME
*Volume
;
1911 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
) {
1912 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1913 Volume
= Volumes
[VolumeIndex
];
1914 if (Volume
->DiskKind
== DISK_KIND_EXTERNAL
)
1915 ScanLegacyVolume(Volume
, VolumeIndex
);
1917 } else if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
1918 ScanLegacyUEFI(BBS_USB
);
1920 } /* static VOID ScanLegacyExternal() */
1923 // pre-boot tool functions
1926 static VOID
StartTool(IN LOADER_ENTRY
*Entry
)
1928 BeginExternalScreen(Entry
->UseGraphicsMode
, Entry
->me
.Title
+ 6); // assumes "Start <title>" as assigned below
1929 StartEFIImage(Entry
->DevicePath
, Entry
->LoadOptions
, TYPE_EFI
,
1930 Basename(Entry
->LoaderPath
), Entry
->OSType
, NULL
, TRUE
);
1931 FinishExternalScreen();
1932 } /* static VOID StartTool() */
1934 static LOADER_ENTRY
* AddToolEntry(EFI_HANDLE DeviceHandle
, IN CHAR16
*LoaderPath
, IN CHAR16
*LoaderTitle
, IN EG_IMAGE
*Image
,
1935 IN CHAR16 ShortcutLetter
, IN BOOLEAN UseGraphicsMode
)
1937 LOADER_ENTRY
*Entry
;
1938 CHAR16
*TitleStr
= NULL
;
1940 Entry
= AllocateZeroPool(sizeof(LOADER_ENTRY
));
1942 TitleStr
= PoolPrint(L
"Start %s", LoaderTitle
);
1943 Entry
->me
.Title
= TitleStr
;
1944 Entry
->me
.Tag
= TAG_TOOL
;
1946 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1947 Entry
->me
.Image
= Image
;
1948 Entry
->LoaderPath
= (LoaderPath
) ? StrDuplicate(LoaderPath
) : NULL
;
1949 Entry
->DevicePath
= FileDevicePath(DeviceHandle
, Entry
->LoaderPath
);
1950 Entry
->UseGraphicsMode
= UseGraphicsMode
;
1952 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1954 } /* static LOADER_ENTRY * AddToolEntry() */
1957 // pre-boot driver functions
1960 static UINTN
ScanDriverDir(IN CHAR16
*Path
)
1963 REFIT_DIR_ITER DirIter
;
1965 EFI_FILE_INFO
*DirEntry
;
1966 CHAR16 FileName
[256];
1968 CleanUpPathNameSlashes(Path
);
1969 // look through contents of the directory
1970 DirIterOpen(SelfRootDir
, Path
, &DirIter
);
1971 while (DirIterNext(&DirIter
, 2, LOADER_MATCH_PATTERNS
, &DirEntry
)) {
1972 if (DirEntry
->FileName
[0] == '.')
1973 continue; // skip this
1975 SPrint(FileName
, 255, L
"%s\\%s", Path
, DirEntry
->FileName
);
1977 Status
= StartEFIImage(FileDevicePath(SelfLoadedImage
->DeviceHandle
, FileName
),
1978 L
"", TYPE_EFI
, DirEntry
->FileName
, 0, NULL
, FALSE
);
1980 Status
= DirIterClose(&DirIter
);
1981 if (Status
!= EFI_NOT_FOUND
) {
1982 SPrint(FileName
, 255, L
"while scanning the %s directory", Path
);
1983 CheckError(Status
, FileName
);
1988 #ifdef __MAKEWITH_GNUEFI
1989 static EFI_STATUS
ConnectAllDriversToAllControllers(VOID
)
1992 UINTN AllHandleCount
;
1993 EFI_HANDLE
*AllHandleBuffer
;
1996 EFI_HANDLE
*HandleBuffer
;
2002 Status
= LibLocateHandle(AllHandles
,
2007 if (EFI_ERROR(Status
))
2010 for (Index
= 0; Index
< AllHandleCount
; Index
++) {
2012 // Scan the handle database
2014 Status
= LibScanHandleDatabase(NULL
,
2016 AllHandleBuffer
[Index
],
2021 if (EFI_ERROR (Status
))
2025 if (HandleType
[Index
] & EFI_HANDLE_TYPE_DRIVER_BINDING_HANDLE
)
2027 if (HandleType
[Index
] & EFI_HANDLE_TYPE_IMAGE_HANDLE
)
2032 for (HandleIndex
= 0; HandleIndex
< HandleCount
; HandleIndex
++) {
2033 if (HandleType
[HandleIndex
] & EFI_HANDLE_TYPE_PARENT_HANDLE
)
2038 if (HandleType
[Index
] & EFI_HANDLE_TYPE_DEVICE_HANDLE
) {
2039 Status
= refit_call4_wrapper(BS
->ConnectController
,
2040 AllHandleBuffer
[Index
],
2048 MyFreePool (HandleBuffer
);
2049 MyFreePool (HandleType
);
2053 MyFreePool (AllHandleBuffer
);
2055 } /* EFI_STATUS ConnectAllDriversToAllControllers() */
2057 static EFI_STATUS
ConnectAllDriversToAllControllers(VOID
) {
2058 BdsLibConnectAllDriversToAllControllers();
2063 // Load all EFI drivers from rEFInd's "drivers" subdirectory and from the
2064 // directories specified by the user in the "scan_driver_dirs" configuration
2066 static VOID
LoadDrivers(VOID
)
2068 CHAR16
*Directory
, *SelfDirectory
;
2069 UINTN i
= 0, Length
, NumFound
= 0;
2071 // load drivers from the subdirectories of rEFInd's home directory specified
2072 // in the DRIVER_DIRS constant.
2073 while ((Directory
= FindCommaDelimited(DRIVER_DIRS
, i
++)) != NULL
) {
2074 SelfDirectory
= SelfDirPath
? StrDuplicate(SelfDirPath
) : NULL
;
2075 CleanUpPathNameSlashes(SelfDirectory
);
2076 MergeStrings(&SelfDirectory
, Directory
, L
'\\');
2077 NumFound
+= ScanDriverDir(SelfDirectory
);
2078 MyFreePool(Directory
);
2079 MyFreePool(SelfDirectory
);
2082 // Scan additional user-specified driver directories....
2084 while ((Directory
= FindCommaDelimited(GlobalConfig
.DriverDirs
, i
++)) != NULL
) {
2085 CleanUpPathNameSlashes(Directory
);
2086 Length
= StrLen(Directory
);
2088 NumFound
+= ScanDriverDir(Directory
);
2090 MyFreePool(Directory
);
2093 // connect all devices
2095 ConnectAllDriversToAllControllers();
2096 } /* static VOID LoadDrivers() */
2098 // Determine what (if any) type of legacy (BIOS) boot support is available
2099 static VOID
FindLegacyBootType(VOID
) {
2100 #ifdef __MAKEWITH_TIANO
2102 EFI_LEGACY_BIOS_PROTOCOL
*LegacyBios
;
2105 GlobalConfig
.LegacyType
= LEGACY_TYPE_NONE
;
2107 // UEFI-style legacy BIOS support is available only with the TianoCore EDK2
2108 // build environment, and then only with some EFI implementations....
2109 #ifdef __MAKEWITH_TIANO
2110 Status
= gBS
->LocateProtocol (&gEfiLegacyBootProtocolGuid
, NULL
, (VOID
**) &LegacyBios
);
2111 if (!EFI_ERROR (Status
))
2112 GlobalConfig
.LegacyType
= LEGACY_TYPE_UEFI
;
2115 // Macs have their own system. If the firmware vendor code contains the
2116 // string "Apple", assume it's available. Note that this overrides the
2117 // UEFI type, and might yield false positives if the vendor string
2118 // contains "Apple" as part of something bigger, so this isn't 100%
2120 if (StriSubCmp(L
"Apple", ST
->FirmwareVendor
))
2121 GlobalConfig
.LegacyType
= LEGACY_TYPE_MAC
;
2122 } // static VOID FindLegacyBootType
2124 // Warn the user if legacy OS scans are enabled but the firmware or this
2125 // application can't support them....
2126 static VOID
WarnIfLegacyProblems() {
2127 BOOLEAN found
= FALSE
;
2130 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_NONE
) {
2132 if (GlobalConfig
.ScanFor
[i
] == 'h' || GlobalConfig
.ScanFor
[i
] == 'b' || GlobalConfig
.ScanFor
[i
] == 'c')
2135 } while ((i
< NUM_SCAN_OPTIONS
) && (!found
));
2137 Print(L
"NOTE: refind.conf's 'scanfor' line specifies scanning for one or more legacy\n");
2138 Print(L
"(BIOS) boot options; however, this is not possible because ");
2139 #ifdef __MAKEWITH_TIANO
2140 Print(L
"your computer lacks\n");
2141 Print(L
"the necessary Compatibility Support Module (CSM) support.\n");
2143 Print(L
"this program was\n");
2144 Print(L
"compiled without the necessary support. Please visit\n");
2145 Print(L
"http://www.rodsbooks.com/refind/getting.html and download and install a rEFInd\n");
2146 Print(L
"binary built with the TianoCore EDK2 to enable legacy boot support.\n");
2150 } // if no legacy support
2151 } // static VOID WarnIfLegacyProblems()
2153 // Locates boot loaders. NOTE: This assumes that GlobalConfig.LegacyType is set correctly.
2154 static VOID
ScanForBootloaders(VOID
) {
2159 // scan for loaders and tools, add them to the menu
2160 for (i
= 0; i
< NUM_SCAN_OPTIONS
; i
++) {
2161 switch(GlobalConfig
.ScanFor
[i
]) {
2166 ScanLegacyInternal();
2169 ScanLegacyExternal();
2172 ScanUserConfigured(CONFIG_FILE_NAME
);
2186 // assign shortcut keys
2187 for (i
= 0; i
< MainMenu
.EntryCount
&& MainMenu
.Entries
[i
]->Row
== 0 && i
< 9; i
++)
2188 MainMenu
.Entries
[i
]->ShortcutDigit
= (CHAR16
)('1' + i
);
2190 // wait for user ACK when there were errors
2191 FinishTextScreen(FALSE
);
2192 } // static VOID ScanForBootloaders()
2194 // Locate a single tool from the specified Locations using one of the
2195 // specified Names and add it to the menu.
2196 static VOID
FindTool(CHAR16
*Locations
, CHAR16
*Names
, CHAR16
*Description
, UINTN Icon
) {
2197 UINTN j
= 0, k
, VolumeIndex
;
2198 CHAR16
*DirName
, *FileName
, *PathName
, FullDescription
[256];
2200 while ((DirName
= FindCommaDelimited(Locations
, j
++)) != NULL
) {
2202 while ((FileName
= FindCommaDelimited(Names
, k
++)) != NULL
) {
2203 PathName
= StrDuplicate(DirName
);
2204 MergeStrings(&PathName
, FileName
, (StriCmp(PathName
, L
"\\") == 0) ? 0 : L
'\\');
2205 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
2206 if ((Volumes
[VolumeIndex
]->RootDir
!= NULL
) && (FileExists(Volumes
[VolumeIndex
]->RootDir
, PathName
))) {
2207 SPrint(FullDescription
, 255, L
"%s at %s on %s", Description
, PathName
, Volumes
[VolumeIndex
]->VolName
);
2208 AddToolEntry(Volumes
[VolumeIndex
]->DeviceHandle
, PathName
, FullDescription
, BuiltinIcon(Icon
), 'S', FALSE
);
2211 MyFreePool(PathName
);
2212 MyFreePool(FileName
);
2214 MyFreePool(DirName
);
2215 } // while Locations
2216 } // VOID FindTool()
2218 // Add the second-row tags containing built-in and external tools (EFI shell,
2220 static VOID
ScanForTools(VOID
) {
2221 CHAR16
*FileName
= NULL
, *MokLocations
, Description
[256];
2222 REFIT_MENU_ENTRY
*TempMenuEntry
;
2223 UINTN i
, j
, VolumeIndex
;
2227 MokLocations
= StrDuplicate(MOK_LOCATIONS
);
2228 if (MokLocations
!= NULL
)
2229 MergeStrings(&MokLocations
, SelfDirPath
, L
',');
2231 for (i
= 0; i
< NUM_TOOLS
; i
++) {
2232 switch(GlobalConfig
.ShowTools
[i
]) {
2233 // NOTE: Be sure that FileName is NULL at the end of each case.
2235 TempMenuEntry
= CopyMenuEntry(&MenuEntryShutdown
);
2236 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_SHUTDOWN
);
2237 AddMenuEntry(&MainMenu
, TempMenuEntry
);
2241 TempMenuEntry
= CopyMenuEntry(&MenuEntryReset
);
2242 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_RESET
);
2243 AddMenuEntry(&MainMenu
, TempMenuEntry
);
2247 TempMenuEntry
= CopyMenuEntry(&MenuEntryAbout
);
2248 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT
);
2249 AddMenuEntry(&MainMenu
, TempMenuEntry
);
2253 TempMenuEntry
= CopyMenuEntry(&MenuEntryExit
);
2254 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_EXIT
);
2255 AddMenuEntry(&MainMenu
, TempMenuEntry
);
2259 if (EfivarGetRaw(&GlobalGuid
, L
"OsIndicationsSupported", &b
, &j
) == EFI_SUCCESS
) {
2261 if (osind
& EFI_OS_INDICATIONS_BOOT_TO_FW_UI
) {
2262 TempMenuEntry
= CopyMenuEntry(&MenuEntryFirmware
);
2263 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_FIRMWARE
);
2264 AddMenuEntry(&MainMenu
, TempMenuEntry
);
2271 while ((FileName
= FindCommaDelimited(SHELL_NAMES
, j
++)) != NULL
) {
2272 if (FileExists(SelfRootDir
, FileName
)) {
2273 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, L
"EFI Shell", BuiltinIcon(BUILTIN_ICON_TOOL_SHELL
),
2276 MyFreePool(FileName
);
2282 while ((FileName
= FindCommaDelimited(GPTSYNC_NAMES
, j
++)) != NULL
) {
2283 if (FileExists(SelfRootDir
, FileName
)) {
2284 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, L
"Hybrid MBR tool", BuiltinIcon(BUILTIN_ICON_TOOL_PART
),
2287 MyFreePool(FileName
);
2292 case TAG_APPLE_RECOVERY
:
2293 FileName
= StrDuplicate(L
"\\com.apple.recovery.boot\\boot.efi");
2294 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
2295 if ((Volumes
[VolumeIndex
]->RootDir
!= NULL
) && (FileExists(Volumes
[VolumeIndex
]->RootDir
, FileName
))) {
2296 SPrint(Description
, 255, L
"Apple Recovery on %s", Volumes
[VolumeIndex
]->VolName
);
2297 AddToolEntry(Volumes
[VolumeIndex
]->DeviceHandle
, FileName
, Description
,
2298 BuiltinIcon(BUILTIN_ICON_TOOL_APPLE_RESCUE
), 'R', TRUE
);
2301 MyFreePool(FileName
);
2306 FindTool(MokLocations
, MOK_NAMES
, L
"MOK utility utility", BUILTIN_ICON_TOOL_MOK_TOOL
);
2310 FindTool(MEMTEST_LOCATIONS
, MEMTEST_NAMES
, L
"Memory test utility", BUILTIN_ICON_TOOL_MEMTEST
);
2315 } // static VOID ScanForTools
2317 // Rescan for boot loaders
2318 VOID
RescanAll(VOID
) {
2325 egDisplayMessage(L
"Scanning for new boot loaders; please wait....", &BGColor
);
2326 FreeList((VOID
***) &(MainMenu
.Entries
), &MainMenu
.EntryCount
);
2327 MainMenu
.Entries
= NULL
;
2328 MainMenu
.EntryCount
= 0;
2329 ReadConfig(CONFIG_FILE_NAME
);
2330 ConnectAllDriversToAllControllers();
2332 ScanForBootloaders();
2335 } // VOID RescanAll()
2337 #ifdef __MAKEWITH_TIANO
2339 // Minimal initialization function
2340 static VOID
InitializeLib(IN EFI_HANDLE ImageHandle
, IN EFI_SYSTEM_TABLE
*SystemTable
) {
2342 // gImageHandle = ImageHandle;
2343 gBS
= SystemTable
->BootServices
;
2344 // gRS = SystemTable->RuntimeServices;
2345 gRT
= SystemTable
->RuntimeServices
; // Some BDS functions need gRT to be set
2346 EfiGetSystemConfigurationTable (&gEfiDxeServicesTableGuid
, (VOID
**) &gDS
);
2348 InitializeConsoleSim();
2353 // Set up our own Secure Boot extensions....
2354 // Returns TRUE on success, FALSE otherwise
2355 static BOOLEAN
SecureBootSetup(VOID
) {
2357 BOOLEAN Success
= FALSE
;
2359 if (secure_mode() && ShimLoaded()) {
2360 Status
= security_policy_install();
2361 if (Status
== EFI_SUCCESS
) {
2364 Print(L
"Failed to install MOK Secure Boot extensions");
2368 } // VOID SecureBootSetup()
2370 // Remove our own Secure Boot extensions....
2371 // Returns TRUE on success, FALSE otherwise
2372 static BOOLEAN
SecureBootUninstall(VOID
) {
2374 BOOLEAN Success
= TRUE
;
2376 if (secure_mode()) {
2377 Status
= security_policy_uninstall();
2378 if (Status
!= EFI_SUCCESS
) {
2380 BeginTextScreen(L
"Secure Boot Policy Failure");
2381 Print(L
"Failed to uninstall MOK Secure Boot extensions; forcing a reboot.");
2383 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);
2387 } // VOID SecureBootUninstall
2394 efi_main (EFI_HANDLE ImageHandle
, EFI_SYSTEM_TABLE
*SystemTable
)
2397 BOOLEAN MainLoopRunning
= TRUE
;
2398 BOOLEAN MokProtocol
;
2399 REFIT_MENU_ENTRY
*ChosenEntry
;
2401 CHAR16
*Selection
= NULL
;
2405 InitializeLib(ImageHandle
, SystemTable
);
2406 Status
= InitRefitLib(ImageHandle
);
2407 if (EFI_ERROR(Status
))
2410 // read configuration
2411 CopyMem(GlobalConfig
.ScanFor
, "ieom ", NUM_SCAN_OPTIONS
);
2412 FindLegacyBootType();
2413 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
)
2414 CopyMem(GlobalConfig
.ScanFor
, "ihebocm ", NUM_SCAN_OPTIONS
);
2415 ReadConfig(CONFIG_FILE_NAME
);
2419 WarnIfLegacyProblems();
2420 MainMenu
.TimeoutSeconds
= GlobalConfig
.Timeout
;
2422 // disable EFI watchdog timer
2423 refit_call4_wrapper(BS
->SetWatchdogTimer
, 0x0000, 0x0000, 0x0000, NULL
);
2425 // further bootstrap (now with config available)
2426 MokProtocol
= SecureBootSetup();
2428 ScanForBootloaders();
2432 if (GlobalConfig
.ScanDelay
> 0) {
2437 egDisplayMessage(L
"Pausing before disk scan; please wait....", &BGColor
);
2438 for (i
= 0; i
< GlobalConfig
.ScanDelay
; i
++)
2439 refit_call1_wrapper(BS
->Stall
, 1000000);
2443 if (GlobalConfig
.DefaultSelection
)
2444 Selection
= StrDuplicate(GlobalConfig
.DefaultSelection
);
2446 while (MainLoopRunning
) {
2447 MenuExit
= RunMainMenu(&MainMenu
, Selection
, &ChosenEntry
);
2449 // The Escape key triggers a re-scan operation....
2450 if (MenuExit
== MENU_EXIT_ESCAPE
) {
2455 switch (ChosenEntry
->Tag
) {
2457 case TAG_REBOOT
: // Reboot
2459 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);
2460 MainLoopRunning
= FALSE
; // just in case we get this far
2463 case TAG_SHUTDOWN
: // Shut Down
2465 refit_call4_wrapper(RT
->ResetSystem
, EfiResetShutdown
, EFI_SUCCESS
, 0, NULL
);
2466 MainLoopRunning
= FALSE
; // just in case we get this far
2469 case TAG_ABOUT
: // About rEFInd
2473 case TAG_LOADER
: // Boot OS via .EFI loader
2474 StartLoader((LOADER_ENTRY
*)ChosenEntry
);
2477 case TAG_LEGACY
: // Boot legacy OS
2478 StartLegacy((LEGACY_ENTRY
*)ChosenEntry
);
2481 #ifdef __MAKEWITH_TIANO
2482 case TAG_LEGACY_UEFI
: // Boot a legacy OS on a non-Mac
2483 StartLegacyUEFI((LEGACY_ENTRY
*)ChosenEntry
);
2487 case TAG_TOOL
: // Start a EFI tool
2488 StartTool((LOADER_ENTRY
*)ChosenEntry
);
2491 case TAG_EXIT
: // Terminate rEFInd
2492 if ((MokProtocol
) && !SecureBootUninstall()) {
2493 MainLoopRunning
= FALSE
; // just in case we get this far
2495 BeginTextScreen(L
" ");
2500 case TAG_FIRMWARE
: // Reboot into firmware's user interface
2501 RebootIntoFirmware();
2505 MyFreePool(Selection
);
2506 Selection
= (ChosenEntry
->Title
) ? StrDuplicate(ChosenEntry
->Title
) : NULL
;
2509 // If we end up here, things have gone wrong. Try to reboot, and if that
2510 // fails, go into an endless loop.
2511 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);