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 #endif // __MAKEWITH_GNUEFI
64 #ifndef EFI_OS_INDICATIONS_BOOT_TO_FW_UI
65 #define EFI_OS_INDICATIONS_BOOT_TO_FW_UI 0x0000000000000001ULL
71 #define MACOSX_LOADER_PATH L"System\\Library\\CoreServices\\boot.efi"
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 MEMTEST_NAMES L"memtest86.efi,memtest86_x64.efi,memtest86x64.efi,bootx64.efi"
76 #define DRIVER_DIRS L"drivers,drivers_x64"
77 #define FALLBACK_FULLNAME L"EFI\\BOOT\\bootx64.efi"
78 #define FALLBACK_BASENAME L"bootx64.efi"
79 #define EFI_STUB_ARCH 0x8664
81 #define SHELL_NAMES L"\\EFI\\tools\\shell.efi,\\EFI\\tools\\shellia32.efi,\\shell.efi,\\shellia32.efi"
82 #define GPTSYNC_NAMES L"\\EFI\\tools\\gptsync.efi,\\EFI\\tools\\gptsync_ia32.efi"
83 #define MEMTEST_NAMES L"memtest86.efi,memtest86_ia32.efi,memtest86ia32.efi,bootia32.efi"
84 #define DRIVER_DIRS L"drivers,drivers_ia32"
85 #define FALLBACK_FULLNAME L"EFI\\BOOT\\bootia32.efi"
86 #define FALLBACK_BASENAME L"bootia32.efi"
87 #define EFI_STUB_ARCH 0x014c
89 #define SHELL_NAMES L"\\EFI\\tools\\shell.efi,\\shell.efi"
90 #define GPTSYNC_NAMES L"\\EFI\\tools\\gptsync.efi"
91 #define MEMTEST_NAMES L"memtest86.efi"
92 #define DRIVER_DIRS L"drivers"
93 #define FALLBACK_FULLNAME L"EFI\\BOOT\\boot.efi" /* Not really correct */
94 #define FALLBACK_BASENAME L"boot.efi" /* Not really correct */
96 #define FAT_ARCH 0x0ef1fab9 /* ID for Apple "fat" binary */
98 // Filename patterns that identify EFI boot loaders. Note that a single case (either L"*.efi" or
99 // L"*.EFI") is fine for most systems; but Gigabyte's buggy Hybrid EFI does a case-sensitive
100 // comparison when it should do a case-insensitive comparison, so I'm doubling this up. It does
101 // no harm on other computers, AFAIK. In theory, every case variation should be done for
102 // completeness, but that's ridiculous....
103 #define LOADER_MATCH_PATTERNS L"*.efi,*.EFI"
105 // Patterns that identify Linux kernels. Added to the loader match pattern when the
106 // scan_all_linux_kernels option is set in the configuration file. Causes kernels WITHOUT
107 // a ".efi" extension to be found when scanning for boot loaders.
108 #define LINUX_MATCH_PATTERNS L"vmlinuz*,bzImage*"
110 // Default hint text for program-launch submenus
111 #define SUBSCREEN_HINT1 L"Use arrow keys to move cursor; Enter to boot;"
112 #define SUBSCREEN_HINT2 L"Insert or F2 to edit options; Esc to return to main menu"
113 #define SUBSCREEN_HINT2_NO_EDITOR L"Esc to return to main menu"
117 #define TYPE_LEGACY 2
119 static REFIT_MENU_ENTRY MenuEntryAbout
= { L
"About rEFInd", TAG_ABOUT
, 1, 0, 'A', NULL
, NULL
, NULL
};
120 static REFIT_MENU_ENTRY MenuEntryReset
= { L
"Reboot Computer", TAG_REBOOT
, 1, 0, 'R', NULL
, NULL
, NULL
};
121 static REFIT_MENU_ENTRY MenuEntryShutdown
= { L
"Shut Down Computer", TAG_SHUTDOWN
, 1, 0, 'U', NULL
, NULL
, NULL
};
122 static REFIT_MENU_ENTRY MenuEntryReturn
= { L
"Return to Main Menu", TAG_RETURN
, 1, 0, 0, NULL
, NULL
, NULL
};
123 static REFIT_MENU_ENTRY MenuEntryExit
= { L
"Exit rEFInd", TAG_EXIT
, 1, 0, 0, NULL
, NULL
, NULL
};
124 static REFIT_MENU_ENTRY MenuEntryFirmware
= { L
"Reboot to Computer Setup Utility", TAG_FIRMWARE
, 1, 0, 0, NULL
, NULL
, NULL
};
126 static REFIT_MENU_SCREEN MainMenu
= { L
"Main Menu", NULL
, 0, NULL
, 0, NULL
, 0, L
"Automatic boot",
127 L
"Use arrow keys to move cursor; Enter to boot;",
128 L
"Insert or F2 for more options; Esc to refresh" };
129 static REFIT_MENU_SCREEN AboutMenu
= { L
"About", NULL
, 0, NULL
, 0, NULL
, 0, NULL
, L
"Press Enter to return to main menu", L
"" };
131 REFIT_CONFIG GlobalConfig
= { FALSE
, FALSE
, 0, 0, 0, DONT_CHANGE_TEXT_MODE
, 20, 0, 0, GRAPHICS_FOR_OSX
, LEGACY_TYPE_MAC
, 0, 0,
132 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
133 { TAG_SHELL
, TAG_MEMTEST
, TAG_APPLE_RECOVERY
, TAG_MOK_TOOL
, TAG_ABOUT
, TAG_SHUTDOWN
,
134 TAG_REBOOT
, TAG_FIRMWARE
, 0, 0, 0, 0, 0, 0 }
137 EFI_GUID GlobalGuid
= EFI_GLOBAL_VARIABLE
;
139 // Structure used to hold boot loader filenames and time stamps in
140 // a linked list; used to sort entries within a directory.
144 struct LOADER_LIST
*NextEntry
;
151 static VOID
AboutrEFInd(VOID
)
153 if (AboutMenu
.EntryCount
== 0) {
154 AboutMenu
.TitleImage
= BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT
);
155 AddMenuInfoLine(&AboutMenu
, L
"rEFInd Version 0.7.3.11");
156 AddMenuInfoLine(&AboutMenu
, L
"");
157 AddMenuInfoLine(&AboutMenu
, L
"Copyright (c) 2006-2010 Christoph Pfisterer");
158 AddMenuInfoLine(&AboutMenu
, L
"Copyright (c) 2012-2013 Roderick W. Smith");
159 AddMenuInfoLine(&AboutMenu
, L
"Portions Copyright (c) Intel Corporation and others");
160 AddMenuInfoLine(&AboutMenu
, L
"Distributed under the terms of the GNU GPLv3 license");
161 AddMenuInfoLine(&AboutMenu
, L
"");
162 AddMenuInfoLine(&AboutMenu
, L
"Running on:");
163 AddMenuInfoLine(&AboutMenu
, PoolPrint(L
" EFI Revision %d.%02d", ST
->Hdr
.Revision
>> 16, ST
->Hdr
.Revision
& ((1 << 16) - 1)));
165 AddMenuInfoLine(&AboutMenu
, L
" Platform: x86 (32 bit)");
166 #elif defined(EFIX64)
167 AddMenuInfoLine(&AboutMenu
, PoolPrint(L
" Platform: x86_64 (64 bit); Secure Boot %s",
168 secure_mode() ? L
"active" : L
"inactive"));
170 AddMenuInfoLine(&AboutMenu
, L
" Platform: unknown");
172 AddMenuInfoLine(&AboutMenu
, PoolPrint(L
" Firmware: %s %d.%02d", ST
->FirmwareVendor
, ST
->FirmwareRevision
>> 16,
173 ST
->FirmwareRevision
& ((1 << 16) - 1)));
174 AddMenuInfoLine(&AboutMenu
, PoolPrint(L
" Screen Output: %s", egScreenDescription()));
175 AddMenuInfoLine(&AboutMenu
, L
"");
176 #if defined(__MAKEWITH_GNUEFI)
177 AddMenuInfoLine(&AboutMenu
, L
"Built with GNU-EFI");
179 AddMenuInfoLine(&AboutMenu
, L
"Built with TianoCore EDK2");
181 AddMenuInfoLine(&AboutMenu
, L
"");
182 AddMenuInfoLine(&AboutMenu
, L
"For more information, see the rEFInd Web site:");
183 AddMenuInfoLine(&AboutMenu
, L
"http://www.rodsbooks.com/refind/");
184 AddMenuEntry(&AboutMenu
, &MenuEntryReturn
);
187 RunMenu(&AboutMenu
, NULL
);
188 } /* VOID AboutrEFInd() */
190 static VOID
WarnSecureBootError(CHAR16
*Name
, BOOLEAN Verbose
) {
192 Name
= L
"the loader";
194 refit_call2_wrapper(ST
->ConOut
->SetAttribute
, ST
->ConOut
, ATTR_ERROR
);
195 Print(L
"Secure Boot validation failure loading %s!\n", Name
);
196 refit_call2_wrapper(ST
->ConOut
->SetAttribute
, ST
->ConOut
, ATTR_BASIC
);
197 if (Verbose
&& secure_mode()) {
198 Print(L
"\nThis computer is configured with Secure Boot active, but\n%s has failed validation.\n", Name
);
199 Print(L
"\nYou can:\n * Launch another boot loader\n");
200 Print(L
" * Disable Secure Boot in your firmware\n");
201 Print(L
" * Sign %s with a machine owner key (MOK)\n", Name
);
202 Print(L
" * Use a MOK utility (often present on the second row) to add a MOK with which\n");
203 Print(L
" %s has already been signed.\n", Name
);
204 Print(L
" * Use a MOK utility to register %s (\"enroll its hash\") without\n", Name
);
205 Print(L
" signing it.\n");
206 Print(L
"\nSee http://www.rodsbooks.com/refind/secureboot.html for more information\n");
209 } // VOID WarnSecureBootError()
211 // Returns TRUE if this file is a valid EFI loader file, and is proper ARCH
212 static BOOLEAN
IsValidLoader(EFI_FILE
*RootDir
, CHAR16
*FileName
) {
213 BOOLEAN IsValid
= TRUE
;
214 #if defined (EFIX64) | defined (EFI32)
216 EFI_FILE_HANDLE FileHandle
;
218 UINTN Size
= sizeof(Header
);
220 if ((RootDir
== NULL
) || (FileName
== NULL
)) {
221 // Assume valid here, because Macs produce NULL RootDir (& maybe FileName)
222 // when launching from a Firewire drive. This should be handled better, but
223 // fix would have to be in StartEFIImageList() and/or in FindVolumeAndFilename().
227 Status
= refit_call5_wrapper(RootDir
->Open
, RootDir
, &FileHandle
, FileName
, EFI_FILE_MODE_READ
, 0);
228 if (EFI_ERROR(Status
))
231 Status
= refit_call3_wrapper(FileHandle
->Read
, FileHandle
, &Size
, Header
);
232 refit_call1_wrapper(FileHandle
->Close
, FileHandle
);
234 IsValid
= !EFI_ERROR(Status
) &&
235 Size
== sizeof(Header
) &&
236 ((Header
[0] == 'M' && Header
[1] == 'Z' &&
237 (Size
= *(UINT32
*)&Header
[0x3c]) < 0x180 &&
238 Header
[Size
] == 'P' && Header
[Size
+1] == 'E' &&
239 Header
[Size
+2] == 0 && Header
[Size
+3] == 0 &&
240 *(UINT16
*)&Header
[Size
+4] == EFI_STUB_ARCH
) ||
241 (*(UINT32
*)&Header
== FAT_ARCH
));
244 } // BOOLEAN IsValidLoader()
246 // Launch an EFI binary.
247 static EFI_STATUS
StartEFIImageList(IN EFI_DEVICE_PATH
**DevicePaths
,
248 IN CHAR16
*LoadOptions
, IN UINTN LoaderType
,
249 IN CHAR16
*ImageTitle
, IN CHAR8 OSType
,
250 OUT UINTN
*ErrorInStep
,
253 EFI_STATUS Status
, ReturnStatus
;
254 EFI_HANDLE ChildImageHandle
;
255 EFI_LOADED_IMAGE
*ChildLoadedImage
= NULL
;
256 REFIT_VOLUME
*Volume
= NULL
;
257 UINTN DevicePathIndex
;
258 CHAR16 ErrorInfo
[256];
259 CHAR16
*FullLoadOptions
= NULL
;
260 CHAR16
*Filename
= NULL
;
263 if (ErrorInStep
!= NULL
)
267 if (LoadOptions
!= NULL
) {
268 FullLoadOptions
= StrDuplicate(LoadOptions
);
269 if ((LoaderType
== TYPE_EFI
) && (OSType
== 'M')) {
270 MergeStrings(&FullLoadOptions
, L
" ", 0);
271 // NOTE: That last space is also added by the EFI shell and seems to be significant
272 // when passing options to Apple's boot.efi...
274 } // if (LoadOptions != NULL)
276 Print(L
"Starting %s\nUsing load options '%s'\n", ImageTitle
, FullLoadOptions
? FullLoadOptions
: L
"");
278 // load the image into memory (and execute it, in the case of a shim/MOK image).
279 ReturnStatus
= Status
= EFI_NOT_FOUND
; // in case the list is empty
280 for (DevicePathIndex
= 0; DevicePaths
[DevicePathIndex
] != NULL
; DevicePathIndex
++) {
281 FindVolumeAndFilename(DevicePaths
[DevicePathIndex
], &Volume
, &Filename
);
282 // Some EFIs crash if attempting to load driver for invalid architecture, so
283 // protect for this condition; but sometimes Volume comes back NULL, so provide
284 // an exception. (TODO: Handle this special condition better.)
285 if ((LoaderType
== TYPE_LEGACY
) || (Volume
== NULL
) || IsValidLoader(Volume
->RootDir
, Filename
)) {
287 Temp
= PoolPrint(L
"\\%s %s", Filename
, FullLoadOptions
? FullLoadOptions
: L
"");
289 MyFreePool(FullLoadOptions
);
290 FullLoadOptions
= Temp
;
294 // NOTE: Below commented-out line could be more efficient if file were read ahead of
295 // time and passed as a pre-loaded image to LoadImage(), but it doesn't work on my
296 // 32-bit Mac Mini or my 64-bit Intel box when launching a Linux kernel; the
297 // kernel returns a "Failed to handle fs_proto" error message.
298 // TODO: Track down the cause of this error and fix it, if possible.
299 // ReturnStatus = Status = refit_call6_wrapper(BS->LoadImage, FALSE, SelfImageHandle, DevicePaths[DevicePathIndex],
300 // ImageData, ImageSize, &ChildImageHandle);
301 ReturnStatus
= Status
= refit_call6_wrapper(BS
->LoadImage
, FALSE
, SelfImageHandle
, DevicePaths
[DevicePathIndex
],
302 NULL
, 0, &ChildImageHandle
);
304 Print(L
"Invalid loader file!\n");
305 ReturnStatus
= EFI_LOAD_ERROR
;
307 if (ReturnStatus
!= EFI_NOT_FOUND
) {
311 if ((Status
== EFI_ACCESS_DENIED
) || (Status
== EFI_SECURITY_VIOLATION
)) {
312 WarnSecureBootError(ImageTitle
, Verbose
);
315 SPrint(ErrorInfo
, 255, L
"while loading %s", ImageTitle
);
316 if (CheckError(Status
, ErrorInfo
)) {
317 if (ErrorInStep
!= NULL
)
322 ReturnStatus
= Status
= refit_call3_wrapper(BS
->HandleProtocol
, ChildImageHandle
, &LoadedImageProtocol
,
323 (VOID
**) &ChildLoadedImage
);
324 if (CheckError(Status
, L
"while getting a LoadedImageProtocol handle")) {
325 if (ErrorInStep
!= NULL
)
329 ChildLoadedImage
->LoadOptions
= (VOID
*)FullLoadOptions
;
330 ChildLoadedImage
->LoadOptionsSize
= FullLoadOptions
? ((UINT32
)StrLen(FullLoadOptions
) + 1) * sizeof(CHAR16
) : 0;
331 // turn control over to the image
332 // TODO: (optionally) re-enable the EFI watchdog timer!
334 // close open file handles
336 ReturnStatus
= Status
= refit_call3_wrapper(BS
->StartImage
, ChildImageHandle
, NULL
, NULL
);
338 // control returns here when the child image calls Exit()
339 SPrint(ErrorInfo
, 255, L
"returned from %s", ImageTitle
);
340 if (CheckError(Status
, ErrorInfo
)) {
341 if (ErrorInStep
!= NULL
)
345 // re-open file handles
349 // unload the image, we don't care if it works or not...
350 Status
= refit_call1_wrapper(BS
->UnloadImage
, ChildImageHandle
);
353 MyFreePool(FullLoadOptions
);
355 } /* static EFI_STATUS StartEFIImageList() */
357 static EFI_STATUS
StartEFIImage(IN EFI_DEVICE_PATH
*DevicePath
,
358 IN CHAR16
*LoadOptions
, IN UINTN LoaderType
,
359 IN CHAR16
*ImageTitle
, IN CHAR8 OSType
,
360 OUT UINTN
*ErrorInStep
,
363 EFI_DEVICE_PATH
*DevicePaths
[2];
365 DevicePaths
[0] = DevicePath
;
366 DevicePaths
[1] = NULL
;
367 return StartEFIImageList(DevicePaths
, LoadOptions
, LoaderType
, ImageTitle
, OSType
, ErrorInStep
, Verbose
);
368 } /* static EFI_STATUS StartEFIImage() */
370 // From gummiboot: Retrieve a raw EFI variable.
371 // Returns EFI status
372 static EFI_STATUS
EfivarGetRaw(EFI_GUID
*vendor
, CHAR16
*name
, CHAR8
**buffer
, UINTN
*size
) {
377 l
= sizeof(CHAR16
*) * EFI_MAXIMUM_VARIABLE_SIZE
;
378 buf
= AllocatePool(l
);
380 return EFI_OUT_OF_RESOURCES
;
382 err
= refit_call5_wrapper(RT
->GetVariable
, name
, vendor
, NULL
, &l
, buf
);
383 if (EFI_ERROR(err
) == EFI_SUCCESS
) {
390 } // EFI_STATUS EfivarGetRaw()
392 // From gummiboot: Set an EFI variable
393 static EFI_STATUS
EfivarSetRaw(EFI_GUID
*vendor
, CHAR16
*name
, CHAR8
*buf
, UINTN size
, BOOLEAN persistent
) {
396 flags
= EFI_VARIABLE_BOOTSERVICE_ACCESS
|EFI_VARIABLE_RUNTIME_ACCESS
;
398 flags
|= EFI_VARIABLE_NON_VOLATILE
;
400 return refit_call5_wrapper(RT
->SetVariable
, name
, vendor
, flags
, size
, buf
);
401 } // EFI_STATUS EfivarSetRaw()
403 // From gummiboot: Reboot the computer into its built-in user interface
404 static EFI_STATUS
RebootIntoFirmware(VOID
) {
410 osind
= EFI_OS_INDICATIONS_BOOT_TO_FW_UI
;
412 err
= EfivarGetRaw(&GlobalGuid
, L
"OsIndications", &b
, &size
);
413 if (err
== EFI_SUCCESS
)
417 err
= EfivarSetRaw(&GlobalGuid
, L
"OsIndications", (CHAR8
*)&osind
, sizeof(UINT64
), TRUE
);
418 if (err
!= EFI_SUCCESS
)
421 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);
422 Print(L
"Error calling ResetSystem: %r", err
);
429 // EFI OS loader functions
432 static VOID
StartLoader(LOADER_ENTRY
*Entry
)
434 UINTN ErrorInStep
= 0;
436 BeginExternalScreen(Entry
->UseGraphicsMode
, L
"Booting OS");
437 StartEFIImage(Entry
->DevicePath
, Entry
->LoadOptions
, TYPE_EFI
,
438 Basename(Entry
->LoaderPath
), Entry
->OSType
, &ErrorInStep
, !Entry
->UseGraphicsMode
);
439 FinishExternalScreen();
442 // Locate an initrd or initramfs file that matches the kernel specified by LoaderPath.
443 // The matching file has a name that begins with "init" and includes the same version
444 // number string as is found in LoaderPath -- but not a longer version number string.
445 // For instance, if LoaderPath is \EFI\kernels\bzImage-3.3.0.efi, and if \EFI\kernels
446 // has a file called initramfs-3.3.0.img, this function will return the string
447 // '\EFI\kernels\initramfs-3.3.0.img'. If the directory ALSO contains the file
448 // initramfs-3.3.0-rc7.img or initramfs-13.3.0.img, those files will NOT match;
449 // however, initmine-3.3.0.img might match. (FindInitrd() returns the first match it
450 // finds). Thus, care should be taken to avoid placing duplicate matching files in
451 // the kernel's directory.
452 // If no matching init file can be found, returns NULL.
453 static CHAR16
* FindInitrd(IN CHAR16
*LoaderPath
, IN REFIT_VOLUME
*Volume
) {
454 CHAR16
*InitrdName
= NULL
, *FileName
, *KernelVersion
, *InitrdVersion
, *Path
;
455 REFIT_DIR_ITER DirIter
;
456 EFI_FILE_INFO
*DirEntry
;
458 FileName
= Basename(LoaderPath
);
459 KernelVersion
= FindNumbers(FileName
);
460 Path
= FindPath(LoaderPath
);
462 // Add trailing backslash for root directory; necessary on some systems, but must
463 // NOT be added to all directories, since on other systems, a trailing backslash on
464 // anything but the root directory causes them to flake out!
465 if (StrLen(Path
) == 0) {
466 MergeStrings(&Path
, L
"\\", 0);
468 DirIterOpen(Volume
->RootDir
, Path
, &DirIter
);
469 // Now add a trailing backslash if it was NOT added earlier, for consistency in
470 // building the InitrdName later....
471 if ((StrLen(Path
) > 0) && (Path
[StrLen(Path
) - 1] != L
'\\'))
472 MergeStrings(&Path
, L
"\\", 0);
473 while ((DirIterNext(&DirIter
, 2, L
"init*", &DirEntry
)) && (InitrdName
== NULL
)) {
474 InitrdVersion
= FindNumbers(DirEntry
->FileName
);
475 if (KernelVersion
!= NULL
) {
476 if (StriCmp(InitrdVersion
, KernelVersion
) == 0) {
477 InitrdName
= PoolPrint(L
"%s%s", Path
, DirEntry
->FileName
);
480 if (InitrdVersion
== NULL
) {
481 InitrdName
= PoolPrint(L
"%s%s", Path
, DirEntry
->FileName
);
484 MyFreePool(InitrdVersion
);
486 DirIterClose(&DirIter
);
488 // Note: Don't FreePool(FileName), since Basename returns a pointer WITHIN the string it's passed.
489 MyFreePool(KernelVersion
);
492 } // static CHAR16 * FindInitrd()
494 LOADER_ENTRY
* AddPreparedLoaderEntry(LOADER_ENTRY
*Entry
) {
495 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
498 } // LOADER_ENTRY * AddPreparedLoaderEntry()
500 // Creates a copy of a menu screen.
501 // Returns a pointer to the copy of the menu screen.
502 static REFIT_MENU_SCREEN
* CopyMenuScreen(REFIT_MENU_SCREEN
*Entry
) {
503 REFIT_MENU_SCREEN
*NewEntry
;
506 NewEntry
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
507 if ((Entry
!= NULL
) && (NewEntry
!= NULL
)) {
508 CopyMem(NewEntry
, Entry
, sizeof(REFIT_MENU_SCREEN
));
509 NewEntry
->Title
= (Entry
->Title
) ? StrDuplicate(Entry
->Title
) : NULL
;
510 NewEntry
->TimeoutText
= (Entry
->TimeoutText
) ? StrDuplicate(Entry
->TimeoutText
) : NULL
;
511 if (Entry
->TitleImage
!= NULL
) {
512 NewEntry
->TitleImage
= AllocatePool(sizeof(EG_IMAGE
));
513 if (NewEntry
->TitleImage
!= NULL
)
514 CopyMem(NewEntry
->TitleImage
, Entry
->TitleImage
, sizeof(EG_IMAGE
));
516 NewEntry
->InfoLines
= (CHAR16
**) AllocateZeroPool(Entry
->InfoLineCount
* (sizeof(CHAR16
*)));
517 for (i
= 0; i
< Entry
->InfoLineCount
&& NewEntry
->InfoLines
; i
++) {
518 NewEntry
->InfoLines
[i
] = (Entry
->InfoLines
[i
]) ? StrDuplicate(Entry
->InfoLines
[i
]) : NULL
;
520 NewEntry
->Entries
= (REFIT_MENU_ENTRY
**) AllocateZeroPool(Entry
->EntryCount
* (sizeof (REFIT_MENU_ENTRY
*)));
521 for (i
= 0; i
< Entry
->EntryCount
&& NewEntry
->Entries
; i
++) {
522 AddMenuEntry(NewEntry
, Entry
->Entries
[i
]);
524 NewEntry
->Hint1
= (Entry
->Hint1
) ? StrDuplicate(Entry
->Hint1
) : NULL
;
525 NewEntry
->Hint2
= (Entry
->Hint2
) ? StrDuplicate(Entry
->Hint2
) : NULL
;
528 } // static REFIT_MENU_SCREEN* CopyMenuScreen()
530 // Creates a copy of a menu entry. Intended to enable moving a stack-based
531 // menu entry (such as the ones for the "reboot" and "exit" functions) to
532 // to the heap. This enables easier deletion of the whole set of menu
533 // entries when re-scanning.
534 // Returns a pointer to the copy of the menu entry.
535 static REFIT_MENU_ENTRY
* CopyMenuEntry(REFIT_MENU_ENTRY
*Entry
) {
536 REFIT_MENU_ENTRY
*NewEntry
;
538 NewEntry
= AllocateZeroPool(sizeof(REFIT_MENU_ENTRY
));
539 if ((Entry
!= NULL
) && (NewEntry
!= NULL
)) {
540 CopyMem(NewEntry
, Entry
, sizeof(REFIT_MENU_ENTRY
));
541 NewEntry
->Title
= (Entry
->Title
) ? StrDuplicate(Entry
->Title
) : NULL
;
542 if (Entry
->BadgeImage
!= NULL
) {
543 NewEntry
->BadgeImage
= AllocatePool(sizeof(EG_IMAGE
));
544 if (NewEntry
->BadgeImage
!= NULL
)
545 CopyMem(NewEntry
->BadgeImage
, Entry
->BadgeImage
, sizeof(EG_IMAGE
));
547 if (Entry
->Image
!= NULL
) {
548 NewEntry
->Image
= AllocatePool(sizeof(EG_IMAGE
));
549 if (NewEntry
->Image
!= NULL
)
550 CopyMem(NewEntry
->Image
, Entry
->Image
, sizeof(EG_IMAGE
));
552 if (Entry
->SubScreen
!= NULL
) {
553 NewEntry
->SubScreen
= CopyMenuScreen(Entry
->SubScreen
);
557 } // REFIT_MENU_ENTRY* CopyMenuEntry()
559 // Creates a new LOADER_ENTRY data structure and populates it with
560 // default values from the specified Entry, or NULL values if Entry
561 // is unspecified (NULL).
562 // Returns a pointer to the new data structure, or NULL if it
563 // couldn't be allocated
564 LOADER_ENTRY
*InitializeLoaderEntry(IN LOADER_ENTRY
*Entry
) {
565 LOADER_ENTRY
*NewEntry
= NULL
;
567 NewEntry
= AllocateZeroPool(sizeof(LOADER_ENTRY
));
568 if (NewEntry
!= NULL
) {
569 NewEntry
->me
.Title
= NULL
;
570 NewEntry
->me
.Tag
= TAG_LOADER
;
571 NewEntry
->Enabled
= TRUE
;
572 NewEntry
->UseGraphicsMode
= FALSE
;
573 NewEntry
->OSType
= 0;
575 NewEntry
->LoaderPath
= (Entry
->LoaderPath
) ? StrDuplicate(Entry
->LoaderPath
) : NULL
;
576 NewEntry
->VolName
= (Entry
->VolName
) ? StrDuplicate(Entry
->VolName
) : NULL
;
577 NewEntry
->DevicePath
= Entry
->DevicePath
;
578 NewEntry
->UseGraphicsMode
= Entry
->UseGraphicsMode
;
579 NewEntry
->LoadOptions
= (Entry
->LoadOptions
) ? StrDuplicate(Entry
->LoadOptions
) : NULL
;
580 NewEntry
->InitrdPath
= (Entry
->InitrdPath
) ? StrDuplicate(Entry
->InitrdPath
) : NULL
;
584 } // LOADER_ENTRY *InitializeLoaderEntry()
586 // Adds InitrdPath to Options, but only if Options doesn't already include an
587 // initrd= line. Done to enable overriding the default initrd selection in a
588 // refind_linux.conf file's options list.
589 // Returns a pointer to a new string. The calling function is responsible for
590 // freeing its memory.
591 static CHAR16
*AddInitrdToOptions(CHAR16
*Options
, CHAR16
*InitrdPath
) {
595 if ((InitrdPath
!= NULL
) && !StriSubCmp(L
"initrd=", Options
)) {
596 NewOptions
= PoolPrint(L
"%s initrd=%s", Options
? Options
: L
"", InitrdPath
);
598 NewOptions
= StrDuplicate(Options
);
601 } // CHAR16 *AddInitrdToOptions()
603 // Prepare a REFIT_MENU_SCREEN data structure for a subscreen entry. This sets up
604 // the default entry that launches the boot loader using the same options as the
605 // main Entry does. Subsequent options can be added by the calling function.
606 // If a subscreen already exists in the Entry that's passed to this function,
607 // it's left unchanged and a pointer to it is returned.
608 // Returns a pointer to the new subscreen data structure, or NULL if there
609 // were problems allocating memory.
610 REFIT_MENU_SCREEN
*InitializeSubScreen(IN LOADER_ENTRY
*Entry
) {
611 CHAR16
*FileName
, *MainOptions
= NULL
;
612 REFIT_MENU_SCREEN
*SubScreen
= NULL
;
613 LOADER_ENTRY
*SubEntry
;
615 FileName
= Basename(Entry
->LoaderPath
);
616 if (Entry
->me
.SubScreen
== NULL
) { // No subscreen yet; initialize default entry....
617 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
618 if (SubScreen
!= NULL
) {
619 SubScreen
->Title
= AllocateZeroPool(sizeof(CHAR16
) * 256);
620 SPrint(SubScreen
->Title
, 255, L
"Boot Options for %s on %s",
621 (Entry
->Title
!= NULL
) ? Entry
->Title
: FileName
, Entry
->VolName
);
622 SubScreen
->TitleImage
= Entry
->me
.Image
;
624 SubEntry
= InitializeLoaderEntry(Entry
);
625 if (SubEntry
!= NULL
) {
626 SubEntry
->me
.Title
= StrDuplicate(L
"Boot using default options");
627 MainOptions
= SubEntry
->LoadOptions
;
628 SubEntry
->LoadOptions
= AddInitrdToOptions(MainOptions
, SubEntry
->InitrdPath
);
629 MyFreePool(MainOptions
);
630 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
631 } // if (SubEntry != NULL)
632 SubScreen
->Hint1
= StrDuplicate(SUBSCREEN_HINT1
);
633 if (GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_EDITOR
) {
634 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2_NO_EDITOR
);
636 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2
);
638 } // if (SubScreen != NULL)
639 } else { // existing subscreen; less initialization, and just add new entry later....
640 SubScreen
= Entry
->me
.SubScreen
;
643 } // REFIT_MENU_SCREEN *InitializeSubScreen()
645 VOID
GenerateSubScreen(LOADER_ENTRY
*Entry
, IN REFIT_VOLUME
*Volume
) {
646 REFIT_MENU_SCREEN
*SubScreen
;
647 LOADER_ENTRY
*SubEntry
;
649 CHAR16 DiagsFileName
[256];
654 // create the submenu
655 if (StrLen(Entry
->Title
) == 0) {
656 MyFreePool(Entry
->Title
);
659 SubScreen
= InitializeSubScreen(Entry
);
661 // loader-specific submenu entries
662 if (Entry
->OSType
== 'M') { // entries for Mac OS X
664 SubEntry
= InitializeLoaderEntry(Entry
);
665 if (SubEntry
!= NULL
) {
666 SubEntry
->me
.Title
= L
"Boot Mac OS X with a 64-bit kernel";
667 SubEntry
->LoadOptions
= L
"arch=x86_64";
668 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
669 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
672 SubEntry
= InitializeLoaderEntry(Entry
);
673 if (SubEntry
!= NULL
) {
674 SubEntry
->me
.Title
= L
"Boot Mac OS X with a 32-bit kernel";
675 SubEntry
->LoadOptions
= L
"arch=i386";
676 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
677 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
681 if (!(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_SINGLEUSER
)) {
682 SubEntry
= InitializeLoaderEntry(Entry
);
683 if (SubEntry
!= NULL
) {
684 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode";
685 SubEntry
->UseGraphicsMode
= FALSE
;
686 SubEntry
->LoadOptions
= L
"-v";
687 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
691 SubEntry
= InitializeLoaderEntry(Entry
);
692 if (SubEntry
!= NULL
) {
693 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode (64-bit)";
694 SubEntry
->UseGraphicsMode
= FALSE
;
695 SubEntry
->LoadOptions
= L
"-v arch=x86_64";
696 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
699 SubEntry
= InitializeLoaderEntry(Entry
);
700 if (SubEntry
!= NULL
) {
701 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode (32-bit)";
702 SubEntry
->UseGraphicsMode
= FALSE
;
703 SubEntry
->LoadOptions
= L
"-v arch=i386";
704 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
708 SubEntry
= InitializeLoaderEntry(Entry
);
709 if (SubEntry
!= NULL
) {
710 SubEntry
->me
.Title
= L
"Boot Mac OS X in single user mode";
711 SubEntry
->UseGraphicsMode
= FALSE
;
712 SubEntry
->LoadOptions
= L
"-v -s";
713 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
715 } // single-user mode allowed
717 if (!(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_SAFEMODE
)) {
718 SubEntry
= InitializeLoaderEntry(Entry
);
719 if (SubEntry
!= NULL
) {
720 SubEntry
->me
.Title
= L
"Boot Mac OS X in safe mode";
721 SubEntry
->UseGraphicsMode
= FALSE
;
722 SubEntry
->LoadOptions
= L
"-v -x";
723 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
725 } // safe mode allowed
727 // check for Apple hardware diagnostics
728 StrCpy(DiagsFileName
, L
"System\\Library\\CoreServices\\.diagnostics\\diags.efi");
729 if (FileExists(Volume
->RootDir
, DiagsFileName
) && !(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_HWTEST
)) {
730 SubEntry
= InitializeLoaderEntry(Entry
);
731 if (SubEntry
!= NULL
) {
732 SubEntry
->me
.Title
= L
"Run Apple Hardware Test";
733 MyFreePool(SubEntry
->LoaderPath
);
734 SubEntry
->LoaderPath
= StrDuplicate(DiagsFileName
);
735 SubEntry
->DevicePath
= FileDevicePath(Volume
->DeviceHandle
, SubEntry
->LoaderPath
);
736 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
737 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
739 } // if diagnostics entry found
741 } else if (Entry
->OSType
== 'L') { // entries for Linux kernels with EFI stub loaders
742 File
= ReadLinuxOptionsFile(Entry
->LoaderPath
, Volume
);
744 InitrdName
= FindInitrd(Entry
->LoaderPath
, Volume
);
745 TokenCount
= ReadTokenLine(File
, &TokenList
);
746 // first entry requires special processing, since it was initially set
747 // up with a default title but correct options by InitializeSubScreen(),
749 if ((SubScreen
->Entries
!= NULL
) && (SubScreen
->Entries
[0] != NULL
)) {
750 MyFreePool(SubScreen
->Entries
[0]->Title
);
751 SubScreen
->Entries
[0]->Title
= TokenList
[0] ? StrDuplicate(TokenList
[0]) : StrDuplicate(L
"Boot Linux");
753 FreeTokenLine(&TokenList
, &TokenCount
);
754 while ((TokenCount
= ReadTokenLine(File
, &TokenList
)) > 1) {
755 SubEntry
= InitializeLoaderEntry(Entry
);
756 SubEntry
->me
.Title
= TokenList
[0] ? StrDuplicate(TokenList
[0]) : StrDuplicate(L
"Boot Linux");
757 MyFreePool(SubEntry
->LoadOptions
);
758 SubEntry
->LoadOptions
= AddInitrdToOptions(TokenList
[1], InitrdName
);
759 FreeTokenLine(&TokenList
, &TokenCount
);
760 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_LINUX
;
761 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
763 MyFreePool(InitrdName
);
767 } else if (Entry
->OSType
== 'E') { // entries for ELILO
768 SubEntry
= InitializeLoaderEntry(Entry
);
769 if (SubEntry
!= NULL
) {
770 SubEntry
->me
.Title
= L
"Run ELILO in interactive mode";
771 SubEntry
->LoadOptions
= L
"-p";
772 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
773 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
776 SubEntry
= InitializeLoaderEntry(Entry
);
777 if (SubEntry
!= NULL
) {
778 SubEntry
->me
.Title
= L
"Boot Linux for a 17\" iMac or a 15\" MacBook Pro (*)";
779 SubEntry
->UseGraphicsMode
= TRUE
;
780 SubEntry
->LoadOptions
= L
"-d 0 i17";
781 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
782 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
785 SubEntry
= InitializeLoaderEntry(Entry
);
786 if (SubEntry
!= NULL
) {
787 SubEntry
->me
.Title
= L
"Boot Linux for a 20\" iMac (*)";
788 SubEntry
->UseGraphicsMode
= TRUE
;
789 SubEntry
->LoadOptions
= L
"-d 0 i20";
790 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
791 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
794 SubEntry
= InitializeLoaderEntry(Entry
);
795 if (SubEntry
!= NULL
) {
796 SubEntry
->me
.Title
= L
"Boot Linux for a Mac Mini (*)";
797 SubEntry
->UseGraphicsMode
= TRUE
;
798 SubEntry
->LoadOptions
= L
"-d 0 mini";
799 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
800 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
803 AddMenuInfoLine(SubScreen
, L
"NOTE: This is an example. Entries");
804 AddMenuInfoLine(SubScreen
, L
"marked with (*) may not work.");
806 } else if (Entry
->OSType
== 'X') { // entries for xom.efi
807 // by default, skip the built-in selection and boot from hard disk only
808 Entry
->LoadOptions
= L
"-s -h";
810 SubEntry
= InitializeLoaderEntry(Entry
);
811 if (SubEntry
!= NULL
) {
812 SubEntry
->me
.Title
= L
"Boot Windows from Hard Disk";
813 SubEntry
->LoadOptions
= L
"-s -h";
814 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
815 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
818 SubEntry
= InitializeLoaderEntry(Entry
);
819 if (SubEntry
!= NULL
) {
820 SubEntry
->me
.Title
= L
"Boot Windows from CD-ROM";
821 SubEntry
->LoadOptions
= L
"-s -c";
822 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
823 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
826 SubEntry
= InitializeLoaderEntry(Entry
);
827 if (SubEntry
!= NULL
) {
828 SubEntry
->me
.Title
= L
"Run XOM in text mode";
829 SubEntry
->UseGraphicsMode
= FALSE
;
830 SubEntry
->LoadOptions
= L
"-v";
831 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
832 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
834 } // entries for xom.efi
835 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
836 Entry
->me
.SubScreen
= SubScreen
;
837 } // VOID GenerateSubScreen()
839 // Returns options for a Linux kernel. Reads them from an options file in the
840 // kernel's directory; and if present, adds an initrd= option for an initial
841 // RAM disk file with the same version number as the kernel file.
842 static CHAR16
* GetMainLinuxOptions(IN CHAR16
* LoaderPath
, IN REFIT_VOLUME
*Volume
) {
843 CHAR16
*Options
= NULL
, *InitrdName
, *FullOptions
= NULL
;
845 Options
= GetFirstOptionsFromFile(LoaderPath
, Volume
);
846 InitrdName
= FindInitrd(LoaderPath
, Volume
);
847 FullOptions
= AddInitrdToOptions(Options
, InitrdName
);
850 MyFreePool(InitrdName
);
851 return (FullOptions
);
852 } // static CHAR16 * GetMainLinuxOptions()
854 // Try to guess the name of the Linux distribution & add that name to
856 static VOID
GuessLinuxDistribution(CHAR16
**OSIconName
, REFIT_VOLUME
*Volume
, CHAR16
*LoaderPath
) {
860 UINTN TokenCount
= 0;
862 // If on Linux root fs, /etc/os-release file probably has clues....
863 if (FileExists(Volume
->RootDir
, L
"etc\\os-release") &&
864 (ReadFile(Volume
->RootDir
, L
"etc\\os-release", &File
, &FileSize
) == EFI_SUCCESS
)) {
866 TokenCount
= ReadTokenLine(&File
, &TokenList
);
867 if ((TokenCount
> 1) && ((StriCmp(TokenList
[0], L
"ID") == 0) || (StriCmp(TokenList
[0], L
"NAME") == 0))) {
868 MergeStrings(OSIconName
, TokenList
[1], L
',');
870 FreeTokenLine(&TokenList
, &TokenCount
);
871 } while (TokenCount
> 0);
872 MyFreePool(File
.Buffer
);
875 // Search for clues in the kernel's filename....
876 if (StriSubCmp(L
".fc", LoaderPath
))
877 MergeStrings(OSIconName
, L
"fedora", L
',');
878 if (StriSubCmp(L
".el", LoaderPath
))
879 MergeStrings(OSIconName
, L
"redhat", L
',');
880 } // VOID GuessLinuxDistribution()
882 // Sets a few defaults for a loader entry -- mainly the icon, but also the OS type
883 // code and shortcut letter. For Linux EFI stub loaders, also sets kernel options
884 // that will (with luck) work fairly automatically.
885 VOID
SetLoaderDefaults(LOADER_ENTRY
*Entry
, CHAR16
*LoaderPath
, REFIT_VOLUME
*Volume
) {
886 CHAR16
*FileName
, *PathOnly
, *NoExtension
, *OSIconName
= NULL
, *Temp
, *SubString
;
887 CHAR16 ShortcutLetter
= 0;
890 FileName
= Basename(LoaderPath
);
891 PathOnly
= FindPath(LoaderPath
);
892 NoExtension
= StripEfiExtension(FileName
);
894 // locate a custom icon for the loader
895 // Anything found here takes precedence over the "hints" in the OSIconName variable
896 if (!Entry
->me
.Image
)
897 Entry
->me
.Image
= egLoadIconAnyType(Volume
->RootDir
, PathOnly
, NoExtension
, 128);
898 if (!Entry
->me
.Image
)
899 Entry
->me
.Image
= egCopyImage(Volume
->VolIconImage
);
901 // Begin creating icon "hints" by using last part of directory path leading
903 Temp
= FindLastDirName(LoaderPath
);
904 MergeStrings(&OSIconName
, Temp
, L
',');
907 if (OSIconName
!= NULL
) {
908 ShortcutLetter
= OSIconName
[0];
911 // Add every "word" in the volume label, delimited by spaces, dashes (-), or
912 // underscores (_), to the list of hints to be used in searching for OS
914 if ((Volume
->VolName
) && (StrLen(Volume
->VolName
) > 0)) {
915 Temp
= SubString
= StrDuplicate(Volume
->VolName
);
917 Length
= StrLen(Temp
);
918 for (i
= 0; i
< Length
; i
++) {
919 if ((Temp
[i
] == L
' ') || (Temp
[i
] == L
'_') || (Temp
[i
] == L
'-')) {
921 if (StrLen(SubString
) > 0)
922 MergeStrings(&OSIconName
, SubString
, L
',');
923 SubString
= Temp
+ i
+ 1;
926 MergeStrings(&OSIconName
, SubString
, L
',');
931 // detect specific loaders
932 if (StriSubCmp(L
"bzImage", LoaderPath
) || StriSubCmp(L
"vmlinuz", LoaderPath
)) {
933 GuessLinuxDistribution(&OSIconName
, Volume
, LoaderPath
);
934 MergeStrings(&OSIconName
, L
"linux", L
',');
936 if (ShortcutLetter
== 0)
937 ShortcutLetter
= 'L';
938 Entry
->LoadOptions
= GetMainLinuxOptions(LoaderPath
, Volume
);
939 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_LINUX
;
940 } else if (StriSubCmp(L
"refit", LoaderPath
)) {
941 MergeStrings(&OSIconName
, L
"refit", L
',');
943 ShortcutLetter
= 'R';
944 } else if (StriSubCmp(L
"refind", LoaderPath
)) {
945 MergeStrings(&OSIconName
, L
"refind", L
',');
947 ShortcutLetter
= 'R';
948 } else if (StriCmp(LoaderPath
, MACOSX_LOADER_PATH
) == 0) {
949 if (Volume
->VolIconImage
!= NULL
) { // custom icon file found
950 Entry
->me
.Image
= Volume
->VolIconImage
;
952 MergeStrings(&OSIconName
, L
"mac", L
',');
954 ShortcutLetter
= 'M';
955 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
956 } else if (StriCmp(FileName
, L
"diags.efi") == 0) {
957 MergeStrings(&OSIconName
, L
"hwtest", L
',');
958 } else if (StriCmp(FileName
, L
"e.efi") == 0 || StriCmp(FileName
, L
"elilo.efi") == 0 || StriSubCmp(L
"elilo", FileName
)) {
959 MergeStrings(&OSIconName
, L
"elilo,linux", L
',');
961 if (ShortcutLetter
== 0)
962 ShortcutLetter
= 'L';
963 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
964 } else if (StriSubCmp(L
"grub", FileName
)) {
966 ShortcutLetter
= 'G';
967 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_GRUB
;
968 } else if (StriCmp(FileName
, L
"cdboot.efi") == 0 ||
969 StriCmp(FileName
, L
"bootmgr.efi") == 0 ||
970 StriCmp(FileName
, L
"bootmgfw.efi") == 0 ||
971 StriCmp(FileName
, L
"bkpbootmgfw.efi") == 0) {
972 MergeStrings(&OSIconName
, L
"win", L
',');
974 ShortcutLetter
= 'W';
975 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
976 } else if (StriCmp(FileName
, L
"xom.efi") == 0) {
977 MergeStrings(&OSIconName
, L
"xom,win", L
',');
978 Entry
->UseGraphicsMode
= TRUE
;
980 ShortcutLetter
= 'W';
981 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
984 if ((ShortcutLetter
>= 'a') && (ShortcutLetter
<= 'z'))
985 ShortcutLetter
= ShortcutLetter
- 'a' + 'A'; // convert lowercase to uppercase
986 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
987 if (Entry
->me
.Image
== NULL
)
988 Entry
->me
.Image
= LoadOSIcon(OSIconName
, L
"unknown", FALSE
);
989 MyFreePool(PathOnly
);
990 } // VOID SetLoaderDefaults()
992 // Add a specified EFI boot loader to the list, using automatic settings
993 // for icons, options, etc.
994 LOADER_ENTRY
* AddLoaderEntry(IN CHAR16
*LoaderPath
, IN CHAR16
*LoaderTitle
, IN REFIT_VOLUME
*Volume
) {
997 CleanUpPathNameSlashes(LoaderPath
);
998 Entry
= InitializeLoaderEntry(NULL
);
1000 Entry
->Title
= StrDuplicate((LoaderTitle
!= NULL
) ? LoaderTitle
: LoaderPath
);
1001 Entry
->me
.Title
= AllocateZeroPool(sizeof(CHAR16
) * 256);
1002 SPrint(Entry
->me
.Title
, 255, L
"Boot %s from %s ", (LoaderTitle
!= NULL
) ? LoaderTitle
: LoaderPath
, Volume
->VolName
);
1004 Entry
->me
.BadgeImage
= Volume
->VolBadgeImage
;
1005 if ((LoaderPath
!= NULL
) && (LoaderPath
[0] != L
'\\')) {
1006 Entry
->LoaderPath
= StrDuplicate(L
"\\");
1008 Entry
->LoaderPath
= NULL
;
1010 MergeStrings(&(Entry
->LoaderPath
), LoaderPath
, 0);
1011 Entry
->VolName
= Volume
->VolName
;
1012 Entry
->DevicePath
= FileDevicePath(Volume
->DeviceHandle
, Entry
->LoaderPath
);
1013 SetLoaderDefaults(Entry
, LoaderPath
, Volume
);
1014 GenerateSubScreen(Entry
, Volume
);
1015 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1019 } // LOADER_ENTRY * AddLoaderEntry()
1021 // Returns -1 if (Time1 < Time2), +1 if (Time1 > Time2), or 0 if
1022 // (Time1 == Time2). Precision is only to the nearest second; since
1023 // this is used for sorting boot loader entries, differences smaller
1024 // than this are likely to be meaningless (and unlikely!).
1025 INTN
TimeComp(IN EFI_TIME
*Time1
, IN EFI_TIME
*Time2
) {
1026 INT64 Time1InSeconds
, Time2InSeconds
;
1028 // Following values are overestimates; I'm assuming 31 days in every month.
1029 // This is fine for the purpose of this function, which is limited
1030 Time1InSeconds
= Time1
->Second
+ (Time1
->Minute
* 60) + (Time1
->Hour
* 3600) + (Time1
->Day
* 86400) +
1031 (Time1
->Month
* 2678400) + ((Time1
->Year
- 1998) * 32140800);
1032 Time2InSeconds
= Time2
->Second
+ (Time2
->Minute
* 60) + (Time2
->Hour
* 3600) + (Time2
->Day
* 86400) +
1033 (Time2
->Month
* 2678400) + ((Time2
->Year
- 1998) * 32140800);
1034 if (Time1InSeconds
< Time2InSeconds
)
1036 else if (Time1InSeconds
> Time2InSeconds
)
1040 } // INTN TimeComp()
1042 // Adds a loader list element, keeping it sorted by date. Returns the new
1043 // first element (the one with the most recent date).
1044 static struct LOADER_LIST
* AddLoaderListEntry(struct LOADER_LIST
*LoaderList
, struct LOADER_LIST
*NewEntry
) {
1045 struct LOADER_LIST
*LatestEntry
, *CurrentEntry
, *PrevEntry
= NULL
;
1047 LatestEntry
= CurrentEntry
= LoaderList
;
1048 if (LoaderList
== NULL
) {
1049 LatestEntry
= NewEntry
;
1051 while ((CurrentEntry
!= NULL
) && (TimeComp(&(NewEntry
->TimeStamp
), &(CurrentEntry
->TimeStamp
)) < 0)) {
1052 PrevEntry
= CurrentEntry
;
1053 CurrentEntry
= CurrentEntry
->NextEntry
;
1055 NewEntry
->NextEntry
= CurrentEntry
;
1056 if (PrevEntry
== NULL
) {
1057 LatestEntry
= NewEntry
;
1059 PrevEntry
->NextEntry
= NewEntry
;
1062 return (LatestEntry
);
1063 } // static VOID AddLoaderListEntry()
1065 // Delete the LOADER_LIST linked list
1066 static VOID
CleanUpLoaderList(struct LOADER_LIST
*LoaderList
) {
1067 struct LOADER_LIST
*Temp
;
1069 while (LoaderList
!= NULL
) {
1071 LoaderList
= LoaderList
->NextEntry
;
1072 MyFreePool(Temp
->FileName
);
1075 } // static VOID CleanUpLoaderList()
1077 // Returns FALSE if the specified file/volume matches the GlobalConfig.DontScanDirs
1078 // or GlobalConfig.DontScanVolumes specification, or if Path points to a volume
1079 // other than the one specified by Volume, or if the specified path is SelfDir.
1080 // Returns TRUE if none of these conditions is met -- that is, if the path is
1081 // eligible for scanning.
1082 static BOOLEAN
ShouldScan(REFIT_VOLUME
*Volume
, CHAR16
*Path
) {
1083 CHAR16
*VolName
= NULL
, *DontScanDir
, *PathCopy
= NULL
;
1084 UINTN i
= 0, VolNum
;
1085 BOOLEAN ScanIt
= TRUE
;
1087 if (IsIn(Volume
->VolName
, GlobalConfig
.DontScanVolumes
))
1090 if ((StriCmp(Path
, SelfDirPath
) == 0) && (Volume
->DeviceHandle
== SelfVolume
->DeviceHandle
))
1093 // See if Path includes an explicit volume declaration that's NOT Volume....
1094 PathCopy
= StrDuplicate(Path
);
1095 if (SplitVolumeAndFilename(&PathCopy
, &VolName
)) {
1096 if (StriCmp(VolName
, Volume
->VolName
) != 0) {
1097 if ((StrLen(VolName
) > 2) && (VolName
[0] == L
'f') && (VolName
[1] == L
's') && (VolName
[2] >= L
'0') && (VolName
[2] <= L
'9')) {
1098 VolNum
= Atoi(VolName
+ 2);
1099 if (VolNum
!= Volume
->VolNumber
) {
1106 } // if Path includes volume specification
1107 MyFreePool(PathCopy
);
1108 MyFreePool(VolName
);
1111 // See if Volume is in GlobalConfig.DontScanDirs....
1112 while ((DontScanDir
= FindCommaDelimited(GlobalConfig
.DontScanDirs
, i
++)) && ScanIt
) {
1113 SplitVolumeAndFilename(&DontScanDir
, &VolName
);
1114 CleanUpPathNameSlashes(DontScanDir
);
1115 if (VolName
!= NULL
) {
1116 if ((StriCmp(VolName
, Volume
->VolName
) == 0) && (StriCmp(DontScanDir
, Path
) == 0))
1118 if ((StrLen(VolName
) > 2) && (VolName
[0] == L
'f') && (VolName
[1] == L
's') && (VolName
[2] >= L
'0') && (VolName
[2] <= L
'9')) {
1119 VolNum
= Atoi(VolName
+ 2);
1120 if ((VolNum
== Volume
->VolNumber
) && (StriCmp(DontScanDir
, Path
) == 0))
1124 if (StriCmp(DontScanDir
, Path
) == 0)
1127 MyFreePool(DontScanDir
);
1128 MyFreePool(VolName
);
1133 } // BOOLEAN ShouldScan()
1135 // Returns TRUE if the file is byte-for-byte identical with the fallback file
1136 // on the volume AND if the file is not itself the fallback file; returns
1137 // FALSE if the file is not identical to the fallback file OR if the file
1138 // IS the fallback file. Intended for use in excluding the fallback boot
1139 // loader when it's a duplicate of another boot loader.
1140 static BOOLEAN
DuplicatesFallback(IN REFIT_VOLUME
*Volume
, IN CHAR16
*FileName
) {
1141 CHAR8
*FileContents
, *FallbackContents
;
1142 EFI_FILE_HANDLE FileHandle
, FallbackHandle
;
1143 EFI_FILE_INFO
*FileInfo
, *FallbackInfo
;
1144 UINTN FileSize
= 0, FallbackSize
= 0;
1146 BOOLEAN AreIdentical
= FALSE
;
1148 if (!FileExists(Volume
->RootDir
, FileName
) || !FileExists(Volume
->RootDir
, FALLBACK_FULLNAME
))
1151 CleanUpPathNameSlashes(FileName
);
1153 if (StriCmp(FileName
, FALLBACK_FULLNAME
) == 0)
1154 return FALSE
; // identical filenames, so not a duplicate....
1156 Status
= refit_call5_wrapper(Volume
->RootDir
->Open
, Volume
->RootDir
, &FileHandle
, FileName
, EFI_FILE_MODE_READ
, 0);
1157 if (Status
== EFI_SUCCESS
) {
1158 FileInfo
= LibFileInfo(FileHandle
);
1159 FileSize
= FileInfo
->FileSize
;
1164 Status
= refit_call5_wrapper(Volume
->RootDir
->Open
, Volume
->RootDir
, &FallbackHandle
, FALLBACK_FULLNAME
, EFI_FILE_MODE_READ
, 0);
1165 if (Status
== EFI_SUCCESS
) {
1166 FallbackInfo
= LibFileInfo(FallbackHandle
);
1167 FallbackSize
= FallbackInfo
->FileSize
;
1169 refit_call1_wrapper(FileHandle
->Close
, FileHandle
);
1173 if (FallbackSize
!= FileSize
) { // not same size, so can't be identical
1174 AreIdentical
= FALSE
;
1175 } else { // could be identical; do full check....
1176 FileContents
= AllocatePool(FileSize
);
1177 FallbackContents
= AllocatePool(FallbackSize
);
1178 if (FileContents
&& FallbackContents
) {
1179 Status
= refit_call3_wrapper(FileHandle
->Read
, FileHandle
, &FileSize
, FileContents
);
1180 if (Status
== EFI_SUCCESS
) {
1181 Status
= refit_call3_wrapper(FallbackHandle
->Read
, FallbackHandle
, &FallbackSize
, FallbackContents
);
1183 if (Status
== EFI_SUCCESS
) {
1184 AreIdentical
= (CompareMem(FileContents
, FallbackContents
, FileSize
) == 0);
1187 MyFreePool(FileContents
);
1188 MyFreePool(FallbackContents
);
1191 // BUG ALERT: Some systems (e.g., DUET, some Macs with large displays) crash if the
1192 // following two calls are reversed. Go figure....
1193 refit_call1_wrapper(FileHandle
->Close
, FallbackHandle
);
1194 refit_call1_wrapper(FileHandle
->Close
, FileHandle
);
1195 return AreIdentical
;
1196 } // BOOLEAN DuplicatesFallback()
1198 // Returns FALSE if two measures of file size are identical for a single file,
1199 // TRUE if not or if the file can't be opened and the other measure is non-0.
1200 // Despite the function's name, this isn't really a direct test of symbolic
1201 // link status, since EFI doesn't officially support symlinks. It does seem
1202 // to be a reliable indicator, though. (OTOH, some disk errors might cause a
1203 // file to fail to open, which would return a false positive -- but as I use
1204 // this function to exclude symbolic links from the list of boot loaders,
1205 // that would be fine, since such boot loaders wouldn't work.)
1206 static BOOLEAN
IsSymbolicLink(REFIT_VOLUME
*Volume
, CHAR16
*Path
, EFI_FILE_INFO
*DirEntry
) {
1207 EFI_FILE_HANDLE FileHandle
;
1208 EFI_FILE_INFO
*FileInfo
= NULL
;
1210 UINTN FileSize2
= 0;
1213 FileName
= StrDuplicate(Path
);
1214 MergeStrings(&FileName
, DirEntry
->FileName
, L
'\\');
1215 CleanUpPathNameSlashes(FileName
);
1217 Status
= refit_call5_wrapper(Volume
->RootDir
->Open
, Volume
->RootDir
, &FileHandle
, FileName
, EFI_FILE_MODE_READ
, 0);
1218 if (Status
== EFI_SUCCESS
) {
1219 FileInfo
= LibFileInfo(FileHandle
);
1220 if (FileInfo
!= NULL
)
1221 FileSize2
= FileInfo
->FileSize
;
1224 MyFreePool(FileName
);
1225 MyFreePool(FileInfo
);
1227 return (DirEntry
->FileSize
!= FileSize2
);
1228 } // BOOLEAN IsSymbolicLink()
1230 // Scan an individual directory for EFI boot loader files and, if found,
1231 // add them to the list. Exception: Ignores FALLBACK_FULLNAME, which is picked
1232 // up in ScanEfiFiles(). Sorts the entries within the loader directory so that
1233 // the most recent one appears first in the list.
1234 // Returns TRUE if a duplicate for FALLBACK_FILENAME was found, FALSE if not.
1235 static BOOLEAN
ScanLoaderDir(IN REFIT_VOLUME
*Volume
, IN CHAR16
*Path
, IN CHAR16
*Pattern
)
1238 REFIT_DIR_ITER DirIter
;
1239 EFI_FILE_INFO
*DirEntry
;
1240 CHAR16 FileName
[256], *Extension
;
1241 struct LOADER_LIST
*LoaderList
= NULL
, *NewLoader
;
1242 BOOLEAN FoundFallbackDuplicate
= FALSE
;
1244 if ((!SelfDirPath
|| !Path
|| ((StriCmp(Path
, SelfDirPath
) == 0) && (Volume
->DeviceHandle
!= SelfVolume
->DeviceHandle
)) ||
1245 (StriCmp(Path
, SelfDirPath
) != 0)) &&
1246 (ShouldScan(Volume
, Path
))) {
1247 // look through contents of the directory
1248 DirIterOpen(Volume
->RootDir
, Path
, &DirIter
);
1249 while (DirIterNext(&DirIter
, 2, Pattern
, &DirEntry
)) {
1250 Extension
= FindExtension(DirEntry
->FileName
);
1251 if (DirEntry
->FileName
[0] == '.' ||
1252 StriCmp(Extension
, L
".icns") == 0 ||
1253 StriCmp(Extension
, L
".png") == 0 ||
1254 (StriCmp(DirEntry
->FileName
, FALLBACK_BASENAME
) == 0 && (StriCmp(Path
, L
"EFI\\BOOT") == 0)) ||
1255 StriSubCmp(L
"shell", DirEntry
->FileName
) ||
1256 IsSymbolicLink(Volume
, Path
, DirEntry
) || /* is symbolic link */
1257 IsIn(DirEntry
->FileName
, GlobalConfig
.DontScanFiles
))
1258 continue; // skip this
1261 SPrint(FileName
, 255, L
"\\%s\\%s", Path
, DirEntry
->FileName
);
1263 SPrint(FileName
, 255, L
"\\%s", DirEntry
->FileName
);
1264 CleanUpPathNameSlashes(FileName
);
1266 if(!IsValidLoader(Volume
->RootDir
, FileName
))
1269 NewLoader
= AllocateZeroPool(sizeof(struct LOADER_LIST
));
1270 if (NewLoader
!= NULL
) {
1271 NewLoader
->FileName
= StrDuplicate(FileName
);
1272 NewLoader
->TimeStamp
= DirEntry
->ModificationTime
;
1273 LoaderList
= AddLoaderListEntry(LoaderList
, NewLoader
);
1274 if (DuplicatesFallback(Volume
, FileName
))
1275 FoundFallbackDuplicate
= TRUE
;
1277 MyFreePool(Extension
);
1280 NewLoader
= LoaderList
;
1281 while (NewLoader
!= NULL
) {
1282 AddLoaderEntry(NewLoader
->FileName
, NULL
, Volume
);
1283 NewLoader
= NewLoader
->NextEntry
;
1286 CleanUpLoaderList(LoaderList
);
1287 Status
= DirIterClose(&DirIter
);
1288 // NOTE: EFI_INVALID_PARAMETER really is an error that should be reported;
1289 // but I've gotten reports from users who are getting this error occasionally
1290 // and I can't find anything wrong or reproduce the problem, so I'm putting
1291 // it down to buggy EFI implementations and ignoring that particular error....
1292 if ((Status
!= EFI_NOT_FOUND
) && (Status
!= EFI_INVALID_PARAMETER
)) {
1294 SPrint(FileName
, 255, L
"while scanning the %s directory", Path
);
1296 StrCpy(FileName
, L
"while scanning the root directory");
1297 CheckError(Status
, FileName
);
1298 } // if (Status != EFI_NOT_FOUND)
1299 } // if not scanning a blacklisted directory
1301 return FoundFallbackDuplicate
;
1302 } /* static VOID ScanLoaderDir() */
1304 static VOID
ScanEfiFiles(REFIT_VOLUME
*Volume
) {
1306 REFIT_DIR_ITER EfiDirIter
;
1307 EFI_FILE_INFO
*EfiDirEntry
;
1308 CHAR16 FileName
[256], *Directory
, *MatchPatterns
, *VolName
= NULL
, *SelfPath
;
1310 BOOLEAN ScanFallbackLoader
= TRUE
;
1311 BOOLEAN FoundBRBackup
= FALSE
;
1313 MatchPatterns
= StrDuplicate(LOADER_MATCH_PATTERNS
);
1314 if (GlobalConfig
.ScanAllLinux
)
1315 MergeStrings(&MatchPatterns
, LINUX_MATCH_PATTERNS
, L
',');
1317 if ((Volume
->RootDir
!= NULL
) && (Volume
->VolName
!= NULL
)) {
1318 // check for Mac OS X boot loader
1319 if (ShouldScan(Volume
, L
"System\\Library\\CoreServices")) {
1320 StrCpy(FileName
, MACOSX_LOADER_PATH
);
1321 if (FileExists(Volume
->RootDir
, FileName
) && !IsIn(L
"boot.efi", GlobalConfig
.DontScanFiles
)) {
1322 AddLoaderEntry(FileName
, L
"Mac OS X", Volume
);
1323 if (DuplicatesFallback(Volume
, FileName
))
1324 ScanFallbackLoader
= FALSE
;
1328 StrCpy(FileName
, L
"System\\Library\\CoreServices\\xom.efi");
1329 if (FileExists(Volume
->RootDir
, FileName
) && !IsIn(L
"boot.efi", GlobalConfig
.DontScanFiles
)) {
1330 AddLoaderEntry(FileName
, L
"Windows XP (XoM)", Volume
);
1331 if (DuplicatesFallback(Volume
, FileName
))
1332 ScanFallbackLoader
= FALSE
;
1334 } // if should scan Mac directory
1336 // check for Microsoft boot loader/menu
1337 if (ShouldScan(Volume
, L
"EFI\\Microsoft\\Boot")) {
1338 StrCpy(FileName
, L
"EFI\\Microsoft\\Boot\\bkpbootmgfw.efi");
1339 if (FileExists(Volume
->RootDir
, FileName
) && !IsIn(L
"bkpbootmgfw.efi", GlobalConfig
.DontScanFiles
)) {
1340 AddLoaderEntry(FileName
, L
"Microsoft EFI boot (Boot Repair backup)", Volume
);
1341 FoundBRBackup
= TRUE
;
1342 if (DuplicatesFallback(Volume
, FileName
))
1343 ScanFallbackLoader
= FALSE
;
1345 StrCpy(FileName
, L
"EFI\\Microsoft\\Boot\\bootmgfw.efi");
1346 if (FileExists(Volume
->RootDir
, FileName
) && !IsIn(L
"bootmgfw.efi", GlobalConfig
.DontScanFiles
)) {
1348 AddLoaderEntry(FileName
, L
"Supposed Microsoft EFI boot (probably GRUB)", Volume
);
1350 AddLoaderEntry(FileName
, L
"Microsoft EFI boot", Volume
);
1351 if (DuplicatesFallback(Volume
, FileName
))
1352 ScanFallbackLoader
= FALSE
;
1356 // scan the root directory for EFI executables
1357 if (ScanLoaderDir(Volume
, L
"\\", MatchPatterns
))
1358 ScanFallbackLoader
= FALSE
;
1360 // scan subdirectories of the EFI directory (as per the standard)
1361 DirIterOpen(Volume
->RootDir
, L
"EFI", &EfiDirIter
);
1362 while (DirIterNext(&EfiDirIter
, 1, NULL
, &EfiDirEntry
)) {
1363 if (StriCmp(EfiDirEntry
->FileName
, L
"tools") == 0 || EfiDirEntry
->FileName
[0] == '.')
1364 continue; // skip this, doesn't contain boot loaders or is scanned later
1365 SPrint(FileName
, 255, L
"EFI\\%s", EfiDirEntry
->FileName
);
1366 if (ScanLoaderDir(Volume
, FileName
, MatchPatterns
))
1367 ScanFallbackLoader
= FALSE
;
1369 Status
= DirIterClose(&EfiDirIter
);
1370 if (Status
!= EFI_NOT_FOUND
)
1371 CheckError(Status
, L
"while scanning the EFI directory");
1373 // Scan user-specified (or additional default) directories....
1375 while ((Directory
= FindCommaDelimited(GlobalConfig
.AlsoScan
, i
++)) != NULL
) {
1376 if (ShouldScan(Volume
, Directory
)) {
1377 SplitVolumeAndFilename(&Directory
, &VolName
);
1378 CleanUpPathNameSlashes(Directory
);
1379 Length
= StrLen(Directory
);
1380 if ((Length
> 0) && ScanLoaderDir(Volume
, Directory
, MatchPatterns
))
1381 ScanFallbackLoader
= FALSE
;
1382 MyFreePool(VolName
);
1383 } // if should scan dir
1384 MyFreePool(Directory
);
1387 // Don't scan the fallback loader if it's on the same volume and a duplicate of rEFInd itself....
1388 SelfPath
= DevicePathToStr(SelfLoadedImage
->FilePath
);
1389 CleanUpPathNameSlashes(SelfPath
);
1390 if ((Volume
->DeviceHandle
== SelfLoadedImage
->DeviceHandle
) && DuplicatesFallback(Volume
, SelfPath
))
1391 ScanFallbackLoader
= FALSE
;
1393 // If not a duplicate & if it exists & if it's not us, create an entry
1394 // for the fallback boot loader
1395 if (ScanFallbackLoader
&& FileExists(Volume
->RootDir
, FALLBACK_FULLNAME
) && ShouldScan(Volume
, L
"EFI\\BOOT"))
1396 AddLoaderEntry(FALLBACK_FULLNAME
, L
"Fallback boot loader", Volume
);
1398 } // static VOID ScanEfiFiles()
1400 // Scan internal disks for valid EFI boot loaders....
1401 static VOID
ScanInternal(VOID
) {
1404 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1405 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_INTERNAL
) {
1406 ScanEfiFiles(Volumes
[VolumeIndex
]);
1409 } // static VOID ScanInternal()
1411 // Scan external disks for valid EFI boot loaders....
1412 static VOID
ScanExternal(VOID
) {
1415 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1416 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_EXTERNAL
) {
1417 ScanEfiFiles(Volumes
[VolumeIndex
]);
1420 } // static VOID ScanExternal()
1422 // Scan internal disks for valid EFI boot loaders....
1423 static VOID
ScanOptical(VOID
) {
1426 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1427 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_OPTICAL
) {
1428 ScanEfiFiles(Volumes
[VolumeIndex
]);
1431 } // static VOID ScanOptical()
1434 // legacy boot functions
1437 static EFI_STATUS
ActivateMbrPartition(IN EFI_BLOCK_IO
*BlockIO
, IN UINTN PartitionIndex
)
1440 UINT8 SectorBuffer
[512];
1441 MBR_PARTITION_INFO
*MbrTable
, *EMbrTable
;
1442 UINT32 ExtBase
, ExtCurrent
, NextExtCurrent
;
1443 UINTN LogicalPartitionIndex
= 4;
1445 BOOLEAN HaveBootCode
;
1448 Status
= refit_call5_wrapper(BlockIO
->ReadBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, 0, 512, SectorBuffer
);
1449 if (EFI_ERROR(Status
))
1451 if (*((UINT16
*)(SectorBuffer
+ 510)) != 0xaa55)
1452 return EFI_NOT_FOUND
; // safety measure #1
1454 // add boot code if necessary
1455 HaveBootCode
= FALSE
;
1456 for (i
= 0; i
< MBR_BOOTCODE_SIZE
; i
++) {
1457 if (SectorBuffer
[i
] != 0) {
1458 HaveBootCode
= TRUE
;
1462 if (!HaveBootCode
) {
1463 // no boot code found in the MBR, add the syslinux MBR code
1464 SetMem(SectorBuffer
, MBR_BOOTCODE_SIZE
, 0);
1465 CopyMem(SectorBuffer
, syslinux_mbr
, SYSLINUX_MBR_SIZE
);
1468 // set the partition active
1469 MbrTable
= (MBR_PARTITION_INFO
*)(SectorBuffer
+ 446);
1471 for (i
= 0; i
< 4; i
++) {
1472 if (MbrTable
[i
].Flags
!= 0x00 && MbrTable
[i
].Flags
!= 0x80)
1473 return EFI_NOT_FOUND
; // safety measure #2
1474 if (i
== PartitionIndex
)
1475 MbrTable
[i
].Flags
= 0x80;
1476 else if (PartitionIndex
>= 4 && IS_EXTENDED_PART_TYPE(MbrTable
[i
].Type
)) {
1477 MbrTable
[i
].Flags
= 0x80;
1478 ExtBase
= MbrTable
[i
].StartLBA
;
1480 MbrTable
[i
].Flags
= 0x00;
1484 Status
= refit_call5_wrapper(BlockIO
->WriteBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, 0, 512, SectorBuffer
);
1485 if (EFI_ERROR(Status
))
1488 if (PartitionIndex
>= 4) {
1489 // we have to activate a logical partition, so walk the EMBR chain
1491 // NOTE: ExtBase was set above while looking at the MBR table
1492 for (ExtCurrent
= ExtBase
; ExtCurrent
; ExtCurrent
= NextExtCurrent
) {
1493 // read current EMBR
1494 Status
= refit_call5_wrapper(BlockIO
->ReadBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, ExtCurrent
, 512, SectorBuffer
);
1495 if (EFI_ERROR(Status
))
1497 if (*((UINT16
*)(SectorBuffer
+ 510)) != 0xaa55)
1498 return EFI_NOT_FOUND
; // safety measure #3
1500 // scan EMBR, set appropriate partition active
1501 EMbrTable
= (MBR_PARTITION_INFO
*)(SectorBuffer
+ 446);
1503 for (i
= 0; i
< 4; i
++) {
1504 if (EMbrTable
[i
].Flags
!= 0x00 && EMbrTable
[i
].Flags
!= 0x80)
1505 return EFI_NOT_FOUND
; // safety measure #4
1506 if (EMbrTable
[i
].StartLBA
== 0 || EMbrTable
[i
].Size
== 0)
1508 if (IS_EXTENDED_PART_TYPE(EMbrTable
[i
].Type
)) {
1509 // link to next EMBR
1510 NextExtCurrent
= ExtBase
+ EMbrTable
[i
].StartLBA
;
1511 EMbrTable
[i
].Flags
= (PartitionIndex
>= LogicalPartitionIndex
) ? 0x80 : 0x00;
1514 // logical partition
1515 EMbrTable
[i
].Flags
= (PartitionIndex
== LogicalPartitionIndex
) ? 0x80 : 0x00;
1516 LogicalPartitionIndex
++;
1520 // write current EMBR
1521 Status
= refit_call5_wrapper(BlockIO
->WriteBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, ExtCurrent
, 512, SectorBuffer
);
1522 if (EFI_ERROR(Status
))
1525 if (PartitionIndex
< LogicalPartitionIndex
)
1526 break; // stop the loop, no need to touch further EMBRs
1532 } /* static EFI_STATUS ActivateMbrPartition() */
1534 // early 2006 Core Duo / Core Solo models
1535 static UINT8 LegacyLoaderDevicePath1Data
[] = {
1536 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1537 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1538 0xFF, 0xFF, 0xF9, 0xFF, 0x00, 0x00, 0x00, 0x00,
1539 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1540 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1541 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1543 // mid-2006 Mac Pro (and probably other Core 2 models)
1544 static UINT8 LegacyLoaderDevicePath2Data
[] = {
1545 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1546 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1547 0xFF, 0xFF, 0xF7, 0xFF, 0x00, 0x00, 0x00, 0x00,
1548 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1549 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1550 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1552 // mid-2007 MBP ("Santa Rosa" based models)
1553 static UINT8 LegacyLoaderDevicePath3Data
[] = {
1554 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1555 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1556 0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
1557 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1558 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1559 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1562 static UINT8 LegacyLoaderDevicePath4Data
[] = {
1563 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1564 0x00, 0x00, 0xC0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1565 0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
1566 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1567 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1568 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1570 // late-2008 MB/MBP (NVidia chipset)
1571 static UINT8 LegacyLoaderDevicePath5Data
[] = {
1572 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1573 0x00, 0x40, 0xCB, 0xFF, 0x00, 0x00, 0x00, 0x00,
1574 0xFF, 0xBF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
1575 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1576 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1577 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1580 static EFI_DEVICE_PATH
*LegacyLoaderList
[] = {
1581 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath1Data
,
1582 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath2Data
,
1583 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath3Data
,
1584 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath4Data
,
1585 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath5Data
,
1589 #define MAX_DISCOVERED_PATHS (16)
1591 static VOID
StartLegacy(IN LEGACY_ENTRY
*Entry
)
1594 EG_IMAGE
*BootLogoImage
;
1595 UINTN ErrorInStep
= 0;
1596 EFI_DEVICE_PATH
*DiscoveredPathList
[MAX_DISCOVERED_PATHS
];
1598 BeginExternalScreen(TRUE
, L
"Booting Legacy OS (Mac mode)");
1600 BootLogoImage
= LoadOSIcon(Entry
->Volume
->OSIconName
, L
"legacy", TRUE
);
1601 if (BootLogoImage
!= NULL
)
1602 BltImageAlpha(BootLogoImage
,
1603 (UGAWidth
- BootLogoImage
->Width
) >> 1,
1604 (UGAHeight
- BootLogoImage
->Height
) >> 1,
1605 &StdBackgroundPixel
);
1607 if (Entry
->Volume
->IsMbrPartition
) {
1608 ActivateMbrPartition(Entry
->Volume
->WholeDiskBlockIO
, Entry
->Volume
->MbrPartitionIndex
);
1611 ExtractLegacyLoaderPaths(DiscoveredPathList
, MAX_DISCOVERED_PATHS
, LegacyLoaderList
);
1613 Status
= StartEFIImageList(DiscoveredPathList
, Entry
->LoadOptions
, TYPE_LEGACY
, L
"legacy loader", 0, &ErrorInStep
, TRUE
);
1614 if (Status
== EFI_NOT_FOUND
) {
1615 if (ErrorInStep
== 1) {
1616 Print(L
"\nPlease make sure that you have the latest firmware update installed.\n");
1617 } else if (ErrorInStep
== 3) {
1618 Print(L
"\nThe firmware refused to boot from the selected volume. Note that external\n"
1619 L
"hard drives are not well-supported by Apple's firmware for legacy OS booting.\n");
1622 FinishExternalScreen();
1623 } /* static VOID StartLegacy() */
1625 // Start a device on a non-Mac using the EFI_LEGACY_BIOS_PROTOCOL
1626 #ifdef __MAKEWITH_TIANO
1627 static VOID
StartLegacyUEFI(IN LEGACY_ENTRY
*Entry
)
1629 BeginExternalScreen(TRUE
, L
"Booting Legacy OS (UEFI mode)");
1631 BdsLibConnectDevicePath (Entry
->BdsOption
->DevicePath
);
1632 BdsLibDoLegacyBoot(Entry
->BdsOption
);
1634 // If we get here, it means that there was a failure....
1635 Print(L
"Failure booting legacy (BIOS) OS.");
1637 FinishExternalScreen();
1638 } // static VOID StartLegacyUEFI()
1639 #endif // __MAKEWITH_TIANO
1641 static LEGACY_ENTRY
* AddLegacyEntry(IN CHAR16
*LoaderTitle
, IN REFIT_VOLUME
*Volume
)
1643 LEGACY_ENTRY
*Entry
, *SubEntry
;
1644 REFIT_MENU_SCREEN
*SubScreen
;
1646 CHAR16 ShortcutLetter
= 0;
1648 if (LoaderTitle
== NULL
) {
1649 if (Volume
->OSName
!= NULL
) {
1650 LoaderTitle
= Volume
->OSName
;
1651 if (LoaderTitle
[0] == 'W' || LoaderTitle
[0] == 'L')
1652 ShortcutLetter
= LoaderTitle
[0];
1654 LoaderTitle
= L
"Legacy OS";
1656 if (Volume
->VolName
!= NULL
)
1657 VolDesc
= Volume
->VolName
;
1659 VolDesc
= (Volume
->DiskKind
== DISK_KIND_OPTICAL
) ? L
"CD" : L
"HD";
1661 // prepare the menu entry
1662 Entry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1663 Entry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1664 SPrint(Entry
->me
.Title
, 255, L
"Boot %s from %s", LoaderTitle
, VolDesc
);
1665 Entry
->me
.Tag
= TAG_LEGACY
;
1667 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1668 Entry
->me
.Image
= LoadOSIcon(Volume
->OSIconName
, L
"legacy", FALSE
);
1669 Entry
->me
.BadgeImage
= Volume
->VolBadgeImage
;
1670 Entry
->Volume
= Volume
;
1671 Entry
->LoadOptions
= (Volume
->DiskKind
== DISK_KIND_OPTICAL
) ? L
"CD" :
1672 ((Volume
->DiskKind
== DISK_KIND_EXTERNAL
) ? L
"USB" : L
"HD");
1673 Entry
->Enabled
= TRUE
;
1675 // create the submenu
1676 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
1677 SubScreen
->Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1678 SPrint(SubScreen
->Title
, 255, L
"Boot Options for %s on %s", LoaderTitle
, VolDesc
);
1679 SubScreen
->TitleImage
= Entry
->me
.Image
;
1680 SubScreen
->Hint1
= StrDuplicate(SUBSCREEN_HINT1
);
1681 if (GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_EDITOR
) {
1682 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2_NO_EDITOR
);
1684 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2
);
1688 SubEntry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1689 SubEntry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1690 SPrint(SubEntry
->me
.Title
, 255, L
"Boot %s", LoaderTitle
);
1691 SubEntry
->me
.Tag
= TAG_LEGACY
;
1692 SubEntry
->Volume
= Entry
->Volume
;
1693 SubEntry
->LoadOptions
= Entry
->LoadOptions
;
1694 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
1696 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
1697 Entry
->me
.SubScreen
= SubScreen
;
1698 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1700 } /* static LEGACY_ENTRY * AddLegacyEntry() */
1703 #ifdef __MAKEWITH_GNUEFI
1704 static VOID
ScanLegacyUEFI(IN UINTN DiskType
){}
1706 // default volume badge icon based on disk kind
1707 static EG_IMAGE
* GetDiskBadge(IN UINTN DiskType
) {
1708 EG_IMAGE
* Badge
= NULL
;
1712 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_INTERNAL
);
1715 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_EXTERNAL
);
1718 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_OPTICAL
);
1722 } // static EG_IMAGE * GetDiskBadge()
1725 Create a rEFInd boot option from a Legacy BIOS protocol option.
1727 static LEGACY_ENTRY
* AddLegacyEntryUEFI(BDS_COMMON_OPTION
*BdsOption
, IN UINT16 DiskType
)
1729 LEGACY_ENTRY
*Entry
, *SubEntry
;
1730 REFIT_MENU_SCREEN
*SubScreen
;
1731 CHAR16 ShortcutLetter
= 0;
1732 CHAR16
*LegacyDescription
= BdsOption
->Description
;
1734 // prepare the menu entry
1735 Entry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1736 Entry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1737 SPrint(Entry
->me
.Title
, 255, L
"Boot legacy target %s", LegacyDescription
);
1738 Entry
->me
.Tag
= TAG_LEGACY_UEFI
;
1740 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1741 Entry
->me
.Image
= LoadOSIcon(L
"legacy", L
"legacy", TRUE
);
1742 Entry
->LoadOptions
= (DiskType
== BBS_CDROM
) ? L
"CD" :
1743 ((DiskType
== BBS_USB
) ? L
"USB" : L
"HD");
1744 Entry
->me
.BadgeImage
= GetDiskBadge(DiskType
);
1745 Entry
->BdsOption
= BdsOption
;
1746 Entry
->Enabled
= TRUE
;
1748 // create the submenu
1749 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
1750 SubScreen
->Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1751 SPrint(SubScreen
->Title
, 255, L
"No boot options for legacy target");
1752 SubScreen
->TitleImage
= Entry
->me
.Image
;
1753 SubScreen
->Hint1
= StrDuplicate(SUBSCREEN_HINT1
);
1754 if (GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_EDITOR
) {
1755 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2_NO_EDITOR
);
1757 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2
);
1761 SubEntry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1762 SubEntry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1763 SPrint(SubEntry
->me
.Title
, 255, L
"Boot %s", LegacyDescription
);
1764 SubEntry
->me
.Tag
= TAG_LEGACY_UEFI
;
1765 Entry
->BdsOption
= BdsOption
;
1766 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
1768 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
1769 Entry
->me
.SubScreen
= SubScreen
;
1770 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1772 } /* static LEGACY_ENTRY * AddLegacyEntryUEFI() */
1775 Scan for legacy BIOS targets on machines that implement EFI_LEGACY_BIOS_PROTOCOL.
1776 In testing, protocol has not been implemented on Macs but has been
1777 implemented on several Dell PCs and an ASUS motherboard.
1778 Restricts output to disks of the specified DiskType.
1780 static VOID
ScanLegacyUEFI(IN UINTN DiskType
)
1783 EFI_LEGACY_BIOS_PROTOCOL
*LegacyBios
;
1784 UINT16
*BootOrder
= NULL
;
1786 CHAR16 BootOption
[10];
1787 UINTN BootOrderSize
= 0;
1789 BDS_COMMON_OPTION
*BdsOption
;
1790 LIST_ENTRY TempList
;
1791 BBS_BBS_DEVICE_PATH
* BbsDevicePath
= NULL
;
1793 InitializeListHead (&TempList
);
1794 ZeroMem (Buffer
, sizeof (Buffer
));
1796 // If LegacyBios protocol is not implemented on this platform, then
1797 //we do not support this type of legacy boot on this machine.
1798 Status
= gBS
->LocateProtocol(&gEfiLegacyBootProtocolGuid
, NULL
, (VOID
**) &LegacyBios
);
1799 if (EFI_ERROR (Status
))
1802 // Grab the boot order
1803 BootOrder
= BdsLibGetVariableAndSize(L
"BootOrder", &gEfiGlobalVariableGuid
, &BootOrderSize
);
1804 if (BootOrder
== NULL
) {
1809 while (Index
< BootOrderSize
/ sizeof (UINT16
))
1811 // Grab each boot option variable from the boot order, and convert
1812 // the variable into a BDS boot option
1813 UnicodeSPrint (BootOption
, sizeof (BootOption
), L
"Boot%04x", BootOrder
[Index
]);
1814 BdsOption
= BdsLibVariableToOption (&TempList
, BootOption
);
1816 if (BdsOption
!= NULL
) {
1817 BbsDevicePath
= (BBS_BBS_DEVICE_PATH
*)BdsOption
->DevicePath
;
1819 // Only add the entry if it is of a requested type (e.g. USB, HD)
1821 // Two checks necessary because some systems return EFI boot loaders
1822 // with a DeviceType value that would inappropriately include them
1823 // as legacy loaders....
1824 if ((BbsDevicePath
->DeviceType
== DiskType
) && (BdsOption
->DevicePath
->Type
== DEVICE_TYPE_BIOS
)) {
1825 AddLegacyEntryUEFI(BdsOption
, BbsDevicePath
->DeviceType
);
1830 } /* static VOID ScanLegacyUEFI() */
1831 #endif // __MAKEWITH_GNUEFI
1833 static VOID
ScanLegacyVolume(REFIT_VOLUME
*Volume
, UINTN VolumeIndex
) {
1835 BOOLEAN ShowVolume
, HideIfOthersFound
;
1838 HideIfOthersFound
= FALSE
;
1839 if (Volume
->IsAppleLegacy
) {
1841 HideIfOthersFound
= TRUE
;
1842 } else if (Volume
->HasBootCode
) {
1844 if (Volume
->BlockIO
== Volume
->WholeDiskBlockIO
&&
1845 Volume
->BlockIOOffset
== 0 &&
1846 Volume
->OSName
== NULL
)
1847 // this is a whole disk (MBR) entry; hide if we have entries for partitions
1848 HideIfOthersFound
= TRUE
;
1850 if (HideIfOthersFound
) {
1851 // check for other bootable entries on the same disk
1852 for (VolumeIndex2
= 0; VolumeIndex2
< VolumesCount
; VolumeIndex2
++) {
1853 if (VolumeIndex2
!= VolumeIndex
&& Volumes
[VolumeIndex2
]->HasBootCode
&&
1854 Volumes
[VolumeIndex2
]->WholeDiskBlockIO
== Volume
->WholeDiskBlockIO
)
1860 AddLegacyEntry(NULL
, Volume
);
1861 } // static VOID ScanLegacyVolume()
1863 // Scan attached optical discs for legacy (BIOS) boot code
1864 // and add anything found to the list....
1865 static VOID
ScanLegacyDisc(VOID
)
1868 REFIT_VOLUME
*Volume
;
1870 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
) {
1871 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1872 Volume
= Volumes
[VolumeIndex
];
1873 if (Volume
->DiskKind
== DISK_KIND_OPTICAL
)
1874 ScanLegacyVolume(Volume
, VolumeIndex
);
1876 } else if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
1877 ScanLegacyUEFI(BBS_CDROM
);
1879 } /* static VOID ScanLegacyDisc() */
1881 // Scan internal hard disks for legacy (BIOS) boot code
1882 // and add anything found to the list....
1883 static VOID
ScanLegacyInternal(VOID
)
1886 REFIT_VOLUME
*Volume
;
1888 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
) {
1889 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1890 Volume
= Volumes
[VolumeIndex
];
1891 if (Volume
->DiskKind
== DISK_KIND_INTERNAL
)
1892 ScanLegacyVolume(Volume
, VolumeIndex
);
1894 } else if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
1895 ScanLegacyUEFI(BBS_HARDDISK
);
1897 } /* static VOID ScanLegacyInternal() */
1899 // Scan external disks for legacy (BIOS) boot code
1900 // and add anything found to the list....
1901 static VOID
ScanLegacyExternal(VOID
)
1904 REFIT_VOLUME
*Volume
;
1906 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
) {
1907 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1908 Volume
= Volumes
[VolumeIndex
];
1909 if (Volume
->DiskKind
== DISK_KIND_EXTERNAL
)
1910 ScanLegacyVolume(Volume
, VolumeIndex
);
1912 } else if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
1913 ScanLegacyUEFI(BBS_USB
);
1915 } /* static VOID ScanLegacyExternal() */
1918 // pre-boot tool functions
1921 static VOID
StartTool(IN LOADER_ENTRY
*Entry
)
1923 BeginExternalScreen(Entry
->UseGraphicsMode
, Entry
->me
.Title
+ 6); // assumes "Start <title>" as assigned below
1924 StartEFIImage(Entry
->DevicePath
, Entry
->LoadOptions
, TYPE_EFI
,
1925 Basename(Entry
->LoaderPath
), Entry
->OSType
, NULL
, TRUE
);
1926 FinishExternalScreen();
1927 } /* static VOID StartTool() */
1929 static LOADER_ENTRY
* AddToolEntry(EFI_HANDLE DeviceHandle
, IN CHAR16
*LoaderPath
, IN CHAR16
*LoaderTitle
, IN EG_IMAGE
*Image
,
1930 IN CHAR16 ShortcutLetter
, IN BOOLEAN UseGraphicsMode
)
1932 LOADER_ENTRY
*Entry
;
1933 CHAR16
*TitleStr
= NULL
;
1935 Entry
= AllocateZeroPool(sizeof(LOADER_ENTRY
));
1937 TitleStr
= PoolPrint(L
"Start %s", LoaderTitle
);
1938 Entry
->me
.Title
= TitleStr
;
1939 Entry
->me
.Tag
= TAG_TOOL
;
1941 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1942 Entry
->me
.Image
= Image
;
1943 Entry
->LoaderPath
= (LoaderPath
) ? StrDuplicate(LoaderPath
) : NULL
;
1944 Entry
->DevicePath
= FileDevicePath(DeviceHandle
, Entry
->LoaderPath
);
1945 Entry
->UseGraphicsMode
= UseGraphicsMode
;
1947 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1949 } /* static LOADER_ENTRY * AddToolEntry() */
1952 // pre-boot driver functions
1955 static UINTN
ScanDriverDir(IN CHAR16
*Path
)
1958 REFIT_DIR_ITER DirIter
;
1960 EFI_FILE_INFO
*DirEntry
;
1961 CHAR16 FileName
[256];
1963 CleanUpPathNameSlashes(Path
);
1964 // look through contents of the directory
1965 DirIterOpen(SelfRootDir
, Path
, &DirIter
);
1966 while (DirIterNext(&DirIter
, 2, LOADER_MATCH_PATTERNS
, &DirEntry
)) {
1967 if (DirEntry
->FileName
[0] == '.')
1968 continue; // skip this
1970 SPrint(FileName
, 255, L
"%s\\%s", Path
, DirEntry
->FileName
);
1972 Status
= StartEFIImage(FileDevicePath(SelfLoadedImage
->DeviceHandle
, FileName
),
1973 L
"", TYPE_EFI
, DirEntry
->FileName
, 0, NULL
, FALSE
);
1975 Status
= DirIterClose(&DirIter
);
1976 if (Status
!= EFI_NOT_FOUND
) {
1977 SPrint(FileName
, 255, L
"while scanning the %s directory", Path
);
1978 CheckError(Status
, FileName
);
1983 #ifdef __MAKEWITH_GNUEFI
1984 static EFI_STATUS
ConnectAllDriversToAllControllers(VOID
)
1987 UINTN AllHandleCount
;
1988 EFI_HANDLE
*AllHandleBuffer
;
1991 EFI_HANDLE
*HandleBuffer
;
1997 Status
= LibLocateHandle(AllHandles
,
2002 if (EFI_ERROR(Status
))
2005 for (Index
= 0; Index
< AllHandleCount
; Index
++) {
2007 // Scan the handle database
2009 Status
= LibScanHandleDatabase(NULL
,
2011 AllHandleBuffer
[Index
],
2016 if (EFI_ERROR (Status
))
2020 if (HandleType
[Index
] & EFI_HANDLE_TYPE_DRIVER_BINDING_HANDLE
)
2022 if (HandleType
[Index
] & EFI_HANDLE_TYPE_IMAGE_HANDLE
)
2027 for (HandleIndex
= 0; HandleIndex
< HandleCount
; HandleIndex
++) {
2028 if (HandleType
[HandleIndex
] & EFI_HANDLE_TYPE_PARENT_HANDLE
)
2033 if (HandleType
[Index
] & EFI_HANDLE_TYPE_DEVICE_HANDLE
) {
2034 Status
= refit_call4_wrapper(BS
->ConnectController
,
2035 AllHandleBuffer
[Index
],
2043 MyFreePool (HandleBuffer
);
2044 MyFreePool (HandleType
);
2048 MyFreePool (AllHandleBuffer
);
2050 } /* EFI_STATUS ConnectAllDriversToAllControllers() */
2052 static EFI_STATUS
ConnectAllDriversToAllControllers(VOID
) {
2053 BdsLibConnectAllDriversToAllControllers();
2058 // Load all EFI drivers from rEFInd's "drivers" subdirectory and from the
2059 // directories specified by the user in the "scan_driver_dirs" configuration
2061 static VOID
LoadDrivers(VOID
)
2063 CHAR16
*Directory
, *SelfDirectory
;
2064 UINTN i
= 0, Length
, NumFound
= 0;
2066 // load drivers from the subdirectories of rEFInd's home directory specified
2067 // in the DRIVER_DIRS constant.
2068 while ((Directory
= FindCommaDelimited(DRIVER_DIRS
, i
++)) != NULL
) {
2069 SelfDirectory
= SelfDirPath
? StrDuplicate(SelfDirPath
) : NULL
;
2070 CleanUpPathNameSlashes(SelfDirectory
);
2071 MergeStrings(&SelfDirectory
, Directory
, L
'\\');
2072 NumFound
+= ScanDriverDir(SelfDirectory
);
2073 MyFreePool(Directory
);
2074 MyFreePool(SelfDirectory
);
2077 // Scan additional user-specified driver directories....
2079 while ((Directory
= FindCommaDelimited(GlobalConfig
.DriverDirs
, i
++)) != NULL
) {
2080 CleanUpPathNameSlashes(Directory
);
2081 Length
= StrLen(Directory
);
2083 NumFound
+= ScanDriverDir(Directory
);
2085 MyFreePool(Directory
);
2088 // connect all devices
2090 ConnectAllDriversToAllControllers();
2091 } /* static VOID LoadDrivers() */
2093 // Determine what (if any) type of legacy (BIOS) boot support is available
2094 static VOID
FindLegacyBootType(VOID
) {
2095 #ifdef __MAKEWITH_TIANO
2097 EFI_LEGACY_BIOS_PROTOCOL
*LegacyBios
;
2100 GlobalConfig
.LegacyType
= LEGACY_TYPE_NONE
;
2102 // UEFI-style legacy BIOS support is available only with the TianoCore EDK2
2103 // build environment, and then only with some EFI implementations....
2104 #ifdef __MAKEWITH_TIANO
2105 Status
= gBS
->LocateProtocol (&gEfiLegacyBootProtocolGuid
, NULL
, (VOID
**) &LegacyBios
);
2106 if (!EFI_ERROR (Status
))
2107 GlobalConfig
.LegacyType
= LEGACY_TYPE_UEFI
;
2110 // Macs have their own system. If the firmware vendor code contains the
2111 // string "Apple", assume it's available. Note that this overrides the
2112 // UEFI type, and might yield false positives if the vendor string
2113 // contains "Apple" as part of something bigger, so this isn't 100%
2115 if (StriSubCmp(L
"Apple", ST
->FirmwareVendor
))
2116 GlobalConfig
.LegacyType
= LEGACY_TYPE_MAC
;
2117 } // static VOID FindLegacyBootType
2119 // Warn the user if legacy OS scans are enabled but the firmware or this
2120 // application can't support them....
2121 static VOID
WarnIfLegacyProblems() {
2122 BOOLEAN found
= FALSE
;
2125 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_NONE
) {
2127 if (GlobalConfig
.ScanFor
[i
] == 'h' || GlobalConfig
.ScanFor
[i
] == 'b' || GlobalConfig
.ScanFor
[i
] == 'c')
2130 } while ((i
< NUM_SCAN_OPTIONS
) && (!found
));
2132 Print(L
"NOTE: refind.conf's 'scanfor' line specifies scanning for one or more legacy\n");
2133 Print(L
"(BIOS) boot options; however, this is not possible because ");
2134 #ifdef __MAKEWITH_TIANO
2135 Print(L
"your computer lacks\n");
2136 Print(L
"the necessary Compatibility Support Module (CSM) support.\n");
2138 Print(L
"this program was\n");
2139 Print(L
"compiled without the necessary support. Please visit\n");
2140 Print(L
"http://www.rodsbooks.com/refind/getting.html and download and install a rEFInd\n");
2141 Print(L
"binary built with the TianoCore EDK2 to enable legacy boot support.\n");
2145 } // if no legacy support
2146 } // static VOID WarnIfLegacyProblems()
2148 // Locates boot loaders. NOTE: This assumes that GlobalConfig.LegacyType is set correctly.
2149 static VOID
ScanForBootloaders(VOID
) {
2154 // scan for loaders and tools, add them to the menu
2155 for (i
= 0; i
< NUM_SCAN_OPTIONS
; i
++) {
2156 switch(GlobalConfig
.ScanFor
[i
]) {
2161 ScanLegacyInternal();
2164 ScanLegacyExternal();
2167 ScanUserConfigured(CONFIG_FILE_NAME
);
2181 // assign shortcut keys
2182 for (i
= 0; i
< MainMenu
.EntryCount
&& MainMenu
.Entries
[i
]->Row
== 0 && i
< 9; i
++)
2183 MainMenu
.Entries
[i
]->ShortcutDigit
= (CHAR16
)('1' + i
);
2185 // wait for user ACK when there were errors
2186 FinishTextScreen(FALSE
);
2187 } // static VOID ScanForBootloaders()
2189 // Locate a single tool from the specified Locations using one of the
2190 // specified Names and add it to the menu.
2191 static VOID
FindTool(CHAR16
*Locations
, CHAR16
*Names
, CHAR16
*Description
, UINTN Icon
) {
2192 UINTN j
= 0, k
, VolumeIndex
;
2193 CHAR16
*DirName
, *FileName
, *PathName
, FullDescription
[256];
2195 while ((DirName
= FindCommaDelimited(Locations
, j
++)) != NULL
) {
2197 while ((FileName
= FindCommaDelimited(Names
, k
++)) != NULL
) {
2198 PathName
= StrDuplicate(DirName
);
2199 MergeStrings(&PathName
, FileName
, (StriCmp(PathName
, L
"\\") == 0) ? 0 : L
'\\');
2200 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
2201 if ((Volumes
[VolumeIndex
]->RootDir
!= NULL
) && (FileExists(Volumes
[VolumeIndex
]->RootDir
, PathName
))) {
2202 SPrint(FullDescription
, 255, L
"%s at %s on %s", Description
, PathName
, Volumes
[VolumeIndex
]->VolName
);
2203 AddToolEntry(Volumes
[VolumeIndex
]->DeviceHandle
, PathName
, FullDescription
, BuiltinIcon(Icon
), 'S', FALSE
);
2206 MyFreePool(PathName
);
2207 MyFreePool(FileName
);
2209 MyFreePool(DirName
);
2210 } // while Locations
2211 } // VOID FindTool()
2213 // Add the second-row tags containing built-in and external tools (EFI shell,
2215 static VOID
ScanForTools(VOID
) {
2216 CHAR16
*FileName
= NULL
, *MokLocations
, Description
[256];
2217 REFIT_MENU_ENTRY
*TempMenuEntry
;
2218 UINTN i
, j
, VolumeIndex
;
2222 MokLocations
= StrDuplicate(MOK_LOCATIONS
);
2223 if (MokLocations
!= NULL
)
2224 MergeStrings(&MokLocations
, SelfDirPath
, L
',');
2226 for (i
= 0; i
< NUM_TOOLS
; i
++) {
2227 switch(GlobalConfig
.ShowTools
[i
]) {
2228 // NOTE: Be sure that FileName is NULL at the end of each case.
2230 TempMenuEntry
= CopyMenuEntry(&MenuEntryShutdown
);
2231 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_SHUTDOWN
);
2232 AddMenuEntry(&MainMenu
, TempMenuEntry
);
2236 TempMenuEntry
= CopyMenuEntry(&MenuEntryReset
);
2237 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_RESET
);
2238 AddMenuEntry(&MainMenu
, TempMenuEntry
);
2242 TempMenuEntry
= CopyMenuEntry(&MenuEntryAbout
);
2243 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT
);
2244 AddMenuEntry(&MainMenu
, TempMenuEntry
);
2248 TempMenuEntry
= CopyMenuEntry(&MenuEntryExit
);
2249 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_EXIT
);
2250 AddMenuEntry(&MainMenu
, TempMenuEntry
);
2254 if (EfivarGetRaw(&GlobalGuid
, L
"OsIndicationsSupported", &b
, &j
) == EFI_SUCCESS
) {
2256 if (osind
& EFI_OS_INDICATIONS_BOOT_TO_FW_UI
) {
2257 TempMenuEntry
= CopyMenuEntry(&MenuEntryFirmware
);
2258 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_FIRMWARE
);
2259 AddMenuEntry(&MainMenu
, TempMenuEntry
);
2266 while ((FileName
= FindCommaDelimited(SHELL_NAMES
, j
++)) != NULL
) {
2267 if (FileExists(SelfRootDir
, FileName
)) {
2268 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, L
"EFI Shell", BuiltinIcon(BUILTIN_ICON_TOOL_SHELL
),
2271 MyFreePool(FileName
);
2277 while ((FileName
= FindCommaDelimited(GPTSYNC_NAMES
, j
++)) != NULL
) {
2278 if (FileExists(SelfRootDir
, FileName
)) {
2279 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, L
"Hybrid MBR tool", BuiltinIcon(BUILTIN_ICON_TOOL_PART
),
2282 MyFreePool(FileName
);
2287 case TAG_APPLE_RECOVERY
:
2288 FileName
= StrDuplicate(L
"\\com.apple.recovery.boot\\boot.efi");
2289 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
2290 if ((Volumes
[VolumeIndex
]->RootDir
!= NULL
) && (FileExists(Volumes
[VolumeIndex
]->RootDir
, FileName
))) {
2291 SPrint(Description
, 255, L
"Apple Recovery on %s", Volumes
[VolumeIndex
]->VolName
);
2292 AddToolEntry(Volumes
[VolumeIndex
]->DeviceHandle
, FileName
, Description
,
2293 BuiltinIcon(BUILTIN_ICON_TOOL_APPLE_RESCUE
), 'R', TRUE
);
2296 MyFreePool(FileName
);
2301 FindTool(MokLocations
, MOK_NAMES
, L
"MOK utility utility", BUILTIN_ICON_TOOL_MOK_TOOL
);
2305 FindTool(MEMTEST_LOCATIONS
, MEMTEST_NAMES
, L
"Memory test utility", BUILTIN_ICON_TOOL_MEMTEST
);
2310 } // static VOID ScanForTools
2312 // Rescan for boot loaders
2313 VOID
RescanAll(VOID
) {
2320 egDisplayMessage(L
"Scanning for new boot loaders; please wait....", &BGColor
);
2321 FreeList((VOID
***) &(MainMenu
.Entries
), &MainMenu
.EntryCount
);
2322 MainMenu
.Entries
= NULL
;
2323 MainMenu
.EntryCount
= 0;
2324 ReadConfig(CONFIG_FILE_NAME
);
2325 ConnectAllDriversToAllControllers();
2327 ScanForBootloaders();
2330 } // VOID RescanAll()
2332 #ifdef __MAKEWITH_TIANO
2334 // Minimal initialization function
2335 static VOID
InitializeLib(IN EFI_HANDLE ImageHandle
, IN EFI_SYSTEM_TABLE
*SystemTable
) {
2337 // gImageHandle = ImageHandle;
2338 gBS
= SystemTable
->BootServices
;
2339 // gRS = SystemTable->RuntimeServices;
2340 gRT
= SystemTable
->RuntimeServices
; // Some BDS functions need gRT to be set
2341 EfiGetSystemConfigurationTable (&gEfiDxeServicesTableGuid
, (VOID
**) &gDS
);
2343 InitializeConsoleSim();
2348 // Set up our own Secure Boot extensions....
2349 // Returns TRUE on success, FALSE otherwise
2350 static BOOLEAN
SecureBootSetup(VOID
) {
2352 BOOLEAN Success
= FALSE
;
2354 if (secure_mode() && ShimLoaded()) {
2355 Status
= security_policy_install();
2356 if (Status
== EFI_SUCCESS
) {
2359 Print(L
"Failed to install MOK Secure Boot extensions");
2363 } // VOID SecureBootSetup()
2365 // Remove our own Secure Boot extensions....
2366 // Returns TRUE on success, FALSE otherwise
2367 static BOOLEAN
SecureBootUninstall(VOID
) {
2369 BOOLEAN Success
= TRUE
;
2371 if (secure_mode()) {
2372 Status
= security_policy_uninstall();
2373 if (Status
!= EFI_SUCCESS
) {
2375 BeginTextScreen(L
"Secure Boot Policy Failure");
2376 Print(L
"Failed to uninstall MOK Secure Boot extensions; forcing a reboot.");
2378 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);
2382 } // VOID SecureBootUninstall
2389 efi_main (EFI_HANDLE ImageHandle
, EFI_SYSTEM_TABLE
*SystemTable
)
2392 BOOLEAN MainLoopRunning
= TRUE
;
2393 BOOLEAN MokProtocol
;
2394 REFIT_MENU_ENTRY
*ChosenEntry
;
2396 CHAR16
*Selection
= NULL
;
2400 InitializeLib(ImageHandle
, SystemTable
);
2401 Status
= InitRefitLib(ImageHandle
);
2402 if (EFI_ERROR(Status
))
2405 // read configuration
2406 CopyMem(GlobalConfig
.ScanFor
, "ieom ", NUM_SCAN_OPTIONS
);
2407 FindLegacyBootType();
2408 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
)
2409 CopyMem(GlobalConfig
.ScanFor
, "ihebocm ", NUM_SCAN_OPTIONS
);
2410 ReadConfig(CONFIG_FILE_NAME
);
2414 WarnIfLegacyProblems();
2415 MainMenu
.TimeoutSeconds
= GlobalConfig
.Timeout
;
2417 // disable EFI watchdog timer
2418 refit_call4_wrapper(BS
->SetWatchdogTimer
, 0x0000, 0x0000, 0x0000, NULL
);
2420 // further bootstrap (now with config available)
2421 MokProtocol
= SecureBootSetup();
2423 ScanForBootloaders();
2427 if (GlobalConfig
.ScanDelay
> 0) {
2432 egDisplayMessage(L
"Pausing before disk scan; please wait....", &BGColor
);
2433 for (i
= 0; i
< GlobalConfig
.ScanDelay
; i
++)
2434 refit_call1_wrapper(BS
->Stall
, 1000000);
2438 if (GlobalConfig
.DefaultSelection
)
2439 Selection
= StrDuplicate(GlobalConfig
.DefaultSelection
);
2441 while (MainLoopRunning
) {
2442 MenuExit
= RunMainMenu(&MainMenu
, Selection
, &ChosenEntry
);
2444 // The Escape key triggers a re-scan operation....
2445 if (MenuExit
== MENU_EXIT_ESCAPE
) {
2450 switch (ChosenEntry
->Tag
) {
2452 case TAG_REBOOT
: // Reboot
2454 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);
2455 MainLoopRunning
= FALSE
; // just in case we get this far
2458 case TAG_SHUTDOWN
: // Shut Down
2460 refit_call4_wrapper(RT
->ResetSystem
, EfiResetShutdown
, EFI_SUCCESS
, 0, NULL
);
2461 MainLoopRunning
= FALSE
; // just in case we get this far
2464 case TAG_ABOUT
: // About rEFInd
2468 case TAG_LOADER
: // Boot OS via .EFI loader
2469 StartLoader((LOADER_ENTRY
*)ChosenEntry
);
2472 case TAG_LEGACY
: // Boot legacy OS
2473 StartLegacy((LEGACY_ENTRY
*)ChosenEntry
);
2476 #ifdef __MAKEWITH_TIANO
2477 case TAG_LEGACY_UEFI
: // Boot a legacy OS on a non-Mac
2478 StartLegacyUEFI((LEGACY_ENTRY
*)ChosenEntry
);
2482 case TAG_TOOL
: // Start a EFI tool
2483 StartTool((LOADER_ENTRY
*)ChosenEntry
);
2486 case TAG_EXIT
: // Terminate rEFInd
2487 if ((MokProtocol
) && !SecureBootUninstall()) {
2488 MainLoopRunning
= FALSE
; // just in case we get this far
2490 BeginTextScreen(L
" ");
2495 case TAG_FIRMWARE
: // Reboot into firmware's user interface
2496 RebootIntoFirmware();
2500 MyFreePool(Selection
);
2501 Selection
= (ChosenEntry
->Title
) ? StrDuplicate(ChosenEntry
->Title
) : NULL
;
2504 // If we end up here, things have gone wrong. Try to reboot, and if that
2505 // fails, go into an endless loop.
2506 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);