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_DIR L"System\\Library\\CoreServices"
80 #define MACOSX_LOADER_PATH ( MACOSX_LOADER_DIR L"\\boot.efi" )
82 #define SHELL_NAMES L"\\EFI\\tools\\shell.efi,\\EFI\\tools\\shellx64.efi,\\shell.efi,\\shellx64.efi"
83 #define GPTSYNC_NAMES L"\\EFI\\tools\\gptsync.efi,\\EFI\\tools\\gptsync_x64.efi"
84 #define GDISK_NAMES L"\\EFI\\tools\\gdisk.efi,\\EFI\\tools\\gdisk_x64.efi"
85 #define NETBOOT_NAMES L"\\EFI\\tools\\ipxe.efi"
86 #define MEMTEST_NAMES L"memtest86.efi,memtest86_x64.efi,memtest86x64.efi,bootx64.efi"
87 #define DRIVER_DIRS L"drivers,drivers_x64"
88 #define FALLBACK_FULLNAME L"EFI\\BOOT\\bootx64.efi"
89 #define FALLBACK_BASENAME L"bootx64.efi"
90 #define EFI_STUB_ARCH 0x8664
92 #define SHELL_NAMES L"\\EFI\\tools\\shell.efi,\\EFI\\tools\\shellia32.efi,\\shell.efi,\\shellia32.efi"
93 #define GPTSYNC_NAMES L"\\EFI\\tools\\gptsync.efi,\\EFI\\tools\\gptsync_ia32.efi"
94 #define GDISK_NAMES L"\\EFI\\tools\\gdisk.efi,\\EFI\\tools\\gdisk_ia32.efi"
95 #define NETBOOT_NAMES L"\\EFI\\tools\\ipxe.efi"
96 #define MEMTEST_NAMES L"memtest86.efi,memtest86_ia32.efi,memtest86ia32.efi,bootia32.efi"
97 #define DRIVER_DIRS L"drivers,drivers_ia32"
98 #define FALLBACK_FULLNAME L"EFI\\BOOT\\bootia32.efi"
99 #define FALLBACK_BASENAME L"bootia32.efi"
100 #define EFI_STUB_ARCH 0x014c
102 #define SHELL_NAMES L"\\EFI\\tools\\shell.efi,\\shell.efi"
103 #define GPTSYNC_NAMES L"\\EFI\\tools\\gptsync.efi"
104 #define GDISK_NAMES L"\\EFI\\tools\\gdisk.efi"
105 #define NETBOOT_NAMES L"\\EFI\\tools\\ipxe.efi"
106 #define MEMTEST_NAMES L"memtest86.efi"
107 #define DRIVER_DIRS L"drivers"
108 #define FALLBACK_FULLNAME L"EFI\\BOOT\\boot.efi" /* Not really correct */
109 #define FALLBACK_BASENAME L"boot.efi" /* Not really correct */
111 #define FAT_ARCH 0x0ef1fab9 /* ID for Apple "fat" binary */
113 #define IPXE_DISCOVER_NAME L"\\efi\\tools\\ipxe_discover.efi"
114 #define IPXE_NAME L"\\efi\\tools\\ipxe.efi"
116 // Filename patterns that identify EFI boot loaders. Note that a single case (either L"*.efi" or
117 // L"*.EFI") is fine for most systems; but Gigabyte's buggy Hybrid EFI does a case-sensitive
118 // comparison when it should do a case-insensitive comparison, so I'm doubling this up. It does
119 // no harm on other computers, AFAIK. In theory, every case variation should be done for
120 // completeness, but that's ridiculous....
121 #define LOADER_MATCH_PATTERNS L"*.efi,*.EFI"
123 // Patterns that identify Linux kernels. Added to the loader match pattern when the
124 // scan_all_linux_kernels option is set in the configuration file. Causes kernels WITHOUT
125 // a ".efi" extension to be found when scanning for boot loaders.
126 #define LINUX_MATCH_PATTERNS L"vmlinuz*,bzImage*"
128 // Default hint text for program-launch submenus
129 #define SUBSCREEN_HINT1 L"Use arrow keys to move cursor; Enter to boot;"
130 #define SUBSCREEN_HINT2 L"Insert or F2 to edit options; Esc to return to main menu"
131 #define SUBSCREEN_HINT2_NO_EDITOR L"Esc to return to main menu"
135 #define TYPE_LEGACY 2
137 static REFIT_MENU_ENTRY MenuEntryAbout
= { L
"About rEFInd", TAG_ABOUT
, 1, 0, 'A', NULL
, NULL
, NULL
};
138 static REFIT_MENU_ENTRY MenuEntryReset
= { L
"Reboot Computer", TAG_REBOOT
, 1, 0, 'R', NULL
, NULL
, NULL
};
139 static REFIT_MENU_ENTRY MenuEntryShutdown
= { L
"Shut Down Computer", TAG_SHUTDOWN
, 1, 0, 'U', NULL
, NULL
, NULL
};
140 static REFIT_MENU_ENTRY MenuEntryReturn
= { L
"Return to Main Menu", TAG_RETURN
, 1, 0, 0, NULL
, NULL
, NULL
};
141 static REFIT_MENU_ENTRY MenuEntryExit
= { L
"Exit rEFInd", TAG_EXIT
, 1, 0, 0, NULL
, NULL
, NULL
};
142 static REFIT_MENU_ENTRY MenuEntryFirmware
= { L
"Reboot to Computer Setup Utility", TAG_FIRMWARE
, 1, 0, 0, NULL
, NULL
, NULL
};
144 static REFIT_MENU_SCREEN MainMenu
= { L
"Main Menu", NULL
, 0, NULL
, 0, NULL
, 0, L
"Automatic boot",
145 L
"Use arrow keys to move cursor; Enter to boot;",
146 L
"Insert or F2 for more options; Esc to refresh" };
147 static REFIT_MENU_SCREEN AboutMenu
= { L
"About", NULL
, 0, NULL
, 0, NULL
, 0, NULL
, L
"Press Enter to return to main menu", L
"" };
149 REFIT_CONFIG GlobalConfig
= { FALSE
, TRUE
, FALSE
, FALSE
, 0, 0, 0, DONT_CHANGE_TEXT_MODE
, 20, 0, 0, GRAPHICS_FOR_OSX
, LEGACY_TYPE_MAC
,
150 0, 0, { DEFAULT_BIG_ICON_SIZE
/ 4, DEFAULT_SMALL_ICON_SIZE
, DEFAULT_BIG_ICON_SIZE
}, BANNER_NOSCALE
,
151 NULL
, NULL
, CONFIG_FILE_NAME
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
152 { TAG_SHELL
, TAG_MEMTEST
, TAG_GDISK
, TAG_APPLE_RECOVERY
, TAG_WINDOWS_RECOVERY
, TAG_MOK_TOOL
,
153 TAG_ABOUT
, TAG_SHUTDOWN
, TAG_REBOOT
, TAG_FIRMWARE
, 0, 0, 0, 0, 0, 0, 0, 0 }
156 EFI_GUID GlobalGuid
= EFI_GLOBAL_VARIABLE
;
157 EFI_GUID RefindGuid
= REFIND_GUID_VALUE
;
159 GPT_DATA
*gPartitions
= NULL
;
161 // Structure used to hold boot loader filenames and time stamps in
162 // a linked list; used to sort entries within a directory.
166 struct LOADER_LIST
*NextEntry
;
173 static VOID
AboutrEFInd(VOID
)
175 CHAR16
*FirmwareVendor
;
177 if (AboutMenu
.EntryCount
== 0) {
178 AboutMenu
.TitleImage
= BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT
);
179 AddMenuInfoLine(&AboutMenu
, L
"rEFInd Version 0.8.3.90");
180 AddMenuInfoLine(&AboutMenu
, L
"");
181 AddMenuInfoLine(&AboutMenu
, L
"Copyright (c) 2006-2010 Christoph Pfisterer");
182 AddMenuInfoLine(&AboutMenu
, L
"Copyright (c) 2012-2014 Roderick W. Smith");
183 AddMenuInfoLine(&AboutMenu
, L
"Portions Copyright (c) Intel Corporation and others");
184 AddMenuInfoLine(&AboutMenu
, L
"Distributed under the terms of the GNU GPLv3 license");
185 AddMenuInfoLine(&AboutMenu
, L
"");
186 AddMenuInfoLine(&AboutMenu
, L
"Running on:");
187 AddMenuInfoLine(&AboutMenu
, PoolPrint(L
" EFI Revision %d.%02d", ST
->Hdr
.Revision
>> 16, ST
->Hdr
.Revision
& ((1 << 16) - 1)));
189 AddMenuInfoLine(&AboutMenu
, L
" Platform: x86 (32 bit)");
190 #elif defined(EFIX64)
191 AddMenuInfoLine(&AboutMenu
, PoolPrint(L
" Platform: x86_64 (64 bit); Secure Boot %s",
192 secure_mode() ? L
"active" : L
"inactive"));
194 AddMenuInfoLine(&AboutMenu
, L
" Platform: unknown");
196 FirmwareVendor
= StrDuplicate(ST
->FirmwareVendor
);
197 LimitStringLength(FirmwareVendor
, 65); // More than ~65 causes empty info page on 800x600 display
198 AddMenuInfoLine(&AboutMenu
, PoolPrint(L
" Firmware: %s %d.%02d", FirmwareVendor
, ST
->FirmwareRevision
>> 16,
199 ST
->FirmwareRevision
& ((1 << 16) - 1)));
200 AddMenuInfoLine(&AboutMenu
, PoolPrint(L
" Screen Output: %s", egScreenDescription()));
201 AddMenuInfoLine(&AboutMenu
, L
"");
202 #if defined(__MAKEWITH_GNUEFI)
203 AddMenuInfoLine(&AboutMenu
, L
"Built with GNU-EFI");
205 AddMenuInfoLine(&AboutMenu
, L
"Built with TianoCore EDK2");
207 AddMenuInfoLine(&AboutMenu
, L
"");
208 AddMenuInfoLine(&AboutMenu
, L
"For more information, see the rEFInd Web site:");
209 AddMenuInfoLine(&AboutMenu
, L
"http://www.rodsbooks.com/refind/");
210 AddMenuEntry(&AboutMenu
, &MenuEntryReturn
);
213 RunMenu(&AboutMenu
, NULL
);
214 } /* VOID AboutrEFInd() */
216 static VOID
WarnSecureBootError(CHAR16
*Name
, BOOLEAN Verbose
) {
218 Name
= L
"the loader";
220 refit_call2_wrapper(ST
->ConOut
->SetAttribute
, ST
->ConOut
, ATTR_ERROR
);
221 Print(L
"Secure Boot validation failure loading %s!\n", Name
);
222 refit_call2_wrapper(ST
->ConOut
->SetAttribute
, ST
->ConOut
, ATTR_BASIC
);
223 if (Verbose
&& secure_mode()) {
224 Print(L
"\nThis computer is configured with Secure Boot active, but\n%s has failed validation.\n", Name
);
225 Print(L
"\nYou can:\n * Launch another boot loader\n");
226 Print(L
" * Disable Secure Boot in your firmware\n");
227 Print(L
" * Sign %s with a machine owner key (MOK)\n", Name
);
228 Print(L
" * Use a MOK utility (often present on the second row) to add a MOK with which\n");
229 Print(L
" %s has already been signed.\n", Name
);
230 Print(L
" * Use a MOK utility to register %s (\"enroll its hash\") without\n", Name
);
231 Print(L
" signing it.\n");
232 Print(L
"\nSee http://www.rodsbooks.com/refind/secureboot.html for more information\n");
235 } // VOID WarnSecureBootError()
237 // Returns TRUE if this file is a valid EFI loader file, and is proper ARCH
238 static BOOLEAN
IsValidLoader(EFI_FILE
*RootDir
, CHAR16
*FileName
) {
239 BOOLEAN IsValid
= TRUE
;
240 #if defined (EFIX64) | defined (EFI32)
242 EFI_FILE_HANDLE FileHandle
;
244 UINTN Size
= sizeof(Header
);
246 if ((RootDir
== NULL
) || (FileName
== NULL
)) {
247 // Assume valid here, because Macs produce NULL RootDir (& maybe FileName)
248 // when launching from a Firewire drive. This should be handled better, but
249 // fix would have to be in StartEFIImageList() and/or in FindVolumeAndFilename().
253 Status
= refit_call5_wrapper(RootDir
->Open
, RootDir
, &FileHandle
, FileName
, EFI_FILE_MODE_READ
, 0);
254 if (EFI_ERROR(Status
))
257 Status
= refit_call3_wrapper(FileHandle
->Read
, FileHandle
, &Size
, Header
);
258 refit_call1_wrapper(FileHandle
->Close
, FileHandle
);
260 IsValid
= !EFI_ERROR(Status
) &&
261 Size
== sizeof(Header
) &&
262 ((Header
[0] == 'M' && Header
[1] == 'Z' &&
263 (Size
= *(UINT32
*)&Header
[0x3c]) < 0x180 &&
264 Header
[Size
] == 'P' && Header
[Size
+1] == 'E' &&
265 Header
[Size
+2] == 0 && Header
[Size
+3] == 0 &&
266 *(UINT16
*)&Header
[Size
+4] == EFI_STUB_ARCH
) ||
267 (*(UINT32
*)&Header
== FAT_ARCH
));
270 } // BOOLEAN IsValidLoader()
272 // Launch an EFI binary.
273 static EFI_STATUS
StartEFIImageList(IN EFI_DEVICE_PATH
**DevicePaths
,
274 IN CHAR16
*LoadOptions
, IN UINTN LoaderType
,
275 IN CHAR16
*ImageTitle
, IN CHAR8 OSType
,
276 OUT UINTN
*ErrorInStep
,
280 EFI_STATUS Status
, ReturnStatus
;
281 EFI_HANDLE ChildImageHandle
;
282 EFI_LOADED_IMAGE
*ChildLoadedImage
= NULL
;
283 REFIT_VOLUME
*Volume
= NULL
;
284 UINTN DevicePathIndex
;
285 CHAR16 ErrorInfo
[256];
286 CHAR16
*FullLoadOptions
= NULL
;
287 CHAR16
*Filename
= NULL
;
290 if (ErrorInStep
!= NULL
)
294 if (LoadOptions
!= NULL
) {
295 FullLoadOptions
= StrDuplicate(LoadOptions
);
296 if ((LoaderType
== TYPE_EFI
) && (OSType
== 'M')) {
297 MergeStrings(&FullLoadOptions
, L
" ", 0);
298 // NOTE: That last space is also added by the EFI shell and seems to be significant
299 // when passing options to Apple's boot.efi...
301 } // if (LoadOptions != NULL)
303 Print(L
"Starting %s\nUsing load options '%s'\n", ImageTitle
, FullLoadOptions
? FullLoadOptions
: L
"");
305 // load the image into memory (and execute it, in the case of a shim/MOK image).
306 ReturnStatus
= Status
= EFI_NOT_FOUND
; // in case the list is empty
307 for (DevicePathIndex
= 0; DevicePaths
[DevicePathIndex
] != NULL
; DevicePathIndex
++) {
308 FindVolumeAndFilename(DevicePaths
[DevicePathIndex
], &Volume
, &Filename
);
309 // Some EFIs crash if attempting to load driver for invalid architecture, so
310 // protect for this condition; but sometimes Volume comes back NULL, so provide
311 // an exception. (TODO: Handle this special condition better.)
312 if ((LoaderType
== TYPE_LEGACY
) || (Volume
== NULL
) || IsValidLoader(Volume
->RootDir
, Filename
)) {
313 if (Filename
&& (LoaderType
!= TYPE_LEGACY
)) {
314 Temp
= PoolPrint(L
"\\%s %s", Filename
, FullLoadOptions
? FullLoadOptions
: L
"");
316 MyFreePool(FullLoadOptions
);
317 FullLoadOptions
= Temp
;
321 // NOTE: Below commented-out line could be more efficient if file were read ahead of
322 // time and passed as a pre-loaded image to LoadImage(), but it doesn't work on my
323 // 32-bit Mac Mini or my 64-bit Intel box when launching a Linux kernel; the
324 // kernel returns a "Failed to handle fs_proto" error message.
325 // TODO: Track down the cause of this error and fix it, if possible.
326 // ReturnStatus = Status = refit_call6_wrapper(BS->LoadImage, FALSE, SelfImageHandle, DevicePaths[DevicePathIndex],
327 // ImageData, ImageSize, &ChildImageHandle);
328 ReturnStatus
= Status
= refit_call6_wrapper(BS
->LoadImage
, FALSE
, SelfImageHandle
, DevicePaths
[DevicePathIndex
],
329 NULL
, 0, &ChildImageHandle
);
331 Print(L
"Invalid loader file!\n");
332 ReturnStatus
= EFI_LOAD_ERROR
;
334 if (ReturnStatus
!= EFI_NOT_FOUND
) {
338 if ((Status
== EFI_ACCESS_DENIED
) || (Status
== EFI_SECURITY_VIOLATION
)) {
339 WarnSecureBootError(ImageTitle
, Verbose
);
342 SPrint(ErrorInfo
, 255, L
"while loading %s", ImageTitle
);
343 if (CheckError(Status
, ErrorInfo
)) {
344 if (ErrorInStep
!= NULL
)
349 ReturnStatus
= Status
= refit_call3_wrapper(BS
->HandleProtocol
, ChildImageHandle
, &LoadedImageProtocol
,
350 (VOID
**) &ChildLoadedImage
);
351 if (CheckError(Status
, L
"while getting a LoadedImageProtocol handle")) {
352 if (ErrorInStep
!= NULL
)
356 ChildLoadedImage
->LoadOptions
= (VOID
*)FullLoadOptions
;
357 ChildLoadedImage
->LoadOptionsSize
= FullLoadOptions
? ((UINT32
)StrLen(FullLoadOptions
) + 1) * sizeof(CHAR16
) : 0;
358 // turn control over to the image
359 // TODO: (optionally) re-enable the EFI watchdog timer!
361 // close open file handles
363 ReturnStatus
= Status
= refit_call3_wrapper(BS
->StartImage
, ChildImageHandle
, NULL
, NULL
);
365 // control returns here when the child image calls Exit()
366 SPrint(ErrorInfo
, 255, L
"returned from %s", ImageTitle
);
367 if (CheckError(Status
, ErrorInfo
)) {
368 if (ErrorInStep
!= NULL
)
372 // re-open file handles
376 // unload the image, we don't care if it works or not...
378 Status
= refit_call1_wrapper(BS
->UnloadImage
, ChildImageHandle
);
381 MyFreePool(FullLoadOptions
);
383 } /* static EFI_STATUS StartEFIImageList() */
385 static EFI_STATUS
StartEFIImage(IN EFI_DEVICE_PATH
*DevicePath
,
386 IN CHAR16
*LoadOptions
, IN UINTN LoaderType
,
387 IN CHAR16
*ImageTitle
, IN CHAR8 OSType
,
388 OUT UINTN
*ErrorInStep
,
393 EFI_DEVICE_PATH
*DevicePaths
[2];
395 DevicePaths
[0] = DevicePath
;
396 DevicePaths
[1] = NULL
;
397 return StartEFIImageList(DevicePaths
, LoadOptions
, LoaderType
, ImageTitle
, OSType
, ErrorInStep
, Verbose
, IsDriver
);
398 } /* static EFI_STATUS StartEFIImage() */
400 // From gummiboot: Reboot the computer into its built-in user interface
401 static EFI_STATUS
RebootIntoFirmware(VOID
) {
407 osind
= EFI_OS_INDICATIONS_BOOT_TO_FW_UI
;
409 err
= EfivarGetRaw(&GlobalGuid
, L
"OsIndications", &b
, &size
);
410 if (err
== EFI_SUCCESS
)
414 err
= EfivarSetRaw(&GlobalGuid
, L
"OsIndications", (CHAR8
*)&osind
, sizeof(UINT64
), TRUE
);
415 if (err
!= EFI_SUCCESS
)
418 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);
419 Print(L
"Error calling ResetSystem: %r", err
);
424 // Record the value of the loader's name/description in rEFInd's "PreviousBoot" EFI variable,
425 // if it's different from what's already stored there.
426 static VOID
StoreLoaderName(IN CHAR16
*Name
) {
428 CHAR16
*OldName
= NULL
;
432 Status
= EfivarGetRaw(&RefindGuid
, L
"PreviousBoot", (CHAR8
**) &OldName
, &Length
);
433 if ((Status
!= EFI_SUCCESS
) || (StrCmp(OldName
, Name
) != 0)) {
434 EfivarSetRaw(&RefindGuid
, L
"PreviousBoot", (CHAR8
*) Name
, StrLen(Name
) * 2 + 2, TRUE
);
438 } // VOID StoreLoaderName()
441 // EFI OS loader functions
444 // See http://www.thomas-krenn.com/en/wiki/Activating_the_Intel_VT_Virtualization_Feature
445 // for information on Intel VMX features
446 static VOID
DoEnableAndLockVMX(VOID
)
449 UINT32 low_bits
= 0, high_bits
= 0;
452 __asm__
volatile ("rdmsr" : "=a" (low_bits
), "=d" (high_bits
) : "c" (msr
));
454 // enable and lock vmx if not locked
455 if ((low_bits
& 1) == 0) {
459 __asm__
volatile ("wrmsr" : : "c" (msr
), "a" (low_bits
), "d" (high_bits
));
461 } // VOID DoEnableAndLockVMX()
463 static VOID
StartLoader(LOADER_ENTRY
*Entry
, CHAR16
*SelectionName
)
465 UINTN ErrorInStep
= 0;
467 if (GlobalConfig
.EnableAndLockVMX
) {
468 DoEnableAndLockVMX();
471 BeginExternalScreen(Entry
->UseGraphicsMode
, L
"Booting OS");
472 StoreLoaderName(SelectionName
);
473 StartEFIImage(Entry
->DevicePath
, Entry
->LoadOptions
, TYPE_EFI
,
474 Basename(Entry
->LoaderPath
), Entry
->OSType
, &ErrorInStep
, !Entry
->UseGraphicsMode
, FALSE
);
475 FinishExternalScreen();
478 // Locate an initrd or initramfs file that matches the kernel specified by LoaderPath.
479 // The matching file has a name that begins with "init" and includes the same version
480 // number string as is found in LoaderPath -- but not a longer version number string.
481 // For instance, if LoaderPath is \EFI\kernels\bzImage-3.3.0.efi, and if \EFI\kernels
482 // has a file called initramfs-3.3.0.img, this function will return the string
483 // '\EFI\kernels\initramfs-3.3.0.img'. If the directory ALSO contains the file
484 // initramfs-3.3.0-rc7.img or initramfs-13.3.0.img, those files will NOT match;
485 // however, initmine-3.3.0.img might match. (FindInitrd() returns the first match it
486 // finds.) Thus, care should be taken to avoid placing duplicate matching files in
487 // the kernel's directory.
488 // If no matching init file can be found, returns NULL.
489 static CHAR16
* FindInitrd(IN CHAR16
*LoaderPath
, IN REFIT_VOLUME
*Volume
) {
490 CHAR16
*InitrdName
= NULL
, *FileName
, *KernelVersion
, *InitrdVersion
, *Path
;
491 REFIT_DIR_ITER DirIter
;
492 EFI_FILE_INFO
*DirEntry
;
494 FileName
= Basename(LoaderPath
);
495 KernelVersion
= FindNumbers(FileName
);
496 Path
= FindPath(LoaderPath
);
498 // Add trailing backslash for root directory; necessary on some systems, but must
499 // NOT be added to all directories, since on other systems, a trailing backslash on
500 // anything but the root directory causes them to flake out!
501 if (StrLen(Path
) == 0) {
502 MergeStrings(&Path
, L
"\\", 0);
504 DirIterOpen(Volume
->RootDir
, Path
, &DirIter
);
505 // Now add a trailing backslash if it was NOT added earlier, for consistency in
506 // building the InitrdName later....
507 if ((StrLen(Path
) > 0) && (Path
[StrLen(Path
) - 1] != L
'\\'))
508 MergeStrings(&Path
, L
"\\", 0);
509 while ((DirIterNext(&DirIter
, 2, L
"init*", &DirEntry
)) && (InitrdName
== NULL
)) {
510 InitrdVersion
= FindNumbers(DirEntry
->FileName
);
511 if (KernelVersion
!= NULL
) {
512 if (StriCmp(InitrdVersion
, KernelVersion
) == 0) {
513 InitrdName
= PoolPrint(L
"%s%s", Path
, DirEntry
->FileName
);
516 if (InitrdVersion
== NULL
) {
517 InitrdName
= PoolPrint(L
"%s%s", Path
, DirEntry
->FileName
);
520 MyFreePool(InitrdVersion
);
522 DirIterClose(&DirIter
);
524 // Note: Don't FreePool(FileName), since Basename returns a pointer WITHIN the string it's passed.
525 MyFreePool(KernelVersion
);
528 } // static CHAR16 * FindInitrd()
530 LOADER_ENTRY
* AddPreparedLoaderEntry(LOADER_ENTRY
*Entry
) {
531 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
534 } // LOADER_ENTRY * AddPreparedLoaderEntry()
536 // Creates a copy of a menu screen.
537 // Returns a pointer to the copy of the menu screen.
538 static REFIT_MENU_SCREEN
* CopyMenuScreen(REFIT_MENU_SCREEN
*Entry
) {
539 REFIT_MENU_SCREEN
*NewEntry
;
542 NewEntry
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
543 if ((Entry
!= NULL
) && (NewEntry
!= NULL
)) {
544 CopyMem(NewEntry
, Entry
, sizeof(REFIT_MENU_SCREEN
));
545 NewEntry
->Title
= (Entry
->Title
) ? StrDuplicate(Entry
->Title
) : NULL
;
546 NewEntry
->TimeoutText
= (Entry
->TimeoutText
) ? StrDuplicate(Entry
->TimeoutText
) : NULL
;
547 if (Entry
->TitleImage
!= NULL
) {
548 NewEntry
->TitleImage
= AllocatePool(sizeof(EG_IMAGE
));
549 if (NewEntry
->TitleImage
!= NULL
)
550 CopyMem(NewEntry
->TitleImage
, Entry
->TitleImage
, sizeof(EG_IMAGE
));
552 NewEntry
->InfoLines
= (CHAR16
**) AllocateZeroPool(Entry
->InfoLineCount
* (sizeof(CHAR16
*)));
553 for (i
= 0; i
< Entry
->InfoLineCount
&& NewEntry
->InfoLines
; i
++) {
554 NewEntry
->InfoLines
[i
] = (Entry
->InfoLines
[i
]) ? StrDuplicate(Entry
->InfoLines
[i
]) : NULL
;
556 NewEntry
->Entries
= (REFIT_MENU_ENTRY
**) AllocateZeroPool(Entry
->EntryCount
* (sizeof (REFIT_MENU_ENTRY
*)));
557 for (i
= 0; i
< Entry
->EntryCount
&& NewEntry
->Entries
; i
++) {
558 AddMenuEntry(NewEntry
, Entry
->Entries
[i
]);
560 NewEntry
->Hint1
= (Entry
->Hint1
) ? StrDuplicate(Entry
->Hint1
) : NULL
;
561 NewEntry
->Hint2
= (Entry
->Hint2
) ? StrDuplicate(Entry
->Hint2
) : NULL
;
564 } // static REFIT_MENU_SCREEN* CopyMenuScreen()
566 // Creates a copy of a menu entry. Intended to enable moving a stack-based
567 // menu entry (such as the ones for the "reboot" and "exit" functions) to
568 // to the heap. This enables easier deletion of the whole set of menu
569 // entries when re-scanning.
570 // Returns a pointer to the copy of the menu entry.
571 static REFIT_MENU_ENTRY
* CopyMenuEntry(REFIT_MENU_ENTRY
*Entry
) {
572 REFIT_MENU_ENTRY
*NewEntry
;
574 NewEntry
= AllocateZeroPool(sizeof(REFIT_MENU_ENTRY
));
575 if ((Entry
!= NULL
) && (NewEntry
!= NULL
)) {
576 CopyMem(NewEntry
, Entry
, sizeof(REFIT_MENU_ENTRY
));
577 NewEntry
->Title
= (Entry
->Title
) ? StrDuplicate(Entry
->Title
) : NULL
;
578 if (Entry
->BadgeImage
!= NULL
) {
579 NewEntry
->BadgeImage
= AllocatePool(sizeof(EG_IMAGE
));
580 if (NewEntry
->BadgeImage
!= NULL
)
581 CopyMem(NewEntry
->BadgeImage
, Entry
->BadgeImage
, sizeof(EG_IMAGE
));
583 if (Entry
->Image
!= NULL
) {
584 NewEntry
->Image
= AllocatePool(sizeof(EG_IMAGE
));
585 if (NewEntry
->Image
!= NULL
)
586 CopyMem(NewEntry
->Image
, Entry
->Image
, sizeof(EG_IMAGE
));
588 if (Entry
->SubScreen
!= NULL
) {
589 NewEntry
->SubScreen
= CopyMenuScreen(Entry
->SubScreen
);
593 } // REFIT_MENU_ENTRY* CopyMenuEntry()
595 // Creates a new LOADER_ENTRY data structure and populates it with
596 // default values from the specified Entry, or NULL values if Entry
597 // is unspecified (NULL).
598 // Returns a pointer to the new data structure, or NULL if it
599 // couldn't be allocated
600 LOADER_ENTRY
*InitializeLoaderEntry(IN LOADER_ENTRY
*Entry
) {
601 LOADER_ENTRY
*NewEntry
= NULL
;
603 NewEntry
= AllocateZeroPool(sizeof(LOADER_ENTRY
));
604 if (NewEntry
!= NULL
) {
605 NewEntry
->me
.Title
= NULL
;
606 NewEntry
->me
.Tag
= TAG_LOADER
;
607 NewEntry
->Enabled
= TRUE
;
608 NewEntry
->UseGraphicsMode
= FALSE
;
609 NewEntry
->OSType
= 0;
611 NewEntry
->LoaderPath
= (Entry
->LoaderPath
) ? StrDuplicate(Entry
->LoaderPath
) : NULL
;
612 NewEntry
->VolName
= (Entry
->VolName
) ? StrDuplicate(Entry
->VolName
) : NULL
;
613 NewEntry
->DevicePath
= Entry
->DevicePath
;
614 NewEntry
->UseGraphicsMode
= Entry
->UseGraphicsMode
;
615 NewEntry
->LoadOptions
= (Entry
->LoadOptions
) ? StrDuplicate(Entry
->LoadOptions
) : NULL
;
616 NewEntry
->InitrdPath
= (Entry
->InitrdPath
) ? StrDuplicate(Entry
->InitrdPath
) : NULL
;
620 } // LOADER_ENTRY *InitializeLoaderEntry()
622 // Adds InitrdPath to Options, but only if Options doesn't already include an
623 // initrd= line. Done to enable overriding the default initrd selection in a
624 // refind_linux.conf file's options list.
625 // Returns a pointer to a new string. The calling function is responsible for
626 // freeing its memory.
627 static CHAR16
*AddInitrdToOptions(CHAR16
*Options
, CHAR16
*InitrdPath
) {
628 CHAR16
*NewOptions
= NULL
;
631 NewOptions
= StrDuplicate(Options
);
632 if ((InitrdPath
!= NULL
) && !StriSubCmp(L
"initrd=", Options
)) {
633 MergeStrings(&NewOptions
, L
"initrd=", L
' ');
634 MergeStrings(&NewOptions
, InitrdPath
, 0);
637 } // CHAR16 *AddInitrdToOptions()
639 // Prepare a REFIT_MENU_SCREEN data structure for a subscreen entry. This sets up
640 // the default entry that launches the boot loader using the same options as the
641 // main Entry does. Subsequent options can be added by the calling function.
642 // If a subscreen already exists in the Entry that's passed to this function,
643 // it's left unchanged and a pointer to it is returned.
644 // Returns a pointer to the new subscreen data structure, or NULL if there
645 // were problems allocating memory.
646 REFIT_MENU_SCREEN
*InitializeSubScreen(IN LOADER_ENTRY
*Entry
) {
647 CHAR16
*FileName
, *MainOptions
= NULL
;
648 REFIT_MENU_SCREEN
*SubScreen
= NULL
;
649 LOADER_ENTRY
*SubEntry
;
651 FileName
= Basename(Entry
->LoaderPath
);
652 if (Entry
->me
.SubScreen
== NULL
) { // No subscreen yet; initialize default entry....
653 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
654 if (SubScreen
!= NULL
) {
655 SubScreen
->Title
= AllocateZeroPool(sizeof(CHAR16
) * 256);
656 SPrint(SubScreen
->Title
, 255, L
"Boot Options for %s on %s",
657 (Entry
->Title
!= NULL
) ? Entry
->Title
: FileName
, Entry
->VolName
);
658 SubScreen
->TitleImage
= Entry
->me
.Image
;
660 SubEntry
= InitializeLoaderEntry(Entry
);
661 if (SubEntry
!= NULL
) {
662 SubEntry
->me
.Title
= StrDuplicate(L
"Boot using default options");
663 MainOptions
= SubEntry
->LoadOptions
;
664 SubEntry
->LoadOptions
= AddInitrdToOptions(MainOptions
, SubEntry
->InitrdPath
);
665 MyFreePool(MainOptions
);
666 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
667 } // if (SubEntry != NULL)
668 SubScreen
->Hint1
= StrDuplicate(SUBSCREEN_HINT1
);
669 if (GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_EDITOR
) {
670 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2_NO_EDITOR
);
672 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2
);
674 } // if (SubScreen != NULL)
675 } else { // existing subscreen; less initialization, and just add new entry later....
676 SubScreen
= Entry
->me
.SubScreen
;
679 } // REFIT_MENU_SCREEN *InitializeSubScreen()
681 VOID
GenerateSubScreen(LOADER_ENTRY
*Entry
, IN REFIT_VOLUME
*Volume
) {
682 REFIT_MENU_SCREEN
*SubScreen
;
683 LOADER_ENTRY
*SubEntry
;
685 CHAR16 DiagsFileName
[256];
690 // create the submenu
691 if (StrLen(Entry
->Title
) == 0) {
692 MyFreePool(Entry
->Title
);
695 SubScreen
= InitializeSubScreen(Entry
);
697 // loader-specific submenu entries
698 if (Entry
->OSType
== 'M') { // entries for Mac OS X
700 SubEntry
= InitializeLoaderEntry(Entry
);
701 if (SubEntry
!= NULL
) {
702 SubEntry
->me
.Title
= L
"Boot Mac OS X with a 64-bit kernel";
703 SubEntry
->LoadOptions
= L
"arch=x86_64";
704 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
705 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
708 SubEntry
= InitializeLoaderEntry(Entry
);
709 if (SubEntry
!= NULL
) {
710 SubEntry
->me
.Title
= L
"Boot Mac OS X with a 32-bit kernel";
711 SubEntry
->LoadOptions
= L
"arch=i386";
712 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
713 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
717 if (!(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_SINGLEUSER
)) {
718 SubEntry
= InitializeLoaderEntry(Entry
);
719 if (SubEntry
!= NULL
) {
720 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode";
721 SubEntry
->UseGraphicsMode
= FALSE
;
722 SubEntry
->LoadOptions
= L
"-v";
723 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
727 SubEntry
= InitializeLoaderEntry(Entry
);
728 if (SubEntry
!= NULL
) {
729 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode (64-bit)";
730 SubEntry
->UseGraphicsMode
= FALSE
;
731 SubEntry
->LoadOptions
= L
"-v arch=x86_64";
732 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
735 SubEntry
= InitializeLoaderEntry(Entry
);
736 if (SubEntry
!= NULL
) {
737 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode (32-bit)";
738 SubEntry
->UseGraphicsMode
= FALSE
;
739 SubEntry
->LoadOptions
= L
"-v arch=i386";
740 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
744 SubEntry
= InitializeLoaderEntry(Entry
);
745 if (SubEntry
!= NULL
) {
746 SubEntry
->me
.Title
= L
"Boot Mac OS X in single user mode";
747 SubEntry
->UseGraphicsMode
= FALSE
;
748 SubEntry
->LoadOptions
= L
"-v -s";
749 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
751 } // single-user mode allowed
753 if (!(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_SAFEMODE
)) {
754 SubEntry
= InitializeLoaderEntry(Entry
);
755 if (SubEntry
!= NULL
) {
756 SubEntry
->me
.Title
= L
"Boot Mac OS X in safe mode";
757 SubEntry
->UseGraphicsMode
= FALSE
;
758 SubEntry
->LoadOptions
= L
"-v -x";
759 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
761 } // safe mode allowed
763 // check for Apple hardware diagnostics
764 StrCpy(DiagsFileName
, L
"System\\Library\\CoreServices\\.diagnostics\\diags.efi");
765 if (FileExists(Volume
->RootDir
, DiagsFileName
) && !(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_HWTEST
)) {
766 SubEntry
= InitializeLoaderEntry(Entry
);
767 if (SubEntry
!= NULL
) {
768 SubEntry
->me
.Title
= L
"Run Apple Hardware Test";
769 MyFreePool(SubEntry
->LoaderPath
);
770 SubEntry
->LoaderPath
= StrDuplicate(DiagsFileName
);
771 SubEntry
->DevicePath
= FileDevicePath(Volume
->DeviceHandle
, SubEntry
->LoaderPath
);
772 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
773 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
775 } // if diagnostics entry found
777 } else if (Entry
->OSType
== 'L') { // entries for Linux kernels with EFI stub loaders
778 File
= ReadLinuxOptionsFile(Entry
->LoaderPath
, Volume
);
780 InitrdName
= FindInitrd(Entry
->LoaderPath
, Volume
);
781 TokenCount
= ReadTokenLine(File
, &TokenList
);
782 // first entry requires special processing, since it was initially set
783 // up with a default title but correct options by InitializeSubScreen(),
785 if ((SubScreen
->Entries
!= NULL
) && (SubScreen
->Entries
[0] != NULL
)) {
786 MyFreePool(SubScreen
->Entries
[0]->Title
);
787 SubScreen
->Entries
[0]->Title
= TokenList
[0] ? StrDuplicate(TokenList
[0]) : StrDuplicate(L
"Boot Linux");
789 FreeTokenLine(&TokenList
, &TokenCount
);
790 while ((TokenCount
= ReadTokenLine(File
, &TokenList
)) > 1) {
791 SubEntry
= InitializeLoaderEntry(Entry
);
792 SubEntry
->me
.Title
= TokenList
[0] ? StrDuplicate(TokenList
[0]) : StrDuplicate(L
"Boot Linux");
793 MyFreePool(SubEntry
->LoadOptions
);
794 SubEntry
->LoadOptions
= AddInitrdToOptions(TokenList
[1], InitrdName
);
795 FreeTokenLine(&TokenList
, &TokenCount
);
796 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_LINUX
;
797 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
799 MyFreePool(InitrdName
);
803 } else if (Entry
->OSType
== 'E') { // entries for ELILO
804 SubEntry
= InitializeLoaderEntry(Entry
);
805 if (SubEntry
!= NULL
) {
806 SubEntry
->me
.Title
= L
"Run ELILO in interactive mode";
807 SubEntry
->LoadOptions
= L
"-p";
808 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
809 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
812 SubEntry
= InitializeLoaderEntry(Entry
);
813 if (SubEntry
!= NULL
) {
814 SubEntry
->me
.Title
= L
"Boot Linux for a 17\" iMac or a 15\" MacBook Pro (*)";
815 SubEntry
->UseGraphicsMode
= TRUE
;
816 SubEntry
->LoadOptions
= L
"-d 0 i17";
817 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
818 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
821 SubEntry
= InitializeLoaderEntry(Entry
);
822 if (SubEntry
!= NULL
) {
823 SubEntry
->me
.Title
= L
"Boot Linux for a 20\" iMac (*)";
824 SubEntry
->UseGraphicsMode
= TRUE
;
825 SubEntry
->LoadOptions
= L
"-d 0 i20";
826 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
827 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
830 SubEntry
= InitializeLoaderEntry(Entry
);
831 if (SubEntry
!= NULL
) {
832 SubEntry
->me
.Title
= L
"Boot Linux for a Mac Mini (*)";
833 SubEntry
->UseGraphicsMode
= TRUE
;
834 SubEntry
->LoadOptions
= L
"-d 0 mini";
835 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
836 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
839 AddMenuInfoLine(SubScreen
, L
"NOTE: This is an example. Entries");
840 AddMenuInfoLine(SubScreen
, L
"marked with (*) may not work.");
842 } else if (Entry
->OSType
== 'X') { // entries for xom.efi
843 // by default, skip the built-in selection and boot from hard disk only
844 Entry
->LoadOptions
= L
"-s -h";
846 SubEntry
= InitializeLoaderEntry(Entry
);
847 if (SubEntry
!= NULL
) {
848 SubEntry
->me
.Title
= L
"Boot Windows from Hard Disk";
849 SubEntry
->LoadOptions
= L
"-s -h";
850 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
851 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
854 SubEntry
= InitializeLoaderEntry(Entry
);
855 if (SubEntry
!= NULL
) {
856 SubEntry
->me
.Title
= L
"Boot Windows from CD-ROM";
857 SubEntry
->LoadOptions
= L
"-s -c";
858 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
859 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
862 SubEntry
= InitializeLoaderEntry(Entry
);
863 if (SubEntry
!= NULL
) {
864 SubEntry
->me
.Title
= L
"Run XOM in text mode";
865 SubEntry
->UseGraphicsMode
= FALSE
;
866 SubEntry
->LoadOptions
= L
"-v";
867 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
868 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
870 } // entries for xom.efi
871 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
872 Entry
->me
.SubScreen
= SubScreen
;
873 } // VOID GenerateSubScreen()
875 // Returns options for a Linux kernel. Reads them from an options file in the
876 // kernel's directory; and if present, adds an initrd= option for an initial
877 // RAM disk file with the same version number as the kernel file.
878 static CHAR16
* GetMainLinuxOptions(IN CHAR16
* LoaderPath
, IN REFIT_VOLUME
*Volume
) {
879 CHAR16
*Options
= NULL
, *InitrdName
, *FullOptions
= NULL
;
881 Options
= GetFirstOptionsFromFile(LoaderPath
, Volume
);
882 InitrdName
= FindInitrd(LoaderPath
, Volume
);
883 FullOptions
= AddInitrdToOptions(Options
, InitrdName
);
886 MyFreePool(InitrdName
);
887 return (FullOptions
);
888 } // static CHAR16 * GetMainLinuxOptions()
890 // Try to guess the name of the Linux distribution & add that name to
892 static VOID
GuessLinuxDistribution(CHAR16
**OSIconName
, REFIT_VOLUME
*Volume
, CHAR16
*LoaderPath
) {
896 UINTN TokenCount
= 0;
898 // If on Linux root fs, /etc/os-release file probably has clues....
899 if (FileExists(Volume
->RootDir
, L
"etc\\os-release") &&
900 (ReadFile(Volume
->RootDir
, L
"etc\\os-release", &File
, &FileSize
) == EFI_SUCCESS
)) {
902 TokenCount
= ReadTokenLine(&File
, &TokenList
);
903 if ((TokenCount
> 1) && ((StriCmp(TokenList
[0], L
"ID") == 0) || (StriCmp(TokenList
[0], L
"NAME") == 0))) {
904 MergeStrings(OSIconName
, TokenList
[1], L
',');
906 FreeTokenLine(&TokenList
, &TokenCount
);
907 } while (TokenCount
> 0);
908 MyFreePool(File
.Buffer
);
911 // Search for clues in the kernel's filename....
912 if (StriSubCmp(L
".fc", LoaderPath
))
913 MergeStrings(OSIconName
, L
"fedora", L
',');
914 if (StriSubCmp(L
".el", LoaderPath
))
915 MergeStrings(OSIconName
, L
"redhat", L
',');
916 } // VOID GuessLinuxDistribution()
918 // Sets a few defaults for a loader entry -- mainly the icon, but also the OS type
919 // code and shortcut letter. For Linux EFI stub loaders, also sets kernel options
920 // that will (with luck) work fairly automatically.
921 VOID
SetLoaderDefaults(LOADER_ENTRY
*Entry
, CHAR16
*LoaderPath
, REFIT_VOLUME
*Volume
) {
922 CHAR16
*NameClues
, *PathOnly
, *NoExtension
, *OSIconName
= NULL
, *Temp
, *SubString
;
923 CHAR16 ShortcutLetter
= 0;
926 NameClues
= Basename(LoaderPath
);
927 PathOnly
= FindPath(LoaderPath
);
928 NoExtension
= StripEfiExtension(NameClues
);
930 if (Volume
->DiskKind
== DISK_KIND_NET
) {
931 MergeStrings(&NameClues
, Entry
->me
.Title
, L
' ');
933 // locate a custom icon for the loader
934 // Anything found here takes precedence over the "hints" in the OSIconName variable
935 if (!Entry
->me
.Image
) {
936 Entry
->me
.Image
= egLoadIconAnyType(Volume
->RootDir
, PathOnly
, NoExtension
, GlobalConfig
.IconSizes
[ICON_SIZE_BIG
]);
938 if (!Entry
->me
.Image
) {
939 Entry
->me
.Image
= egCopyImage(Volume
->VolIconImage
);
942 // Begin creating icon "hints" by using last part of directory path leading
944 Temp
= FindLastDirName(LoaderPath
);
945 MergeStrings(&OSIconName
, Temp
, L
',');
948 if (OSIconName
!= NULL
) {
949 ShortcutLetter
= OSIconName
[0];
952 // Add every "word" in the volume label, delimited by spaces, dashes (-), or
953 // underscores (_), to the list of hints to be used in searching for OS
955 if ((Volume
->VolName
) && (StrLen(Volume
->VolName
) > 0)) {
956 Temp
= SubString
= StrDuplicate(Volume
->VolName
);
958 Length
= StrLen(Temp
);
959 for (i
= 0; i
< Length
; i
++) {
960 if ((Temp
[i
] == L
' ') || (Temp
[i
] == L
'_') || (Temp
[i
] == L
'-')) {
962 if (StrLen(SubString
) > 0)
963 MergeStrings(&OSIconName
, SubString
, L
',');
964 SubString
= Temp
+ i
+ 1;
967 MergeStrings(&OSIconName
, SubString
, L
',');
971 } // if/else network boot
973 // detect specific loaders
974 if (StriSubCmp(L
"bzImage", NameClues
) || StriSubCmp(L
"vmlinuz", NameClues
)) {
975 if (Volume
->DiskKind
!= DISK_KIND_NET
) {
976 GuessLinuxDistribution(&OSIconName
, Volume
, LoaderPath
);
977 Entry
->LoadOptions
= GetMainLinuxOptions(LoaderPath
, Volume
);
979 MergeStrings(&OSIconName
, L
"linux", L
',');
981 if (ShortcutLetter
== 0)
982 ShortcutLetter
= 'L';
983 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_LINUX
;
984 } else if (StriSubCmp(L
"refit", LoaderPath
)) {
985 MergeStrings(&OSIconName
, L
"refit", L
',');
987 ShortcutLetter
= 'R';
988 } else if (StriSubCmp(L
"refind", LoaderPath
)) {
989 MergeStrings(&OSIconName
, L
"refind", L
',');
991 ShortcutLetter
= 'R';
992 } else if (StriCmp(LoaderPath
, MACOSX_LOADER_PATH
) == 0) {
993 MergeStrings(&OSIconName
, L
"mac", L
',');
995 ShortcutLetter
= 'M';
996 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
997 } else if (StriCmp(NameClues
, L
"diags.efi") == 0) {
998 MergeStrings(&OSIconName
, L
"hwtest", L
',');
999 } else if (StriCmp(NameClues
, L
"e.efi") == 0 || StriCmp(NameClues
, L
"elilo.efi") == 0 || StriSubCmp(L
"elilo", NameClues
)) {
1000 MergeStrings(&OSIconName
, L
"elilo,linux", L
',');
1001 Entry
->OSType
= 'E';
1002 if (ShortcutLetter
== 0)
1003 ShortcutLetter
= 'L';
1004 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
1005 } else if (StriSubCmp(L
"grub", NameClues
)) {
1006 MergeStrings(&OSIconName
, L
"grub,linux", L
',');
1007 Entry
->OSType
= 'G';
1008 ShortcutLetter
= 'G';
1009 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_GRUB
;
1010 } else if (StriCmp(NameClues
, L
"cdboot.efi") == 0 ||
1011 StriCmp(NameClues
, L
"bootmgr.efi") == 0 ||
1012 StriCmp(NameClues
, L
"bootmgfw.efi") == 0 ||
1013 StriCmp(NameClues
, L
"bkpbootmgfw.efi") == 0) {
1014 MergeStrings(&OSIconName
, L
"win", L
',');
1015 Entry
->OSType
= 'W';
1016 ShortcutLetter
= 'W';
1017 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
1018 } else if (StriCmp(NameClues
, L
"xom.efi") == 0) {
1019 MergeStrings(&OSIconName
, L
"xom,win", L
',');
1020 Entry
->UseGraphicsMode
= TRUE
;
1021 Entry
->OSType
= 'X';
1022 ShortcutLetter
= 'W';
1023 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
1025 else if (StriSubCmp(L
"ipxe", NameClues
)) {
1026 Entry
->OSType
= 'N';
1027 ShortcutLetter
= 'N';
1028 MergeStrings(&OSIconName
, L
"network", L
',');
1031 if ((ShortcutLetter
>= 'a') && (ShortcutLetter
<= 'z'))
1032 ShortcutLetter
= ShortcutLetter
- 'a' + 'A'; // convert lowercase to uppercase
1033 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1034 if (Entry
->me
.Image
== NULL
)
1035 Entry
->me
.Image
= LoadOSIcon(OSIconName
, L
"unknown", FALSE
);
1036 MyFreePool(PathOnly
);
1037 } // VOID SetLoaderDefaults()
1039 // Add a specified EFI boot loader to the list, using automatic settings
1040 // for icons, options, etc.
1041 LOADER_ENTRY
* AddLoaderEntry(IN CHAR16
*LoaderPath
, IN CHAR16
*LoaderTitle
, IN REFIT_VOLUME
*Volume
) {
1042 LOADER_ENTRY
*Entry
;
1044 CleanUpPathNameSlashes(LoaderPath
);
1045 Entry
= InitializeLoaderEntry(NULL
);
1046 if (Entry
!= NULL
) {
1047 Entry
->Title
= StrDuplicate((LoaderTitle
!= NULL
) ? LoaderTitle
: LoaderPath
);
1048 Entry
->me
.Title
= AllocateZeroPool(sizeof(CHAR16
) * 256);
1049 // Extra space at end of Entry->me.Title enables searching on Volume->VolName even if another volume
1050 // name is identical except for something added to the end (e.g., VolB1 vs. VolB12).
1051 // Note: Volume->VolName will be NULL for network boot programs.
1052 if (Volume
->VolName
)
1053 SPrint(Entry
->me
.Title
, 255, L
"Boot %s from %s ", (LoaderTitle
!= NULL
) ? LoaderTitle
: LoaderPath
, Volume
->VolName
);
1055 SPrint(Entry
->me
.Title
, 255, L
"Boot %s ", (LoaderTitle
!= NULL
) ? LoaderTitle
: LoaderPath
);
1057 Entry
->me
.BadgeImage
= Volume
->VolBadgeImage
;
1058 if ((LoaderPath
!= NULL
) && (LoaderPath
[0] != L
'\\')) {
1059 Entry
->LoaderPath
= StrDuplicate(L
"\\");
1061 Entry
->LoaderPath
= NULL
;
1063 MergeStrings(&(Entry
->LoaderPath
), LoaderPath
, 0);
1064 Entry
->VolName
= Volume
->VolName
;
1065 Entry
->DevicePath
= FileDevicePath(Volume
->DeviceHandle
, Entry
->LoaderPath
);
1066 SetLoaderDefaults(Entry
, LoaderPath
, Volume
);
1067 GenerateSubScreen(Entry
, Volume
);
1068 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1072 } // LOADER_ENTRY * AddLoaderEntry()
1074 // Returns -1 if (Time1 < Time2), +1 if (Time1 > Time2), or 0 if
1075 // (Time1 == Time2). Precision is only to the nearest second; since
1076 // this is used for sorting boot loader entries, differences smaller
1077 // than this are likely to be meaningless (and unlikely!).
1078 INTN
TimeComp(IN EFI_TIME
*Time1
, IN EFI_TIME
*Time2
) {
1079 INT64 Time1InSeconds
, Time2InSeconds
;
1081 // Following values are overestimates; I'm assuming 31 days in every month.
1082 // This is fine for the purpose of this function, which is limited
1083 Time1InSeconds
= Time1
->Second
+ (Time1
->Minute
* 60) + (Time1
->Hour
* 3600) + (Time1
->Day
* 86400) +
1084 (Time1
->Month
* 2678400) + ((Time1
->Year
- 1998) * 32140800);
1085 Time2InSeconds
= Time2
->Second
+ (Time2
->Minute
* 60) + (Time2
->Hour
* 3600) + (Time2
->Day
* 86400) +
1086 (Time2
->Month
* 2678400) + ((Time2
->Year
- 1998) * 32140800);
1087 if (Time1InSeconds
< Time2InSeconds
)
1089 else if (Time1InSeconds
> Time2InSeconds
)
1093 } // INTN TimeComp()
1095 // Adds a loader list element, keeping it sorted by date. Returns the new
1096 // first element (the one with the most recent date).
1097 static struct LOADER_LIST
* AddLoaderListEntry(struct LOADER_LIST
*LoaderList
, struct LOADER_LIST
*NewEntry
) {
1098 struct LOADER_LIST
*LatestEntry
, *CurrentEntry
, *PrevEntry
= NULL
;
1100 LatestEntry
= CurrentEntry
= LoaderList
;
1101 if (LoaderList
== NULL
) {
1102 LatestEntry
= NewEntry
;
1104 while ((CurrentEntry
!= NULL
) && (TimeComp(&(NewEntry
->TimeStamp
), &(CurrentEntry
->TimeStamp
)) < 0)) {
1105 PrevEntry
= CurrentEntry
;
1106 CurrentEntry
= CurrentEntry
->NextEntry
;
1108 NewEntry
->NextEntry
= CurrentEntry
;
1109 if (PrevEntry
== NULL
) {
1110 LatestEntry
= NewEntry
;
1112 PrevEntry
->NextEntry
= NewEntry
;
1115 return (LatestEntry
);
1116 } // static VOID AddLoaderListEntry()
1118 // Delete the LOADER_LIST linked list
1119 static VOID
CleanUpLoaderList(struct LOADER_LIST
*LoaderList
) {
1120 struct LOADER_LIST
*Temp
;
1122 while (LoaderList
!= NULL
) {
1124 LoaderList
= LoaderList
->NextEntry
;
1125 MyFreePool(Temp
->FileName
);
1128 } // static VOID CleanUpLoaderList()
1130 // Returns FALSE if the specified file/volume matches the GlobalConfig.DontScanDirs
1131 // or GlobalConfig.DontScanVolumes specification, or if Path points to a volume
1132 // other than the one specified by Volume, or if the specified path is SelfDir.
1133 // Returns TRUE if none of these conditions is met -- that is, if the path is
1134 // eligible for scanning.
1135 static BOOLEAN
ShouldScan(REFIT_VOLUME
*Volume
, CHAR16
*Path
) {
1136 CHAR16
*VolName
= NULL
, *DontScanDir
, *PathCopy
= NULL
;
1138 BOOLEAN ScanIt
= TRUE
;
1140 if ((IsIn(Volume
->VolName
, GlobalConfig
.DontScanVolumes
)) || (IsIn(Volume
->PartName
, GlobalConfig
.DontScanVolumes
)))
1143 if ((StriCmp(Path
, SelfDirPath
) == 0) && (Volume
->DeviceHandle
== SelfVolume
->DeviceHandle
))
1146 // See if Path includes an explicit volume declaration that's NOT Volume....
1147 PathCopy
= StrDuplicate(Path
);
1148 if (SplitVolumeAndFilename(&PathCopy
, &VolName
)) {
1149 VolumeNumberToName(Volume
, &VolName
);
1150 if (VolName
&& StriCmp(VolName
, Volume
->VolName
) != 0) {
1153 } // if Path includes volume specification
1154 MyFreePool(PathCopy
);
1155 MyFreePool(VolName
);
1158 // See if Volume is in GlobalConfig.DontScanDirs....
1159 while (ScanIt
&& (DontScanDir
= FindCommaDelimited(GlobalConfig
.DontScanDirs
, i
++))) {
1160 SplitVolumeAndFilename(&DontScanDir
, &VolName
);
1161 CleanUpPathNameSlashes(DontScanDir
);
1162 VolumeNumberToName(Volume
, &VolName
);
1163 if (VolName
!= NULL
) {
1164 if ((StriCmp(VolName
, Volume
->VolName
) == 0) && (StriCmp(DontScanDir
, Path
) == 0))
1167 if (StriCmp(DontScanDir
, Path
) == 0)
1170 MyFreePool(DontScanDir
);
1171 MyFreePool(VolName
);
1177 } // BOOLEAN ShouldScan()
1179 // Returns TRUE if the file is byte-for-byte identical with the fallback file
1180 // on the volume AND if the file is not itself the fallback file; returns
1181 // FALSE if the file is not identical to the fallback file OR if the file
1182 // IS the fallback file. Intended for use in excluding the fallback boot
1183 // loader when it's a duplicate of another boot loader.
1184 static BOOLEAN
DuplicatesFallback(IN REFIT_VOLUME
*Volume
, IN CHAR16
*FileName
) {
1185 CHAR8
*FileContents
, *FallbackContents
;
1186 EFI_FILE_HANDLE FileHandle
, FallbackHandle
;
1187 EFI_FILE_INFO
*FileInfo
, *FallbackInfo
;
1188 UINTN FileSize
= 0, FallbackSize
= 0;
1190 BOOLEAN AreIdentical
= FALSE
;
1192 if (!FileExists(Volume
->RootDir
, FileName
) || !FileExists(Volume
->RootDir
, FALLBACK_FULLNAME
))
1195 CleanUpPathNameSlashes(FileName
);
1197 if (StriCmp(FileName
, FALLBACK_FULLNAME
) == 0)
1198 return FALSE
; // identical filenames, so not a duplicate....
1200 Status
= refit_call5_wrapper(Volume
->RootDir
->Open
, Volume
->RootDir
, &FileHandle
, FileName
, EFI_FILE_MODE_READ
, 0);
1201 if (Status
== EFI_SUCCESS
) {
1202 FileInfo
= LibFileInfo(FileHandle
);
1203 FileSize
= FileInfo
->FileSize
;
1208 Status
= refit_call5_wrapper(Volume
->RootDir
->Open
, Volume
->RootDir
, &FallbackHandle
, FALLBACK_FULLNAME
, EFI_FILE_MODE_READ
, 0);
1209 if (Status
== EFI_SUCCESS
) {
1210 FallbackInfo
= LibFileInfo(FallbackHandle
);
1211 FallbackSize
= FallbackInfo
->FileSize
;
1213 refit_call1_wrapper(FileHandle
->Close
, FileHandle
);
1217 if (FallbackSize
!= FileSize
) { // not same size, so can't be identical
1218 AreIdentical
= FALSE
;
1219 } else { // could be identical; do full check....
1220 FileContents
= AllocatePool(FileSize
);
1221 FallbackContents
= AllocatePool(FallbackSize
);
1222 if (FileContents
&& FallbackContents
) {
1223 Status
= refit_call3_wrapper(FileHandle
->Read
, FileHandle
, &FileSize
, FileContents
);
1224 if (Status
== EFI_SUCCESS
) {
1225 Status
= refit_call3_wrapper(FallbackHandle
->Read
, FallbackHandle
, &FallbackSize
, FallbackContents
);
1227 if (Status
== EFI_SUCCESS
) {
1228 AreIdentical
= (CompareMem(FileContents
, FallbackContents
, FileSize
) == 0);
1231 MyFreePool(FileContents
);
1232 MyFreePool(FallbackContents
);
1235 // BUG ALERT: Some systems (e.g., DUET, some Macs with large displays) crash if the
1236 // following two calls are reversed. Go figure....
1237 refit_call1_wrapper(FileHandle
->Close
, FallbackHandle
);
1238 refit_call1_wrapper(FileHandle
->Close
, FileHandle
);
1239 return AreIdentical
;
1240 } // BOOLEAN DuplicatesFallback()
1242 // Returns FALSE if two measures of file size are identical for a single file,
1243 // TRUE if not or if the file can't be opened and the other measure is non-0.
1244 // Despite the function's name, this isn't really a direct test of symbolic
1245 // link status, since EFI doesn't officially support symlinks. It does seem
1246 // to be a reliable indicator, though. (OTOH, some disk errors might cause a
1247 // file to fail to open, which would return a false positive -- but as I use
1248 // this function to exclude symbolic links from the list of boot loaders,
1249 // that would be fine, since such boot loaders wouldn't work.)
1250 static BOOLEAN
IsSymbolicLink(REFIT_VOLUME
*Volume
, CHAR16
*Path
, EFI_FILE_INFO
*DirEntry
) {
1251 EFI_FILE_HANDLE FileHandle
;
1252 EFI_FILE_INFO
*FileInfo
= NULL
;
1254 UINTN FileSize2
= 0;
1257 FileName
= StrDuplicate(Path
);
1258 MergeStrings(&FileName
, DirEntry
->FileName
, L
'\\');
1259 CleanUpPathNameSlashes(FileName
);
1261 Status
= refit_call5_wrapper(Volume
->RootDir
->Open
, Volume
->RootDir
, &FileHandle
, FileName
, EFI_FILE_MODE_READ
, 0);
1262 if (Status
== EFI_SUCCESS
) {
1263 FileInfo
= LibFileInfo(FileHandle
);
1264 if (FileInfo
!= NULL
)
1265 FileSize2
= FileInfo
->FileSize
;
1268 MyFreePool(FileName
);
1269 MyFreePool(FileInfo
);
1271 return (DirEntry
->FileSize
!= FileSize2
);
1272 } // BOOLEAN IsSymbolicLink()
1274 // Returns TRUE if a file with the same name as the original but with
1275 // ".efi.signed" is also present in the same directory. Ubuntu is using
1276 // this filename as a signed version of the original unsigned kernel, and
1277 // there's no point in cluttering the display with two kernels that will
1278 // behave identically on non-SB systems, or when one will fail when SB
1280 static BOOLEAN
HasSignedCounterpart(IN REFIT_VOLUME
*Volume
, IN CHAR16
*Path
, IN CHAR16
*Filename
) {
1281 CHAR16
*NewFile
= NULL
;
1282 BOOLEAN retval
= FALSE
;
1284 MergeStrings(&NewFile
, Path
, 0);
1285 MergeStrings(&NewFile
, Filename
, L
'\\');
1286 MergeStrings(&NewFile
, L
".efi.signed", 0);
1287 if (NewFile
!= NULL
) {
1288 CleanUpPathNameSlashes(NewFile
);
1289 if (FileExists(Volume
->RootDir
, NewFile
))
1291 MyFreePool(NewFile
);
1295 } // BOOLEAN HasSignedCounterpart()
1297 // Scan an individual directory for EFI boot loader files and, if found,
1298 // add them to the list. Exception: Ignores FALLBACK_FULLNAME, which is picked
1299 // up in ScanEfiFiles(). Sorts the entries within the loader directory so that
1300 // the most recent one appears first in the list.
1301 // Returns TRUE if a duplicate for FALLBACK_FILENAME was found, FALSE if not.
1302 static BOOLEAN
ScanLoaderDir(IN REFIT_VOLUME
*Volume
, IN CHAR16
*Path
, IN CHAR16
*Pattern
)
1305 REFIT_DIR_ITER DirIter
;
1306 EFI_FILE_INFO
*DirEntry
;
1307 CHAR16 FileName
[256], *Extension
;
1308 struct LOADER_LIST
*LoaderList
= NULL
, *NewLoader
;
1309 BOOLEAN FoundFallbackDuplicate
= FALSE
;
1311 if ((!SelfDirPath
|| !Path
|| ((StriCmp(Path
, SelfDirPath
) == 0) && (Volume
->DeviceHandle
!= SelfVolume
->DeviceHandle
)) ||
1312 (StriCmp(Path
, SelfDirPath
) != 0)) && (ShouldScan(Volume
, Path
))) {
1313 // look through contents of the directory
1314 DirIterOpen(Volume
->RootDir
, Path
, &DirIter
);
1315 while (DirIterNext(&DirIter
, 2, Pattern
, &DirEntry
)) {
1316 Extension
= FindExtension(DirEntry
->FileName
);
1317 if (DirEntry
->FileName
[0] == '.' ||
1318 StriCmp(Extension
, L
".icns") == 0 ||
1319 StriCmp(Extension
, L
".png") == 0 ||
1320 (StriCmp(DirEntry
->FileName
, FALLBACK_BASENAME
) == 0 && (StriCmp(Path
, L
"EFI\\BOOT") == 0)) ||
1321 StriSubCmp(L
"shell", DirEntry
->FileName
) ||
1322 IsSymbolicLink(Volume
, Path
, DirEntry
) || /* is symbolic link */
1323 HasSignedCounterpart(Volume
, Path
, DirEntry
->FileName
) || /* a file with same name plus ".efi.signed" is present */
1324 FilenameIn(Volume
, Path
, DirEntry
->FileName
, GlobalConfig
.DontScanFiles
))
1325 continue; // skip this
1328 SPrint(FileName
, 255, L
"\\%s\\%s", Path
, DirEntry
->FileName
);
1330 SPrint(FileName
, 255, L
"\\%s", DirEntry
->FileName
);
1331 CleanUpPathNameSlashes(FileName
);
1333 if(!IsValidLoader(Volume
->RootDir
, FileName
))
1336 NewLoader
= AllocateZeroPool(sizeof(struct LOADER_LIST
));
1337 if (NewLoader
!= NULL
) {
1338 NewLoader
->FileName
= StrDuplicate(FileName
);
1339 NewLoader
->TimeStamp
= DirEntry
->ModificationTime
;
1340 LoaderList
= AddLoaderListEntry(LoaderList
, NewLoader
);
1341 if (DuplicatesFallback(Volume
, FileName
))
1342 FoundFallbackDuplicate
= TRUE
;
1344 MyFreePool(Extension
);
1347 NewLoader
= LoaderList
;
1348 while (NewLoader
!= NULL
) {
1349 AddLoaderEntry(NewLoader
->FileName
, NULL
, Volume
);
1350 NewLoader
= NewLoader
->NextEntry
;
1353 CleanUpLoaderList(LoaderList
);
1354 Status
= DirIterClose(&DirIter
);
1355 // NOTE: EFI_INVALID_PARAMETER really is an error that should be reported;
1356 // but I've gotten reports from users who are getting this error occasionally
1357 // and I can't find anything wrong or reproduce the problem, so I'm putting
1358 // it down to buggy EFI implementations and ignoring that particular error....
1359 if ((Status
!= EFI_NOT_FOUND
) && (Status
!= EFI_INVALID_PARAMETER
)) {
1361 SPrint(FileName
, 255, L
"while scanning the %s directory", Path
);
1363 StrCpy(FileName
, L
"while scanning the root directory");
1364 CheckError(Status
, FileName
);
1365 } // if (Status != EFI_NOT_FOUND)
1366 } // if not scanning a blacklisted directory
1368 return FoundFallbackDuplicate
;
1369 } /* static VOID ScanLoaderDir() */
1371 // Run the IPXE_DISCOVER_NAME program, which obtains the IP address of the boot
1372 // server and the name of the boot file it delivers.
1373 CHAR16
* RuniPXEDiscover(EFI_HANDLE Volume
)
1376 EFI_DEVICE_PATH
*FilePath
;
1377 EFI_HANDLE iPXEHandle
;
1378 CHAR16
*boot_info
= NULL
;
1379 UINTN boot_info_size
= 0;
1381 FilePath
= FileDevicePath (Volume
, IPXE_DISCOVER_NAME
);
1382 Status
= refit_call6_wrapper(BS
->LoadImage
, FALSE
, SelfImageHandle
, FilePath
,
1383 NULL
, 0, &iPXEHandle
);
1387 Status
= refit_call3_wrapper(BS
->StartImage
, iPXEHandle
, &boot_info_size
, &boot_info
);
1390 } // RuniPXEDiscover()
1392 // Scan for network (PXE) boot servers. This function relies on the presence
1393 // of the IPXE_DISCOVER_NAME and IPXE_NAME program files on the volume from
1394 // which rEFInd launched. As of December 6, 2014, these tools aren't entirely
1395 // reliable. See BUILDING.txt for information on building them.
1396 static VOID
ScanNetboot() {
1397 CHAR16
*iPXEFileName
= IPXE_NAME
;
1399 REFIT_VOLUME
*NetVolume
;
1401 if (FileExists(SelfVolume
->RootDir
, IPXE_DISCOVER_NAME
) &&
1402 FileExists(SelfVolume
->RootDir
, IPXE_NAME
) &&
1403 IsValidLoader(SelfVolume
->RootDir
, IPXE_DISCOVER_NAME
) &&
1404 IsValidLoader(SelfVolume
->RootDir
, IPXE_NAME
)) {
1405 Location
= RuniPXEDiscover(SelfVolume
->DeviceHandle
);
1406 if (Location
!= NULL
&& FileExists(SelfVolume
->RootDir
, iPXEFileName
)) {
1407 NetVolume
= AllocatePool(sizeof(REFIT_VOLUME
));
1408 CopyMem(NetVolume
, SelfVolume
, sizeof(REFIT_VOLUME
));
1409 NetVolume
->DiskKind
= DISK_KIND_NET
;
1410 NetVolume
->VolBadgeImage
= BuiltinIcon(BUILTIN_ICON_VOL_NET
);
1411 NetVolume
->PartName
= NetVolume
->VolName
= NULL
;
1412 AddLoaderEntry(iPXEFileName
, Location
, NetVolume
);
1413 MyFreePool(NetVolume
);
1414 } // if support files exist and are valid
1416 } // VOID ScanNetBoot()
1418 static VOID
ScanEfiFiles(REFIT_VOLUME
*Volume
) {
1420 REFIT_DIR_ITER EfiDirIter
;
1421 EFI_FILE_INFO
*EfiDirEntry
;
1422 CHAR16 FileName
[256], *Directory
= NULL
, *MatchPatterns
, *VolName
= NULL
, *SelfPath
;
1424 BOOLEAN ScanFallbackLoader
= TRUE
;
1425 BOOLEAN FoundBRBackup
= FALSE
;
1427 if ((Volume
->RootDir
!= NULL
) && (Volume
->VolName
!= NULL
) && (Volume
->IsReadable
)) {
1428 MatchPatterns
= StrDuplicate(LOADER_MATCH_PATTERNS
);
1429 if (GlobalConfig
.ScanAllLinux
)
1430 MergeStrings(&MatchPatterns
, LINUX_MATCH_PATTERNS
, L
',');
1432 // check for Mac OS X boot loader
1433 if (ShouldScan(Volume
, MACOSX_LOADER_DIR
)) {
1434 StrCpy(FileName
, MACOSX_LOADER_PATH
);
1435 if (FileExists(Volume
->RootDir
, FileName
) && !FilenameIn(Volume
, MACOSX_LOADER_DIR
, L
"boot.efi", GlobalConfig
.DontScanFiles
)) {
1436 AddLoaderEntry(FileName
, L
"Mac OS X", Volume
);
1437 if (DuplicatesFallback(Volume
, FileName
))
1438 ScanFallbackLoader
= FALSE
;
1442 StrCpy(FileName
, L
"System\\Library\\CoreServices\\xom.efi");
1443 if (FileExists(Volume
->RootDir
, FileName
) && !FilenameIn(Volume
, MACOSX_LOADER_DIR
, L
"xom.efi", GlobalConfig
.DontScanFiles
)) {
1444 AddLoaderEntry(FileName
, L
"Windows XP (XoM)", Volume
);
1445 if (DuplicatesFallback(Volume
, FileName
))
1446 ScanFallbackLoader
= FALSE
;
1448 } // if should scan Mac directory
1450 // check for Microsoft boot loader/menu
1451 if (ShouldScan(Volume
, L
"EFI\\Microsoft\\Boot")) {
1452 StrCpy(FileName
, L
"EFI\\Microsoft\\Boot\\bkpbootmgfw.efi");
1453 if (FileExists(Volume
->RootDir
, FileName
) && !FilenameIn(Volume
, L
"EFI\\Microsoft\\Boot", L
"bkpbootmgfw.efi",
1454 GlobalConfig
.DontScanFiles
)) {
1455 AddLoaderEntry(FileName
, L
"Microsoft EFI boot (Boot Repair backup)", Volume
);
1456 FoundBRBackup
= TRUE
;
1457 if (DuplicatesFallback(Volume
, FileName
))
1458 ScanFallbackLoader
= FALSE
;
1460 StrCpy(FileName
, L
"EFI\\Microsoft\\Boot\\bootmgfw.efi");
1461 if (FileExists(Volume
->RootDir
, FileName
) && !FilenameIn(Volume
, L
"EFI\\Microsoft\\Boot", L
"bootmgfw.efi", GlobalConfig
.DontScanFiles
)) {
1463 AddLoaderEntry(FileName
, L
"Supposed Microsoft EFI boot (probably GRUB)", Volume
);
1465 AddLoaderEntry(FileName
, L
"Microsoft EFI boot", Volume
);
1466 if (DuplicatesFallback(Volume
, FileName
))
1467 ScanFallbackLoader
= FALSE
;
1471 // scan the root directory for EFI executables
1472 if (ScanLoaderDir(Volume
, L
"\\", MatchPatterns
))
1473 ScanFallbackLoader
= FALSE
;
1475 // scan subdirectories of the EFI directory (as per the standard)
1476 DirIterOpen(Volume
->RootDir
, L
"EFI", &EfiDirIter
);
1477 while (DirIterNext(&EfiDirIter
, 1, NULL
, &EfiDirEntry
)) {
1478 if (StriCmp(EfiDirEntry
->FileName
, L
"tools") == 0 || EfiDirEntry
->FileName
[0] == '.')
1479 continue; // skip this, doesn't contain boot loaders or is scanned later
1480 SPrint(FileName
, 255, L
"EFI\\%s", EfiDirEntry
->FileName
);
1481 if (ScanLoaderDir(Volume
, FileName
, MatchPatterns
))
1482 ScanFallbackLoader
= FALSE
;
1484 Status
= DirIterClose(&EfiDirIter
);
1485 if (Status
!= EFI_NOT_FOUND
)
1486 CheckError(Status
, L
"while scanning the EFI directory");
1488 // Scan user-specified (or additional default) directories....
1490 while ((Directory
= FindCommaDelimited(GlobalConfig
.AlsoScan
, i
++)) != NULL
) {
1491 if (ShouldScan(Volume
, Directory
)) {
1492 SplitVolumeAndFilename(&Directory
, &VolName
);
1493 CleanUpPathNameSlashes(Directory
);
1494 Length
= StrLen(Directory
);
1495 if ((Length
> 0) && ScanLoaderDir(Volume
, Directory
, MatchPatterns
))
1496 ScanFallbackLoader
= FALSE
;
1497 MyFreePool(VolName
);
1498 } // if should scan dir
1499 MyFreePool(Directory
);
1502 // Don't scan the fallback loader if it's on the same volume and a duplicate of rEFInd itself....
1503 SelfPath
= DevicePathToStr(SelfLoadedImage
->FilePath
);
1504 CleanUpPathNameSlashes(SelfPath
);
1505 if ((Volume
->DeviceHandle
== SelfLoadedImage
->DeviceHandle
) && DuplicatesFallback(Volume
, SelfPath
))
1506 ScanFallbackLoader
= FALSE
;
1508 // If not a duplicate & if it exists & if it's not us, create an entry
1509 // for the fallback boot loader
1510 if (ScanFallbackLoader
&& FileExists(Volume
->RootDir
, FALLBACK_FULLNAME
) && ShouldScan(Volume
, L
"EFI\\BOOT"))
1511 AddLoaderEntry(FALLBACK_FULLNAME
, L
"Fallback boot loader", Volume
);
1513 } // static VOID ScanEfiFiles()
1515 // Scan internal disks for valid EFI boot loaders....
1516 static VOID
ScanInternal(VOID
) {
1519 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1520 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_INTERNAL
) {
1521 ScanEfiFiles(Volumes
[VolumeIndex
]);
1524 } // static VOID ScanInternal()
1526 // Scan external disks for valid EFI boot loaders....
1527 static VOID
ScanExternal(VOID
) {
1530 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1531 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_EXTERNAL
) {
1532 ScanEfiFiles(Volumes
[VolumeIndex
]);
1535 } // static VOID ScanExternal()
1537 // Scan internal disks for valid EFI boot loaders....
1538 static VOID
ScanOptical(VOID
) {
1541 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1542 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_OPTICAL
) {
1543 ScanEfiFiles(Volumes
[VolumeIndex
]);
1546 } // static VOID ScanOptical()
1549 // legacy boot functions
1552 static EFI_STATUS
ActivateMbrPartition(IN EFI_BLOCK_IO
*BlockIO
, IN UINTN PartitionIndex
)
1555 UINT8 SectorBuffer
[512];
1556 MBR_PARTITION_INFO
*MbrTable
, *EMbrTable
;
1557 UINT32 ExtBase
, ExtCurrent
, NextExtCurrent
;
1558 UINTN LogicalPartitionIndex
= 4;
1560 BOOLEAN HaveBootCode
;
1563 Status
= refit_call5_wrapper(BlockIO
->ReadBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, 0, 512, SectorBuffer
);
1564 if (EFI_ERROR(Status
))
1566 if (*((UINT16
*)(SectorBuffer
+ 510)) != 0xaa55)
1567 return EFI_NOT_FOUND
; // safety measure #1
1569 // add boot code if necessary
1570 HaveBootCode
= FALSE
;
1571 for (i
= 0; i
< MBR_BOOTCODE_SIZE
; i
++) {
1572 if (SectorBuffer
[i
] != 0) {
1573 HaveBootCode
= TRUE
;
1577 if (!HaveBootCode
) {
1578 // no boot code found in the MBR, add the syslinux MBR code
1579 SetMem(SectorBuffer
, MBR_BOOTCODE_SIZE
, 0);
1580 CopyMem(SectorBuffer
, syslinux_mbr
, SYSLINUX_MBR_SIZE
);
1583 // set the partition active
1584 MbrTable
= (MBR_PARTITION_INFO
*)(SectorBuffer
+ 446);
1586 for (i
= 0; i
< 4; i
++) {
1587 if (MbrTable
[i
].Flags
!= 0x00 && MbrTable
[i
].Flags
!= 0x80)
1588 return EFI_NOT_FOUND
; // safety measure #2
1589 if (i
== PartitionIndex
)
1590 MbrTable
[i
].Flags
= 0x80;
1591 else if (PartitionIndex
>= 4 && IS_EXTENDED_PART_TYPE(MbrTable
[i
].Type
)) {
1592 MbrTable
[i
].Flags
= 0x80;
1593 ExtBase
= MbrTable
[i
].StartLBA
;
1595 MbrTable
[i
].Flags
= 0x00;
1599 Status
= refit_call5_wrapper(BlockIO
->WriteBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, 0, 512, SectorBuffer
);
1600 if (EFI_ERROR(Status
))
1603 if (PartitionIndex
>= 4) {
1604 // we have to activate a logical partition, so walk the EMBR chain
1606 // NOTE: ExtBase was set above while looking at the MBR table
1607 for (ExtCurrent
= ExtBase
; ExtCurrent
; ExtCurrent
= NextExtCurrent
) {
1608 // read current EMBR
1609 Status
= refit_call5_wrapper(BlockIO
->ReadBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, ExtCurrent
, 512, SectorBuffer
);
1610 if (EFI_ERROR(Status
))
1612 if (*((UINT16
*)(SectorBuffer
+ 510)) != 0xaa55)
1613 return EFI_NOT_FOUND
; // safety measure #3
1615 // scan EMBR, set appropriate partition active
1616 EMbrTable
= (MBR_PARTITION_INFO
*)(SectorBuffer
+ 446);
1618 for (i
= 0; i
< 4; i
++) {
1619 if (EMbrTable
[i
].Flags
!= 0x00 && EMbrTable
[i
].Flags
!= 0x80)
1620 return EFI_NOT_FOUND
; // safety measure #4
1621 if (EMbrTable
[i
].StartLBA
== 0 || EMbrTable
[i
].Size
== 0)
1623 if (IS_EXTENDED_PART_TYPE(EMbrTable
[i
].Type
)) {
1624 // link to next EMBR
1625 NextExtCurrent
= ExtBase
+ EMbrTable
[i
].StartLBA
;
1626 EMbrTable
[i
].Flags
= (PartitionIndex
>= LogicalPartitionIndex
) ? 0x80 : 0x00;
1629 // logical partition
1630 EMbrTable
[i
].Flags
= (PartitionIndex
== LogicalPartitionIndex
) ? 0x80 : 0x00;
1631 LogicalPartitionIndex
++;
1635 // write current EMBR
1636 Status
= refit_call5_wrapper(BlockIO
->WriteBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, ExtCurrent
, 512, SectorBuffer
);
1637 if (EFI_ERROR(Status
))
1640 if (PartitionIndex
< LogicalPartitionIndex
)
1641 break; // stop the loop, no need to touch further EMBRs
1647 } /* static EFI_STATUS ActivateMbrPartition() */
1649 // early 2006 Core Duo / Core Solo models
1650 static UINT8 LegacyLoaderDevicePath1Data
[] = {
1651 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1652 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1653 0xFF, 0xFF, 0xF9, 0xFF, 0x00, 0x00, 0x00, 0x00,
1654 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1655 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1656 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1658 // mid-2006 Mac Pro (and probably other Core 2 models)
1659 static UINT8 LegacyLoaderDevicePath2Data
[] = {
1660 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1661 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1662 0xFF, 0xFF, 0xF7, 0xFF, 0x00, 0x00, 0x00, 0x00,
1663 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1664 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1665 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1667 // mid-2007 MBP ("Santa Rosa" based models)
1668 static UINT8 LegacyLoaderDevicePath3Data
[] = {
1669 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1670 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1671 0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
1672 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1673 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1674 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1677 static UINT8 LegacyLoaderDevicePath4Data
[] = {
1678 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1679 0x00, 0x00, 0xC0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1680 0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
1681 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1682 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1683 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1685 // late-2008 MB/MBP (NVidia chipset)
1686 static UINT8 LegacyLoaderDevicePath5Data
[] = {
1687 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1688 0x00, 0x40, 0xCB, 0xFF, 0x00, 0x00, 0x00, 0x00,
1689 0xFF, 0xBF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
1690 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1691 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1692 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1695 static EFI_DEVICE_PATH
*LegacyLoaderList
[] = {
1696 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath1Data
,
1697 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath2Data
,
1698 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath3Data
,
1699 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath4Data
,
1700 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath5Data
,
1704 #define MAX_DISCOVERED_PATHS (16)
1706 static VOID
StartLegacy(IN LEGACY_ENTRY
*Entry
, IN CHAR16
*SelectionName
)
1709 EG_IMAGE
*BootLogoImage
;
1710 UINTN ErrorInStep
= 0;
1711 EFI_DEVICE_PATH
*DiscoveredPathList
[MAX_DISCOVERED_PATHS
];
1713 BeginExternalScreen(TRUE
, L
"Booting Legacy OS (Mac mode)");
1715 BootLogoImage
= LoadOSIcon(Entry
->Volume
->OSIconName
, L
"legacy", TRUE
);
1716 if (BootLogoImage
!= NULL
)
1717 BltImageAlpha(BootLogoImage
,
1718 (UGAWidth
- BootLogoImage
->Width
) >> 1,
1719 (UGAHeight
- BootLogoImage
->Height
) >> 1,
1720 &StdBackgroundPixel
);
1722 if (Entry
->Volume
->IsMbrPartition
) {
1723 ActivateMbrPartition(Entry
->Volume
->WholeDiskBlockIO
, Entry
->Volume
->MbrPartitionIndex
);
1726 ExtractLegacyLoaderPaths(DiscoveredPathList
, MAX_DISCOVERED_PATHS
, LegacyLoaderList
);
1728 StoreLoaderName(SelectionName
);
1729 Status
= StartEFIImageList(DiscoveredPathList
, Entry
->LoadOptions
, TYPE_LEGACY
, L
"legacy loader", 0, &ErrorInStep
, TRUE
, FALSE
);
1730 if (Status
== EFI_NOT_FOUND
) {
1731 if (ErrorInStep
== 1) {
1732 Print(L
"\nPlease make sure that you have the latest firmware update installed.\n");
1733 } else if (ErrorInStep
== 3) {
1734 Print(L
"\nThe firmware refused to boot from the selected volume. Note that external\n"
1735 L
"hard drives are not well-supported by Apple's firmware for legacy OS booting.\n");
1738 FinishExternalScreen();
1739 } /* static VOID StartLegacy() */
1741 // Start a device on a non-Mac using the EFI_LEGACY_BIOS_PROTOCOL
1742 static VOID
StartLegacyUEFI(LEGACY_ENTRY
*Entry
, CHAR16
*SelectionName
)
1744 BeginExternalScreen(TRUE
, L
"Booting Legacy OS (UEFI mode)");
1745 StoreLoaderName(SelectionName
);
1747 BdsLibConnectDevicePath (Entry
->BdsOption
->DevicePath
);
1748 BdsLibDoLegacyBoot(Entry
->BdsOption
);
1750 // If we get here, it means that there was a failure....
1751 Print(L
"Failure booting legacy (BIOS) OS.");
1753 FinishExternalScreen();
1754 } // static VOID StartLegacyUEFI()
1756 static LEGACY_ENTRY
* AddLegacyEntry(IN CHAR16
*LoaderTitle
, IN REFIT_VOLUME
*Volume
)
1758 LEGACY_ENTRY
*Entry
, *SubEntry
;
1759 REFIT_MENU_SCREEN
*SubScreen
;
1760 CHAR16
*VolDesc
, *LegacyTitle
;
1761 CHAR16 ShortcutLetter
= 0;
1763 if (LoaderTitle
== NULL
) {
1764 if (Volume
->OSName
!= NULL
) {
1765 LoaderTitle
= Volume
->OSName
;
1766 if (LoaderTitle
[0] == 'W' || LoaderTitle
[0] == 'L')
1767 ShortcutLetter
= LoaderTitle
[0];
1769 LoaderTitle
= L
"Legacy OS";
1771 if (Volume
->VolName
!= NULL
)
1772 VolDesc
= Volume
->VolName
;
1774 VolDesc
= (Volume
->DiskKind
== DISK_KIND_OPTICAL
) ? L
"CD" : L
"HD";
1776 LegacyTitle
= AllocateZeroPool(256 * sizeof(CHAR16
));
1777 if (LegacyTitle
!= NULL
)
1778 SPrint(LegacyTitle
, 255, L
"Boot %s from %s", LoaderTitle
, VolDesc
);
1779 if (IsInSubstring(LegacyTitle
, GlobalConfig
.DontScanVolumes
)) {
1780 MyFreePool(LegacyTitle
);
1784 // prepare the menu entry
1785 Entry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1786 Entry
->me
.Title
= LegacyTitle
;
1787 Entry
->me
.Tag
= TAG_LEGACY
;
1789 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1790 Entry
->me
.Image
= LoadOSIcon(Volume
->OSIconName
, L
"legacy", FALSE
);
1791 Entry
->me
.BadgeImage
= Volume
->VolBadgeImage
;
1792 Entry
->Volume
= Volume
;
1793 Entry
->LoadOptions
= (Volume
->DiskKind
== DISK_KIND_OPTICAL
) ? L
"CD" :
1794 ((Volume
->DiskKind
== DISK_KIND_EXTERNAL
) ? L
"USB" : L
"HD");
1795 Entry
->Enabled
= TRUE
;
1797 // create the submenu
1798 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
1799 SubScreen
->Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1800 SPrint(SubScreen
->Title
, 255, L
"Boot Options for %s on %s", LoaderTitle
, VolDesc
);
1801 SubScreen
->TitleImage
= Entry
->me
.Image
;
1802 SubScreen
->Hint1
= StrDuplicate(SUBSCREEN_HINT1
);
1803 if (GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_EDITOR
) {
1804 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2_NO_EDITOR
);
1806 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2
);
1810 SubEntry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1811 SubEntry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1812 SPrint(SubEntry
->me
.Title
, 255, L
"Boot %s", LoaderTitle
);
1813 SubEntry
->me
.Tag
= TAG_LEGACY
;
1814 SubEntry
->Volume
= Entry
->Volume
;
1815 SubEntry
->LoadOptions
= Entry
->LoadOptions
;
1816 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
1818 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
1819 Entry
->me
.SubScreen
= SubScreen
;
1820 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1822 } /* static LEGACY_ENTRY * AddLegacyEntry() */
1825 // default volume badge icon based on disk kind
1826 static EG_IMAGE
* GetDiskBadge(IN UINTN DiskType
) {
1827 EG_IMAGE
* Badge
= NULL
;
1831 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_INTERNAL
);
1834 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_EXTERNAL
);
1837 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_OPTICAL
);
1841 } // static EG_IMAGE * GetDiskBadge()
1844 Create a rEFInd boot option from a Legacy BIOS protocol option.
1846 static LEGACY_ENTRY
* AddLegacyEntryUEFI(BDS_COMMON_OPTION
*BdsOption
, IN UINT16 DiskType
)
1848 LEGACY_ENTRY
*Entry
, *SubEntry
;
1849 REFIT_MENU_SCREEN
*SubScreen
;
1850 CHAR16 ShortcutLetter
= 0;
1851 CHAR16
*LegacyDescription
= StrDuplicate(BdsOption
->Description
);
1853 if (IsInSubstring(LegacyDescription
, GlobalConfig
.DontScanVolumes
))
1856 // Remove stray spaces, since many EFIs produce descriptions with lots of
1857 // extra spaces, especially at the end; this throws off centering of the
1858 // description on the screen....
1859 LimitStringLength(LegacyDescription
, 100);
1861 // prepare the menu entry
1862 Entry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1863 Entry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1864 SPrint(Entry
->me
.Title
, 255, L
"Boot legacy target %s", LegacyDescription
);
1865 Entry
->me
.Tag
= TAG_LEGACY_UEFI
;
1867 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1868 Entry
->me
.Image
= LoadOSIcon(L
"legacy", L
"legacy", TRUE
);
1869 Entry
->LoadOptions
= (DiskType
== BBS_CDROM
) ? L
"CD" :
1870 ((DiskType
== BBS_USB
) ? L
"USB" : L
"HD");
1871 Entry
->me
.BadgeImage
= GetDiskBadge(DiskType
);
1872 Entry
->BdsOption
= BdsOption
;
1873 Entry
->Enabled
= TRUE
;
1875 // create the submenu
1876 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
1877 SubScreen
->Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1878 SPrint(SubScreen
->Title
, 255, L
"No boot options for legacy target");
1879 SubScreen
->TitleImage
= Entry
->me
.Image
;
1880 SubScreen
->Hint1
= StrDuplicate(SUBSCREEN_HINT1
);
1881 if (GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_EDITOR
) {
1882 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2_NO_EDITOR
);
1884 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2
);
1888 SubEntry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1889 SubEntry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1890 SPrint(SubEntry
->me
.Title
, 255, L
"Boot %s", LegacyDescription
);
1891 SubEntry
->me
.Tag
= TAG_LEGACY_UEFI
;
1892 Entry
->BdsOption
= BdsOption
;
1893 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
1895 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
1896 Entry
->me
.SubScreen
= SubScreen
;
1897 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1899 } /* static LEGACY_ENTRY * AddLegacyEntryUEFI() */
1902 Scan for legacy BIOS targets on machines that implement EFI_LEGACY_BIOS_PROTOCOL.
1903 In testing, protocol has not been implemented on Macs but has been
1904 implemented on several Dell PCs and an ASUS motherboard.
1905 Restricts output to disks of the specified DiskType.
1907 static VOID
ScanLegacyUEFI(IN UINTN DiskType
)
1910 EFI_LEGACY_BIOS_PROTOCOL
*LegacyBios
;
1911 UINT16
*BootOrder
= NULL
;
1913 CHAR16 BootOption
[10];
1914 UINTN BootOrderSize
= 0;
1916 BDS_COMMON_OPTION
*BdsOption
;
1917 LIST_ENTRY TempList
;
1918 BBS_BBS_DEVICE_PATH
*BbsDevicePath
= NULL
;
1919 BOOLEAN SearchingForUsb
= FALSE
;
1921 InitializeListHead (&TempList
);
1922 ZeroMem (Buffer
, sizeof (Buffer
));
1924 // If LegacyBios protocol is not implemented on this platform, then
1925 //we do not support this type of legacy boot on this machine.
1926 Status
= refit_call3_wrapper(gBS
->LocateProtocol
, &gEfiLegacyBootProtocolGuid
, NULL
, (VOID
**) &LegacyBios
);
1927 if (EFI_ERROR (Status
))
1930 // EFI calls USB drives BBS_HARDDRIVE, but we want to distinguish them,
1931 // so we set DiskType inappropriately elsewhere in the program and
1932 // "translate" it here.
1933 if (DiskType
== BBS_USB
) {
1934 DiskType
= BBS_HARDDISK
;
1935 SearchingForUsb
= TRUE
;
1938 // Grab the boot order
1939 BootOrder
= BdsLibGetVariableAndSize(L
"BootOrder", &gEfiGlobalVariableGuid
, &BootOrderSize
);
1940 if (BootOrder
== NULL
) {
1945 while (Index
< BootOrderSize
/ sizeof (UINT16
))
1947 // Grab each boot option variable from the boot order, and convert
1948 // the variable into a BDS boot option
1949 UnicodeSPrint (BootOption
, sizeof (BootOption
), L
"Boot%04x", BootOrder
[Index
]);
1950 BdsOption
= BdsLibVariableToOption (&TempList
, BootOption
);
1952 if (BdsOption
!= NULL
) {
1953 BbsDevicePath
= (BBS_BBS_DEVICE_PATH
*)BdsOption
->DevicePath
;
1954 // Only add the entry if it is of a requested type (e.g. USB, HD)
1955 // Two checks necessary because some systems return EFI boot loaders
1956 // with a DeviceType value that would inappropriately include them
1957 // as legacy loaders....
1958 if ((BbsDevicePath
->DeviceType
== DiskType
) && (BdsOption
->DevicePath
->Type
== DEVICE_TYPE_BIOS
)) {
1959 // USB flash drives appear as hard disks with certain media flags set.
1960 // Look for this, and if present, pass it on with the (technically
1961 // incorrect, but internally useful) BBS_TYPE_USB flag set.
1962 if (DiskType
== BBS_HARDDISK
) {
1963 if (SearchingForUsb
&& (BbsDevicePath
->StatusFlag
& (BBS_MEDIA_PRESENT
| BBS_MEDIA_MAYBE_PRESENT
))) {
1964 AddLegacyEntryUEFI(BdsOption
, BBS_USB
);
1965 } else if (!SearchingForUsb
&& !(BbsDevicePath
->StatusFlag
& (BBS_MEDIA_PRESENT
| BBS_MEDIA_MAYBE_PRESENT
))) {
1966 AddLegacyEntryUEFI(BdsOption
, DiskType
);
1969 AddLegacyEntryUEFI(BdsOption
, DiskType
);
1972 } // if (BdsOption != NULL)
1975 } /* static VOID ScanLegacyUEFI() */
1977 static VOID
ScanLegacyVolume(REFIT_VOLUME
*Volume
, UINTN VolumeIndex
) {
1979 BOOLEAN ShowVolume
, HideIfOthersFound
;
1982 HideIfOthersFound
= FALSE
;
1983 if (Volume
->IsAppleLegacy
) {
1985 HideIfOthersFound
= TRUE
;
1986 } else if (Volume
->HasBootCode
) {
1988 if (Volume
->BlockIO
== Volume
->WholeDiskBlockIO
&&
1989 Volume
->BlockIOOffset
== 0 &&
1990 Volume
->OSName
== NULL
)
1991 // this is a whole disk (MBR) entry; hide if we have entries for partitions
1992 HideIfOthersFound
= TRUE
;
1994 if (HideIfOthersFound
) {
1995 // check for other bootable entries on the same disk
1996 for (VolumeIndex2
= 0; VolumeIndex2
< VolumesCount
; VolumeIndex2
++) {
1997 if (VolumeIndex2
!= VolumeIndex
&& Volumes
[VolumeIndex2
]->HasBootCode
&&
1998 Volumes
[VolumeIndex2
]->WholeDiskBlockIO
== Volume
->WholeDiskBlockIO
)
2004 AddLegacyEntry(NULL
, Volume
);
2005 } // static VOID ScanLegacyVolume()
2007 // Scan attached optical discs for legacy (BIOS) boot code
2008 // and add anything found to the list....
2009 static VOID
ScanLegacyDisc(VOID
)
2012 REFIT_VOLUME
*Volume
;
2014 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
) {
2015 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
2016 Volume
= Volumes
[VolumeIndex
];
2017 if (Volume
->DiskKind
== DISK_KIND_OPTICAL
)
2018 ScanLegacyVolume(Volume
, VolumeIndex
);
2020 } else if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
2021 ScanLegacyUEFI(BBS_CDROM
);
2023 } /* static VOID ScanLegacyDisc() */
2025 // Scan internal hard disks for legacy (BIOS) boot code
2026 // and add anything found to the list....
2027 static VOID
ScanLegacyInternal(VOID
)
2030 REFIT_VOLUME
*Volume
;
2032 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
) {
2033 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
2034 Volume
= Volumes
[VolumeIndex
];
2035 if (Volume
->DiskKind
== DISK_KIND_INTERNAL
)
2036 ScanLegacyVolume(Volume
, VolumeIndex
);
2038 } else if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
2039 // TODO: This actually picks up USB flash drives, too; try to find
2040 // a way to differentiate the two....
2041 ScanLegacyUEFI(BBS_HARDDISK
);
2043 } /* static VOID ScanLegacyInternal() */
2045 // Scan external disks for legacy (BIOS) boot code
2046 // and add anything found to the list....
2047 static VOID
ScanLegacyExternal(VOID
)
2050 REFIT_VOLUME
*Volume
;
2052 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
) {
2053 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
2054 Volume
= Volumes
[VolumeIndex
];
2055 if (Volume
->DiskKind
== DISK_KIND_EXTERNAL
)
2056 ScanLegacyVolume(Volume
, VolumeIndex
);
2058 } else if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
2059 // TODO: This actually doesn't do anything useful; leaving in hopes of
2060 // fixing it later....
2061 ScanLegacyUEFI(BBS_USB
);
2063 } /* static VOID ScanLegacyExternal() */
2066 // pre-boot tool functions
2069 static VOID
StartTool(IN LOADER_ENTRY
*Entry
)
2071 BeginExternalScreen(Entry
->UseGraphicsMode
, Entry
->me
.Title
+ 6); // assumes "Start <title>" as assigned below
2072 StoreLoaderName(Entry
->me
.Title
);
2073 StartEFIImage(Entry
->DevicePath
, Entry
->LoadOptions
, TYPE_EFI
,
2074 Basename(Entry
->LoaderPath
), Entry
->OSType
, NULL
, TRUE
, FALSE
);
2075 FinishExternalScreen();
2076 } /* static VOID StartTool() */
2078 static LOADER_ENTRY
* AddToolEntry(EFI_HANDLE DeviceHandle
, IN CHAR16
*LoaderPath
, IN CHAR16
*LoaderTitle
, IN EG_IMAGE
*Image
,
2079 IN CHAR16 ShortcutLetter
, IN BOOLEAN UseGraphicsMode
)
2081 LOADER_ENTRY
*Entry
;
2082 CHAR16
*TitleStr
= NULL
;
2084 Entry
= AllocateZeroPool(sizeof(LOADER_ENTRY
));
2086 TitleStr
= PoolPrint(L
"Start %s", LoaderTitle
);
2087 Entry
->me
.Title
= TitleStr
;
2088 Entry
->me
.Tag
= TAG_TOOL
;
2090 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
2091 Entry
->me
.Image
= Image
;
2092 Entry
->LoaderPath
= (LoaderPath
) ? StrDuplicate(LoaderPath
) : NULL
;
2093 Entry
->DevicePath
= FileDevicePath(DeviceHandle
, Entry
->LoaderPath
);
2094 Entry
->UseGraphicsMode
= UseGraphicsMode
;
2096 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
2098 } /* static LOADER_ENTRY * AddToolEntry() */
2101 // pre-boot driver functions
2104 static UINTN
ScanDriverDir(IN CHAR16
*Path
)
2107 REFIT_DIR_ITER DirIter
;
2109 EFI_FILE_INFO
*DirEntry
;
2110 CHAR16 FileName
[256];
2112 CleanUpPathNameSlashes(Path
);
2113 // look through contents of the directory
2114 DirIterOpen(SelfRootDir
, Path
, &DirIter
);
2115 while (DirIterNext(&DirIter
, 2, LOADER_MATCH_PATTERNS
, &DirEntry
)) {
2116 if (DirEntry
->FileName
[0] == '.')
2117 continue; // skip this
2119 SPrint(FileName
, 255, L
"%s\\%s", Path
, DirEntry
->FileName
);
2121 Status
= StartEFIImage(FileDevicePath(SelfLoadedImage
->DeviceHandle
, FileName
),
2122 L
"", TYPE_EFI
, DirEntry
->FileName
, 0, NULL
, FALSE
, TRUE
);
2124 Status
= DirIterClose(&DirIter
);
2125 if (Status
!= EFI_NOT_FOUND
) {
2126 SPrint(FileName
, 255, L
"while scanning the %s directory", Path
);
2127 CheckError(Status
, FileName
);
2132 #ifdef __MAKEWITH_GNUEFI
2133 static EFI_STATUS
ConnectAllDriversToAllControllers(VOID
)
2136 UINTN AllHandleCount
;
2137 EFI_HANDLE
*AllHandleBuffer
;
2140 EFI_HANDLE
*HandleBuffer
;
2146 Status
= LibLocateHandle(AllHandles
,
2151 if (EFI_ERROR(Status
))
2154 for (Index
= 0; Index
< AllHandleCount
; Index
++) {
2156 // Scan the handle database
2158 Status
= LibScanHandleDatabase(NULL
,
2160 AllHandleBuffer
[Index
],
2165 if (EFI_ERROR (Status
))
2169 if (HandleType
[Index
] & EFI_HANDLE_TYPE_DRIVER_BINDING_HANDLE
)
2171 if (HandleType
[Index
] & EFI_HANDLE_TYPE_IMAGE_HANDLE
)
2176 for (HandleIndex
= 0; HandleIndex
< HandleCount
; HandleIndex
++) {
2177 if (HandleType
[HandleIndex
] & EFI_HANDLE_TYPE_PARENT_HANDLE
)
2182 if (HandleType
[Index
] & EFI_HANDLE_TYPE_DEVICE_HANDLE
) {
2183 Status
= refit_call4_wrapper(BS
->ConnectController
,
2184 AllHandleBuffer
[Index
],
2192 MyFreePool (HandleBuffer
);
2193 MyFreePool (HandleType
);
2197 MyFreePool (AllHandleBuffer
);
2199 } /* EFI_STATUS ConnectAllDriversToAllControllers() */
2201 static EFI_STATUS
ConnectAllDriversToAllControllers(VOID
) {
2202 BdsLibConnectAllDriversToAllControllers();
2207 // Load all EFI drivers from rEFInd's "drivers" subdirectory and from the
2208 // directories specified by the user in the "scan_driver_dirs" configuration
2210 static VOID
LoadDrivers(VOID
)
2212 CHAR16
*Directory
, *SelfDirectory
;
2213 UINTN i
= 0, Length
, NumFound
= 0;
2215 // load drivers from the subdirectories of rEFInd's home directory specified
2216 // in the DRIVER_DIRS constant.
2217 while ((Directory
= FindCommaDelimited(DRIVER_DIRS
, i
++)) != NULL
) {
2218 SelfDirectory
= SelfDirPath
? StrDuplicate(SelfDirPath
) : NULL
;
2219 CleanUpPathNameSlashes(SelfDirectory
);
2220 MergeStrings(&SelfDirectory
, Directory
, L
'\\');
2221 NumFound
+= ScanDriverDir(SelfDirectory
);
2222 MyFreePool(Directory
);
2223 MyFreePool(SelfDirectory
);
2226 // Scan additional user-specified driver directories....
2228 while ((Directory
= FindCommaDelimited(GlobalConfig
.DriverDirs
, i
++)) != NULL
) {
2229 CleanUpPathNameSlashes(Directory
);
2230 Length
= StrLen(Directory
);
2232 NumFound
+= ScanDriverDir(Directory
);
2234 MyFreePool(Directory
);
2237 // connect all devices
2239 ConnectAllDriversToAllControllers();
2240 } /* static VOID LoadDrivers() */
2242 // Determine what (if any) type of legacy (BIOS) boot support is available
2243 static VOID
FindLegacyBootType(VOID
) {
2245 EFI_LEGACY_BIOS_PROTOCOL
*LegacyBios
;
2247 GlobalConfig
.LegacyType
= LEGACY_TYPE_NONE
;
2249 // UEFI-style legacy BIOS support is available only with some EFI implementations....
2250 Status
= refit_call3_wrapper(gBS
->LocateProtocol
, &gEfiLegacyBootProtocolGuid
, NULL
, (VOID
**) &LegacyBios
);
2251 if (!EFI_ERROR (Status
))
2252 GlobalConfig
.LegacyType
= LEGACY_TYPE_UEFI
;
2254 // Macs have their own system. If the firmware vendor code contains the
2255 // string "Apple", assume it's available. Note that this overrides the
2256 // UEFI type, and might yield false positives if the vendor string
2257 // contains "Apple" as part of something bigger, so this isn't 100%
2259 if (StriSubCmp(L
"Apple", ST
->FirmwareVendor
))
2260 GlobalConfig
.LegacyType
= LEGACY_TYPE_MAC
;
2261 } // static VOID FindLegacyBootType
2263 // Warn the user if legacy OS scans are enabled but the firmware can't support them....
2264 static VOID
WarnIfLegacyProblems(VOID
) {
2265 BOOLEAN found
= FALSE
;
2268 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_NONE
) {
2270 if (GlobalConfig
.ScanFor
[i
] == 'h' || GlobalConfig
.ScanFor
[i
] == 'b' || GlobalConfig
.ScanFor
[i
] == 'c' ||
2271 GlobalConfig
.ScanFor
[i
] == 'H' || GlobalConfig
.ScanFor
[i
] == 'B' || GlobalConfig
.ScanFor
[i
] == 'C')
2274 } while ((i
< NUM_SCAN_OPTIONS
) && (!found
));
2277 Print(L
"NOTE: refind.conf's 'scanfor' line specifies scanning for one or more legacy\n");
2278 Print(L
"(BIOS) boot options; however, this is not possible because your computer lacks\n");
2279 Print(L
"the necessary Compatibility Support Module (CSM) support or that support is\n");
2280 Print(L
"disabled in your firmware.\n");
2283 } // if no legacy support
2284 } // static VOID WarnIfLegacyProblems()
2286 // Locates boot loaders. NOTE: This assumes that GlobalConfig.LegacyType is set correctly.
2287 static VOID
ScanForBootloaders(VOID
) {
2290 BOOLEAN ScanForLegacy
= FALSE
;
2292 // Determine up-front if we'll be scanning for legacy loaders....
2293 for (i
= 0; i
< NUM_SCAN_OPTIONS
; i
++) {
2294 s
= GlobalConfig
.ScanFor
[i
];
2295 if ((s
== 'c') || (s
== 'C') || (s
== 'h') || (s
== 'H') || (s
== 'b') || (s
== 'B'))
2296 ScanForLegacy
= TRUE
;
2299 // If UEFI & scanning for legacy loaders & deep legacy scan, update NVRAM boot manager list
2300 if ((GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) && ScanForLegacy
&& GlobalConfig
.DeepLegacyScan
) {
2301 BdsDeleteAllInvalidLegacyBootOptions();
2302 BdsAddNonExistingLegacyBootOptions();
2305 // scan for loaders and tools, add them to the menu
2306 for (i
= 0; i
< NUM_SCAN_OPTIONS
; i
++) {
2307 switch(GlobalConfig
.ScanFor
[i
]) {
2312 ScanLegacyInternal();
2315 ScanLegacyExternal();
2318 ScanUserConfigured(GlobalConfig
.ConfigFilename
);
2335 // assign shortcut keys
2336 for (i
= 0; i
< MainMenu
.EntryCount
&& MainMenu
.Entries
[i
]->Row
== 0 && i
< 9; i
++)
2337 MainMenu
.Entries
[i
]->ShortcutDigit
= (CHAR16
)('1' + i
);
2339 // wait for user ACK when there were errors
2340 FinishTextScreen(FALSE
);
2341 } // static VOID ScanForBootloaders()
2343 // Locate a single tool from the specified Locations using one of the
2344 // specified Names and add it to the menu.
2345 static VOID
FindTool(CHAR16
*Locations
, CHAR16
*Names
, CHAR16
*Description
, UINTN Icon
) {
2346 UINTN j
= 0, k
, VolumeIndex
;
2347 CHAR16
*DirName
, *FileName
, *PathName
, FullDescription
[256];
2349 while ((DirName
= FindCommaDelimited(Locations
, j
++)) != NULL
) {
2351 while ((FileName
= FindCommaDelimited(Names
, k
++)) != NULL
) {
2352 PathName
= StrDuplicate(DirName
);
2353 MergeStrings(&PathName
, FileName
, (StriCmp(PathName
, L
"\\") == 0) ? 0 : L
'\\');
2354 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
2355 if ((Volumes
[VolumeIndex
]->RootDir
!= NULL
) && (FileExists(Volumes
[VolumeIndex
]->RootDir
, PathName
))) {
2356 SPrint(FullDescription
, 255, L
"%s at %s on %s", Description
, PathName
, Volumes
[VolumeIndex
]->VolName
);
2357 AddToolEntry(Volumes
[VolumeIndex
]->DeviceHandle
, PathName
, FullDescription
, BuiltinIcon(Icon
), 'S', FALSE
);
2360 MyFreePool(PathName
);
2361 MyFreePool(FileName
);
2363 MyFreePool(DirName
);
2364 } // while Locations
2365 } // VOID FindTool()
2367 // Add the second-row tags containing built-in and external tools (EFI shell,
2369 static VOID
ScanForTools(VOID
) {
2370 CHAR16
*FileName
= NULL
, *VolName
= NULL
, *MokLocations
, Description
[256];
2371 REFIT_MENU_ENTRY
*TempMenuEntry
;
2372 UINTN i
, j
, VolumeIndex
;
2376 MokLocations
= StrDuplicate(MOK_LOCATIONS
);
2377 if (MokLocations
!= NULL
)
2378 MergeStrings(&MokLocations
, SelfDirPath
, L
',');
2380 for (i
= 0; i
< NUM_TOOLS
; i
++) {
2381 switch(GlobalConfig
.ShowTools
[i
]) {
2382 // NOTE: Be sure that FileName is NULL at the end of each case.
2384 TempMenuEntry
= CopyMenuEntry(&MenuEntryShutdown
);
2385 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_SHUTDOWN
);
2386 AddMenuEntry(&MainMenu
, TempMenuEntry
);
2390 TempMenuEntry
= CopyMenuEntry(&MenuEntryReset
);
2391 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_RESET
);
2392 AddMenuEntry(&MainMenu
, TempMenuEntry
);
2396 TempMenuEntry
= CopyMenuEntry(&MenuEntryAbout
);
2397 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT
);
2398 AddMenuEntry(&MainMenu
, TempMenuEntry
);
2402 TempMenuEntry
= CopyMenuEntry(&MenuEntryExit
);
2403 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_EXIT
);
2404 AddMenuEntry(&MainMenu
, TempMenuEntry
);
2408 if (EfivarGetRaw(&GlobalGuid
, L
"OsIndicationsSupported", &b
, &j
) == EFI_SUCCESS
) {
2410 if (osind
& EFI_OS_INDICATIONS_BOOT_TO_FW_UI
) {
2411 TempMenuEntry
= CopyMenuEntry(&MenuEntryFirmware
);
2412 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_FIRMWARE
);
2413 AddMenuEntry(&MainMenu
, TempMenuEntry
);
2420 while ((FileName
= FindCommaDelimited(SHELL_NAMES
, j
++)) != NULL
) {
2421 if (FileExists(SelfRootDir
, FileName
)) {
2422 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, L
"EFI Shell", BuiltinIcon(BUILTIN_ICON_TOOL_SHELL
),
2425 MyFreePool(FileName
);
2431 while ((FileName
= FindCommaDelimited(GPTSYNC_NAMES
, j
++)) != NULL
) {
2432 if (FileExists(SelfRootDir
, FileName
)) {
2433 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, L
"Hybrid MBR tool", BuiltinIcon(BUILTIN_ICON_TOOL_PART
),
2436 MyFreePool(FileName
);
2443 while ((FileName
= FindCommaDelimited(GDISK_NAMES
, j
++)) != NULL
) {
2444 if (FileExists(SelfRootDir
, FileName
)) {
2445 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, L
"disk partitioning tool",
2446 BuiltinIcon(BUILTIN_ICON_TOOL_PART
), 'G', FALSE
);
2448 MyFreePool(FileName
);
2455 while ((FileName
= FindCommaDelimited(NETBOOT_NAMES
, j
++)) != NULL
) {
2456 if (FileExists(SelfRootDir
, FileName
)) {
2457 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, L
"Netboot",
2458 BuiltinIcon(BUILTIN_ICON_TOOL_NETBOOT
), 'N', FALSE
);
2460 MyFreePool(FileName
);
2465 case TAG_APPLE_RECOVERY
:
2466 FileName
= StrDuplicate(L
"\\com.apple.recovery.boot\\boot.efi");
2467 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
2468 if ((Volumes
[VolumeIndex
]->RootDir
!= NULL
) && (FileExists(Volumes
[VolumeIndex
]->RootDir
, FileName
))) {
2469 SPrint(Description
, 255, L
"Apple Recovery on %s", Volumes
[VolumeIndex
]->VolName
);
2470 AddToolEntry(Volumes
[VolumeIndex
]->DeviceHandle
, FileName
, Description
,
2471 BuiltinIcon(BUILTIN_ICON_TOOL_APPLE_RESCUE
), 'R', TRUE
);
2474 MyFreePool(FileName
);
2478 case TAG_WINDOWS_RECOVERY
:
2480 while ((FileName
= FindCommaDelimited(GlobalConfig
.WindowsRecoveryFiles
, j
++)) != NULL
) {
2481 SplitVolumeAndFilename(&FileName
, &VolName
);
2482 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
2483 if ((Volumes
[VolumeIndex
]->RootDir
!= NULL
) && (FileExists(Volumes
[VolumeIndex
]->RootDir
, FileName
)) &&
2484 ((VolName
== NULL
) || (StriCmp(VolName
, Volumes
[VolumeIndex
]->VolName
) == 0))) {
2485 SPrint(Description
, 255, L
"Microsoft Recovery on %s", Volumes
[VolumeIndex
]->VolName
);
2486 AddToolEntry(Volumes
[VolumeIndex
]->DeviceHandle
, FileName
, Description
,
2487 BuiltinIcon(BUILTIN_ICON_TOOL_WINDOWS_RESCUE
), 'R', TRUE
);
2491 MyFreePool(FileName
);
2493 MyFreePool(VolName
);
2498 FindTool(MokLocations
, MOK_NAMES
, L
"MOK utility", BUILTIN_ICON_TOOL_MOK_TOOL
);
2502 FindTool(MEMTEST_LOCATIONS
, MEMTEST_NAMES
, L
"Memory test utility", BUILTIN_ICON_TOOL_MEMTEST
);
2507 } // static VOID ScanForTools
2509 // Rescan for boot loaders
2510 static VOID
RescanAll(BOOLEAN DisplayMessage
) {
2518 egDisplayMessage(L
"Scanning for new boot loaders; please wait....", &BGColor
);
2519 FreeList((VOID
***) &(MainMenu
.Entries
), &MainMenu
.EntryCount
);
2520 MainMenu
.Entries
= NULL
;
2521 MainMenu
.EntryCount
= 0;
2522 ReadConfig(GlobalConfig
.ConfigFilename
);
2523 ConnectAllDriversToAllControllers();
2525 ScanForBootloaders();
2528 } // VOID RescanAll()
2530 #ifdef __MAKEWITH_TIANO
2532 // Minimal initialization function
2533 static VOID
InitializeLib(IN EFI_HANDLE ImageHandle
, IN EFI_SYSTEM_TABLE
*SystemTable
) {
2535 // gImageHandle = ImageHandle;
2536 gBS
= SystemTable
->BootServices
;
2537 // gRS = SystemTable->RuntimeServices;
2538 gRT
= SystemTable
->RuntimeServices
; // Some BDS functions need gRT to be set
2539 EfiGetSystemConfigurationTable (&gEfiDxeServicesTableGuid
, (VOID
**) &gDS
);
2541 // InitializeConsoleSim();
2546 // Set up our own Secure Boot extensions....
2547 // Returns TRUE on success, FALSE otherwise
2548 static BOOLEAN
SecureBootSetup(VOID
) {
2550 BOOLEAN Success
= FALSE
;
2552 if (secure_mode() && ShimLoaded()) {
2553 Status
= security_policy_install();
2554 if (Status
== EFI_SUCCESS
) {
2557 Print(L
"Failed to install MOK Secure Boot extensions");
2561 } // VOID SecureBootSetup()
2563 // Remove our own Secure Boot extensions....
2564 // Returns TRUE on success, FALSE otherwise
2565 static BOOLEAN
SecureBootUninstall(VOID
) {
2567 BOOLEAN Success
= TRUE
;
2569 if (secure_mode()) {
2570 Status
= security_policy_uninstall();
2571 if (Status
!= EFI_SUCCESS
) {
2573 BeginTextScreen(L
"Secure Boot Policy Failure");
2574 Print(L
"Failed to uninstall MOK Secure Boot extensions; forcing a reboot.");
2576 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);
2580 } // VOID SecureBootUninstall
2582 // Sets the global configuration filename; will be CONFIG_FILE_NAME unless the
2583 // "-c" command-line option is set, in which case that takes precedence.
2584 // If an error is encountered, leaves the value alone (it should be set to
2585 // CONFIG_FILE_NAME when GlobalConfig is initialized).
2586 static VOID
SetConfigFilename(EFI_HANDLE ImageHandle
) {
2587 EFI_LOADED_IMAGE
*Info
;
2588 CHAR16
*Options
, *FileName
;
2592 Status
= refit_call3_wrapper(BS
->HandleProtocol
, ImageHandle
, &LoadedImageProtocol
, (VOID
**) &Info
);
2593 if ((Status
== EFI_SUCCESS
) && (Info
->LoadOptionsSize
> 0)) {
2594 Options
= (CHAR16
*) Info
->LoadOptions
;
2595 Where
= FindSubString(L
" -c ", Options
);
2597 FileName
= StrDuplicate(&Options
[Where
+ 4]);
2598 Where
= FindSubString(L
" ", FileName
);
2600 FileName
[Where
] = L
'\0';
2602 if (FileExists(SelfDir
, FileName
)) {
2603 GlobalConfig
.ConfigFilename
= FileName
;
2605 Print(L
"Specified configuration file (%s) doesn't exist; using\n'refind.conf' default\n", FileName
);
2606 MyFreePool(FileName
);
2610 } // VOID SetConfigFilename()
2617 efi_main (EFI_HANDLE ImageHandle
, EFI_SYSTEM_TABLE
*SystemTable
)
2620 BOOLEAN MainLoopRunning
= TRUE
;
2621 BOOLEAN MokProtocol
;
2622 REFIT_MENU_ENTRY
*ChosenEntry
;
2624 CHAR16
*SelectionName
= NULL
;
2628 InitializeLib(ImageHandle
, SystemTable
);
2629 Status
= InitRefitLib(ImageHandle
);
2630 if (EFI_ERROR(Status
))
2633 // read configuration
2634 CopyMem(GlobalConfig
.ScanFor
, "ieom ", NUM_SCAN_OPTIONS
);
2635 FindLegacyBootType();
2636 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
)
2637 CopyMem(GlobalConfig
.ScanFor
, "ihebocm ", NUM_SCAN_OPTIONS
);
2638 SetConfigFilename(ImageHandle
);
2639 ReadConfig(GlobalConfig
.ConfigFilename
);
2642 WarnIfLegacyProblems();
2643 MainMenu
.TimeoutSeconds
= GlobalConfig
.Timeout
;
2645 // disable EFI watchdog timer
2646 refit_call4_wrapper(BS
->SetWatchdogTimer
, 0x0000, 0x0000, 0x0000, NULL
);
2648 // further bootstrap (now with config available)
2649 MokProtocol
= SecureBootSetup();
2652 ScanForBootloaders();
2656 if (GlobalConfig
.ScanDelay
> 0) {
2661 if (GlobalConfig
.ScanDelay
> 1)
2662 egDisplayMessage(L
"Pausing before disk scan; please wait....", &BGColor
);
2663 for (i
= 0; i
< GlobalConfig
.ScanDelay
; i
++)
2664 refit_call1_wrapper(BS
->Stall
, 1000000);
2665 RescanAll(GlobalConfig
.ScanDelay
> 1);
2668 if (GlobalConfig
.DefaultSelection
)
2669 SelectionName
= StrDuplicate(GlobalConfig
.DefaultSelection
);
2671 while (MainLoopRunning
) {
2672 MenuExit
= RunMainMenu(&MainMenu
, &SelectionName
, &ChosenEntry
);
2674 // The Escape key triggers a re-scan operation....
2675 if (MenuExit
== MENU_EXIT_ESCAPE
) {
2681 switch (ChosenEntry
->Tag
) {
2683 case TAG_REBOOT
: // Reboot
2685 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);
2686 MainLoopRunning
= FALSE
; // just in case we get this far
2689 case TAG_SHUTDOWN
: // Shut Down
2691 refit_call4_wrapper(RT
->ResetSystem
, EfiResetShutdown
, EFI_SUCCESS
, 0, NULL
);
2692 MainLoopRunning
= FALSE
; // just in case we get this far
2695 case TAG_ABOUT
: // About rEFInd
2699 case TAG_LOADER
: // Boot OS via .EFI loader
2700 StartLoader((LOADER_ENTRY
*)ChosenEntry
, SelectionName
);
2703 case TAG_LEGACY
: // Boot legacy OS
2704 StartLegacy((LEGACY_ENTRY
*)ChosenEntry
, SelectionName
);
2707 case TAG_LEGACY_UEFI
: // Boot a legacy OS on a non-Mac
2708 StartLegacyUEFI((LEGACY_ENTRY
*)ChosenEntry
, SelectionName
);
2711 case TAG_TOOL
: // Start a EFI tool
2712 StartTool((LOADER_ENTRY
*)ChosenEntry
);
2715 case TAG_EXIT
: // Terminate rEFInd
2716 if ((MokProtocol
) && !SecureBootUninstall()) {
2717 MainLoopRunning
= FALSE
; // just in case we get this far
2719 BeginTextScreen(L
" ");
2724 case TAG_FIRMWARE
: // Reboot into firmware's user interface
2725 RebootIntoFirmware();
2731 // If we end up here, things have gone wrong. Try to reboot, and if that
2732 // fails, go into an endless loop.
2733 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);