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-2014 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.
53 #include "security_policy.h"
54 #include "../include/Handle.h"
55 #include "../include/refit_call_wrapper.h"
56 #include "driver_support.h"
57 #include "../include/syslinux_mbr.h"
59 #ifdef __MAKEWITH_GNUEFI
60 #ifndef EFI_SECURITY_VIOLATION
61 #define EFI_SECURITY_VIOLATION EFIERR (26)
65 #include "../EfiLib/BdsHelper.h"
66 #include "../EfiLib/legacy.h"
68 #ifndef EFI_OS_INDICATIONS_BOOT_TO_FW_UI
69 #define EFI_OS_INDICATIONS_BOOT_TO_FW_UI 0x0000000000000001ULL
72 #ifdef __MAKEWITH_TIANO
73 #define LibLocateHandle gBS->LocateHandleBuffer
79 #define MACOSX_LOADER_PATH L"System\\Library\\CoreServices\\boot.efi"
81 #define SHELL_NAMES L"\\EFI\\tools\\shell.efi,\\EFI\\tools\\shellx64.efi,\\shell.efi,\\shellx64.efi"
82 #define GPTSYNC_NAMES L"\\EFI\\tools\\gptsync.efi,\\EFI\\tools\\gptsync_x64.efi"
83 #define GDISK_NAMES L"\\EFI\\tools\\gdisk.efi,\\EFI\\tools\\gdisk_x64.efi"
84 #define MEMTEST_NAMES L"memtest86.efi,memtest86_x64.efi,memtest86x64.efi,bootx64.efi"
85 #define DRIVER_DIRS L"drivers,drivers_x64"
86 #define FALLBACK_FULLNAME L"EFI\\BOOT\\bootx64.efi"
87 #define FALLBACK_BASENAME L"bootx64.efi"
88 #define EFI_STUB_ARCH 0x8664
90 #define SHELL_NAMES L"\\EFI\\tools\\shell.efi,\\EFI\\tools\\shellia32.efi,\\shell.efi,\\shellia32.efi"
91 #define GPTSYNC_NAMES L"\\EFI\\tools\\gptsync.efi,\\EFI\\tools\\gptsync_ia32.efi"
92 #define GDISK_NAMES L"\\EFI\\tools\\gdisk.efi,\\EFI\\tools\\gdisk_ia32.efi"
93 #define MEMTEST_NAMES L"memtest86.efi,memtest86_ia32.efi,memtest86ia32.efi,bootia32.efi"
94 #define DRIVER_DIRS L"drivers,drivers_ia32"
95 #define FALLBACK_FULLNAME L"EFI\\BOOT\\bootia32.efi"
96 #define FALLBACK_BASENAME L"bootia32.efi"
97 #define EFI_STUB_ARCH 0x014c
99 #define SHELL_NAMES L"\\EFI\\tools\\shell.efi,\\shell.efi"
100 #define GPTSYNC_NAMES L"\\EFI\\tools\\gptsync.efi"
101 #define GDISK_NAMES L"\\EFI\\tools\\gdisk.efi"
102 #define MEMTEST_NAMES L"memtest86.efi"
103 #define DRIVER_DIRS L"drivers"
104 #define FALLBACK_FULLNAME L"EFI\\BOOT\\boot.efi" /* Not really correct */
105 #define FALLBACK_BASENAME L"boot.efi" /* Not really correct */
107 #define FAT_ARCH 0x0ef1fab9 /* ID for Apple "fat" binary */
109 // Filename patterns that identify EFI boot loaders. Note that a single case (either L"*.efi" or
110 // L"*.EFI") is fine for most systems; but Gigabyte's buggy Hybrid EFI does a case-sensitive
111 // comparison when it should do a case-insensitive comparison, so I'm doubling this up. It does
112 // no harm on other computers, AFAIK. In theory, every case variation should be done for
113 // completeness, but that's ridiculous....
114 #define LOADER_MATCH_PATTERNS L"*.efi,*.EFI"
116 // Patterns that identify Linux kernels. Added to the loader match pattern when the
117 // scan_all_linux_kernels option is set in the configuration file. Causes kernels WITHOUT
118 // a ".efi" extension to be found when scanning for boot loaders.
119 #define LINUX_MATCH_PATTERNS L"vmlinuz*,bzImage*"
121 // Default hint text for program-launch submenus
122 #define SUBSCREEN_HINT1 L"Use arrow keys to move cursor; Enter to boot;"
123 #define SUBSCREEN_HINT2 L"Insert or F2 to edit options; Esc to return to main menu"
124 #define SUBSCREEN_HINT2_NO_EDITOR L"Esc to return to main menu"
128 #define TYPE_LEGACY 2
130 static REFIT_MENU_ENTRY MenuEntryAbout
= { L
"About rEFInd", TAG_ABOUT
, 1, 0, 'A', NULL
, NULL
, NULL
};
131 static REFIT_MENU_ENTRY MenuEntryReset
= { L
"Reboot Computer", TAG_REBOOT
, 1, 0, 'R', NULL
, NULL
, NULL
};
132 static REFIT_MENU_ENTRY MenuEntryShutdown
= { L
"Shut Down Computer", TAG_SHUTDOWN
, 1, 0, 'U', NULL
, NULL
, NULL
};
133 static REFIT_MENU_ENTRY MenuEntryReturn
= { L
"Return to Main Menu", TAG_RETURN
, 1, 0, 0, NULL
, NULL
, NULL
};
134 static REFIT_MENU_ENTRY MenuEntryExit
= { L
"Exit rEFInd", TAG_EXIT
, 1, 0, 0, NULL
, NULL
, NULL
};
135 static REFIT_MENU_ENTRY MenuEntryFirmware
= { L
"Reboot to Computer Setup Utility", TAG_FIRMWARE
, 1, 0, 0, NULL
, NULL
, NULL
};
137 static REFIT_MENU_SCREEN MainMenu
= { L
"Main Menu", NULL
, 0, NULL
, 0, NULL
, 0, L
"Automatic boot",
138 L
"Use arrow keys to move cursor; Enter to boot;",
139 L
"Insert or F2 for more options; Esc to refresh" };
140 static REFIT_MENU_SCREEN AboutMenu
= { L
"About", NULL
, 0, NULL
, 0, NULL
, 0, NULL
, L
"Press Enter to return to main menu", L
"" };
142 REFIT_CONFIG GlobalConfig
= { FALSE
, TRUE
, FALSE
, 0, 0, 0, DONT_CHANGE_TEXT_MODE
, 20, 0, 0, GRAPHICS_FOR_OSX
, LEGACY_TYPE_MAC
, 0, 0,
143 { DEFAULT_BIG_ICON_SIZE
/ 4, DEFAULT_SMALL_ICON_SIZE
, DEFAULT_BIG_ICON_SIZE
}, BANNER_NOSCALE
,
144 NULL
, NULL
, CONFIG_FILE_NAME
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
145 { TAG_SHELL
, TAG_MEMTEST
, TAG_GDISK
, TAG_APPLE_RECOVERY
, TAG_WINDOWS_RECOVERY
, TAG_MOK_TOOL
,
146 TAG_ABOUT
, TAG_SHUTDOWN
, TAG_REBOOT
, TAG_FIRMWARE
, 0, 0, 0, 0, 0, 0 }
149 EFI_GUID GlobalGuid
= EFI_GLOBAL_VARIABLE
;
150 EFI_GUID RefindGuid
= REFIND_GUID_VALUE
;
152 GPT_DATA
*gPartitions
= NULL
;
154 // Structure used to hold boot loader filenames and time stamps in
155 // a linked list; used to sort entries within a directory.
159 struct LOADER_LIST
*NextEntry
;
166 static VOID
AboutrEFInd(VOID
)
168 CHAR16
*FirmwareVendor
;
170 if (AboutMenu
.EntryCount
== 0) {
171 AboutMenu
.TitleImage
= BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT
);
172 AddMenuInfoLine(&AboutMenu
, L
"rEFInd Version 0.8.2.3");
173 AddMenuInfoLine(&AboutMenu
, L
"");
174 AddMenuInfoLine(&AboutMenu
, L
"Copyright (c) 2006-2010 Christoph Pfisterer");
175 AddMenuInfoLine(&AboutMenu
, L
"Copyright (c) 2012-2014 Roderick W. Smith");
176 AddMenuInfoLine(&AboutMenu
, L
"Portions Copyright (c) Intel Corporation and others");
177 AddMenuInfoLine(&AboutMenu
, L
"Distributed under the terms of the GNU GPLv3 license");
178 AddMenuInfoLine(&AboutMenu
, L
"");
179 AddMenuInfoLine(&AboutMenu
, L
"Running on:");
180 AddMenuInfoLine(&AboutMenu
, PoolPrint(L
" EFI Revision %d.%02d", ST
->Hdr
.Revision
>> 16, ST
->Hdr
.Revision
& ((1 << 16) - 1)));
182 AddMenuInfoLine(&AboutMenu
, L
" Platform: x86 (32 bit)");
183 #elif defined(EFIX64)
184 AddMenuInfoLine(&AboutMenu
, PoolPrint(L
" Platform: x86_64 (64 bit); Secure Boot %s",
185 secure_mode() ? L
"active" : L
"inactive"));
187 AddMenuInfoLine(&AboutMenu
, L
" Platform: unknown");
189 FirmwareVendor
= StrDuplicate(ST
->FirmwareVendor
);
190 LimitStringLength(FirmwareVendor
, 65); // More than ~65 causes empty info page on 800x600 display
191 AddMenuInfoLine(&AboutMenu
, PoolPrint(L
" Firmware: %s %d.%02d", FirmwareVendor
, ST
->FirmwareRevision
>> 16,
192 ST
->FirmwareRevision
& ((1 << 16) - 1)));
193 AddMenuInfoLine(&AboutMenu
, PoolPrint(L
" Screen Output: %s", egScreenDescription()));
194 AddMenuInfoLine(&AboutMenu
, L
"");
195 #if defined(__MAKEWITH_GNUEFI)
196 AddMenuInfoLine(&AboutMenu
, L
"Built with GNU-EFI");
198 AddMenuInfoLine(&AboutMenu
, L
"Built with TianoCore EDK2");
200 AddMenuInfoLine(&AboutMenu
, L
"");
201 AddMenuInfoLine(&AboutMenu
, L
"For more information, see the rEFInd Web site:");
202 AddMenuInfoLine(&AboutMenu
, L
"http://www.rodsbooks.com/refind/");
203 AddMenuEntry(&AboutMenu
, &MenuEntryReturn
);
206 RunMenu(&AboutMenu
, NULL
);
207 } /* VOID AboutrEFInd() */
209 static VOID
WarnSecureBootError(CHAR16
*Name
, BOOLEAN Verbose
) {
211 Name
= L
"the loader";
213 refit_call2_wrapper(ST
->ConOut
->SetAttribute
, ST
->ConOut
, ATTR_ERROR
);
214 Print(L
"Secure Boot validation failure loading %s!\n", Name
);
215 refit_call2_wrapper(ST
->ConOut
->SetAttribute
, ST
->ConOut
, ATTR_BASIC
);
216 if (Verbose
&& secure_mode()) {
217 Print(L
"\nThis computer is configured with Secure Boot active, but\n%s has failed validation.\n", Name
);
218 Print(L
"\nYou can:\n * Launch another boot loader\n");
219 Print(L
" * Disable Secure Boot in your firmware\n");
220 Print(L
" * Sign %s with a machine owner key (MOK)\n", Name
);
221 Print(L
" * Use a MOK utility (often present on the second row) to add a MOK with which\n");
222 Print(L
" %s has already been signed.\n", Name
);
223 Print(L
" * Use a MOK utility to register %s (\"enroll its hash\") without\n", Name
);
224 Print(L
" signing it.\n");
225 Print(L
"\nSee http://www.rodsbooks.com/refind/secureboot.html for more information\n");
228 } // VOID WarnSecureBootError()
230 // Returns TRUE if this file is a valid EFI loader file, and is proper ARCH
231 static BOOLEAN
IsValidLoader(EFI_FILE
*RootDir
, CHAR16
*FileName
) {
232 BOOLEAN IsValid
= TRUE
;
233 #if defined (EFIX64) | defined (EFI32)
235 EFI_FILE_HANDLE FileHandle
;
237 UINTN Size
= sizeof(Header
);
239 if ((RootDir
== NULL
) || (FileName
== NULL
)) {
240 // Assume valid here, because Macs produce NULL RootDir (& maybe FileName)
241 // when launching from a Firewire drive. This should be handled better, but
242 // fix would have to be in StartEFIImageList() and/or in FindVolumeAndFilename().
246 Status
= refit_call5_wrapper(RootDir
->Open
, RootDir
, &FileHandle
, FileName
, EFI_FILE_MODE_READ
, 0);
247 if (EFI_ERROR(Status
))
250 Status
= refit_call3_wrapper(FileHandle
->Read
, FileHandle
, &Size
, Header
);
251 refit_call1_wrapper(FileHandle
->Close
, FileHandle
);
253 IsValid
= !EFI_ERROR(Status
) &&
254 Size
== sizeof(Header
) &&
255 ((Header
[0] == 'M' && Header
[1] == 'Z' &&
256 (Size
= *(UINT32
*)&Header
[0x3c]) < 0x180 &&
257 Header
[Size
] == 'P' && Header
[Size
+1] == 'E' &&
258 Header
[Size
+2] == 0 && Header
[Size
+3] == 0 &&
259 *(UINT16
*)&Header
[Size
+4] == EFI_STUB_ARCH
) ||
260 (*(UINT32
*)&Header
== FAT_ARCH
));
263 } // BOOLEAN IsValidLoader()
265 // Launch an EFI binary.
266 static EFI_STATUS
StartEFIImageList(IN EFI_DEVICE_PATH
**DevicePaths
,
267 IN CHAR16
*LoadOptions
, IN UINTN LoaderType
,
268 IN CHAR16
*ImageTitle
, IN CHAR8 OSType
,
269 OUT UINTN
*ErrorInStep
,
273 EFI_STATUS Status
, ReturnStatus
;
274 EFI_HANDLE ChildImageHandle
;
275 EFI_LOADED_IMAGE
*ChildLoadedImage
= NULL
;
276 REFIT_VOLUME
*Volume
= NULL
;
277 UINTN DevicePathIndex
;
278 CHAR16 ErrorInfo
[256];
279 CHAR16
*FullLoadOptions
= NULL
;
280 CHAR16
*Filename
= NULL
;
283 if (ErrorInStep
!= NULL
)
287 if (LoadOptions
!= NULL
) {
288 FullLoadOptions
= StrDuplicate(LoadOptions
);
289 if ((LoaderType
== TYPE_EFI
) && (OSType
== 'M')) {
290 MergeStrings(&FullLoadOptions
, L
" ", 0);
291 // NOTE: That last space is also added by the EFI shell and seems to be significant
292 // when passing options to Apple's boot.efi...
294 } // if (LoadOptions != NULL)
296 Print(L
"Starting %s\nUsing load options '%s'\n", ImageTitle
, FullLoadOptions
? FullLoadOptions
: L
"");
298 // load the image into memory (and execute it, in the case of a shim/MOK image).
299 ReturnStatus
= Status
= EFI_NOT_FOUND
; // in case the list is empty
300 for (DevicePathIndex
= 0; DevicePaths
[DevicePathIndex
] != NULL
; DevicePathIndex
++) {
301 FindVolumeAndFilename(DevicePaths
[DevicePathIndex
], &Volume
, &Filename
);
302 // Some EFIs crash if attempting to load driver for invalid architecture, so
303 // protect for this condition; but sometimes Volume comes back NULL, so provide
304 // an exception. (TODO: Handle this special condition better.)
305 if ((LoaderType
== TYPE_LEGACY
) || (Volume
== NULL
) || IsValidLoader(Volume
->RootDir
, Filename
)) {
306 if (Filename
&& (LoaderType
!= TYPE_LEGACY
)) {
307 Temp
= PoolPrint(L
"\\%s %s", Filename
, FullLoadOptions
? FullLoadOptions
: L
"");
309 MyFreePool(FullLoadOptions
);
310 FullLoadOptions
= Temp
;
314 // NOTE: Below commented-out line could be more efficient if file were read ahead of
315 // time and passed as a pre-loaded image to LoadImage(), but it doesn't work on my
316 // 32-bit Mac Mini or my 64-bit Intel box when launching a Linux kernel; the
317 // kernel returns a "Failed to handle fs_proto" error message.
318 // TODO: Track down the cause of this error and fix it, if possible.
319 // ReturnStatus = Status = refit_call6_wrapper(BS->LoadImage, FALSE, SelfImageHandle, DevicePaths[DevicePathIndex],
320 // ImageData, ImageSize, &ChildImageHandle);
321 ReturnStatus
= Status
= refit_call6_wrapper(BS
->LoadImage
, FALSE
, SelfImageHandle
, DevicePaths
[DevicePathIndex
],
322 NULL
, 0, &ChildImageHandle
);
324 Print(L
"Invalid loader file!\n");
325 ReturnStatus
= EFI_LOAD_ERROR
;
327 if (ReturnStatus
!= EFI_NOT_FOUND
) {
331 if ((Status
== EFI_ACCESS_DENIED
) || (Status
== EFI_SECURITY_VIOLATION
)) {
332 WarnSecureBootError(ImageTitle
, Verbose
);
335 SPrint(ErrorInfo
, 255, L
"while loading %s", ImageTitle
);
336 if (CheckError(Status
, ErrorInfo
)) {
337 if (ErrorInStep
!= NULL
)
342 ReturnStatus
= Status
= refit_call3_wrapper(BS
->HandleProtocol
, ChildImageHandle
, &LoadedImageProtocol
,
343 (VOID
**) &ChildLoadedImage
);
344 if (CheckError(Status
, L
"while getting a LoadedImageProtocol handle")) {
345 if (ErrorInStep
!= NULL
)
349 ChildLoadedImage
->LoadOptions
= (VOID
*)FullLoadOptions
;
350 ChildLoadedImage
->LoadOptionsSize
= FullLoadOptions
? ((UINT32
)StrLen(FullLoadOptions
) + 1) * sizeof(CHAR16
) : 0;
351 // turn control over to the image
352 // TODO: (optionally) re-enable the EFI watchdog timer!
354 // close open file handles
356 ReturnStatus
= Status
= refit_call3_wrapper(BS
->StartImage
, ChildImageHandle
, NULL
, NULL
);
358 // control returns here when the child image calls Exit()
359 SPrint(ErrorInfo
, 255, L
"returned from %s", ImageTitle
);
360 if (CheckError(Status
, ErrorInfo
)) {
361 if (ErrorInStep
!= NULL
)
365 // re-open file handles
369 // unload the image, we don't care if it works or not...
371 Status
= refit_call1_wrapper(BS
->UnloadImage
, ChildImageHandle
);
374 MyFreePool(FullLoadOptions
);
376 } /* static EFI_STATUS StartEFIImageList() */
378 static EFI_STATUS
StartEFIImage(IN EFI_DEVICE_PATH
*DevicePath
,
379 IN CHAR16
*LoadOptions
, IN UINTN LoaderType
,
380 IN CHAR16
*ImageTitle
, IN CHAR8 OSType
,
381 OUT UINTN
*ErrorInStep
,
386 EFI_DEVICE_PATH
*DevicePaths
[2];
388 DevicePaths
[0] = DevicePath
;
389 DevicePaths
[1] = NULL
;
390 return StartEFIImageList(DevicePaths
, LoadOptions
, LoaderType
, ImageTitle
, OSType
, ErrorInStep
, Verbose
, IsDriver
);
391 } /* static EFI_STATUS StartEFIImage() */
393 // From gummiboot: Reboot the computer into its built-in user interface
394 static EFI_STATUS
RebootIntoFirmware(VOID
) {
400 osind
= EFI_OS_INDICATIONS_BOOT_TO_FW_UI
;
402 err
= EfivarGetRaw(&GlobalGuid
, L
"OsIndications", &b
, &size
);
403 if (err
== EFI_SUCCESS
)
407 err
= EfivarSetRaw(&GlobalGuid
, L
"OsIndications", (CHAR8
*)&osind
, sizeof(UINT64
), TRUE
);
408 if (err
!= EFI_SUCCESS
)
411 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);
412 Print(L
"Error calling ResetSystem: %r", err
);
417 // Record the value of the loader's name/description in rEFInd's "PreviousBoot" EFI variable,
418 // if it's different from what's already stored there.
419 static VOID
StoreLoaderName(IN CHAR16
*Name
) {
421 CHAR16
*OldName
= NULL
;
425 Status
= EfivarGetRaw(&RefindGuid
, L
"PreviousBoot", (CHAR8
**) &OldName
, &Length
);
426 if ((Status
!= EFI_SUCCESS
) || (StrCmp(OldName
, Name
) != 0)) {
427 EfivarSetRaw(&RefindGuid
, L
"PreviousBoot", (CHAR8
*) Name
, StrLen(Name
) * 2 + 2, TRUE
);
431 } // VOID StoreLoaderName()
434 // EFI OS loader functions
437 static VOID
StartLoader(LOADER_ENTRY
*Entry
, CHAR16
*SelectionName
)
439 UINTN ErrorInStep
= 0;
441 BeginExternalScreen(Entry
->UseGraphicsMode
, L
"Booting OS");
442 StoreLoaderName(SelectionName
);
443 StartEFIImage(Entry
->DevicePath
, Entry
->LoadOptions
, TYPE_EFI
,
444 Basename(Entry
->LoaderPath
), Entry
->OSType
, &ErrorInStep
, !Entry
->UseGraphicsMode
, FALSE
);
445 FinishExternalScreen();
448 // Locate an initrd or initramfs file that matches the kernel specified by LoaderPath.
449 // The matching file has a name that begins with "init" and includes the same version
450 // number string as is found in LoaderPath -- but not a longer version number string.
451 // For instance, if LoaderPath is \EFI\kernels\bzImage-3.3.0.efi, and if \EFI\kernels
452 // has a file called initramfs-3.3.0.img, this function will return the string
453 // '\EFI\kernels\initramfs-3.3.0.img'. If the directory ALSO contains the file
454 // initramfs-3.3.0-rc7.img or initramfs-13.3.0.img, those files will NOT match;
455 // however, initmine-3.3.0.img might match. (FindInitrd() returns the first match it
456 // finds). Thus, care should be taken to avoid placing duplicate matching files in
457 // the kernel's directory.
458 // If no matching init file can be found, returns NULL.
459 static CHAR16
* FindInitrd(IN CHAR16
*LoaderPath
, IN REFIT_VOLUME
*Volume
) {
460 CHAR16
*InitrdName
= NULL
, *FileName
, *KernelVersion
, *InitrdVersion
, *Path
;
461 REFIT_DIR_ITER DirIter
;
462 EFI_FILE_INFO
*DirEntry
;
464 FileName
= Basename(LoaderPath
);
465 KernelVersion
= FindNumbers(FileName
);
466 Path
= FindPath(LoaderPath
);
468 // Add trailing backslash for root directory; necessary on some systems, but must
469 // NOT be added to all directories, since on other systems, a trailing backslash on
470 // anything but the root directory causes them to flake out!
471 if (StrLen(Path
) == 0) {
472 MergeStrings(&Path
, L
"\\", 0);
474 DirIterOpen(Volume
->RootDir
, Path
, &DirIter
);
475 // Now add a trailing backslash if it was NOT added earlier, for consistency in
476 // building the InitrdName later....
477 if ((StrLen(Path
) > 0) && (Path
[StrLen(Path
) - 1] != L
'\\'))
478 MergeStrings(&Path
, L
"\\", 0);
479 while ((DirIterNext(&DirIter
, 2, L
"init*", &DirEntry
)) && (InitrdName
== NULL
)) {
480 InitrdVersion
= FindNumbers(DirEntry
->FileName
);
481 if (KernelVersion
!= NULL
) {
482 if (StriCmp(InitrdVersion
, KernelVersion
) == 0) {
483 InitrdName
= PoolPrint(L
"%s%s", Path
, DirEntry
->FileName
);
486 if (InitrdVersion
== NULL
) {
487 InitrdName
= PoolPrint(L
"%s%s", Path
, DirEntry
->FileName
);
490 MyFreePool(InitrdVersion
);
492 DirIterClose(&DirIter
);
494 // Note: Don't FreePool(FileName), since Basename returns a pointer WITHIN the string it's passed.
495 MyFreePool(KernelVersion
);
498 } // static CHAR16 * FindInitrd()
500 LOADER_ENTRY
* AddPreparedLoaderEntry(LOADER_ENTRY
*Entry
) {
501 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
504 } // LOADER_ENTRY * AddPreparedLoaderEntry()
506 // Creates a copy of a menu screen.
507 // Returns a pointer to the copy of the menu screen.
508 static REFIT_MENU_SCREEN
* CopyMenuScreen(REFIT_MENU_SCREEN
*Entry
) {
509 REFIT_MENU_SCREEN
*NewEntry
;
512 NewEntry
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
513 if ((Entry
!= NULL
) && (NewEntry
!= NULL
)) {
514 CopyMem(NewEntry
, Entry
, sizeof(REFIT_MENU_SCREEN
));
515 NewEntry
->Title
= (Entry
->Title
) ? StrDuplicate(Entry
->Title
) : NULL
;
516 NewEntry
->TimeoutText
= (Entry
->TimeoutText
) ? StrDuplicate(Entry
->TimeoutText
) : NULL
;
517 if (Entry
->TitleImage
!= NULL
) {
518 NewEntry
->TitleImage
= AllocatePool(sizeof(EG_IMAGE
));
519 if (NewEntry
->TitleImage
!= NULL
)
520 CopyMem(NewEntry
->TitleImage
, Entry
->TitleImage
, sizeof(EG_IMAGE
));
522 NewEntry
->InfoLines
= (CHAR16
**) AllocateZeroPool(Entry
->InfoLineCount
* (sizeof(CHAR16
*)));
523 for (i
= 0; i
< Entry
->InfoLineCount
&& NewEntry
->InfoLines
; i
++) {
524 NewEntry
->InfoLines
[i
] = (Entry
->InfoLines
[i
]) ? StrDuplicate(Entry
->InfoLines
[i
]) : NULL
;
526 NewEntry
->Entries
= (REFIT_MENU_ENTRY
**) AllocateZeroPool(Entry
->EntryCount
* (sizeof (REFIT_MENU_ENTRY
*)));
527 for (i
= 0; i
< Entry
->EntryCount
&& NewEntry
->Entries
; i
++) {
528 AddMenuEntry(NewEntry
, Entry
->Entries
[i
]);
530 NewEntry
->Hint1
= (Entry
->Hint1
) ? StrDuplicate(Entry
->Hint1
) : NULL
;
531 NewEntry
->Hint2
= (Entry
->Hint2
) ? StrDuplicate(Entry
->Hint2
) : NULL
;
534 } // static REFIT_MENU_SCREEN* CopyMenuScreen()
536 // Creates a copy of a menu entry. Intended to enable moving a stack-based
537 // menu entry (such as the ones for the "reboot" and "exit" functions) to
538 // to the heap. This enables easier deletion of the whole set of menu
539 // entries when re-scanning.
540 // Returns a pointer to the copy of the menu entry.
541 static REFIT_MENU_ENTRY
* CopyMenuEntry(REFIT_MENU_ENTRY
*Entry
) {
542 REFIT_MENU_ENTRY
*NewEntry
;
544 NewEntry
= AllocateZeroPool(sizeof(REFIT_MENU_ENTRY
));
545 if ((Entry
!= NULL
) && (NewEntry
!= NULL
)) {
546 CopyMem(NewEntry
, Entry
, sizeof(REFIT_MENU_ENTRY
));
547 NewEntry
->Title
= (Entry
->Title
) ? StrDuplicate(Entry
->Title
) : NULL
;
548 if (Entry
->BadgeImage
!= NULL
) {
549 NewEntry
->BadgeImage
= AllocatePool(sizeof(EG_IMAGE
));
550 if (NewEntry
->BadgeImage
!= NULL
)
551 CopyMem(NewEntry
->BadgeImage
, Entry
->BadgeImage
, sizeof(EG_IMAGE
));
553 if (Entry
->Image
!= NULL
) {
554 NewEntry
->Image
= AllocatePool(sizeof(EG_IMAGE
));
555 if (NewEntry
->Image
!= NULL
)
556 CopyMem(NewEntry
->Image
, Entry
->Image
, sizeof(EG_IMAGE
));
558 if (Entry
->SubScreen
!= NULL
) {
559 NewEntry
->SubScreen
= CopyMenuScreen(Entry
->SubScreen
);
563 } // REFIT_MENU_ENTRY* CopyMenuEntry()
565 // Creates a new LOADER_ENTRY data structure and populates it with
566 // default values from the specified Entry, or NULL values if Entry
567 // is unspecified (NULL).
568 // Returns a pointer to the new data structure, or NULL if it
569 // couldn't be allocated
570 LOADER_ENTRY
*InitializeLoaderEntry(IN LOADER_ENTRY
*Entry
) {
571 LOADER_ENTRY
*NewEntry
= NULL
;
573 NewEntry
= AllocateZeroPool(sizeof(LOADER_ENTRY
));
574 if (NewEntry
!= NULL
) {
575 NewEntry
->me
.Title
= NULL
;
576 NewEntry
->me
.Tag
= TAG_LOADER
;
577 NewEntry
->Enabled
= TRUE
;
578 NewEntry
->UseGraphicsMode
= FALSE
;
579 NewEntry
->OSType
= 0;
581 NewEntry
->LoaderPath
= (Entry
->LoaderPath
) ? StrDuplicate(Entry
->LoaderPath
) : NULL
;
582 NewEntry
->VolName
= (Entry
->VolName
) ? StrDuplicate(Entry
->VolName
) : NULL
;
583 NewEntry
->DevicePath
= Entry
->DevicePath
;
584 NewEntry
->UseGraphicsMode
= Entry
->UseGraphicsMode
;
585 NewEntry
->LoadOptions
= (Entry
->LoadOptions
) ? StrDuplicate(Entry
->LoadOptions
) : NULL
;
586 NewEntry
->InitrdPath
= (Entry
->InitrdPath
) ? StrDuplicate(Entry
->InitrdPath
) : NULL
;
590 } // LOADER_ENTRY *InitializeLoaderEntry()
592 // Adds InitrdPath to Options, but only if Options doesn't already include an
593 // initrd= line. Done to enable overriding the default initrd selection in a
594 // refind_linux.conf file's options list.
595 // Returns a pointer to a new string. The calling function is responsible for
596 // freeing its memory.
597 static CHAR16
*AddInitrdToOptions(CHAR16
*Options
, CHAR16
*InitrdPath
) {
598 CHAR16
*NewOptions
= NULL
;
601 NewOptions
= StrDuplicate(Options
);
602 if ((InitrdPath
!= NULL
) && !StriSubCmp(L
"initrd=", Options
)) {
603 MergeStrings(&NewOptions
, L
"initrd=", L
' ');
604 MergeStrings(&NewOptions
, InitrdPath
, 0);
607 } // CHAR16 *AddInitrdToOptions()
609 // Prepare a REFIT_MENU_SCREEN data structure for a subscreen entry. This sets up
610 // the default entry that launches the boot loader using the same options as the
611 // main Entry does. Subsequent options can be added by the calling function.
612 // If a subscreen already exists in the Entry that's passed to this function,
613 // it's left unchanged and a pointer to it is returned.
614 // Returns a pointer to the new subscreen data structure, or NULL if there
615 // were problems allocating memory.
616 REFIT_MENU_SCREEN
*InitializeSubScreen(IN LOADER_ENTRY
*Entry
) {
617 CHAR16
*FileName
, *MainOptions
= NULL
;
618 REFIT_MENU_SCREEN
*SubScreen
= NULL
;
619 LOADER_ENTRY
*SubEntry
;
621 FileName
= Basename(Entry
->LoaderPath
);
622 if (Entry
->me
.SubScreen
== NULL
) { // No subscreen yet; initialize default entry....
623 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
624 if (SubScreen
!= NULL
) {
625 SubScreen
->Title
= AllocateZeroPool(sizeof(CHAR16
) * 256);
626 SPrint(SubScreen
->Title
, 255, L
"Boot Options for %s on %s",
627 (Entry
->Title
!= NULL
) ? Entry
->Title
: FileName
, Entry
->VolName
);
628 SubScreen
->TitleImage
= Entry
->me
.Image
;
630 SubEntry
= InitializeLoaderEntry(Entry
);
631 if (SubEntry
!= NULL
) {
632 SubEntry
->me
.Title
= StrDuplicate(L
"Boot using default options");
633 MainOptions
= SubEntry
->LoadOptions
;
634 SubEntry
->LoadOptions
= AddInitrdToOptions(MainOptions
, SubEntry
->InitrdPath
);
635 MyFreePool(MainOptions
);
636 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
637 } // if (SubEntry != NULL)
638 SubScreen
->Hint1
= StrDuplicate(SUBSCREEN_HINT1
);
639 if (GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_EDITOR
) {
640 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2_NO_EDITOR
);
642 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2
);
644 } // if (SubScreen != NULL)
645 } else { // existing subscreen; less initialization, and just add new entry later....
646 SubScreen
= Entry
->me
.SubScreen
;
649 } // REFIT_MENU_SCREEN *InitializeSubScreen()
651 VOID
GenerateSubScreen(LOADER_ENTRY
*Entry
, IN REFIT_VOLUME
*Volume
) {
652 REFIT_MENU_SCREEN
*SubScreen
;
653 LOADER_ENTRY
*SubEntry
;
655 CHAR16 DiagsFileName
[256];
660 // create the submenu
661 if (StrLen(Entry
->Title
) == 0) {
662 MyFreePool(Entry
->Title
);
665 SubScreen
= InitializeSubScreen(Entry
);
667 // loader-specific submenu entries
668 if (Entry
->OSType
== 'M') { // entries for Mac OS X
670 SubEntry
= InitializeLoaderEntry(Entry
);
671 if (SubEntry
!= NULL
) {
672 SubEntry
->me
.Title
= L
"Boot Mac OS X with a 64-bit kernel";
673 SubEntry
->LoadOptions
= L
"arch=x86_64";
674 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
675 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
678 SubEntry
= InitializeLoaderEntry(Entry
);
679 if (SubEntry
!= NULL
) {
680 SubEntry
->me
.Title
= L
"Boot Mac OS X with a 32-bit kernel";
681 SubEntry
->LoadOptions
= L
"arch=i386";
682 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
683 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
687 if (!(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_SINGLEUSER
)) {
688 SubEntry
= InitializeLoaderEntry(Entry
);
689 if (SubEntry
!= NULL
) {
690 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode";
691 SubEntry
->UseGraphicsMode
= FALSE
;
692 SubEntry
->LoadOptions
= L
"-v";
693 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
697 SubEntry
= InitializeLoaderEntry(Entry
);
698 if (SubEntry
!= NULL
) {
699 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode (64-bit)";
700 SubEntry
->UseGraphicsMode
= FALSE
;
701 SubEntry
->LoadOptions
= L
"-v arch=x86_64";
702 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
705 SubEntry
= InitializeLoaderEntry(Entry
);
706 if (SubEntry
!= NULL
) {
707 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode (32-bit)";
708 SubEntry
->UseGraphicsMode
= FALSE
;
709 SubEntry
->LoadOptions
= L
"-v arch=i386";
710 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
714 SubEntry
= InitializeLoaderEntry(Entry
);
715 if (SubEntry
!= NULL
) {
716 SubEntry
->me
.Title
= L
"Boot Mac OS X in single user mode";
717 SubEntry
->UseGraphicsMode
= FALSE
;
718 SubEntry
->LoadOptions
= L
"-v -s";
719 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
721 } // single-user mode allowed
723 if (!(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_SAFEMODE
)) {
724 SubEntry
= InitializeLoaderEntry(Entry
);
725 if (SubEntry
!= NULL
) {
726 SubEntry
->me
.Title
= L
"Boot Mac OS X in safe mode";
727 SubEntry
->UseGraphicsMode
= FALSE
;
728 SubEntry
->LoadOptions
= L
"-v -x";
729 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
731 } // safe mode allowed
733 // check for Apple hardware diagnostics
734 StrCpy(DiagsFileName
, L
"System\\Library\\CoreServices\\.diagnostics\\diags.efi");
735 if (FileExists(Volume
->RootDir
, DiagsFileName
) && !(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_HWTEST
)) {
736 SubEntry
= InitializeLoaderEntry(Entry
);
737 if (SubEntry
!= NULL
) {
738 SubEntry
->me
.Title
= L
"Run Apple Hardware Test";
739 MyFreePool(SubEntry
->LoaderPath
);
740 SubEntry
->LoaderPath
= StrDuplicate(DiagsFileName
);
741 SubEntry
->DevicePath
= FileDevicePath(Volume
->DeviceHandle
, SubEntry
->LoaderPath
);
742 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
743 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
745 } // if diagnostics entry found
747 } else if (Entry
->OSType
== 'L') { // entries for Linux kernels with EFI stub loaders
748 File
= ReadLinuxOptionsFile(Entry
->LoaderPath
, Volume
);
750 InitrdName
= FindInitrd(Entry
->LoaderPath
, Volume
);
751 TokenCount
= ReadTokenLine(File
, &TokenList
);
752 // first entry requires special processing, since it was initially set
753 // up with a default title but correct options by InitializeSubScreen(),
755 if ((SubScreen
->Entries
!= NULL
) && (SubScreen
->Entries
[0] != NULL
)) {
756 MyFreePool(SubScreen
->Entries
[0]->Title
);
757 SubScreen
->Entries
[0]->Title
= TokenList
[0] ? StrDuplicate(TokenList
[0]) : StrDuplicate(L
"Boot Linux");
759 FreeTokenLine(&TokenList
, &TokenCount
);
760 while ((TokenCount
= ReadTokenLine(File
, &TokenList
)) > 1) {
761 SubEntry
= InitializeLoaderEntry(Entry
);
762 SubEntry
->me
.Title
= TokenList
[0] ? StrDuplicate(TokenList
[0]) : StrDuplicate(L
"Boot Linux");
763 MyFreePool(SubEntry
->LoadOptions
);
764 SubEntry
->LoadOptions
= AddInitrdToOptions(TokenList
[1], InitrdName
);
765 FreeTokenLine(&TokenList
, &TokenCount
);
766 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_LINUX
;
767 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
769 MyFreePool(InitrdName
);
773 } else if (Entry
->OSType
== 'E') { // entries for ELILO
774 SubEntry
= InitializeLoaderEntry(Entry
);
775 if (SubEntry
!= NULL
) {
776 SubEntry
->me
.Title
= L
"Run ELILO in interactive mode";
777 SubEntry
->LoadOptions
= L
"-p";
778 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
779 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
782 SubEntry
= InitializeLoaderEntry(Entry
);
783 if (SubEntry
!= NULL
) {
784 SubEntry
->me
.Title
= L
"Boot Linux for a 17\" iMac or a 15\" MacBook Pro (*)";
785 SubEntry
->UseGraphicsMode
= TRUE
;
786 SubEntry
->LoadOptions
= L
"-d 0 i17";
787 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
788 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
791 SubEntry
= InitializeLoaderEntry(Entry
);
792 if (SubEntry
!= NULL
) {
793 SubEntry
->me
.Title
= L
"Boot Linux for a 20\" iMac (*)";
794 SubEntry
->UseGraphicsMode
= TRUE
;
795 SubEntry
->LoadOptions
= L
"-d 0 i20";
796 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
797 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
800 SubEntry
= InitializeLoaderEntry(Entry
);
801 if (SubEntry
!= NULL
) {
802 SubEntry
->me
.Title
= L
"Boot Linux for a Mac Mini (*)";
803 SubEntry
->UseGraphicsMode
= TRUE
;
804 SubEntry
->LoadOptions
= L
"-d 0 mini";
805 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
806 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
809 AddMenuInfoLine(SubScreen
, L
"NOTE: This is an example. Entries");
810 AddMenuInfoLine(SubScreen
, L
"marked with (*) may not work.");
812 } else if (Entry
->OSType
== 'X') { // entries for xom.efi
813 // by default, skip the built-in selection and boot from hard disk only
814 Entry
->LoadOptions
= L
"-s -h";
816 SubEntry
= InitializeLoaderEntry(Entry
);
817 if (SubEntry
!= NULL
) {
818 SubEntry
->me
.Title
= L
"Boot Windows from Hard Disk";
819 SubEntry
->LoadOptions
= L
"-s -h";
820 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
821 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
824 SubEntry
= InitializeLoaderEntry(Entry
);
825 if (SubEntry
!= NULL
) {
826 SubEntry
->me
.Title
= L
"Boot Windows from CD-ROM";
827 SubEntry
->LoadOptions
= L
"-s -c";
828 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
829 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
832 SubEntry
= InitializeLoaderEntry(Entry
);
833 if (SubEntry
!= NULL
) {
834 SubEntry
->me
.Title
= L
"Run XOM in text mode";
835 SubEntry
->UseGraphicsMode
= FALSE
;
836 SubEntry
->LoadOptions
= L
"-v";
837 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
838 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
840 } // entries for xom.efi
841 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
842 Entry
->me
.SubScreen
= SubScreen
;
843 } // VOID GenerateSubScreen()
845 // Returns options for a Linux kernel. Reads them from an options file in the
846 // kernel's directory; and if present, adds an initrd= option for an initial
847 // RAM disk file with the same version number as the kernel file.
848 static CHAR16
* GetMainLinuxOptions(IN CHAR16
* LoaderPath
, IN REFIT_VOLUME
*Volume
) {
849 CHAR16
*Options
= NULL
, *InitrdName
, *FullOptions
= NULL
;
851 Options
= GetFirstOptionsFromFile(LoaderPath
, Volume
);
852 InitrdName
= FindInitrd(LoaderPath
, Volume
);
853 FullOptions
= AddInitrdToOptions(Options
, InitrdName
);
856 MyFreePool(InitrdName
);
857 return (FullOptions
);
858 } // static CHAR16 * GetMainLinuxOptions()
860 // Try to guess the name of the Linux distribution & add that name to
862 static VOID
GuessLinuxDistribution(CHAR16
**OSIconName
, REFIT_VOLUME
*Volume
, CHAR16
*LoaderPath
) {
866 UINTN TokenCount
= 0;
868 // If on Linux root fs, /etc/os-release file probably has clues....
869 if (FileExists(Volume
->RootDir
, L
"etc\\os-release") &&
870 (ReadFile(Volume
->RootDir
, L
"etc\\os-release", &File
, &FileSize
) == EFI_SUCCESS
)) {
872 TokenCount
= ReadTokenLine(&File
, &TokenList
);
873 if ((TokenCount
> 1) && ((StriCmp(TokenList
[0], L
"ID") == 0) || (StriCmp(TokenList
[0], L
"NAME") == 0))) {
874 MergeStrings(OSIconName
, TokenList
[1], L
',');
876 FreeTokenLine(&TokenList
, &TokenCount
);
877 } while (TokenCount
> 0);
878 MyFreePool(File
.Buffer
);
881 // Search for clues in the kernel's filename....
882 if (StriSubCmp(L
".fc", LoaderPath
))
883 MergeStrings(OSIconName
, L
"fedora", L
',');
884 if (StriSubCmp(L
".el", LoaderPath
))
885 MergeStrings(OSIconName
, L
"redhat", L
',');
886 } // VOID GuessLinuxDistribution()
888 // Sets a few defaults for a loader entry -- mainly the icon, but also the OS type
889 // code and shortcut letter. For Linux EFI stub loaders, also sets kernel options
890 // that will (with luck) work fairly automatically.
891 VOID
SetLoaderDefaults(LOADER_ENTRY
*Entry
, CHAR16
*LoaderPath
, REFIT_VOLUME
*Volume
) {
892 CHAR16
*FileName
, *PathOnly
, *NoExtension
, *OSIconName
= NULL
, *Temp
, *SubString
;
893 CHAR16 ShortcutLetter
= 0;
896 FileName
= Basename(LoaderPath
);
897 PathOnly
= FindPath(LoaderPath
);
898 NoExtension
= StripEfiExtension(FileName
);
900 // locate a custom icon for the loader
901 // Anything found here takes precedence over the "hints" in the OSIconName variable
902 if (!Entry
->me
.Image
) {
903 Entry
->me
.Image
= egLoadIconAnyType(Volume
->RootDir
, PathOnly
, NoExtension
, GlobalConfig
.IconSizes
[ICON_SIZE_BIG
]);
905 if (!Entry
->me
.Image
) {
906 Entry
->me
.Image
= egCopyImage(Volume
->VolIconImage
);
909 // Begin creating icon "hints" by using last part of directory path leading
911 Temp
= FindLastDirName(LoaderPath
);
912 MergeStrings(&OSIconName
, Temp
, L
',');
915 if (OSIconName
!= NULL
) {
916 ShortcutLetter
= OSIconName
[0];
919 // Add every "word" in the volume label, delimited by spaces, dashes (-), or
920 // underscores (_), to the list of hints to be used in searching for OS
922 if ((Volume
->VolName
) && (StrLen(Volume
->VolName
) > 0)) {
923 Temp
= SubString
= StrDuplicate(Volume
->VolName
);
925 Length
= StrLen(Temp
);
926 for (i
= 0; i
< Length
; i
++) {
927 if ((Temp
[i
] == L
' ') || (Temp
[i
] == L
'_') || (Temp
[i
] == L
'-')) {
929 if (StrLen(SubString
) > 0)
930 MergeStrings(&OSIconName
, SubString
, L
',');
931 SubString
= Temp
+ i
+ 1;
934 MergeStrings(&OSIconName
, SubString
, L
',');
939 // detect specific loaders
940 if (StriSubCmp(L
"bzImage", FileName
) || StriSubCmp(L
"vmlinuz", FileName
)) {
941 GuessLinuxDistribution(&OSIconName
, Volume
, LoaderPath
);
942 MergeStrings(&OSIconName
, L
"linux", L
',');
944 if (ShortcutLetter
== 0)
945 ShortcutLetter
= 'L';
946 Entry
->LoadOptions
= GetMainLinuxOptions(LoaderPath
, Volume
);
947 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_LINUX
;
948 } else if (StriSubCmp(L
"refit", LoaderPath
)) {
949 MergeStrings(&OSIconName
, L
"refit", L
',');
951 ShortcutLetter
= 'R';
952 } else if (StriSubCmp(L
"refind", LoaderPath
)) {
953 MergeStrings(&OSIconName
, L
"refind", L
',');
955 ShortcutLetter
= 'R';
956 } else if (StriCmp(LoaderPath
, MACOSX_LOADER_PATH
) == 0) {
957 MergeStrings(&OSIconName
, L
"mac", L
',');
959 ShortcutLetter
= 'M';
960 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
961 } else if (StriCmp(FileName
, L
"diags.efi") == 0) {
962 MergeStrings(&OSIconName
, L
"hwtest", L
',');
963 } else if (StriCmp(FileName
, L
"e.efi") == 0 || StriCmp(FileName
, L
"elilo.efi") == 0 || StriSubCmp(L
"elilo", FileName
)) {
964 MergeStrings(&OSIconName
, L
"elilo,linux", L
',');
966 if (ShortcutLetter
== 0)
967 ShortcutLetter
= 'L';
968 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
969 } else if (StriSubCmp(L
"grub", FileName
)) {
971 ShortcutLetter
= 'G';
972 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_GRUB
;
973 } else if (StriCmp(FileName
, L
"cdboot.efi") == 0 ||
974 StriCmp(FileName
, L
"bootmgr.efi") == 0 ||
975 StriCmp(FileName
, L
"bootmgfw.efi") == 0 ||
976 StriCmp(FileName
, L
"bkpbootmgfw.efi") == 0) {
977 MergeStrings(&OSIconName
, L
"win", L
',');
979 ShortcutLetter
= 'W';
980 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
981 } else if (StriCmp(FileName
, L
"xom.efi") == 0) {
982 MergeStrings(&OSIconName
, L
"xom,win", L
',');
983 Entry
->UseGraphicsMode
= TRUE
;
985 ShortcutLetter
= 'W';
986 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
989 if ((ShortcutLetter
>= 'a') && (ShortcutLetter
<= 'z'))
990 ShortcutLetter
= ShortcutLetter
- 'a' + 'A'; // convert lowercase to uppercase
991 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
992 if (Entry
->me
.Image
== NULL
)
993 Entry
->me
.Image
= LoadOSIcon(OSIconName
, L
"unknown", FALSE
);
994 MyFreePool(PathOnly
);
995 } // VOID SetLoaderDefaults()
997 // Add a specified EFI boot loader to the list, using automatic settings
998 // for icons, options, etc.
999 LOADER_ENTRY
* AddLoaderEntry(IN CHAR16
*LoaderPath
, IN CHAR16
*LoaderTitle
, IN REFIT_VOLUME
*Volume
) {
1000 LOADER_ENTRY
*Entry
;
1002 CleanUpPathNameSlashes(LoaderPath
);
1003 Entry
= InitializeLoaderEntry(NULL
);
1004 if (Entry
!= NULL
) {
1005 Entry
->Title
= StrDuplicate((LoaderTitle
!= NULL
) ? LoaderTitle
: LoaderPath
);
1006 Entry
->me
.Title
= AllocateZeroPool(sizeof(CHAR16
) * 256);
1007 // Extra space at end of Entry->me.Title enables searching on Volume->VolName even if another volume
1008 // name is identical except for something added to the end (e.g., VolB1 vs. VolB12).
1009 SPrint(Entry
->me
.Title
, 255, L
"Boot %s from %s ", (LoaderTitle
!= NULL
) ? LoaderTitle
: LoaderPath
, Volume
->VolName
);
1011 Entry
->me
.BadgeImage
= Volume
->VolBadgeImage
;
1012 if ((LoaderPath
!= NULL
) && (LoaderPath
[0] != L
'\\')) {
1013 Entry
->LoaderPath
= StrDuplicate(L
"\\");
1015 Entry
->LoaderPath
= NULL
;
1017 MergeStrings(&(Entry
->LoaderPath
), LoaderPath
, 0);
1018 Entry
->VolName
= Volume
->VolName
;
1019 Entry
->DevicePath
= FileDevicePath(Volume
->DeviceHandle
, Entry
->LoaderPath
);
1020 SetLoaderDefaults(Entry
, LoaderPath
, Volume
);
1021 GenerateSubScreen(Entry
, Volume
);
1022 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1026 } // LOADER_ENTRY * AddLoaderEntry()
1028 // Returns -1 if (Time1 < Time2), +1 if (Time1 > Time2), or 0 if
1029 // (Time1 == Time2). Precision is only to the nearest second; since
1030 // this is used for sorting boot loader entries, differences smaller
1031 // than this are likely to be meaningless (and unlikely!).
1032 INTN
TimeComp(IN EFI_TIME
*Time1
, IN EFI_TIME
*Time2
) {
1033 INT64 Time1InSeconds
, Time2InSeconds
;
1035 // Following values are overestimates; I'm assuming 31 days in every month.
1036 // This is fine for the purpose of this function, which is limited
1037 Time1InSeconds
= Time1
->Second
+ (Time1
->Minute
* 60) + (Time1
->Hour
* 3600) + (Time1
->Day
* 86400) +
1038 (Time1
->Month
* 2678400) + ((Time1
->Year
- 1998) * 32140800);
1039 Time2InSeconds
= Time2
->Second
+ (Time2
->Minute
* 60) + (Time2
->Hour
* 3600) + (Time2
->Day
* 86400) +
1040 (Time2
->Month
* 2678400) + ((Time2
->Year
- 1998) * 32140800);
1041 if (Time1InSeconds
< Time2InSeconds
)
1043 else if (Time1InSeconds
> Time2InSeconds
)
1047 } // INTN TimeComp()
1049 // Adds a loader list element, keeping it sorted by date. Returns the new
1050 // first element (the one with the most recent date).
1051 static struct LOADER_LIST
* AddLoaderListEntry(struct LOADER_LIST
*LoaderList
, struct LOADER_LIST
*NewEntry
) {
1052 struct LOADER_LIST
*LatestEntry
, *CurrentEntry
, *PrevEntry
= NULL
;
1054 LatestEntry
= CurrentEntry
= LoaderList
;
1055 if (LoaderList
== NULL
) {
1056 LatestEntry
= NewEntry
;
1058 while ((CurrentEntry
!= NULL
) && (TimeComp(&(NewEntry
->TimeStamp
), &(CurrentEntry
->TimeStamp
)) < 0)) {
1059 PrevEntry
= CurrentEntry
;
1060 CurrentEntry
= CurrentEntry
->NextEntry
;
1062 NewEntry
->NextEntry
= CurrentEntry
;
1063 if (PrevEntry
== NULL
) {
1064 LatestEntry
= NewEntry
;
1066 PrevEntry
->NextEntry
= NewEntry
;
1069 return (LatestEntry
);
1070 } // static VOID AddLoaderListEntry()
1072 // Delete the LOADER_LIST linked list
1073 static VOID
CleanUpLoaderList(struct LOADER_LIST
*LoaderList
) {
1074 struct LOADER_LIST
*Temp
;
1076 while (LoaderList
!= NULL
) {
1078 LoaderList
= LoaderList
->NextEntry
;
1079 MyFreePool(Temp
->FileName
);
1082 } // static VOID CleanUpLoaderList()
1084 // Returns FALSE if the specified file/volume matches the GlobalConfig.DontScanDirs
1085 // or GlobalConfig.DontScanVolumes specification, or if Path points to a volume
1086 // other than the one specified by Volume, or if the specified path is SelfDir.
1087 // Returns TRUE if none of these conditions is met -- that is, if the path is
1088 // eligible for scanning.
1089 static BOOLEAN
ShouldScan(REFIT_VOLUME
*Volume
, CHAR16
*Path
) {
1090 CHAR16
*VolName
= NULL
, *DontScanDir
, *PathCopy
= NULL
;
1092 BOOLEAN ScanIt
= TRUE
;
1094 if ((IsIn(Volume
->VolName
, GlobalConfig
.DontScanVolumes
)) || (IsIn(Volume
->PartName
, GlobalConfig
.DontScanVolumes
)))
1097 if ((StriCmp(Path
, SelfDirPath
) == 0) && (Volume
->DeviceHandle
== SelfVolume
->DeviceHandle
))
1100 // See if Path includes an explicit volume declaration that's NOT Volume....
1101 PathCopy
= StrDuplicate(Path
);
1102 if (SplitVolumeAndFilename(&PathCopy
, &VolName
)) {
1103 VolumeNumberToName(Volume
, &VolName
);
1104 if (VolName
&& StriCmp(VolName
, Volume
->VolName
) != 0) {
1107 } // if Path includes volume specification
1108 MyFreePool(PathCopy
);
1109 MyFreePool(VolName
);
1112 // See if Volume is in GlobalConfig.DontScanDirs....
1113 while (ScanIt
&& (DontScanDir
= FindCommaDelimited(GlobalConfig
.DontScanDirs
, i
++))) {
1114 SplitVolumeAndFilename(&DontScanDir
, &VolName
);
1115 CleanUpPathNameSlashes(DontScanDir
);
1116 VolumeNumberToName(Volume
, &VolName
);
1117 if (VolName
!= NULL
) {
1118 if ((StriCmp(VolName
, Volume
->VolName
) == 0) && (StriCmp(DontScanDir
, Path
) == 0))
1121 if (StriCmp(DontScanDir
, Path
) == 0)
1124 MyFreePool(DontScanDir
);
1125 MyFreePool(VolName
);
1131 } // BOOLEAN ShouldScan()
1133 // Returns TRUE if the file is byte-for-byte identical with the fallback file
1134 // on the volume AND if the file is not itself the fallback file; returns
1135 // FALSE if the file is not identical to the fallback file OR if the file
1136 // IS the fallback file. Intended for use in excluding the fallback boot
1137 // loader when it's a duplicate of another boot loader.
1138 static BOOLEAN
DuplicatesFallback(IN REFIT_VOLUME
*Volume
, IN CHAR16
*FileName
) {
1139 CHAR8
*FileContents
, *FallbackContents
;
1140 EFI_FILE_HANDLE FileHandle
, FallbackHandle
;
1141 EFI_FILE_INFO
*FileInfo
, *FallbackInfo
;
1142 UINTN FileSize
= 0, FallbackSize
= 0;
1144 BOOLEAN AreIdentical
= FALSE
;
1146 if (!FileExists(Volume
->RootDir
, FileName
) || !FileExists(Volume
->RootDir
, FALLBACK_FULLNAME
))
1149 CleanUpPathNameSlashes(FileName
);
1151 if (StriCmp(FileName
, FALLBACK_FULLNAME
) == 0)
1152 return FALSE
; // identical filenames, so not a duplicate....
1154 Status
= refit_call5_wrapper(Volume
->RootDir
->Open
, Volume
->RootDir
, &FileHandle
, FileName
, EFI_FILE_MODE_READ
, 0);
1155 if (Status
== EFI_SUCCESS
) {
1156 FileInfo
= LibFileInfo(FileHandle
);
1157 FileSize
= FileInfo
->FileSize
;
1162 Status
= refit_call5_wrapper(Volume
->RootDir
->Open
, Volume
->RootDir
, &FallbackHandle
, FALLBACK_FULLNAME
, EFI_FILE_MODE_READ
, 0);
1163 if (Status
== EFI_SUCCESS
) {
1164 FallbackInfo
= LibFileInfo(FallbackHandle
);
1165 FallbackSize
= FallbackInfo
->FileSize
;
1167 refit_call1_wrapper(FileHandle
->Close
, FileHandle
);
1171 if (FallbackSize
!= FileSize
) { // not same size, so can't be identical
1172 AreIdentical
= FALSE
;
1173 } else { // could be identical; do full check....
1174 FileContents
= AllocatePool(FileSize
);
1175 FallbackContents
= AllocatePool(FallbackSize
);
1176 if (FileContents
&& FallbackContents
) {
1177 Status
= refit_call3_wrapper(FileHandle
->Read
, FileHandle
, &FileSize
, FileContents
);
1178 if (Status
== EFI_SUCCESS
) {
1179 Status
= refit_call3_wrapper(FallbackHandle
->Read
, FallbackHandle
, &FallbackSize
, FallbackContents
);
1181 if (Status
== EFI_SUCCESS
) {
1182 AreIdentical
= (CompareMem(FileContents
, FallbackContents
, FileSize
) == 0);
1185 MyFreePool(FileContents
);
1186 MyFreePool(FallbackContents
);
1189 // BUG ALERT: Some systems (e.g., DUET, some Macs with large displays) crash if the
1190 // following two calls are reversed. Go figure....
1191 refit_call1_wrapper(FileHandle
->Close
, FallbackHandle
);
1192 refit_call1_wrapper(FileHandle
->Close
, FileHandle
);
1193 return AreIdentical
;
1194 } // BOOLEAN DuplicatesFallback()
1196 // Returns FALSE if two measures of file size are identical for a single file,
1197 // TRUE if not or if the file can't be opened and the other measure is non-0.
1198 // Despite the function's name, this isn't really a direct test of symbolic
1199 // link status, since EFI doesn't officially support symlinks. It does seem
1200 // to be a reliable indicator, though. (OTOH, some disk errors might cause a
1201 // file to fail to open, which would return a false positive -- but as I use
1202 // this function to exclude symbolic links from the list of boot loaders,
1203 // that would be fine, since such boot loaders wouldn't work.)
1204 static BOOLEAN
IsSymbolicLink(REFIT_VOLUME
*Volume
, CHAR16
*Path
, EFI_FILE_INFO
*DirEntry
) {
1205 EFI_FILE_HANDLE FileHandle
;
1206 EFI_FILE_INFO
*FileInfo
= NULL
;
1208 UINTN FileSize2
= 0;
1211 FileName
= StrDuplicate(Path
);
1212 MergeStrings(&FileName
, DirEntry
->FileName
, L
'\\');
1213 CleanUpPathNameSlashes(FileName
);
1215 Status
= refit_call5_wrapper(Volume
->RootDir
->Open
, Volume
->RootDir
, &FileHandle
, FileName
, EFI_FILE_MODE_READ
, 0);
1216 if (Status
== EFI_SUCCESS
) {
1217 FileInfo
= LibFileInfo(FileHandle
);
1218 if (FileInfo
!= NULL
)
1219 FileSize2
= FileInfo
->FileSize
;
1222 MyFreePool(FileName
);
1223 MyFreePool(FileInfo
);
1225 return (DirEntry
->FileSize
!= FileSize2
);
1226 } // BOOLEAN IsSymbolicLink()
1228 // Returns TRUE if a file with the same name as the original but with
1229 // ".efi.signed" is also present in the same directory. Ubuntu is using
1230 // this filename as a signed version of the original unsigned kernel, and
1231 // there's no point in cluttering the display with two kernels that will
1232 // behave identically on non-SB systems, or when one will fail when SB
1234 static BOOLEAN
HasSignedCounterpart(IN REFIT_VOLUME
*Volume
, IN CHAR16
*Path
, IN CHAR16
*Filename
) {
1235 CHAR16
*NewFile
= NULL
;
1236 BOOLEAN retval
= FALSE
;
1238 MergeStrings(&NewFile
, Path
, 0);
1239 MergeStrings(&NewFile
, Filename
, L
'\\');
1240 MergeStrings(&NewFile
, L
".efi.signed", 0);
1241 if (NewFile
!= NULL
) {
1242 CleanUpPathNameSlashes(NewFile
);
1243 if (FileExists(Volume
->RootDir
, NewFile
))
1245 MyFreePool(NewFile
);
1249 } // BOOLEAN HasSignedCounterpart()
1251 // Scan an individual directory for EFI boot loader files and, if found,
1252 // add them to the list. Exception: Ignores FALLBACK_FULLNAME, which is picked
1253 // up in ScanEfiFiles(). Sorts the entries within the loader directory so that
1254 // the most recent one appears first in the list.
1255 // Returns TRUE if a duplicate for FALLBACK_FILENAME was found, FALSE if not.
1256 static BOOLEAN
ScanLoaderDir(IN REFIT_VOLUME
*Volume
, IN CHAR16
*Path
, IN CHAR16
*Pattern
)
1259 REFIT_DIR_ITER DirIter
;
1260 EFI_FILE_INFO
*DirEntry
;
1261 CHAR16 FileName
[256], *Extension
;
1262 struct LOADER_LIST
*LoaderList
= NULL
, *NewLoader
;
1263 BOOLEAN FoundFallbackDuplicate
= FALSE
;
1265 if ((!SelfDirPath
|| !Path
|| ((StriCmp(Path
, SelfDirPath
) == 0) && (Volume
->DeviceHandle
!= SelfVolume
->DeviceHandle
)) ||
1266 (StriCmp(Path
, SelfDirPath
) != 0)) && (ShouldScan(Volume
, Path
))) {
1267 // look through contents of the directory
1268 DirIterOpen(Volume
->RootDir
, Path
, &DirIter
);
1269 while (DirIterNext(&DirIter
, 2, Pattern
, &DirEntry
)) {
1270 Extension
= FindExtension(DirEntry
->FileName
);
1271 if (DirEntry
->FileName
[0] == '.' ||
1272 StriCmp(Extension
, L
".icns") == 0 ||
1273 StriCmp(Extension
, L
".png") == 0 ||
1274 (StriCmp(DirEntry
->FileName
, FALLBACK_BASENAME
) == 0 && (StriCmp(Path
, L
"EFI\\BOOT") == 0)) ||
1275 StriSubCmp(L
"shell", DirEntry
->FileName
) ||
1276 IsSymbolicLink(Volume
, Path
, DirEntry
) || /* is symbolic link */
1277 HasSignedCounterpart(Volume
, Path
, DirEntry
->FileName
) || /* a file with same name plus ".efi.signed" is present */
1278 FilenameIn(Volume
, Path
, DirEntry
->FileName
, GlobalConfig
.DontScanFiles
))
1279 continue; // skip this
1282 SPrint(FileName
, 255, L
"\\%s\\%s", Path
, DirEntry
->FileName
);
1284 SPrint(FileName
, 255, L
"\\%s", DirEntry
->FileName
);
1285 CleanUpPathNameSlashes(FileName
);
1287 if(!IsValidLoader(Volume
->RootDir
, FileName
))
1290 NewLoader
= AllocateZeroPool(sizeof(struct LOADER_LIST
));
1291 if (NewLoader
!= NULL
) {
1292 NewLoader
->FileName
= StrDuplicate(FileName
);
1293 NewLoader
->TimeStamp
= DirEntry
->ModificationTime
;
1294 LoaderList
= AddLoaderListEntry(LoaderList
, NewLoader
);
1295 if (DuplicatesFallback(Volume
, FileName
))
1296 FoundFallbackDuplicate
= TRUE
;
1298 MyFreePool(Extension
);
1301 NewLoader
= LoaderList
;
1302 while (NewLoader
!= NULL
) {
1303 AddLoaderEntry(NewLoader
->FileName
, NULL
, Volume
);
1304 NewLoader
= NewLoader
->NextEntry
;
1307 CleanUpLoaderList(LoaderList
);
1308 Status
= DirIterClose(&DirIter
);
1309 // NOTE: EFI_INVALID_PARAMETER really is an error that should be reported;
1310 // but I've gotten reports from users who are getting this error occasionally
1311 // and I can't find anything wrong or reproduce the problem, so I'm putting
1312 // it down to buggy EFI implementations and ignoring that particular error....
1313 if ((Status
!= EFI_NOT_FOUND
) && (Status
!= EFI_INVALID_PARAMETER
)) {
1315 SPrint(FileName
, 255, L
"while scanning the %s directory", Path
);
1317 StrCpy(FileName
, L
"while scanning the root directory");
1318 CheckError(Status
, FileName
);
1319 } // if (Status != EFI_NOT_FOUND)
1320 } // if not scanning a blacklisted directory
1322 return FoundFallbackDuplicate
;
1323 } /* static VOID ScanLoaderDir() */
1325 static VOID
ScanEfiFiles(REFIT_VOLUME
*Volume
) {
1327 REFIT_DIR_ITER EfiDirIter
;
1328 EFI_FILE_INFO
*EfiDirEntry
;
1329 CHAR16 FileName
[256], *Directory
= NULL
, *MatchPatterns
, *VolName
= NULL
, *SelfPath
;
1331 BOOLEAN ScanFallbackLoader
= TRUE
;
1332 BOOLEAN FoundBRBackup
= FALSE
;
1334 if ((Volume
->RootDir
!= NULL
) && (Volume
->VolName
!= NULL
) && (Volume
->IsReadable
)) {
1335 MatchPatterns
= StrDuplicate(LOADER_MATCH_PATTERNS
);
1336 if (GlobalConfig
.ScanAllLinux
)
1337 MergeStrings(&MatchPatterns
, LINUX_MATCH_PATTERNS
, L
',');
1339 // check for Mac OS X boot loader
1340 if (ShouldScan(Volume
, L
"System\\Library\\CoreServices")) {
1341 StrCpy(FileName
, MACOSX_LOADER_PATH
);
1342 if (FileExists(Volume
->RootDir
, FileName
) && !FilenameIn(Volume
, Directory
, L
"boot.efi", GlobalConfig
.DontScanFiles
)) {
1343 AddLoaderEntry(FileName
, L
"Mac OS X", Volume
);
1344 if (DuplicatesFallback(Volume
, FileName
))
1345 ScanFallbackLoader
= FALSE
;
1349 StrCpy(FileName
, L
"System\\Library\\CoreServices\\xom.efi");
1350 if (FileExists(Volume
->RootDir
, FileName
) && !FilenameIn(Volume
, Directory
, L
"boot.efi", GlobalConfig
.DontScanFiles
)) {
1351 AddLoaderEntry(FileName
, L
"Windows XP (XoM)", Volume
);
1352 if (DuplicatesFallback(Volume
, FileName
))
1353 ScanFallbackLoader
= FALSE
;
1355 } // if should scan Mac directory
1357 // check for Microsoft boot loader/menu
1358 if (ShouldScan(Volume
, L
"EFI\\Microsoft\\Boot")) {
1359 StrCpy(FileName
, L
"EFI\\Microsoft\\Boot\\bkpbootmgfw.efi");
1360 if (FileExists(Volume
->RootDir
, FileName
) && !FilenameIn(Volume
, Directory
, L
"bkpbootmgfw.efi",
1361 GlobalConfig
.DontScanFiles
)) {
1362 AddLoaderEntry(FileName
, L
"Microsoft EFI boot (Boot Repair backup)", Volume
);
1363 FoundBRBackup
= TRUE
;
1364 if (DuplicatesFallback(Volume
, FileName
))
1365 ScanFallbackLoader
= FALSE
;
1367 StrCpy(FileName
, L
"EFI\\Microsoft\\Boot\\bootmgfw.efi");
1368 if (FileExists(Volume
->RootDir
, FileName
) && !FilenameIn(Volume
, Directory
, L
"bootmgfw.efi", GlobalConfig
.DontScanFiles
)) {
1370 AddLoaderEntry(FileName
, L
"Supposed Microsoft EFI boot (probably GRUB)", Volume
);
1372 AddLoaderEntry(FileName
, L
"Microsoft EFI boot", Volume
);
1373 if (DuplicatesFallback(Volume
, FileName
))
1374 ScanFallbackLoader
= FALSE
;
1378 // scan the root directory for EFI executables
1379 if (ScanLoaderDir(Volume
, L
"\\", MatchPatterns
))
1380 ScanFallbackLoader
= FALSE
;
1382 // scan subdirectories of the EFI directory (as per the standard)
1383 DirIterOpen(Volume
->RootDir
, L
"EFI", &EfiDirIter
);
1384 while (DirIterNext(&EfiDirIter
, 1, NULL
, &EfiDirEntry
)) {
1385 if (StriCmp(EfiDirEntry
->FileName
, L
"tools") == 0 || EfiDirEntry
->FileName
[0] == '.')
1386 continue; // skip this, doesn't contain boot loaders or is scanned later
1387 SPrint(FileName
, 255, L
"EFI\\%s", EfiDirEntry
->FileName
);
1388 if (ScanLoaderDir(Volume
, FileName
, MatchPatterns
))
1389 ScanFallbackLoader
= FALSE
;
1391 Status
= DirIterClose(&EfiDirIter
);
1392 if (Status
!= EFI_NOT_FOUND
)
1393 CheckError(Status
, L
"while scanning the EFI directory");
1395 // Scan user-specified (or additional default) directories....
1397 while ((Directory
= FindCommaDelimited(GlobalConfig
.AlsoScan
, i
++)) != NULL
) {
1398 if (ShouldScan(Volume
, Directory
)) {
1399 SplitVolumeAndFilename(&Directory
, &VolName
);
1400 CleanUpPathNameSlashes(Directory
);
1401 Length
= StrLen(Directory
);
1402 if ((Length
> 0) && ScanLoaderDir(Volume
, Directory
, MatchPatterns
))
1403 ScanFallbackLoader
= FALSE
;
1404 MyFreePool(VolName
);
1405 } // if should scan dir
1406 MyFreePool(Directory
);
1409 // Don't scan the fallback loader if it's on the same volume and a duplicate of rEFInd itself....
1410 SelfPath
= DevicePathToStr(SelfLoadedImage
->FilePath
);
1411 CleanUpPathNameSlashes(SelfPath
);
1412 if ((Volume
->DeviceHandle
== SelfLoadedImage
->DeviceHandle
) && DuplicatesFallback(Volume
, SelfPath
))
1413 ScanFallbackLoader
= FALSE
;
1415 // If not a duplicate & if it exists & if it's not us, create an entry
1416 // for the fallback boot loader
1417 if (ScanFallbackLoader
&& FileExists(Volume
->RootDir
, FALLBACK_FULLNAME
) && ShouldScan(Volume
, L
"EFI\\BOOT"))
1418 AddLoaderEntry(FALLBACK_FULLNAME
, L
"Fallback boot loader", Volume
);
1420 } // static VOID ScanEfiFiles()
1422 // Scan internal disks for valid EFI boot loaders....
1423 static VOID
ScanInternal(VOID
) {
1426 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1427 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_INTERNAL
) {
1428 ScanEfiFiles(Volumes
[VolumeIndex
]);
1431 } // static VOID ScanInternal()
1433 // Scan external disks for valid EFI boot loaders....
1434 static VOID
ScanExternal(VOID
) {
1437 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1438 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_EXTERNAL
) {
1439 ScanEfiFiles(Volumes
[VolumeIndex
]);
1442 } // static VOID ScanExternal()
1444 // Scan internal disks for valid EFI boot loaders....
1445 static VOID
ScanOptical(VOID
) {
1448 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1449 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_OPTICAL
) {
1450 ScanEfiFiles(Volumes
[VolumeIndex
]);
1453 } // static VOID ScanOptical()
1456 // legacy boot functions
1459 static EFI_STATUS
ActivateMbrPartition(IN EFI_BLOCK_IO
*BlockIO
, IN UINTN PartitionIndex
)
1462 UINT8 SectorBuffer
[512];
1463 MBR_PARTITION_INFO
*MbrTable
, *EMbrTable
;
1464 UINT32 ExtBase
, ExtCurrent
, NextExtCurrent
;
1465 UINTN LogicalPartitionIndex
= 4;
1467 BOOLEAN HaveBootCode
;
1470 Status
= refit_call5_wrapper(BlockIO
->ReadBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, 0, 512, SectorBuffer
);
1471 if (EFI_ERROR(Status
))
1473 if (*((UINT16
*)(SectorBuffer
+ 510)) != 0xaa55)
1474 return EFI_NOT_FOUND
; // safety measure #1
1476 // add boot code if necessary
1477 HaveBootCode
= FALSE
;
1478 for (i
= 0; i
< MBR_BOOTCODE_SIZE
; i
++) {
1479 if (SectorBuffer
[i
] != 0) {
1480 HaveBootCode
= TRUE
;
1484 if (!HaveBootCode
) {
1485 // no boot code found in the MBR, add the syslinux MBR code
1486 SetMem(SectorBuffer
, MBR_BOOTCODE_SIZE
, 0);
1487 CopyMem(SectorBuffer
, syslinux_mbr
, SYSLINUX_MBR_SIZE
);
1490 // set the partition active
1491 MbrTable
= (MBR_PARTITION_INFO
*)(SectorBuffer
+ 446);
1493 for (i
= 0; i
< 4; i
++) {
1494 if (MbrTable
[i
].Flags
!= 0x00 && MbrTable
[i
].Flags
!= 0x80)
1495 return EFI_NOT_FOUND
; // safety measure #2
1496 if (i
== PartitionIndex
)
1497 MbrTable
[i
].Flags
= 0x80;
1498 else if (PartitionIndex
>= 4 && IS_EXTENDED_PART_TYPE(MbrTable
[i
].Type
)) {
1499 MbrTable
[i
].Flags
= 0x80;
1500 ExtBase
= MbrTable
[i
].StartLBA
;
1502 MbrTable
[i
].Flags
= 0x00;
1506 Status
= refit_call5_wrapper(BlockIO
->WriteBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, 0, 512, SectorBuffer
);
1507 if (EFI_ERROR(Status
))
1510 if (PartitionIndex
>= 4) {
1511 // we have to activate a logical partition, so walk the EMBR chain
1513 // NOTE: ExtBase was set above while looking at the MBR table
1514 for (ExtCurrent
= ExtBase
; ExtCurrent
; ExtCurrent
= NextExtCurrent
) {
1515 // read current EMBR
1516 Status
= refit_call5_wrapper(BlockIO
->ReadBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, ExtCurrent
, 512, SectorBuffer
);
1517 if (EFI_ERROR(Status
))
1519 if (*((UINT16
*)(SectorBuffer
+ 510)) != 0xaa55)
1520 return EFI_NOT_FOUND
; // safety measure #3
1522 // scan EMBR, set appropriate partition active
1523 EMbrTable
= (MBR_PARTITION_INFO
*)(SectorBuffer
+ 446);
1525 for (i
= 0; i
< 4; i
++) {
1526 if (EMbrTable
[i
].Flags
!= 0x00 && EMbrTable
[i
].Flags
!= 0x80)
1527 return EFI_NOT_FOUND
; // safety measure #4
1528 if (EMbrTable
[i
].StartLBA
== 0 || EMbrTable
[i
].Size
== 0)
1530 if (IS_EXTENDED_PART_TYPE(EMbrTable
[i
].Type
)) {
1531 // link to next EMBR
1532 NextExtCurrent
= ExtBase
+ EMbrTable
[i
].StartLBA
;
1533 EMbrTable
[i
].Flags
= (PartitionIndex
>= LogicalPartitionIndex
) ? 0x80 : 0x00;
1536 // logical partition
1537 EMbrTable
[i
].Flags
= (PartitionIndex
== LogicalPartitionIndex
) ? 0x80 : 0x00;
1538 LogicalPartitionIndex
++;
1542 // write current EMBR
1543 Status
= refit_call5_wrapper(BlockIO
->WriteBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, ExtCurrent
, 512, SectorBuffer
);
1544 if (EFI_ERROR(Status
))
1547 if (PartitionIndex
< LogicalPartitionIndex
)
1548 break; // stop the loop, no need to touch further EMBRs
1554 } /* static EFI_STATUS ActivateMbrPartition() */
1556 // early 2006 Core Duo / Core Solo models
1557 static UINT8 LegacyLoaderDevicePath1Data
[] = {
1558 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1559 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1560 0xFF, 0xFF, 0xF9, 0xFF, 0x00, 0x00, 0x00, 0x00,
1561 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1562 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1563 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1565 // mid-2006 Mac Pro (and probably other Core 2 models)
1566 static UINT8 LegacyLoaderDevicePath2Data
[] = {
1567 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1568 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1569 0xFF, 0xFF, 0xF7, 0xFF, 0x00, 0x00, 0x00, 0x00,
1570 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1571 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1572 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1574 // mid-2007 MBP ("Santa Rosa" based models)
1575 static UINT8 LegacyLoaderDevicePath3Data
[] = {
1576 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1577 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1578 0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
1579 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1580 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1581 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1584 static UINT8 LegacyLoaderDevicePath4Data
[] = {
1585 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1586 0x00, 0x00, 0xC0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1587 0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
1588 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1589 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1590 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1592 // late-2008 MB/MBP (NVidia chipset)
1593 static UINT8 LegacyLoaderDevicePath5Data
[] = {
1594 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1595 0x00, 0x40, 0xCB, 0xFF, 0x00, 0x00, 0x00, 0x00,
1596 0xFF, 0xBF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
1597 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1598 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1599 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1602 static EFI_DEVICE_PATH
*LegacyLoaderList
[] = {
1603 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath1Data
,
1604 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath2Data
,
1605 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath3Data
,
1606 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath4Data
,
1607 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath5Data
,
1611 #define MAX_DISCOVERED_PATHS (16)
1613 static VOID
StartLegacy(IN LEGACY_ENTRY
*Entry
, IN CHAR16
*SelectionName
)
1616 EG_IMAGE
*BootLogoImage
;
1617 UINTN ErrorInStep
= 0;
1618 EFI_DEVICE_PATH
*DiscoveredPathList
[MAX_DISCOVERED_PATHS
];
1620 BeginExternalScreen(TRUE
, L
"Booting Legacy OS (Mac mode)");
1622 BootLogoImage
= LoadOSIcon(Entry
->Volume
->OSIconName
, L
"legacy", TRUE
);
1623 if (BootLogoImage
!= NULL
)
1624 BltImageAlpha(BootLogoImage
,
1625 (UGAWidth
- BootLogoImage
->Width
) >> 1,
1626 (UGAHeight
- BootLogoImage
->Height
) >> 1,
1627 &StdBackgroundPixel
);
1629 if (Entry
->Volume
->IsMbrPartition
) {
1630 ActivateMbrPartition(Entry
->Volume
->WholeDiskBlockIO
, Entry
->Volume
->MbrPartitionIndex
);
1633 ExtractLegacyLoaderPaths(DiscoveredPathList
, MAX_DISCOVERED_PATHS
, LegacyLoaderList
);
1635 StoreLoaderName(SelectionName
);
1636 Status
= StartEFIImageList(DiscoveredPathList
, Entry
->LoadOptions
, TYPE_LEGACY
, L
"legacy loader", 0, &ErrorInStep
, TRUE
, FALSE
);
1637 if (Status
== EFI_NOT_FOUND
) {
1638 if (ErrorInStep
== 1) {
1639 Print(L
"\nPlease make sure that you have the latest firmware update installed.\n");
1640 } else if (ErrorInStep
== 3) {
1641 Print(L
"\nThe firmware refused to boot from the selected volume. Note that external\n"
1642 L
"hard drives are not well-supported by Apple's firmware for legacy OS booting.\n");
1645 FinishExternalScreen();
1646 } /* static VOID StartLegacy() */
1648 // Start a device on a non-Mac using the EFI_LEGACY_BIOS_PROTOCOL
1649 static VOID
StartLegacyUEFI(LEGACY_ENTRY
*Entry
, CHAR16
*SelectionName
)
1651 BeginExternalScreen(TRUE
, L
"Booting Legacy OS (UEFI mode)");
1652 StoreLoaderName(SelectionName
);
1654 BdsLibConnectDevicePath (Entry
->BdsOption
->DevicePath
);
1655 BdsLibDoLegacyBoot(Entry
->BdsOption
);
1657 // If we get here, it means that there was a failure....
1658 Print(L
"Failure booting legacy (BIOS) OS.");
1660 FinishExternalScreen();
1661 } // static VOID StartLegacyUEFI()
1663 static LEGACY_ENTRY
* AddLegacyEntry(IN CHAR16
*LoaderTitle
, IN REFIT_VOLUME
*Volume
)
1665 LEGACY_ENTRY
*Entry
, *SubEntry
;
1666 REFIT_MENU_SCREEN
*SubScreen
;
1667 CHAR16
*VolDesc
, *LegacyTitle
;
1668 CHAR16 ShortcutLetter
= 0;
1670 if (LoaderTitle
== NULL
) {
1671 if (Volume
->OSName
!= NULL
) {
1672 LoaderTitle
= Volume
->OSName
;
1673 if (LoaderTitle
[0] == 'W' || LoaderTitle
[0] == 'L')
1674 ShortcutLetter
= LoaderTitle
[0];
1676 LoaderTitle
= L
"Legacy OS";
1678 if (Volume
->VolName
!= NULL
)
1679 VolDesc
= Volume
->VolName
;
1681 VolDesc
= (Volume
->DiskKind
== DISK_KIND_OPTICAL
) ? L
"CD" : L
"HD";
1683 LegacyTitle
= AllocateZeroPool(256 * sizeof(CHAR16
));
1684 if (LegacyTitle
!= NULL
)
1685 SPrint(LegacyTitle
, 255, L
"Boot %s from %s", LoaderTitle
, VolDesc
);
1686 if (IsInSubstring(LegacyTitle
, GlobalConfig
.DontScanVolumes
)) {
1687 MyFreePool(LegacyTitle
);
1691 // prepare the menu entry
1692 Entry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1693 Entry
->me
.Title
= LegacyTitle
;
1694 Entry
->me
.Tag
= TAG_LEGACY
;
1696 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1697 Entry
->me
.Image
= LoadOSIcon(Volume
->OSIconName
, L
"legacy", FALSE
);
1698 Entry
->me
.BadgeImage
= Volume
->VolBadgeImage
;
1699 Entry
->Volume
= Volume
;
1700 Entry
->LoadOptions
= (Volume
->DiskKind
== DISK_KIND_OPTICAL
) ? L
"CD" :
1701 ((Volume
->DiskKind
== DISK_KIND_EXTERNAL
) ? L
"USB" : L
"HD");
1702 Entry
->Enabled
= TRUE
;
1704 // create the submenu
1705 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
1706 SubScreen
->Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1707 SPrint(SubScreen
->Title
, 255, L
"Boot Options for %s on %s", LoaderTitle
, VolDesc
);
1708 SubScreen
->TitleImage
= Entry
->me
.Image
;
1709 SubScreen
->Hint1
= StrDuplicate(SUBSCREEN_HINT1
);
1710 if (GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_EDITOR
) {
1711 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2_NO_EDITOR
);
1713 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2
);
1717 SubEntry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1718 SubEntry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1719 SPrint(SubEntry
->me
.Title
, 255, L
"Boot %s", LoaderTitle
);
1720 SubEntry
->me
.Tag
= TAG_LEGACY
;
1721 SubEntry
->Volume
= Entry
->Volume
;
1722 SubEntry
->LoadOptions
= Entry
->LoadOptions
;
1723 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
1725 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
1726 Entry
->me
.SubScreen
= SubScreen
;
1727 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1729 } /* static LEGACY_ENTRY * AddLegacyEntry() */
1732 // default volume badge icon based on disk kind
1733 static EG_IMAGE
* GetDiskBadge(IN UINTN DiskType
) {
1734 EG_IMAGE
* Badge
= NULL
;
1738 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_INTERNAL
);
1741 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_EXTERNAL
);
1744 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_OPTICAL
);
1748 } // static EG_IMAGE * GetDiskBadge()
1751 Create a rEFInd boot option from a Legacy BIOS protocol option.
1753 static LEGACY_ENTRY
* AddLegacyEntryUEFI(BDS_COMMON_OPTION
*BdsOption
, IN UINT16 DiskType
)
1755 LEGACY_ENTRY
*Entry
, *SubEntry
;
1756 REFIT_MENU_SCREEN
*SubScreen
;
1757 CHAR16 ShortcutLetter
= 0;
1758 CHAR16
*LegacyDescription
= StrDuplicate(BdsOption
->Description
);
1760 if (IsInSubstring(LegacyDescription
, GlobalConfig
.DontScanVolumes
))
1763 // Remove stray spaces, since many EFIs produce descriptions with lots of
1764 // extra spaces, especially at the end; this throws off centering of the
1765 // description on the screen....
1766 LimitStringLength(LegacyDescription
, 100);
1768 // prepare the menu entry
1769 Entry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1770 Entry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1771 SPrint(Entry
->me
.Title
, 255, L
"Boot legacy target %s", LegacyDescription
);
1772 Entry
->me
.Tag
= TAG_LEGACY_UEFI
;
1774 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1775 Entry
->me
.Image
= LoadOSIcon(L
"legacy", L
"legacy", TRUE
);
1776 Entry
->LoadOptions
= (DiskType
== BBS_CDROM
) ? L
"CD" :
1777 ((DiskType
== BBS_USB
) ? L
"USB" : L
"HD");
1778 Entry
->me
.BadgeImage
= GetDiskBadge(DiskType
);
1779 Entry
->BdsOption
= BdsOption
;
1780 Entry
->Enabled
= TRUE
;
1782 // create the submenu
1783 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
1784 SubScreen
->Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1785 SPrint(SubScreen
->Title
, 255, L
"No boot options for legacy target");
1786 SubScreen
->TitleImage
= Entry
->me
.Image
;
1787 SubScreen
->Hint1
= StrDuplicate(SUBSCREEN_HINT1
);
1788 if (GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_EDITOR
) {
1789 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2_NO_EDITOR
);
1791 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2
);
1795 SubEntry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1796 SubEntry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1797 SPrint(SubEntry
->me
.Title
, 255, L
"Boot %s", LegacyDescription
);
1798 SubEntry
->me
.Tag
= TAG_LEGACY_UEFI
;
1799 Entry
->BdsOption
= BdsOption
;
1800 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
1802 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
1803 Entry
->me
.SubScreen
= SubScreen
;
1804 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1806 } /* static LEGACY_ENTRY * AddLegacyEntryUEFI() */
1809 Scan for legacy BIOS targets on machines that implement EFI_LEGACY_BIOS_PROTOCOL.
1810 In testing, protocol has not been implemented on Macs but has been
1811 implemented on several Dell PCs and an ASUS motherboard.
1812 Restricts output to disks of the specified DiskType.
1814 static VOID
ScanLegacyUEFI(IN UINTN DiskType
)
1817 EFI_LEGACY_BIOS_PROTOCOL
*LegacyBios
;
1818 UINT16
*BootOrder
= NULL
;
1820 CHAR16 BootOption
[10];
1821 UINTN BootOrderSize
= 0;
1823 BDS_COMMON_OPTION
*BdsOption
;
1824 LIST_ENTRY TempList
;
1825 BBS_BBS_DEVICE_PATH
*BbsDevicePath
= NULL
;
1826 BOOLEAN SearchingForUsb
= FALSE
;
1828 InitializeListHead (&TempList
);
1829 ZeroMem (Buffer
, sizeof (Buffer
));
1831 // If LegacyBios protocol is not implemented on this platform, then
1832 //we do not support this type of legacy boot on this machine.
1833 Status
= refit_call3_wrapper(gBS
->LocateProtocol
, &gEfiLegacyBootProtocolGuid
, NULL
, (VOID
**) &LegacyBios
);
1834 if (EFI_ERROR (Status
))
1837 // EFI calls USB drives BBS_HARDDRIVE, but we want to distinguish them,
1838 // so we set DiskType inappropriately elsewhere in the program and
1839 // "translate" it here.
1840 if (DiskType
== BBS_USB
) {
1841 DiskType
= BBS_HARDDISK
;
1842 SearchingForUsb
= TRUE
;
1845 // Grab the boot order
1846 BootOrder
= BdsLibGetVariableAndSize(L
"BootOrder", &gEfiGlobalVariableGuid
, &BootOrderSize
);
1847 if (BootOrder
== NULL
) {
1852 while (Index
< BootOrderSize
/ sizeof (UINT16
))
1854 // Grab each boot option variable from the boot order, and convert
1855 // the variable into a BDS boot option
1856 UnicodeSPrint (BootOption
, sizeof (BootOption
), L
"Boot%04x", BootOrder
[Index
]);
1857 BdsOption
= BdsLibVariableToOption (&TempList
, BootOption
);
1859 if (BdsOption
!= NULL
) {
1860 BbsDevicePath
= (BBS_BBS_DEVICE_PATH
*)BdsOption
->DevicePath
;
1861 // Only add the entry if it is of a requested type (e.g. USB, HD)
1862 // Two checks necessary because some systems return EFI boot loaders
1863 // with a DeviceType value that would inappropriately include them
1864 // as legacy loaders....
1865 if ((BbsDevicePath
->DeviceType
== DiskType
) && (BdsOption
->DevicePath
->Type
== DEVICE_TYPE_BIOS
)) {
1866 // USB flash drives appear as hard disks with certain media flags set.
1867 // Look for this, and if present, pass it on with the (technically
1868 // incorrect, but internally useful) BBS_TYPE_USB flag set.
1869 if (DiskType
== BBS_HARDDISK
) {
1870 if (SearchingForUsb
&& (BbsDevicePath
->StatusFlag
& (BBS_MEDIA_PRESENT
| BBS_MEDIA_MAYBE_PRESENT
))) {
1871 AddLegacyEntryUEFI(BdsOption
, BBS_USB
);
1872 } else if (!SearchingForUsb
&& !(BbsDevicePath
->StatusFlag
& (BBS_MEDIA_PRESENT
| BBS_MEDIA_MAYBE_PRESENT
))) {
1873 AddLegacyEntryUEFI(BdsOption
, DiskType
);
1876 AddLegacyEntryUEFI(BdsOption
, DiskType
);
1879 } // if (BdsOption != NULL)
1882 } /* static VOID ScanLegacyUEFI() */
1884 static VOID
ScanLegacyVolume(REFIT_VOLUME
*Volume
, UINTN VolumeIndex
) {
1886 BOOLEAN ShowVolume
, HideIfOthersFound
;
1889 HideIfOthersFound
= FALSE
;
1890 if (Volume
->IsAppleLegacy
) {
1892 HideIfOthersFound
= TRUE
;
1893 } else if (Volume
->HasBootCode
) {
1895 if (Volume
->BlockIO
== Volume
->WholeDiskBlockIO
&&
1896 Volume
->BlockIOOffset
== 0 &&
1897 Volume
->OSName
== NULL
)
1898 // this is a whole disk (MBR) entry; hide if we have entries for partitions
1899 HideIfOthersFound
= TRUE
;
1901 if (HideIfOthersFound
) {
1902 // check for other bootable entries on the same disk
1903 for (VolumeIndex2
= 0; VolumeIndex2
< VolumesCount
; VolumeIndex2
++) {
1904 if (VolumeIndex2
!= VolumeIndex
&& Volumes
[VolumeIndex2
]->HasBootCode
&&
1905 Volumes
[VolumeIndex2
]->WholeDiskBlockIO
== Volume
->WholeDiskBlockIO
)
1911 AddLegacyEntry(NULL
, Volume
);
1912 } // static VOID ScanLegacyVolume()
1914 // Scan attached optical discs for legacy (BIOS) boot code
1915 // and add anything found to the list....
1916 static VOID
ScanLegacyDisc(VOID
)
1919 REFIT_VOLUME
*Volume
;
1921 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
) {
1922 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1923 Volume
= Volumes
[VolumeIndex
];
1924 if (Volume
->DiskKind
== DISK_KIND_OPTICAL
)
1925 ScanLegacyVolume(Volume
, VolumeIndex
);
1927 } else if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
1928 ScanLegacyUEFI(BBS_CDROM
);
1930 } /* static VOID ScanLegacyDisc() */
1932 // Scan internal hard disks for legacy (BIOS) boot code
1933 // and add anything found to the list....
1934 static VOID
ScanLegacyInternal(VOID
)
1937 REFIT_VOLUME
*Volume
;
1939 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
) {
1940 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1941 Volume
= Volumes
[VolumeIndex
];
1942 if (Volume
->DiskKind
== DISK_KIND_INTERNAL
)
1943 ScanLegacyVolume(Volume
, VolumeIndex
);
1945 } else if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
1946 // TODO: This actually picks up USB flash drives, too; try to find
1947 // a way to differentiate the two....
1948 ScanLegacyUEFI(BBS_HARDDISK
);
1950 } /* static VOID ScanLegacyInternal() */
1952 // Scan external disks for legacy (BIOS) boot code
1953 // and add anything found to the list....
1954 static VOID
ScanLegacyExternal(VOID
)
1957 REFIT_VOLUME
*Volume
;
1959 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
) {
1960 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1961 Volume
= Volumes
[VolumeIndex
];
1962 if (Volume
->DiskKind
== DISK_KIND_EXTERNAL
)
1963 ScanLegacyVolume(Volume
, VolumeIndex
);
1965 } else if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
1966 // TODO: This actually doesn't do anything useful; leaving in hopes of
1967 // fixing it later....
1968 ScanLegacyUEFI(BBS_USB
);
1970 } /* static VOID ScanLegacyExternal() */
1973 // pre-boot tool functions
1976 static VOID
StartTool(IN LOADER_ENTRY
*Entry
)
1978 BeginExternalScreen(Entry
->UseGraphicsMode
, Entry
->me
.Title
+ 6); // assumes "Start <title>" as assigned below
1979 StoreLoaderName(Entry
->me
.Title
);
1980 StartEFIImage(Entry
->DevicePath
, Entry
->LoadOptions
, TYPE_EFI
,
1981 Basename(Entry
->LoaderPath
), Entry
->OSType
, NULL
, TRUE
, FALSE
);
1982 FinishExternalScreen();
1983 } /* static VOID StartTool() */
1985 static LOADER_ENTRY
* AddToolEntry(EFI_HANDLE DeviceHandle
, IN CHAR16
*LoaderPath
, IN CHAR16
*LoaderTitle
, IN EG_IMAGE
*Image
,
1986 IN CHAR16 ShortcutLetter
, IN BOOLEAN UseGraphicsMode
)
1988 LOADER_ENTRY
*Entry
;
1989 CHAR16
*TitleStr
= NULL
;
1991 Entry
= AllocateZeroPool(sizeof(LOADER_ENTRY
));
1993 TitleStr
= PoolPrint(L
"Start %s", LoaderTitle
);
1994 Entry
->me
.Title
= TitleStr
;
1995 Entry
->me
.Tag
= TAG_TOOL
;
1997 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1998 Entry
->me
.Image
= Image
;
1999 Entry
->LoaderPath
= (LoaderPath
) ? StrDuplicate(LoaderPath
) : NULL
;
2000 Entry
->DevicePath
= FileDevicePath(DeviceHandle
, Entry
->LoaderPath
);
2001 Entry
->UseGraphicsMode
= UseGraphicsMode
;
2003 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
2005 } /* static LOADER_ENTRY * AddToolEntry() */
2008 // pre-boot driver functions
2011 static UINTN
ScanDriverDir(IN CHAR16
*Path
)
2014 REFIT_DIR_ITER DirIter
;
2016 EFI_FILE_INFO
*DirEntry
;
2017 CHAR16 FileName
[256];
2019 CleanUpPathNameSlashes(Path
);
2020 // look through contents of the directory
2021 DirIterOpen(SelfRootDir
, Path
, &DirIter
);
2022 while (DirIterNext(&DirIter
, 2, LOADER_MATCH_PATTERNS
, &DirEntry
)) {
2023 if (DirEntry
->FileName
[0] == '.')
2024 continue; // skip this
2026 SPrint(FileName
, 255, L
"%s\\%s", Path
, DirEntry
->FileName
);
2028 Status
= StartEFIImage(FileDevicePath(SelfLoadedImage
->DeviceHandle
, FileName
),
2029 L
"", TYPE_EFI
, DirEntry
->FileName
, 0, NULL
, FALSE
, TRUE
);
2031 Status
= DirIterClose(&DirIter
);
2032 if (Status
!= EFI_NOT_FOUND
) {
2033 SPrint(FileName
, 255, L
"while scanning the %s directory", Path
);
2034 CheckError(Status
, FileName
);
2039 #ifdef __MAKEWITH_GNUEFI
2040 static EFI_STATUS
ConnectAllDriversToAllControllers(VOID
)
2043 UINTN AllHandleCount
;
2044 EFI_HANDLE
*AllHandleBuffer
;
2047 EFI_HANDLE
*HandleBuffer
;
2053 Status
= LibLocateHandle(AllHandles
,
2058 if (EFI_ERROR(Status
))
2061 for (Index
= 0; Index
< AllHandleCount
; Index
++) {
2063 // Scan the handle database
2065 Status
= LibScanHandleDatabase(NULL
,
2067 AllHandleBuffer
[Index
],
2072 if (EFI_ERROR (Status
))
2076 if (HandleType
[Index
] & EFI_HANDLE_TYPE_DRIVER_BINDING_HANDLE
)
2078 if (HandleType
[Index
] & EFI_HANDLE_TYPE_IMAGE_HANDLE
)
2083 for (HandleIndex
= 0; HandleIndex
< HandleCount
; HandleIndex
++) {
2084 if (HandleType
[HandleIndex
] & EFI_HANDLE_TYPE_PARENT_HANDLE
)
2089 if (HandleType
[Index
] & EFI_HANDLE_TYPE_DEVICE_HANDLE
) {
2090 Status
= refit_call4_wrapper(BS
->ConnectController
,
2091 AllHandleBuffer
[Index
],
2099 MyFreePool (HandleBuffer
);
2100 MyFreePool (HandleType
);
2104 MyFreePool (AllHandleBuffer
);
2106 } /* EFI_STATUS ConnectAllDriversToAllControllers() */
2108 static EFI_STATUS
ConnectAllDriversToAllControllers(VOID
) {
2109 BdsLibConnectAllDriversToAllControllers();
2114 // Load all EFI drivers from rEFInd's "drivers" subdirectory and from the
2115 // directories specified by the user in the "scan_driver_dirs" configuration
2117 static VOID
LoadDrivers(VOID
)
2119 CHAR16
*Directory
, *SelfDirectory
;
2120 UINTN i
= 0, Length
, NumFound
= 0;
2122 // load drivers from the subdirectories of rEFInd's home directory specified
2123 // in the DRIVER_DIRS constant.
2124 while ((Directory
= FindCommaDelimited(DRIVER_DIRS
, i
++)) != NULL
) {
2125 SelfDirectory
= SelfDirPath
? StrDuplicate(SelfDirPath
) : NULL
;
2126 CleanUpPathNameSlashes(SelfDirectory
);
2127 MergeStrings(&SelfDirectory
, Directory
, L
'\\');
2128 NumFound
+= ScanDriverDir(SelfDirectory
);
2129 MyFreePool(Directory
);
2130 MyFreePool(SelfDirectory
);
2133 // Scan additional user-specified driver directories....
2135 while ((Directory
= FindCommaDelimited(GlobalConfig
.DriverDirs
, i
++)) != NULL
) {
2136 CleanUpPathNameSlashes(Directory
);
2137 Length
= StrLen(Directory
);
2139 NumFound
+= ScanDriverDir(Directory
);
2141 MyFreePool(Directory
);
2144 // connect all devices
2146 ConnectAllDriversToAllControllers();
2147 } /* static VOID LoadDrivers() */
2149 // Determine what (if any) type of legacy (BIOS) boot support is available
2150 static VOID
FindLegacyBootType(VOID
) {
2152 EFI_LEGACY_BIOS_PROTOCOL
*LegacyBios
;
2154 GlobalConfig
.LegacyType
= LEGACY_TYPE_NONE
;
2156 // UEFI-style legacy BIOS support is available only with some EFI implementations....
2157 Status
= refit_call3_wrapper(gBS
->LocateProtocol
, &gEfiLegacyBootProtocolGuid
, NULL
, (VOID
**) &LegacyBios
);
2158 if (!EFI_ERROR (Status
))
2159 GlobalConfig
.LegacyType
= LEGACY_TYPE_UEFI
;
2161 // Macs have their own system. If the firmware vendor code contains the
2162 // string "Apple", assume it's available. Note that this overrides the
2163 // UEFI type, and might yield false positives if the vendor string
2164 // contains "Apple" as part of something bigger, so this isn't 100%
2166 if (StriSubCmp(L
"Apple", ST
->FirmwareVendor
))
2167 GlobalConfig
.LegacyType
= LEGACY_TYPE_MAC
;
2168 } // static VOID FindLegacyBootType
2170 // Warn the user if legacy OS scans are enabled but the firmware can't support them....
2171 static VOID
WarnIfLegacyProblems(VOID
) {
2172 BOOLEAN found
= FALSE
;
2175 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_NONE
) {
2177 if (GlobalConfig
.ScanFor
[i
] == 'h' || GlobalConfig
.ScanFor
[i
] == 'b' || GlobalConfig
.ScanFor
[i
] == 'c' ||
2178 GlobalConfig
.ScanFor
[i
] == 'H' || GlobalConfig
.ScanFor
[i
] == 'B' || GlobalConfig
.ScanFor
[i
] == 'C')
2181 } while ((i
< NUM_SCAN_OPTIONS
) && (!found
));
2184 Print(L
"NOTE: refind.conf's 'scanfor' line specifies scanning for one or more legacy\n");
2185 Print(L
"(BIOS) boot options; however, this is not possible because your computer lacks\n");
2186 Print(L
"the necessary Compatibility Support Module (CSM) support or that support is\n");
2187 Print(L
"disabled in your firmware.\n");
2190 } // if no legacy support
2191 } // static VOID WarnIfLegacyProblems()
2193 // Locates boot loaders. NOTE: This assumes that GlobalConfig.LegacyType is set correctly.
2194 static VOID
ScanForBootloaders(VOID
) {
2197 BOOLEAN ScanForLegacy
= FALSE
;
2199 // Determine up-front if we'll be scanning for legacy loaders....
2200 for (i
= 0; i
< NUM_SCAN_OPTIONS
; i
++) {
2201 s
= GlobalConfig
.ScanFor
[i
];
2202 if ((s
== 'c') || (s
== 'C') || (s
== 'h') || (s
== 'H') || (s
== 'b') || (s
== 'B'))
2203 ScanForLegacy
= TRUE
;
2206 // If UEFI & scanning for legacy loaders & deep legacy scan, update NVRAM boot manager list
2207 if ((GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) && ScanForLegacy
&& GlobalConfig
.DeepLegacyScan
) {
2208 BdsDeleteAllInvalidLegacyBootOptions();
2209 BdsAddNonExistingLegacyBootOptions();
2212 // scan for loaders and tools, add them to the menu
2213 for (i
= 0; i
< NUM_SCAN_OPTIONS
; i
++) {
2214 switch(GlobalConfig
.ScanFor
[i
]) {
2219 ScanLegacyInternal();
2222 ScanLegacyExternal();
2225 ScanUserConfigured(GlobalConfig
.ConfigFilename
);
2239 // assign shortcut keys
2240 for (i
= 0; i
< MainMenu
.EntryCount
&& MainMenu
.Entries
[i
]->Row
== 0 && i
< 9; i
++)
2241 MainMenu
.Entries
[i
]->ShortcutDigit
= (CHAR16
)('1' + i
);
2243 // wait for user ACK when there were errors
2244 FinishTextScreen(FALSE
);
2245 } // static VOID ScanForBootloaders()
2247 // Locate a single tool from the specified Locations using one of the
2248 // specified Names and add it to the menu.
2249 static VOID
FindTool(CHAR16
*Locations
, CHAR16
*Names
, CHAR16
*Description
, UINTN Icon
) {
2250 UINTN j
= 0, k
, VolumeIndex
;
2251 CHAR16
*DirName
, *FileName
, *PathName
, FullDescription
[256];
2253 while ((DirName
= FindCommaDelimited(Locations
, j
++)) != NULL
) {
2255 while ((FileName
= FindCommaDelimited(Names
, k
++)) != NULL
) {
2256 PathName
= StrDuplicate(DirName
);
2257 MergeStrings(&PathName
, FileName
, (StriCmp(PathName
, L
"\\") == 0) ? 0 : L
'\\');
2258 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
2259 if ((Volumes
[VolumeIndex
]->RootDir
!= NULL
) && (FileExists(Volumes
[VolumeIndex
]->RootDir
, PathName
))) {
2260 SPrint(FullDescription
, 255, L
"%s at %s on %s", Description
, PathName
, Volumes
[VolumeIndex
]->VolName
);
2261 AddToolEntry(Volumes
[VolumeIndex
]->DeviceHandle
, PathName
, FullDescription
, BuiltinIcon(Icon
), 'S', FALSE
);
2264 MyFreePool(PathName
);
2265 MyFreePool(FileName
);
2267 MyFreePool(DirName
);
2268 } // while Locations
2269 } // VOID FindTool()
2271 // Add the second-row tags containing built-in and external tools (EFI shell,
2273 static VOID
ScanForTools(VOID
) {
2274 CHAR16
*FileName
= NULL
, *VolName
= NULL
, *MokLocations
, Description
[256];
2275 REFIT_MENU_ENTRY
*TempMenuEntry
;
2276 UINTN i
, j
, VolumeIndex
;
2280 MokLocations
= StrDuplicate(MOK_LOCATIONS
);
2281 if (MokLocations
!= NULL
)
2282 MergeStrings(&MokLocations
, SelfDirPath
, L
',');
2284 for (i
= 0; i
< NUM_TOOLS
; i
++) {
2285 switch(GlobalConfig
.ShowTools
[i
]) {
2286 // NOTE: Be sure that FileName is NULL at the end of each case.
2288 TempMenuEntry
= CopyMenuEntry(&MenuEntryShutdown
);
2289 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_SHUTDOWN
);
2290 AddMenuEntry(&MainMenu
, TempMenuEntry
);
2294 TempMenuEntry
= CopyMenuEntry(&MenuEntryReset
);
2295 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_RESET
);
2296 AddMenuEntry(&MainMenu
, TempMenuEntry
);
2300 TempMenuEntry
= CopyMenuEntry(&MenuEntryAbout
);
2301 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT
);
2302 AddMenuEntry(&MainMenu
, TempMenuEntry
);
2306 TempMenuEntry
= CopyMenuEntry(&MenuEntryExit
);
2307 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_EXIT
);
2308 AddMenuEntry(&MainMenu
, TempMenuEntry
);
2312 if (EfivarGetRaw(&GlobalGuid
, L
"OsIndicationsSupported", &b
, &j
) == EFI_SUCCESS
) {
2314 if (osind
& EFI_OS_INDICATIONS_BOOT_TO_FW_UI
) {
2315 TempMenuEntry
= CopyMenuEntry(&MenuEntryFirmware
);
2316 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_FIRMWARE
);
2317 AddMenuEntry(&MainMenu
, TempMenuEntry
);
2324 while ((FileName
= FindCommaDelimited(SHELL_NAMES
, j
++)) != NULL
) {
2325 if (FileExists(SelfRootDir
, FileName
)) {
2326 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, L
"EFI Shell", BuiltinIcon(BUILTIN_ICON_TOOL_SHELL
),
2329 MyFreePool(FileName
);
2335 while ((FileName
= FindCommaDelimited(GPTSYNC_NAMES
, j
++)) != NULL
) {
2336 if (FileExists(SelfRootDir
, FileName
)) {
2337 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, L
"Hybrid MBR tool", BuiltinIcon(BUILTIN_ICON_TOOL_PART
),
2340 MyFreePool(FileName
);
2347 while ((FileName
= FindCommaDelimited(GDISK_NAMES
, j
++)) != NULL
) {
2348 if (FileExists(SelfRootDir
, FileName
)) {
2349 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, L
"disk partitioning tool",
2350 BuiltinIcon(BUILTIN_ICON_TOOL_PART
), 'G', FALSE
);
2352 MyFreePool(FileName
);
2357 case TAG_APPLE_RECOVERY
:
2358 FileName
= StrDuplicate(L
"\\com.apple.recovery.boot\\boot.efi");
2359 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
2360 if ((Volumes
[VolumeIndex
]->RootDir
!= NULL
) && (FileExists(Volumes
[VolumeIndex
]->RootDir
, FileName
))) {
2361 SPrint(Description
, 255, L
"Apple Recovery on %s", Volumes
[VolumeIndex
]->VolName
);
2362 AddToolEntry(Volumes
[VolumeIndex
]->DeviceHandle
, FileName
, Description
,
2363 BuiltinIcon(BUILTIN_ICON_TOOL_APPLE_RESCUE
), 'R', TRUE
);
2366 MyFreePool(FileName
);
2370 case TAG_WINDOWS_RECOVERY
:
2372 while ((FileName
= FindCommaDelimited(GlobalConfig
.WindowsRecoveryFiles
, j
++)) != NULL
) {
2373 SplitVolumeAndFilename(&FileName
, &VolName
);
2374 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
2375 if ((Volumes
[VolumeIndex
]->RootDir
!= NULL
) && (FileExists(Volumes
[VolumeIndex
]->RootDir
, FileName
)) &&
2376 ((VolName
== NULL
) || (StriCmp(VolName
, Volumes
[VolumeIndex
]->VolName
) == 0))) {
2377 SPrint(Description
, 255, L
"Microsoft Recovery on %s", Volumes
[VolumeIndex
]->VolName
);
2378 AddToolEntry(Volumes
[VolumeIndex
]->DeviceHandle
, FileName
, Description
,
2379 BuiltinIcon(BUILTIN_ICON_TOOL_WINDOWS_RESCUE
), 'R', TRUE
);
2383 MyFreePool(FileName
);
2385 MyFreePool(VolName
);
2390 FindTool(MokLocations
, MOK_NAMES
, L
"MOK utility", BUILTIN_ICON_TOOL_MOK_TOOL
);
2394 FindTool(MEMTEST_LOCATIONS
, MEMTEST_NAMES
, L
"Memory test utility", BUILTIN_ICON_TOOL_MEMTEST
);
2399 } // static VOID ScanForTools
2401 // Rescan for boot loaders
2402 static VOID
RescanAll(BOOLEAN DisplayMessage
) {
2410 egDisplayMessage(L
"Scanning for new boot loaders; please wait....", &BGColor
);
2411 FreeList((VOID
***) &(MainMenu
.Entries
), &MainMenu
.EntryCount
);
2412 MainMenu
.Entries
= NULL
;
2413 MainMenu
.EntryCount
= 0;
2414 ReadConfig(GlobalConfig
.ConfigFilename
);
2415 ConnectAllDriversToAllControllers();
2417 ScanForBootloaders();
2420 } // VOID RescanAll()
2422 #ifdef __MAKEWITH_TIANO
2424 // Minimal initialization function
2425 static VOID
InitializeLib(IN EFI_HANDLE ImageHandle
, IN EFI_SYSTEM_TABLE
*SystemTable
) {
2427 // gImageHandle = ImageHandle;
2428 gBS
= SystemTable
->BootServices
;
2429 // gRS = SystemTable->RuntimeServices;
2430 gRT
= SystemTable
->RuntimeServices
; // Some BDS functions need gRT to be set
2431 EfiGetSystemConfigurationTable (&gEfiDxeServicesTableGuid
, (VOID
**) &gDS
);
2433 // InitializeConsoleSim();
2438 // Set up our own Secure Boot extensions....
2439 // Returns TRUE on success, FALSE otherwise
2440 static BOOLEAN
SecureBootSetup(VOID
) {
2442 BOOLEAN Success
= FALSE
;
2444 if (secure_mode() && ShimLoaded()) {
2445 Status
= security_policy_install();
2446 if (Status
== EFI_SUCCESS
) {
2449 Print(L
"Failed to install MOK Secure Boot extensions");
2453 } // VOID SecureBootSetup()
2455 // Remove our own Secure Boot extensions....
2456 // Returns TRUE on success, FALSE otherwise
2457 static BOOLEAN
SecureBootUninstall(VOID
) {
2459 BOOLEAN Success
= TRUE
;
2461 if (secure_mode()) {
2462 Status
= security_policy_uninstall();
2463 if (Status
!= EFI_SUCCESS
) {
2465 BeginTextScreen(L
"Secure Boot Policy Failure");
2466 Print(L
"Failed to uninstall MOK Secure Boot extensions; forcing a reboot.");
2468 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);
2472 } // VOID SecureBootUninstall
2474 // Sets the global configuration filename; will be CONFIG_FILE_NAME unless the
2475 // "-c" command-line option is set, in which case that takes precedence.
2476 // If an error is encountered, leaves the value alone (it should be set to
2477 // CONFIG_FILE_NAME when GlobalConfig is initialized).
2478 static VOID
SetConfigFilename(EFI_HANDLE ImageHandle
) {
2479 EFI_LOADED_IMAGE
*Info
;
2480 CHAR16
*Options
, *FileName
;
2484 Status
= refit_call3_wrapper(BS
->HandleProtocol
, ImageHandle
, &LoadedImageProtocol
, (VOID
**) &Info
);
2485 if ((Status
== EFI_SUCCESS
) && (Info
->LoadOptionsSize
> 0)) {
2486 Options
= (CHAR16
*) Info
->LoadOptions
;
2487 Where
= FindSubString(L
" -c ", Options
);
2489 FileName
= StrDuplicate(&Options
[Where
+ 4]);
2490 Where
= FindSubString(L
" ", FileName
);
2492 FileName
[Where
] = L
'\0';
2494 if (FileExists(SelfDir
, FileName
)) {
2495 GlobalConfig
.ConfigFilename
= FileName
;
2497 Print(L
"Specified configuration file (%s) doesn't exist; using\n'refind.conf' default\n", FileName
);
2498 MyFreePool(FileName
);
2502 } // VOID SetConfigFilename()
2509 efi_main (EFI_HANDLE ImageHandle
, EFI_SYSTEM_TABLE
*SystemTable
)
2512 BOOLEAN MainLoopRunning
= TRUE
;
2513 BOOLEAN MokProtocol
;
2514 REFIT_MENU_ENTRY
*ChosenEntry
;
2516 CHAR16
*SelectionName
= NULL
;
2520 InitializeLib(ImageHandle
, SystemTable
);
2521 Status
= InitRefitLib(ImageHandle
);
2522 if (EFI_ERROR(Status
))
2525 // read configuration
2526 CopyMem(GlobalConfig
.ScanFor
, "ieom ", NUM_SCAN_OPTIONS
);
2527 FindLegacyBootType();
2528 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
)
2529 CopyMem(GlobalConfig
.ScanFor
, "ihebocm ", NUM_SCAN_OPTIONS
);
2530 SetConfigFilename(ImageHandle
);
2531 ReadConfig(GlobalConfig
.ConfigFilename
);
2534 WarnIfLegacyProblems();
2535 MainMenu
.TimeoutSeconds
= GlobalConfig
.Timeout
;
2537 // disable EFI watchdog timer
2538 refit_call4_wrapper(BS
->SetWatchdogTimer
, 0x0000, 0x0000, 0x0000, NULL
);
2540 // further bootstrap (now with config available)
2541 MokProtocol
= SecureBootSetup();
2544 ScanForBootloaders();
2548 if (GlobalConfig
.ScanDelay
> 0) {
2553 if (GlobalConfig
.ScanDelay
> 1)
2554 egDisplayMessage(L
"Pausing before disk scan; please wait....", &BGColor
);
2555 for (i
= 0; i
< GlobalConfig
.ScanDelay
; i
++)
2556 refit_call1_wrapper(BS
->Stall
, 1000000);
2557 RescanAll(GlobalConfig
.ScanDelay
> 1);
2560 if (GlobalConfig
.DefaultSelection
)
2561 SelectionName
= StrDuplicate(GlobalConfig
.DefaultSelection
);
2563 while (MainLoopRunning
) {
2564 MenuExit
= RunMainMenu(&MainMenu
, &SelectionName
, &ChosenEntry
);
2566 // The Escape key triggers a re-scan operation....
2567 if (MenuExit
== MENU_EXIT_ESCAPE
) {
2573 switch (ChosenEntry
->Tag
) {
2575 case TAG_REBOOT
: // Reboot
2577 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);
2578 MainLoopRunning
= FALSE
; // just in case we get this far
2581 case TAG_SHUTDOWN
: // Shut Down
2583 refit_call4_wrapper(RT
->ResetSystem
, EfiResetShutdown
, EFI_SUCCESS
, 0, NULL
);
2584 MainLoopRunning
= FALSE
; // just in case we get this far
2587 case TAG_ABOUT
: // About rEFInd
2591 case TAG_LOADER
: // Boot OS via .EFI loader
2592 StartLoader((LOADER_ENTRY
*)ChosenEntry
, SelectionName
);
2595 case TAG_LEGACY
: // Boot legacy OS
2596 StartLegacy((LEGACY_ENTRY
*)ChosenEntry
, SelectionName
);
2599 case TAG_LEGACY_UEFI
: // Boot a legacy OS on a non-Mac
2600 StartLegacyUEFI((LEGACY_ENTRY
*)ChosenEntry
, SelectionName
);
2603 case TAG_TOOL
: // Start a EFI tool
2604 StartTool((LOADER_ENTRY
*)ChosenEntry
);
2607 case TAG_EXIT
: // Terminate rEFInd
2608 if ((MokProtocol
) && !SecureBootUninstall()) {
2609 MainLoopRunning
= FALSE
; // just in case we get this far
2611 BeginTextScreen(L
" ");
2616 case TAG_FIRMWARE
: // Reboot into firmware's user interface
2617 RebootIntoFirmware();
2623 // If we end up here, things have gone wrong. Try to reboot, and if that
2624 // fails, go into an endless loop.
2625 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);