3 * Main code for the boot menu
5 * Copyright (c) 2006-2010 Christoph Pfisterer
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions are
12 * * Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
15 * * Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the
20 * * Neither the name of Christoph Pfisterer nor the names of the
21 * contributors may be used to endorse or promote products derived
22 * from this software without specific prior written permission.
24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
26 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
27 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
28 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
29 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
30 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
31 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
32 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
33 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
34 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37 * Modifications copyright (c) 2012-2014 Roderick W. Smith
39 * Modifications distributed under the terms of the GNU General Public
40 * License (GPL) version 3 (GPLv3), a copy of which must be distributed
41 * with this source code or binaries made from it.
53 #include "security_policy.h"
54 #include "../include/Handle.h"
55 #include "../include/refit_call_wrapper.h"
56 #include "driver_support.h"
57 #include "../include/syslinux_mbr.h"
59 #ifdef __MAKEWITH_GNUEFI
60 #ifndef EFI_SECURITY_VIOLATION
61 #define EFI_SECURITY_VIOLATION EFIERR (26)
65 #include "../EfiLib/BdsHelper.h"
66 #include "../EfiLib/legacy.h"
68 #ifndef EFI_OS_INDICATIONS_BOOT_TO_FW_UI
69 #define EFI_OS_INDICATIONS_BOOT_TO_FW_UI 0x0000000000000001ULL
72 #ifdef __MAKEWITH_TIANO
73 #define LibLocateHandle gBS->LocateHandleBuffer
79 #define MACOSX_LOADER_PATH L"System\\Library\\CoreServices\\boot.efi"
81 #define SHELL_NAMES L"\\EFI\\tools\\shell.efi,\\EFI\\tools\\shellx64.efi,\\shell.efi,\\shellx64.efi"
82 #define GPTSYNC_NAMES L"\\EFI\\tools\\gptsync.efi,\\EFI\\tools\\gptsync_x64.efi"
83 #define GDISK_NAMES L"\\EFI\\tools\\gdisk.efi,\\EFI\\tools\\gdisk_x64.efi"
84 #define MEMTEST_NAMES L"memtest86.efi,memtest86_x64.efi,memtest86x64.efi,bootx64.efi"
85 #define DRIVER_DIRS L"drivers,drivers_x64"
86 #define FALLBACK_FULLNAME L"EFI\\BOOT\\bootx64.efi"
87 #define FALLBACK_BASENAME L"bootx64.efi"
88 #define EFI_STUB_ARCH 0x8664
90 #define SHELL_NAMES L"\\EFI\\tools\\shell.efi,\\EFI\\tools\\shellia32.efi,\\shell.efi,\\shellia32.efi"
91 #define GPTSYNC_NAMES L"\\EFI\\tools\\gptsync.efi,\\EFI\\tools\\gptsync_ia32.efi"
92 #define GDISK_NAMES L"\\EFI\\tools\\gdisk.efi,\\EFI\\tools\\gdisk_ia32.efi"
93 #define MEMTEST_NAMES L"memtest86.efi,memtest86_ia32.efi,memtest86ia32.efi,bootia32.efi"
94 #define DRIVER_DIRS L"drivers,drivers_ia32"
95 #define FALLBACK_FULLNAME L"EFI\\BOOT\\bootia32.efi"
96 #define FALLBACK_BASENAME L"bootia32.efi"
97 #define EFI_STUB_ARCH 0x014c
99 #define SHELL_NAMES L"\\EFI\\tools\\shell.efi,\\shell.efi"
100 #define GPTSYNC_NAMES L"\\EFI\\tools\\gptsync.efi"
101 #define GDISK_NAMES L"\\EFI\\tools\\gdisk.efi"
102 #define MEMTEST_NAMES L"memtest86.efi"
103 #define DRIVER_DIRS L"drivers"
104 #define FALLBACK_FULLNAME L"EFI\\BOOT\\boot.efi" /* Not really correct */
105 #define FALLBACK_BASENAME L"boot.efi" /* Not really correct */
107 #define FAT_ARCH 0x0ef1fab9 /* ID for Apple "fat" binary */
109 // Filename patterns that identify EFI boot loaders. Note that a single case (either L"*.efi" or
110 // L"*.EFI") is fine for most systems; but Gigabyte's buggy Hybrid EFI does a case-sensitive
111 // comparison when it should do a case-insensitive comparison, so I'm doubling this up. It does
112 // no harm on other computers, AFAIK. In theory, every case variation should be done for
113 // completeness, but that's ridiculous....
114 #define LOADER_MATCH_PATTERNS L"*.efi,*.EFI"
116 // Patterns that identify Linux kernels. Added to the loader match pattern when the
117 // scan_all_linux_kernels option is set in the configuration file. Causes kernels WITHOUT
118 // a ".efi" extension to be found when scanning for boot loaders.
119 #define LINUX_MATCH_PATTERNS L"vmlinuz*,bzImage*"
121 // Default hint text for program-launch submenus
122 #define SUBSCREEN_HINT1 L"Use arrow keys to move cursor; Enter to boot;"
123 #define SUBSCREEN_HINT2 L"Insert or F2 to edit options; Esc to return to main menu"
124 #define SUBSCREEN_HINT2_NO_EDITOR L"Esc to return to main menu"
128 #define TYPE_LEGACY 2
130 static REFIT_MENU_ENTRY MenuEntryAbout
= { L
"About rEFInd", TAG_ABOUT
, 1, 0, 'A', NULL
, NULL
, NULL
};
131 static REFIT_MENU_ENTRY MenuEntryReset
= { L
"Reboot Computer", TAG_REBOOT
, 1, 0, 'R', NULL
, NULL
, NULL
};
132 static REFIT_MENU_ENTRY MenuEntryShutdown
= { L
"Shut Down Computer", TAG_SHUTDOWN
, 1, 0, 'U', NULL
, NULL
, NULL
};
133 static REFIT_MENU_ENTRY MenuEntryReturn
= { L
"Return to Main Menu", TAG_RETURN
, 1, 0, 0, NULL
, NULL
, NULL
};
134 static REFIT_MENU_ENTRY MenuEntryExit
= { L
"Exit rEFInd", TAG_EXIT
, 1, 0, 0, NULL
, NULL
, NULL
};
135 static REFIT_MENU_ENTRY MenuEntryFirmware
= { L
"Reboot to Computer Setup Utility", TAG_FIRMWARE
, 1, 0, 0, NULL
, NULL
, NULL
};
137 static REFIT_MENU_SCREEN MainMenu
= { L
"Main Menu", NULL
, 0, NULL
, 0, NULL
, 0, L
"Automatic boot",
138 L
"Use arrow keys to move cursor; Enter to boot;",
139 L
"Insert or F2 for more options; Esc to refresh" };
140 static REFIT_MENU_SCREEN AboutMenu
= { L
"About", NULL
, 0, NULL
, 0, NULL
, 0, NULL
, L
"Press Enter to return to main menu", L
"" };
142 REFIT_CONFIG GlobalConfig
= { FALSE
, TRUE
, FALSE
, FALSE
, 0, 0, 0, DONT_CHANGE_TEXT_MODE
, 20, 0, 0, GRAPHICS_FOR_OSX
, LEGACY_TYPE_MAC
,
143 0, 0, { DEFAULT_BIG_ICON_SIZE
/ 4, DEFAULT_SMALL_ICON_SIZE
, DEFAULT_BIG_ICON_SIZE
}, BANNER_NOSCALE
,
144 NULL
, NULL
, CONFIG_FILE_NAME
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
145 { TAG_SHELL
, TAG_MEMTEST
, TAG_GDISK
, TAG_APPLE_RECOVERY
, TAG_WINDOWS_RECOVERY
, TAG_MOK_TOOL
,
146 TAG_ABOUT
, TAG_SHUTDOWN
, TAG_REBOOT
, TAG_FIRMWARE
, 0, 0, 0, 0, 0, 0 }
149 EFI_GUID GlobalGuid
= EFI_GLOBAL_VARIABLE
;
150 EFI_GUID RefindGuid
= REFIND_GUID_VALUE
;
152 GPT_DATA
*gPartitions
= NULL
;
154 // Structure used to hold boot loader filenames and time stamps in
155 // a linked list; used to sort entries within a directory.
159 struct LOADER_LIST
*NextEntry
;
166 static VOID
AboutrEFInd(VOID
)
168 CHAR16
*FirmwareVendor
;
170 if (AboutMenu
.EntryCount
== 0) {
171 AboutMenu
.TitleImage
= BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT
);
172 AddMenuInfoLine(&AboutMenu
, L
"rEFInd Version 0.8.3.2");
173 AddMenuInfoLine(&AboutMenu
, L
"");
174 AddMenuInfoLine(&AboutMenu
, L
"Copyright (c) 2006-2010 Christoph Pfisterer");
175 AddMenuInfoLine(&AboutMenu
, L
"Copyright (c) 2012-2014 Roderick W. Smith");
176 AddMenuInfoLine(&AboutMenu
, L
"Portions Copyright (c) Intel Corporation and others");
177 AddMenuInfoLine(&AboutMenu
, L
"Distributed under the terms of the GNU GPLv3 license");
178 AddMenuInfoLine(&AboutMenu
, L
"");
179 AddMenuInfoLine(&AboutMenu
, L
"Running on:");
180 AddMenuInfoLine(&AboutMenu
, PoolPrint(L
" EFI Revision %d.%02d", ST
->Hdr
.Revision
>> 16, ST
->Hdr
.Revision
& ((1 << 16) - 1)));
182 AddMenuInfoLine(&AboutMenu
, L
" Platform: x86 (32 bit)");
183 #elif defined(EFIX64)
184 AddMenuInfoLine(&AboutMenu
, PoolPrint(L
" Platform: x86_64 (64 bit); Secure Boot %s",
185 secure_mode() ? L
"active" : L
"inactive"));
187 AddMenuInfoLine(&AboutMenu
, L
" Platform: unknown");
189 FirmwareVendor
= StrDuplicate(ST
->FirmwareVendor
);
190 LimitStringLength(FirmwareVendor
, 65); // More than ~65 causes empty info page on 800x600 display
191 AddMenuInfoLine(&AboutMenu
, PoolPrint(L
" Firmware: %s %d.%02d", FirmwareVendor
, ST
->FirmwareRevision
>> 16,
192 ST
->FirmwareRevision
& ((1 << 16) - 1)));
193 AddMenuInfoLine(&AboutMenu
, PoolPrint(L
" Screen Output: %s", egScreenDescription()));
194 AddMenuInfoLine(&AboutMenu
, L
"");
195 #if defined(__MAKEWITH_GNUEFI)
196 AddMenuInfoLine(&AboutMenu
, L
"Built with GNU-EFI");
198 AddMenuInfoLine(&AboutMenu
, L
"Built with TianoCore EDK2");
200 AddMenuInfoLine(&AboutMenu
, L
"");
201 AddMenuInfoLine(&AboutMenu
, L
"For more information, see the rEFInd Web site:");
202 AddMenuInfoLine(&AboutMenu
, L
"http://www.rodsbooks.com/refind/");
203 AddMenuEntry(&AboutMenu
, &MenuEntryReturn
);
206 RunMenu(&AboutMenu
, NULL
);
207 } /* VOID AboutrEFInd() */
209 static VOID
WarnSecureBootError(CHAR16
*Name
, BOOLEAN Verbose
) {
211 Name
= L
"the loader";
213 refit_call2_wrapper(ST
->ConOut
->SetAttribute
, ST
->ConOut
, ATTR_ERROR
);
214 Print(L
"Secure Boot validation failure loading %s!\n", Name
);
215 refit_call2_wrapper(ST
->ConOut
->SetAttribute
, ST
->ConOut
, ATTR_BASIC
);
216 if (Verbose
&& secure_mode()) {
217 Print(L
"\nThis computer is configured with Secure Boot active, but\n%s has failed validation.\n", Name
);
218 Print(L
"\nYou can:\n * Launch another boot loader\n");
219 Print(L
" * Disable Secure Boot in your firmware\n");
220 Print(L
" * Sign %s with a machine owner key (MOK)\n", Name
);
221 Print(L
" * Use a MOK utility (often present on the second row) to add a MOK with which\n");
222 Print(L
" %s has already been signed.\n", Name
);
223 Print(L
" * Use a MOK utility to register %s (\"enroll its hash\") without\n", Name
);
224 Print(L
" signing it.\n");
225 Print(L
"\nSee http://www.rodsbooks.com/refind/secureboot.html for more information\n");
228 } // VOID WarnSecureBootError()
230 // Returns TRUE if this file is a valid EFI loader file, and is proper ARCH
231 static BOOLEAN
IsValidLoader(EFI_FILE
*RootDir
, CHAR16
*FileName
) {
232 BOOLEAN IsValid
= TRUE
;
233 #if defined (EFIX64) | defined (EFI32)
235 EFI_FILE_HANDLE FileHandle
;
237 UINTN Size
= sizeof(Header
);
239 if ((RootDir
== NULL
) || (FileName
== NULL
)) {
240 // Assume valid here, because Macs produce NULL RootDir (& maybe FileName)
241 // when launching from a Firewire drive. This should be handled better, but
242 // fix would have to be in StartEFIImageList() and/or in FindVolumeAndFilename().
246 Status
= refit_call5_wrapper(RootDir
->Open
, RootDir
, &FileHandle
, FileName
, EFI_FILE_MODE_READ
, 0);
247 if (EFI_ERROR(Status
))
250 Status
= refit_call3_wrapper(FileHandle
->Read
, FileHandle
, &Size
, Header
);
251 refit_call1_wrapper(FileHandle
->Close
, FileHandle
);
253 IsValid
= !EFI_ERROR(Status
) &&
254 Size
== sizeof(Header
) &&
255 ((Header
[0] == 'M' && Header
[1] == 'Z' &&
256 (Size
= *(UINT32
*)&Header
[0x3c]) < 0x180 &&
257 Header
[Size
] == 'P' && Header
[Size
+1] == 'E' &&
258 Header
[Size
+2] == 0 && Header
[Size
+3] == 0 &&
259 *(UINT16
*)&Header
[Size
+4] == EFI_STUB_ARCH
) ||
260 (*(UINT32
*)&Header
== FAT_ARCH
));
263 } // BOOLEAN IsValidLoader()
265 // Launch an EFI binary.
266 static EFI_STATUS
StartEFIImageList(IN EFI_DEVICE_PATH
**DevicePaths
,
267 IN CHAR16
*LoadOptions
, IN UINTN LoaderType
,
268 IN CHAR16
*ImageTitle
, IN CHAR8 OSType
,
269 OUT UINTN
*ErrorInStep
,
273 EFI_STATUS Status
, ReturnStatus
;
274 EFI_HANDLE ChildImageHandle
;
275 EFI_LOADED_IMAGE
*ChildLoadedImage
= NULL
;
276 REFIT_VOLUME
*Volume
= NULL
;
277 UINTN DevicePathIndex
;
278 CHAR16 ErrorInfo
[256];
279 CHAR16
*FullLoadOptions
= NULL
;
280 CHAR16
*Filename
= NULL
;
283 if (ErrorInStep
!= NULL
)
287 if (LoadOptions
!= NULL
) {
288 FullLoadOptions
= StrDuplicate(LoadOptions
);
289 if ((LoaderType
== TYPE_EFI
) && (OSType
== 'M')) {
290 MergeStrings(&FullLoadOptions
, L
" ", 0);
291 // NOTE: That last space is also added by the EFI shell and seems to be significant
292 // when passing options to Apple's boot.efi...
294 } // if (LoadOptions != NULL)
296 Print(L
"Starting %s\nUsing load options '%s'\n", ImageTitle
, FullLoadOptions
? FullLoadOptions
: L
"");
298 // load the image into memory (and execute it, in the case of a shim/MOK image).
299 ReturnStatus
= Status
= EFI_NOT_FOUND
; // in case the list is empty
300 for (DevicePathIndex
= 0; DevicePaths
[DevicePathIndex
] != NULL
; DevicePathIndex
++) {
301 FindVolumeAndFilename(DevicePaths
[DevicePathIndex
], &Volume
, &Filename
);
302 // Some EFIs crash if attempting to load driver for invalid architecture, so
303 // protect for this condition; but sometimes Volume comes back NULL, so provide
304 // an exception. (TODO: Handle this special condition better.)
305 if ((LoaderType
== TYPE_LEGACY
) || (Volume
== NULL
) || IsValidLoader(Volume
->RootDir
, Filename
)) {
306 if (Filename
&& (LoaderType
!= TYPE_LEGACY
)) {
307 Temp
= PoolPrint(L
"\\%s %s", Filename
, FullLoadOptions
? FullLoadOptions
: L
"");
309 MyFreePool(FullLoadOptions
);
310 FullLoadOptions
= Temp
;
314 // NOTE: Below commented-out line could be more efficient if file were read ahead of
315 // time and passed as a pre-loaded image to LoadImage(), but it doesn't work on my
316 // 32-bit Mac Mini or my 64-bit Intel box when launching a Linux kernel; the
317 // kernel returns a "Failed to handle fs_proto" error message.
318 // TODO: Track down the cause of this error and fix it, if possible.
319 // ReturnStatus = Status = refit_call6_wrapper(BS->LoadImage, FALSE, SelfImageHandle, DevicePaths[DevicePathIndex],
320 // ImageData, ImageSize, &ChildImageHandle);
321 ReturnStatus
= Status
= refit_call6_wrapper(BS
->LoadImage
, FALSE
, SelfImageHandle
, DevicePaths
[DevicePathIndex
],
322 NULL
, 0, &ChildImageHandle
);
324 Print(L
"Invalid loader file!\n");
325 ReturnStatus
= EFI_LOAD_ERROR
;
327 if (ReturnStatus
!= EFI_NOT_FOUND
) {
331 if ((Status
== EFI_ACCESS_DENIED
) || (Status
== EFI_SECURITY_VIOLATION
)) {
332 WarnSecureBootError(ImageTitle
, Verbose
);
335 SPrint(ErrorInfo
, 255, L
"while loading %s", ImageTitle
);
336 if (CheckError(Status
, ErrorInfo
)) {
337 if (ErrorInStep
!= NULL
)
342 ReturnStatus
= Status
= refit_call3_wrapper(BS
->HandleProtocol
, ChildImageHandle
, &LoadedImageProtocol
,
343 (VOID
**) &ChildLoadedImage
);
344 if (CheckError(Status
, L
"while getting a LoadedImageProtocol handle")) {
345 if (ErrorInStep
!= NULL
)
349 ChildLoadedImage
->LoadOptions
= (VOID
*)FullLoadOptions
;
350 ChildLoadedImage
->LoadOptionsSize
= FullLoadOptions
? ((UINT32
)StrLen(FullLoadOptions
) + 1) * sizeof(CHAR16
) : 0;
351 // turn control over to the image
352 // TODO: (optionally) re-enable the EFI watchdog timer!
354 // close open file handles
356 ReturnStatus
= Status
= refit_call3_wrapper(BS
->StartImage
, ChildImageHandle
, NULL
, NULL
);
358 // control returns here when the child image calls Exit()
359 SPrint(ErrorInfo
, 255, L
"returned from %s", ImageTitle
);
360 if (CheckError(Status
, ErrorInfo
)) {
361 if (ErrorInStep
!= NULL
)
365 // re-open file handles
369 // unload the image, we don't care if it works or not...
371 Status
= refit_call1_wrapper(BS
->UnloadImage
, ChildImageHandle
);
374 MyFreePool(FullLoadOptions
);
376 } /* static EFI_STATUS StartEFIImageList() */
378 static EFI_STATUS
StartEFIImage(IN EFI_DEVICE_PATH
*DevicePath
,
379 IN CHAR16
*LoadOptions
, IN UINTN LoaderType
,
380 IN CHAR16
*ImageTitle
, IN CHAR8 OSType
,
381 OUT UINTN
*ErrorInStep
,
386 EFI_DEVICE_PATH
*DevicePaths
[2];
388 DevicePaths
[0] = DevicePath
;
389 DevicePaths
[1] = NULL
;
390 return StartEFIImageList(DevicePaths
, LoadOptions
, LoaderType
, ImageTitle
, OSType
, ErrorInStep
, Verbose
, IsDriver
);
391 } /* static EFI_STATUS StartEFIImage() */
393 // From gummiboot: Reboot the computer into its built-in user interface
394 static EFI_STATUS
RebootIntoFirmware(VOID
) {
400 osind
= EFI_OS_INDICATIONS_BOOT_TO_FW_UI
;
402 err
= EfivarGetRaw(&GlobalGuid
, L
"OsIndications", &b
, &size
);
403 if (err
== EFI_SUCCESS
)
407 err
= EfivarSetRaw(&GlobalGuid
, L
"OsIndications", (CHAR8
*)&osind
, sizeof(UINT64
), TRUE
);
408 if (err
!= EFI_SUCCESS
)
411 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);
412 Print(L
"Error calling ResetSystem: %r", err
);
417 // Record the value of the loader's name/description in rEFInd's "PreviousBoot" EFI variable,
418 // if it's different from what's already stored there.
419 static VOID
StoreLoaderName(IN CHAR16
*Name
) {
421 CHAR16
*OldName
= NULL
;
425 Status
= EfivarGetRaw(&RefindGuid
, L
"PreviousBoot", (CHAR8
**) &OldName
, &Length
);
426 if ((Status
!= EFI_SUCCESS
) || (StrCmp(OldName
, Name
) != 0)) {
427 EfivarSetRaw(&RefindGuid
, L
"PreviousBoot", (CHAR8
*) Name
, StrLen(Name
) * 2 + 2, TRUE
);
431 } // VOID StoreLoaderName()
434 // EFI OS loader functions
437 // See http://www.thomas-krenn.com/en/wiki/Activating_the_Intel_VT_Virtualization_Feature
438 // for information on Intel VMX features
439 static VOID
DoEnableAndLockVMX(VOID
)
442 UINT32 low_bits
= 0, high_bits
= 0;
445 __asm__
volatile ("rdmsr" : "=a" (low_bits
), "=d" (high_bits
) : "c" (msr
));
447 // enable and lock vmx if not locked
448 if ((low_bits
& 1) == 0) {
452 __asm__
volatile ("wrmsr" : : "c" (msr
), "a" (low_bits
), "d" (high_bits
));
454 } // VOID DoEnableAndLockVMX
456 static VOID
StartLoader(LOADER_ENTRY
*Entry
, CHAR16
*SelectionName
)
458 UINTN ErrorInStep
= 0;
460 if (GlobalConfig
.EnableAndLockVMX
) {
461 DoEnableAndLockVMX();
464 BeginExternalScreen(Entry
->UseGraphicsMode
, L
"Booting OS");
465 StoreLoaderName(SelectionName
);
466 StartEFIImage(Entry
->DevicePath
, Entry
->LoadOptions
, TYPE_EFI
,
467 Basename(Entry
->LoaderPath
), Entry
->OSType
, &ErrorInStep
, !Entry
->UseGraphicsMode
, FALSE
);
468 FinishExternalScreen();
471 // Locate an initrd or initramfs file that matches the kernel specified by LoaderPath.
472 // The matching file has a name that begins with "init" and includes the same version
473 // number string as is found in LoaderPath -- but not a longer version number string.
474 // For instance, if LoaderPath is \EFI\kernels\bzImage-3.3.0.efi, and if \EFI\kernels
475 // has a file called initramfs-3.3.0.img, this function will return the string
476 // '\EFI\kernels\initramfs-3.3.0.img'. If the directory ALSO contains the file
477 // initramfs-3.3.0-rc7.img or initramfs-13.3.0.img, those files will NOT match;
478 // however, initmine-3.3.0.img might match. (FindInitrd() returns the first match it
479 // finds). Thus, care should be taken to avoid placing duplicate matching files in
480 // the kernel's directory.
481 // If no matching init file can be found, returns NULL.
482 static CHAR16
* FindInitrd(IN CHAR16
*LoaderPath
, IN REFIT_VOLUME
*Volume
) {
483 CHAR16
*InitrdName
= NULL
, *FileName
, *KernelVersion
, *InitrdVersion
, *Path
;
484 REFIT_DIR_ITER DirIter
;
485 EFI_FILE_INFO
*DirEntry
;
487 FileName
= Basename(LoaderPath
);
488 KernelVersion
= FindNumbers(FileName
);
489 Path
= FindPath(LoaderPath
);
491 // Add trailing backslash for root directory; necessary on some systems, but must
492 // NOT be added to all directories, since on other systems, a trailing backslash on
493 // anything but the root directory causes them to flake out!
494 if (StrLen(Path
) == 0) {
495 MergeStrings(&Path
, L
"\\", 0);
497 DirIterOpen(Volume
->RootDir
, Path
, &DirIter
);
498 // Now add a trailing backslash if it was NOT added earlier, for consistency in
499 // building the InitrdName later....
500 if ((StrLen(Path
) > 0) && (Path
[StrLen(Path
) - 1] != L
'\\'))
501 MergeStrings(&Path
, L
"\\", 0);
502 while ((DirIterNext(&DirIter
, 2, L
"init*", &DirEntry
)) && (InitrdName
== NULL
)) {
503 InitrdVersion
= FindNumbers(DirEntry
->FileName
);
504 if (KernelVersion
!= NULL
) {
505 if (StriCmp(InitrdVersion
, KernelVersion
) == 0) {
506 InitrdName
= PoolPrint(L
"%s%s", Path
, DirEntry
->FileName
);
509 if (InitrdVersion
== NULL
) {
510 InitrdName
= PoolPrint(L
"%s%s", Path
, DirEntry
->FileName
);
513 MyFreePool(InitrdVersion
);
515 DirIterClose(&DirIter
);
517 // Note: Don't FreePool(FileName), since Basename returns a pointer WITHIN the string it's passed.
518 MyFreePool(KernelVersion
);
521 } // static CHAR16 * FindInitrd()
523 LOADER_ENTRY
* AddPreparedLoaderEntry(LOADER_ENTRY
*Entry
) {
524 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
527 } // LOADER_ENTRY * AddPreparedLoaderEntry()
529 // Creates a copy of a menu screen.
530 // Returns a pointer to the copy of the menu screen.
531 static REFIT_MENU_SCREEN
* CopyMenuScreen(REFIT_MENU_SCREEN
*Entry
) {
532 REFIT_MENU_SCREEN
*NewEntry
;
535 NewEntry
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
536 if ((Entry
!= NULL
) && (NewEntry
!= NULL
)) {
537 CopyMem(NewEntry
, Entry
, sizeof(REFIT_MENU_SCREEN
));
538 NewEntry
->Title
= (Entry
->Title
) ? StrDuplicate(Entry
->Title
) : NULL
;
539 NewEntry
->TimeoutText
= (Entry
->TimeoutText
) ? StrDuplicate(Entry
->TimeoutText
) : NULL
;
540 if (Entry
->TitleImage
!= NULL
) {
541 NewEntry
->TitleImage
= AllocatePool(sizeof(EG_IMAGE
));
542 if (NewEntry
->TitleImage
!= NULL
)
543 CopyMem(NewEntry
->TitleImage
, Entry
->TitleImage
, sizeof(EG_IMAGE
));
545 NewEntry
->InfoLines
= (CHAR16
**) AllocateZeroPool(Entry
->InfoLineCount
* (sizeof(CHAR16
*)));
546 for (i
= 0; i
< Entry
->InfoLineCount
&& NewEntry
->InfoLines
; i
++) {
547 NewEntry
->InfoLines
[i
] = (Entry
->InfoLines
[i
]) ? StrDuplicate(Entry
->InfoLines
[i
]) : NULL
;
549 NewEntry
->Entries
= (REFIT_MENU_ENTRY
**) AllocateZeroPool(Entry
->EntryCount
* (sizeof (REFIT_MENU_ENTRY
*)));
550 for (i
= 0; i
< Entry
->EntryCount
&& NewEntry
->Entries
; i
++) {
551 AddMenuEntry(NewEntry
, Entry
->Entries
[i
]);
553 NewEntry
->Hint1
= (Entry
->Hint1
) ? StrDuplicate(Entry
->Hint1
) : NULL
;
554 NewEntry
->Hint2
= (Entry
->Hint2
) ? StrDuplicate(Entry
->Hint2
) : NULL
;
557 } // static REFIT_MENU_SCREEN* CopyMenuScreen()
559 // Creates a copy of a menu entry. Intended to enable moving a stack-based
560 // menu entry (such as the ones for the "reboot" and "exit" functions) to
561 // to the heap. This enables easier deletion of the whole set of menu
562 // entries when re-scanning.
563 // Returns a pointer to the copy of the menu entry.
564 static REFIT_MENU_ENTRY
* CopyMenuEntry(REFIT_MENU_ENTRY
*Entry
) {
565 REFIT_MENU_ENTRY
*NewEntry
;
567 NewEntry
= AllocateZeroPool(sizeof(REFIT_MENU_ENTRY
));
568 if ((Entry
!= NULL
) && (NewEntry
!= NULL
)) {
569 CopyMem(NewEntry
, Entry
, sizeof(REFIT_MENU_ENTRY
));
570 NewEntry
->Title
= (Entry
->Title
) ? StrDuplicate(Entry
->Title
) : NULL
;
571 if (Entry
->BadgeImage
!= NULL
) {
572 NewEntry
->BadgeImage
= AllocatePool(sizeof(EG_IMAGE
));
573 if (NewEntry
->BadgeImage
!= NULL
)
574 CopyMem(NewEntry
->BadgeImage
, Entry
->BadgeImage
, sizeof(EG_IMAGE
));
576 if (Entry
->Image
!= NULL
) {
577 NewEntry
->Image
= AllocatePool(sizeof(EG_IMAGE
));
578 if (NewEntry
->Image
!= NULL
)
579 CopyMem(NewEntry
->Image
, Entry
->Image
, sizeof(EG_IMAGE
));
581 if (Entry
->SubScreen
!= NULL
) {
582 NewEntry
->SubScreen
= CopyMenuScreen(Entry
->SubScreen
);
586 } // REFIT_MENU_ENTRY* CopyMenuEntry()
588 // Creates a new LOADER_ENTRY data structure and populates it with
589 // default values from the specified Entry, or NULL values if Entry
590 // is unspecified (NULL).
591 // Returns a pointer to the new data structure, or NULL if it
592 // couldn't be allocated
593 LOADER_ENTRY
*InitializeLoaderEntry(IN LOADER_ENTRY
*Entry
) {
594 LOADER_ENTRY
*NewEntry
= NULL
;
596 NewEntry
= AllocateZeroPool(sizeof(LOADER_ENTRY
));
597 if (NewEntry
!= NULL
) {
598 NewEntry
->me
.Title
= NULL
;
599 NewEntry
->me
.Tag
= TAG_LOADER
;
600 NewEntry
->Enabled
= TRUE
;
601 NewEntry
->UseGraphicsMode
= FALSE
;
602 NewEntry
->OSType
= 0;
604 NewEntry
->LoaderPath
= (Entry
->LoaderPath
) ? StrDuplicate(Entry
->LoaderPath
) : NULL
;
605 NewEntry
->VolName
= (Entry
->VolName
) ? StrDuplicate(Entry
->VolName
) : NULL
;
606 NewEntry
->DevicePath
= Entry
->DevicePath
;
607 NewEntry
->UseGraphicsMode
= Entry
->UseGraphicsMode
;
608 NewEntry
->LoadOptions
= (Entry
->LoadOptions
) ? StrDuplicate(Entry
->LoadOptions
) : NULL
;
609 NewEntry
->InitrdPath
= (Entry
->InitrdPath
) ? StrDuplicate(Entry
->InitrdPath
) : NULL
;
613 } // LOADER_ENTRY *InitializeLoaderEntry()
615 // Adds InitrdPath to Options, but only if Options doesn't already include an
616 // initrd= line. Done to enable overriding the default initrd selection in a
617 // refind_linux.conf file's options list.
618 // Returns a pointer to a new string. The calling function is responsible for
619 // freeing its memory.
620 static CHAR16
*AddInitrdToOptions(CHAR16
*Options
, CHAR16
*InitrdPath
) {
621 CHAR16
*NewOptions
= NULL
;
624 NewOptions
= StrDuplicate(Options
);
625 if ((InitrdPath
!= NULL
) && !StriSubCmp(L
"initrd=", Options
)) {
626 MergeStrings(&NewOptions
, L
"initrd=", L
' ');
627 MergeStrings(&NewOptions
, InitrdPath
, 0);
630 } // CHAR16 *AddInitrdToOptions()
632 // Prepare a REFIT_MENU_SCREEN data structure for a subscreen entry. This sets up
633 // the default entry that launches the boot loader using the same options as the
634 // main Entry does. Subsequent options can be added by the calling function.
635 // If a subscreen already exists in the Entry that's passed to this function,
636 // it's left unchanged and a pointer to it is returned.
637 // Returns a pointer to the new subscreen data structure, or NULL if there
638 // were problems allocating memory.
639 REFIT_MENU_SCREEN
*InitializeSubScreen(IN LOADER_ENTRY
*Entry
) {
640 CHAR16
*FileName
, *MainOptions
= NULL
;
641 REFIT_MENU_SCREEN
*SubScreen
= NULL
;
642 LOADER_ENTRY
*SubEntry
;
644 FileName
= Basename(Entry
->LoaderPath
);
645 if (Entry
->me
.SubScreen
== NULL
) { // No subscreen yet; initialize default entry....
646 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
647 if (SubScreen
!= NULL
) {
648 SubScreen
->Title
= AllocateZeroPool(sizeof(CHAR16
) * 256);
649 SPrint(SubScreen
->Title
, 255, L
"Boot Options for %s on %s",
650 (Entry
->Title
!= NULL
) ? Entry
->Title
: FileName
, Entry
->VolName
);
651 SubScreen
->TitleImage
= Entry
->me
.Image
;
653 SubEntry
= InitializeLoaderEntry(Entry
);
654 if (SubEntry
!= NULL
) {
655 SubEntry
->me
.Title
= StrDuplicate(L
"Boot using default options");
656 MainOptions
= SubEntry
->LoadOptions
;
657 SubEntry
->LoadOptions
= AddInitrdToOptions(MainOptions
, SubEntry
->InitrdPath
);
658 MyFreePool(MainOptions
);
659 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
660 } // if (SubEntry != NULL)
661 SubScreen
->Hint1
= StrDuplicate(SUBSCREEN_HINT1
);
662 if (GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_EDITOR
) {
663 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2_NO_EDITOR
);
665 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2
);
667 } // if (SubScreen != NULL)
668 } else { // existing subscreen; less initialization, and just add new entry later....
669 SubScreen
= Entry
->me
.SubScreen
;
672 } // REFIT_MENU_SCREEN *InitializeSubScreen()
674 VOID
GenerateSubScreen(LOADER_ENTRY
*Entry
, IN REFIT_VOLUME
*Volume
) {
675 REFIT_MENU_SCREEN
*SubScreen
;
676 LOADER_ENTRY
*SubEntry
;
678 CHAR16 DiagsFileName
[256];
683 // create the submenu
684 if (StrLen(Entry
->Title
) == 0) {
685 MyFreePool(Entry
->Title
);
688 SubScreen
= InitializeSubScreen(Entry
);
690 // loader-specific submenu entries
691 if (Entry
->OSType
== 'M') { // entries for Mac OS X
693 SubEntry
= InitializeLoaderEntry(Entry
);
694 if (SubEntry
!= NULL
) {
695 SubEntry
->me
.Title
= L
"Boot Mac OS X with a 64-bit kernel";
696 SubEntry
->LoadOptions
= L
"arch=x86_64";
697 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
698 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
701 SubEntry
= InitializeLoaderEntry(Entry
);
702 if (SubEntry
!= NULL
) {
703 SubEntry
->me
.Title
= L
"Boot Mac OS X with a 32-bit kernel";
704 SubEntry
->LoadOptions
= L
"arch=i386";
705 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
706 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
710 if (!(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_SINGLEUSER
)) {
711 SubEntry
= InitializeLoaderEntry(Entry
);
712 if (SubEntry
!= NULL
) {
713 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode";
714 SubEntry
->UseGraphicsMode
= FALSE
;
715 SubEntry
->LoadOptions
= L
"-v";
716 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
720 SubEntry
= InitializeLoaderEntry(Entry
);
721 if (SubEntry
!= NULL
) {
722 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode (64-bit)";
723 SubEntry
->UseGraphicsMode
= FALSE
;
724 SubEntry
->LoadOptions
= L
"-v arch=x86_64";
725 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
728 SubEntry
= InitializeLoaderEntry(Entry
);
729 if (SubEntry
!= NULL
) {
730 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode (32-bit)";
731 SubEntry
->UseGraphicsMode
= FALSE
;
732 SubEntry
->LoadOptions
= L
"-v arch=i386";
733 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
737 SubEntry
= InitializeLoaderEntry(Entry
);
738 if (SubEntry
!= NULL
) {
739 SubEntry
->me
.Title
= L
"Boot Mac OS X in single user mode";
740 SubEntry
->UseGraphicsMode
= FALSE
;
741 SubEntry
->LoadOptions
= L
"-v -s";
742 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
744 } // single-user mode allowed
746 if (!(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_SAFEMODE
)) {
747 SubEntry
= InitializeLoaderEntry(Entry
);
748 if (SubEntry
!= NULL
) {
749 SubEntry
->me
.Title
= L
"Boot Mac OS X in safe mode";
750 SubEntry
->UseGraphicsMode
= FALSE
;
751 SubEntry
->LoadOptions
= L
"-v -x";
752 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
754 } // safe mode allowed
756 // check for Apple hardware diagnostics
757 StrCpy(DiagsFileName
, L
"System\\Library\\CoreServices\\.diagnostics\\diags.efi");
758 if (FileExists(Volume
->RootDir
, DiagsFileName
) && !(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_HWTEST
)) {
759 SubEntry
= InitializeLoaderEntry(Entry
);
760 if (SubEntry
!= NULL
) {
761 SubEntry
->me
.Title
= L
"Run Apple Hardware Test";
762 MyFreePool(SubEntry
->LoaderPath
);
763 SubEntry
->LoaderPath
= StrDuplicate(DiagsFileName
);
764 SubEntry
->DevicePath
= FileDevicePath(Volume
->DeviceHandle
, SubEntry
->LoaderPath
);
765 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
766 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
768 } // if diagnostics entry found
770 } else if (Entry
->OSType
== 'L') { // entries for Linux kernels with EFI stub loaders
771 File
= ReadLinuxOptionsFile(Entry
->LoaderPath
, Volume
);
773 InitrdName
= FindInitrd(Entry
->LoaderPath
, Volume
);
774 TokenCount
= ReadTokenLine(File
, &TokenList
);
775 // first entry requires special processing, since it was initially set
776 // up with a default title but correct options by InitializeSubScreen(),
778 if ((SubScreen
->Entries
!= NULL
) && (SubScreen
->Entries
[0] != NULL
)) {
779 MyFreePool(SubScreen
->Entries
[0]->Title
);
780 SubScreen
->Entries
[0]->Title
= TokenList
[0] ? StrDuplicate(TokenList
[0]) : StrDuplicate(L
"Boot Linux");
782 FreeTokenLine(&TokenList
, &TokenCount
);
783 while ((TokenCount
= ReadTokenLine(File
, &TokenList
)) > 1) {
784 SubEntry
= InitializeLoaderEntry(Entry
);
785 SubEntry
->me
.Title
= TokenList
[0] ? StrDuplicate(TokenList
[0]) : StrDuplicate(L
"Boot Linux");
786 MyFreePool(SubEntry
->LoadOptions
);
787 SubEntry
->LoadOptions
= AddInitrdToOptions(TokenList
[1], InitrdName
);
788 FreeTokenLine(&TokenList
, &TokenCount
);
789 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_LINUX
;
790 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
792 MyFreePool(InitrdName
);
796 } else if (Entry
->OSType
== 'E') { // entries for ELILO
797 SubEntry
= InitializeLoaderEntry(Entry
);
798 if (SubEntry
!= NULL
) {
799 SubEntry
->me
.Title
= L
"Run ELILO in interactive mode";
800 SubEntry
->LoadOptions
= L
"-p";
801 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
802 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
805 SubEntry
= InitializeLoaderEntry(Entry
);
806 if (SubEntry
!= NULL
) {
807 SubEntry
->me
.Title
= L
"Boot Linux for a 17\" iMac or a 15\" MacBook Pro (*)";
808 SubEntry
->UseGraphicsMode
= TRUE
;
809 SubEntry
->LoadOptions
= L
"-d 0 i17";
810 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
811 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
814 SubEntry
= InitializeLoaderEntry(Entry
);
815 if (SubEntry
!= NULL
) {
816 SubEntry
->me
.Title
= L
"Boot Linux for a 20\" iMac (*)";
817 SubEntry
->UseGraphicsMode
= TRUE
;
818 SubEntry
->LoadOptions
= L
"-d 0 i20";
819 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
820 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
823 SubEntry
= InitializeLoaderEntry(Entry
);
824 if (SubEntry
!= NULL
) {
825 SubEntry
->me
.Title
= L
"Boot Linux for a Mac Mini (*)";
826 SubEntry
->UseGraphicsMode
= TRUE
;
827 SubEntry
->LoadOptions
= L
"-d 0 mini";
828 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
829 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
832 AddMenuInfoLine(SubScreen
, L
"NOTE: This is an example. Entries");
833 AddMenuInfoLine(SubScreen
, L
"marked with (*) may not work.");
835 } else if (Entry
->OSType
== 'X') { // entries for xom.efi
836 // by default, skip the built-in selection and boot from hard disk only
837 Entry
->LoadOptions
= L
"-s -h";
839 SubEntry
= InitializeLoaderEntry(Entry
);
840 if (SubEntry
!= NULL
) {
841 SubEntry
->me
.Title
= L
"Boot Windows from Hard Disk";
842 SubEntry
->LoadOptions
= L
"-s -h";
843 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
844 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
847 SubEntry
= InitializeLoaderEntry(Entry
);
848 if (SubEntry
!= NULL
) {
849 SubEntry
->me
.Title
= L
"Boot Windows from CD-ROM";
850 SubEntry
->LoadOptions
= L
"-s -c";
851 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
852 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
855 SubEntry
= InitializeLoaderEntry(Entry
);
856 if (SubEntry
!= NULL
) {
857 SubEntry
->me
.Title
= L
"Run XOM in text mode";
858 SubEntry
->UseGraphicsMode
= FALSE
;
859 SubEntry
->LoadOptions
= L
"-v";
860 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
861 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
863 } // entries for xom.efi
864 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
865 Entry
->me
.SubScreen
= SubScreen
;
866 } // VOID GenerateSubScreen()
868 // Returns options for a Linux kernel. Reads them from an options file in the
869 // kernel's directory; and if present, adds an initrd= option for an initial
870 // RAM disk file with the same version number as the kernel file.
871 static CHAR16
* GetMainLinuxOptions(IN CHAR16
* LoaderPath
, IN REFIT_VOLUME
*Volume
) {
872 CHAR16
*Options
= NULL
, *InitrdName
, *FullOptions
= NULL
;
874 Options
= GetFirstOptionsFromFile(LoaderPath
, Volume
);
875 InitrdName
= FindInitrd(LoaderPath
, Volume
);
876 FullOptions
= AddInitrdToOptions(Options
, InitrdName
);
879 MyFreePool(InitrdName
);
880 return (FullOptions
);
881 } // static CHAR16 * GetMainLinuxOptions()
883 // Try to guess the name of the Linux distribution & add that name to
885 static VOID
GuessLinuxDistribution(CHAR16
**OSIconName
, REFIT_VOLUME
*Volume
, CHAR16
*LoaderPath
) {
889 UINTN TokenCount
= 0;
891 // If on Linux root fs, /etc/os-release file probably has clues....
892 if (FileExists(Volume
->RootDir
, L
"etc\\os-release") &&
893 (ReadFile(Volume
->RootDir
, L
"etc\\os-release", &File
, &FileSize
) == EFI_SUCCESS
)) {
895 TokenCount
= ReadTokenLine(&File
, &TokenList
);
896 if ((TokenCount
> 1) && ((StriCmp(TokenList
[0], L
"ID") == 0) || (StriCmp(TokenList
[0], L
"NAME") == 0))) {
897 MergeStrings(OSIconName
, TokenList
[1], L
',');
899 FreeTokenLine(&TokenList
, &TokenCount
);
900 } while (TokenCount
> 0);
901 MyFreePool(File
.Buffer
);
904 // Search for clues in the kernel's filename....
905 if (StriSubCmp(L
".fc", LoaderPath
))
906 MergeStrings(OSIconName
, L
"fedora", L
',');
907 if (StriSubCmp(L
".el", LoaderPath
))
908 MergeStrings(OSIconName
, L
"redhat", L
',');
909 } // VOID GuessLinuxDistribution()
911 // Sets a few defaults for a loader entry -- mainly the icon, but also the OS type
912 // code and shortcut letter. For Linux EFI stub loaders, also sets kernel options
913 // that will (with luck) work fairly automatically.
914 VOID
SetLoaderDefaults(LOADER_ENTRY
*Entry
, CHAR16
*LoaderPath
, REFIT_VOLUME
*Volume
) {
915 CHAR16
*FileName
, *PathOnly
, *NoExtension
, *OSIconName
= NULL
, *Temp
, *SubString
;
916 CHAR16 ShortcutLetter
= 0;
919 FileName
= Basename(LoaderPath
);
920 PathOnly
= FindPath(LoaderPath
);
921 NoExtension
= StripEfiExtension(FileName
);
923 // locate a custom icon for the loader
924 // Anything found here takes precedence over the "hints" in the OSIconName variable
925 if (!Entry
->me
.Image
) {
926 Entry
->me
.Image
= egLoadIconAnyType(Volume
->RootDir
, PathOnly
, NoExtension
, GlobalConfig
.IconSizes
[ICON_SIZE_BIG
]);
928 if (!Entry
->me
.Image
) {
929 Entry
->me
.Image
= egCopyImage(Volume
->VolIconImage
);
932 // Begin creating icon "hints" by using last part of directory path leading
934 Temp
= FindLastDirName(LoaderPath
);
935 MergeStrings(&OSIconName
, Temp
, L
',');
938 if (OSIconName
!= NULL
) {
939 ShortcutLetter
= OSIconName
[0];
942 // Add every "word" in the volume label, delimited by spaces, dashes (-), or
943 // underscores (_), to the list of hints to be used in searching for OS
945 if ((Volume
->VolName
) && (StrLen(Volume
->VolName
) > 0)) {
946 Temp
= SubString
= StrDuplicate(Volume
->VolName
);
948 Length
= StrLen(Temp
);
949 for (i
= 0; i
< Length
; i
++) {
950 if ((Temp
[i
] == L
' ') || (Temp
[i
] == L
'_') || (Temp
[i
] == L
'-')) {
952 if (StrLen(SubString
) > 0)
953 MergeStrings(&OSIconName
, SubString
, L
',');
954 SubString
= Temp
+ i
+ 1;
957 MergeStrings(&OSIconName
, SubString
, L
',');
962 // detect specific loaders
963 if (StriSubCmp(L
"bzImage", FileName
) || StriSubCmp(L
"vmlinuz", FileName
)) {
964 GuessLinuxDistribution(&OSIconName
, Volume
, LoaderPath
);
965 MergeStrings(&OSIconName
, L
"linux", L
',');
967 if (ShortcutLetter
== 0)
968 ShortcutLetter
= 'L';
969 Entry
->LoadOptions
= GetMainLinuxOptions(LoaderPath
, Volume
);
970 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_LINUX
;
971 } else if (StriSubCmp(L
"refit", LoaderPath
)) {
972 MergeStrings(&OSIconName
, L
"refit", L
',');
974 ShortcutLetter
= 'R';
975 } else if (StriSubCmp(L
"refind", LoaderPath
)) {
976 MergeStrings(&OSIconName
, L
"refind", L
',');
978 ShortcutLetter
= 'R';
979 } else if (StriCmp(LoaderPath
, MACOSX_LOADER_PATH
) == 0) {
980 MergeStrings(&OSIconName
, L
"mac", L
',');
982 ShortcutLetter
= 'M';
983 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
984 } else if (StriCmp(FileName
, L
"diags.efi") == 0) {
985 MergeStrings(&OSIconName
, L
"hwtest", L
',');
986 } else if (StriCmp(FileName
, L
"e.efi") == 0 || StriCmp(FileName
, L
"elilo.efi") == 0 || StriSubCmp(L
"elilo", FileName
)) {
987 MergeStrings(&OSIconName
, L
"elilo,linux", L
',');
989 if (ShortcutLetter
== 0)
990 ShortcutLetter
= 'L';
991 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
992 } else if (StriSubCmp(L
"grub", FileName
)) {
994 ShortcutLetter
= 'G';
995 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_GRUB
;
996 } else if (StriCmp(FileName
, L
"cdboot.efi") == 0 ||
997 StriCmp(FileName
, L
"bootmgr.efi") == 0 ||
998 StriCmp(FileName
, L
"bootmgfw.efi") == 0 ||
999 StriCmp(FileName
, L
"bkpbootmgfw.efi") == 0) {
1000 MergeStrings(&OSIconName
, L
"win", L
',');
1001 Entry
->OSType
= 'W';
1002 ShortcutLetter
= 'W';
1003 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
1004 } else if (StriCmp(FileName
, L
"xom.efi") == 0) {
1005 MergeStrings(&OSIconName
, L
"xom,win", L
',');
1006 Entry
->UseGraphicsMode
= TRUE
;
1007 Entry
->OSType
= 'X';
1008 ShortcutLetter
= 'W';
1009 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
1012 if ((ShortcutLetter
>= 'a') && (ShortcutLetter
<= 'z'))
1013 ShortcutLetter
= ShortcutLetter
- 'a' + 'A'; // convert lowercase to uppercase
1014 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1015 if (Entry
->me
.Image
== NULL
)
1016 Entry
->me
.Image
= LoadOSIcon(OSIconName
, L
"unknown", FALSE
);
1017 MyFreePool(PathOnly
);
1018 } // VOID SetLoaderDefaults()
1020 // Add a specified EFI boot loader to the list, using automatic settings
1021 // for icons, options, etc.
1022 LOADER_ENTRY
* AddLoaderEntry(IN CHAR16
*LoaderPath
, IN CHAR16
*LoaderTitle
, IN REFIT_VOLUME
*Volume
) {
1023 LOADER_ENTRY
*Entry
;
1025 CleanUpPathNameSlashes(LoaderPath
);
1026 Entry
= InitializeLoaderEntry(NULL
);
1027 if (Entry
!= NULL
) {
1028 Entry
->Title
= StrDuplicate((LoaderTitle
!= NULL
) ? LoaderTitle
: LoaderPath
);
1029 Entry
->me
.Title
= AllocateZeroPool(sizeof(CHAR16
) * 256);
1030 // Extra space at end of Entry->me.Title enables searching on Volume->VolName even if another volume
1031 // name is identical except for something added to the end (e.g., VolB1 vs. VolB12).
1032 SPrint(Entry
->me
.Title
, 255, L
"Boot %s from %s ", (LoaderTitle
!= NULL
) ? LoaderTitle
: LoaderPath
, Volume
->VolName
);
1034 Entry
->me
.BadgeImage
= Volume
->VolBadgeImage
;
1035 if ((LoaderPath
!= NULL
) && (LoaderPath
[0] != L
'\\')) {
1036 Entry
->LoaderPath
= StrDuplicate(L
"\\");
1038 Entry
->LoaderPath
= NULL
;
1040 MergeStrings(&(Entry
->LoaderPath
), LoaderPath
, 0);
1041 Entry
->VolName
= Volume
->VolName
;
1042 Entry
->DevicePath
= FileDevicePath(Volume
->DeviceHandle
, Entry
->LoaderPath
);
1043 SetLoaderDefaults(Entry
, LoaderPath
, Volume
);
1044 GenerateSubScreen(Entry
, Volume
);
1045 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1049 } // LOADER_ENTRY * AddLoaderEntry()
1051 // Returns -1 if (Time1 < Time2), +1 if (Time1 > Time2), or 0 if
1052 // (Time1 == Time2). Precision is only to the nearest second; since
1053 // this is used for sorting boot loader entries, differences smaller
1054 // than this are likely to be meaningless (and unlikely!).
1055 INTN
TimeComp(IN EFI_TIME
*Time1
, IN EFI_TIME
*Time2
) {
1056 INT64 Time1InSeconds
, Time2InSeconds
;
1058 // Following values are overestimates; I'm assuming 31 days in every month.
1059 // This is fine for the purpose of this function, which is limited
1060 Time1InSeconds
= Time1
->Second
+ (Time1
->Minute
* 60) + (Time1
->Hour
* 3600) + (Time1
->Day
* 86400) +
1061 (Time1
->Month
* 2678400) + ((Time1
->Year
- 1998) * 32140800);
1062 Time2InSeconds
= Time2
->Second
+ (Time2
->Minute
* 60) + (Time2
->Hour
* 3600) + (Time2
->Day
* 86400) +
1063 (Time2
->Month
* 2678400) + ((Time2
->Year
- 1998) * 32140800);
1064 if (Time1InSeconds
< Time2InSeconds
)
1066 else if (Time1InSeconds
> Time2InSeconds
)
1070 } // INTN TimeComp()
1072 // Adds a loader list element, keeping it sorted by date. Returns the new
1073 // first element (the one with the most recent date).
1074 static struct LOADER_LIST
* AddLoaderListEntry(struct LOADER_LIST
*LoaderList
, struct LOADER_LIST
*NewEntry
) {
1075 struct LOADER_LIST
*LatestEntry
, *CurrentEntry
, *PrevEntry
= NULL
;
1077 LatestEntry
= CurrentEntry
= LoaderList
;
1078 if (LoaderList
== NULL
) {
1079 LatestEntry
= NewEntry
;
1081 while ((CurrentEntry
!= NULL
) && (TimeComp(&(NewEntry
->TimeStamp
), &(CurrentEntry
->TimeStamp
)) < 0)) {
1082 PrevEntry
= CurrentEntry
;
1083 CurrentEntry
= CurrentEntry
->NextEntry
;
1085 NewEntry
->NextEntry
= CurrentEntry
;
1086 if (PrevEntry
== NULL
) {
1087 LatestEntry
= NewEntry
;
1089 PrevEntry
->NextEntry
= NewEntry
;
1092 return (LatestEntry
);
1093 } // static VOID AddLoaderListEntry()
1095 // Delete the LOADER_LIST linked list
1096 static VOID
CleanUpLoaderList(struct LOADER_LIST
*LoaderList
) {
1097 struct LOADER_LIST
*Temp
;
1099 while (LoaderList
!= NULL
) {
1101 LoaderList
= LoaderList
->NextEntry
;
1102 MyFreePool(Temp
->FileName
);
1105 } // static VOID CleanUpLoaderList()
1107 // Returns FALSE if the specified file/volume matches the GlobalConfig.DontScanDirs
1108 // or GlobalConfig.DontScanVolumes specification, or if Path points to a volume
1109 // other than the one specified by Volume, or if the specified path is SelfDir.
1110 // Returns TRUE if none of these conditions is met -- that is, if the path is
1111 // eligible for scanning.
1112 static BOOLEAN
ShouldScan(REFIT_VOLUME
*Volume
, CHAR16
*Path
) {
1113 CHAR16
*VolName
= NULL
, *DontScanDir
, *PathCopy
= NULL
;
1115 BOOLEAN ScanIt
= TRUE
;
1117 if ((IsIn(Volume
->VolName
, GlobalConfig
.DontScanVolumes
)) || (IsIn(Volume
->PartName
, GlobalConfig
.DontScanVolumes
)))
1120 if ((StriCmp(Path
, SelfDirPath
) == 0) && (Volume
->DeviceHandle
== SelfVolume
->DeviceHandle
))
1123 // See if Path includes an explicit volume declaration that's NOT Volume....
1124 PathCopy
= StrDuplicate(Path
);
1125 if (SplitVolumeAndFilename(&PathCopy
, &VolName
)) {
1126 VolumeNumberToName(Volume
, &VolName
);
1127 if (VolName
&& StriCmp(VolName
, Volume
->VolName
) != 0) {
1130 } // if Path includes volume specification
1131 MyFreePool(PathCopy
);
1132 MyFreePool(VolName
);
1135 // See if Volume is in GlobalConfig.DontScanDirs....
1136 while (ScanIt
&& (DontScanDir
= FindCommaDelimited(GlobalConfig
.DontScanDirs
, i
++))) {
1137 SplitVolumeAndFilename(&DontScanDir
, &VolName
);
1138 CleanUpPathNameSlashes(DontScanDir
);
1139 VolumeNumberToName(Volume
, &VolName
);
1140 if (VolName
!= NULL
) {
1141 if ((StriCmp(VolName
, Volume
->VolName
) == 0) && (StriCmp(DontScanDir
, Path
) == 0))
1144 if (StriCmp(DontScanDir
, Path
) == 0)
1147 MyFreePool(DontScanDir
);
1148 MyFreePool(VolName
);
1154 } // BOOLEAN ShouldScan()
1156 // Returns TRUE if the file is byte-for-byte identical with the fallback file
1157 // on the volume AND if the file is not itself the fallback file; returns
1158 // FALSE if the file is not identical to the fallback file OR if the file
1159 // IS the fallback file. Intended for use in excluding the fallback boot
1160 // loader when it's a duplicate of another boot loader.
1161 static BOOLEAN
DuplicatesFallback(IN REFIT_VOLUME
*Volume
, IN CHAR16
*FileName
) {
1162 CHAR8
*FileContents
, *FallbackContents
;
1163 EFI_FILE_HANDLE FileHandle
, FallbackHandle
;
1164 EFI_FILE_INFO
*FileInfo
, *FallbackInfo
;
1165 UINTN FileSize
= 0, FallbackSize
= 0;
1167 BOOLEAN AreIdentical
= FALSE
;
1169 if (!FileExists(Volume
->RootDir
, FileName
) || !FileExists(Volume
->RootDir
, FALLBACK_FULLNAME
))
1172 CleanUpPathNameSlashes(FileName
);
1174 if (StriCmp(FileName
, FALLBACK_FULLNAME
) == 0)
1175 return FALSE
; // identical filenames, so not a duplicate....
1177 Status
= refit_call5_wrapper(Volume
->RootDir
->Open
, Volume
->RootDir
, &FileHandle
, FileName
, EFI_FILE_MODE_READ
, 0);
1178 if (Status
== EFI_SUCCESS
) {
1179 FileInfo
= LibFileInfo(FileHandle
);
1180 FileSize
= FileInfo
->FileSize
;
1185 Status
= refit_call5_wrapper(Volume
->RootDir
->Open
, Volume
->RootDir
, &FallbackHandle
, FALLBACK_FULLNAME
, EFI_FILE_MODE_READ
, 0);
1186 if (Status
== EFI_SUCCESS
) {
1187 FallbackInfo
= LibFileInfo(FallbackHandle
);
1188 FallbackSize
= FallbackInfo
->FileSize
;
1190 refit_call1_wrapper(FileHandle
->Close
, FileHandle
);
1194 if (FallbackSize
!= FileSize
) { // not same size, so can't be identical
1195 AreIdentical
= FALSE
;
1196 } else { // could be identical; do full check....
1197 FileContents
= AllocatePool(FileSize
);
1198 FallbackContents
= AllocatePool(FallbackSize
);
1199 if (FileContents
&& FallbackContents
) {
1200 Status
= refit_call3_wrapper(FileHandle
->Read
, FileHandle
, &FileSize
, FileContents
);
1201 if (Status
== EFI_SUCCESS
) {
1202 Status
= refit_call3_wrapper(FallbackHandle
->Read
, FallbackHandle
, &FallbackSize
, FallbackContents
);
1204 if (Status
== EFI_SUCCESS
) {
1205 AreIdentical
= (CompareMem(FileContents
, FallbackContents
, FileSize
) == 0);
1208 MyFreePool(FileContents
);
1209 MyFreePool(FallbackContents
);
1212 // BUG ALERT: Some systems (e.g., DUET, some Macs with large displays) crash if the
1213 // following two calls are reversed. Go figure....
1214 refit_call1_wrapper(FileHandle
->Close
, FallbackHandle
);
1215 refit_call1_wrapper(FileHandle
->Close
, FileHandle
);
1216 return AreIdentical
;
1217 } // BOOLEAN DuplicatesFallback()
1219 // Returns FALSE if two measures of file size are identical for a single file,
1220 // TRUE if not or if the file can't be opened and the other measure is non-0.
1221 // Despite the function's name, this isn't really a direct test of symbolic
1222 // link status, since EFI doesn't officially support symlinks. It does seem
1223 // to be a reliable indicator, though. (OTOH, some disk errors might cause a
1224 // file to fail to open, which would return a false positive -- but as I use
1225 // this function to exclude symbolic links from the list of boot loaders,
1226 // that would be fine, since such boot loaders wouldn't work.)
1227 static BOOLEAN
IsSymbolicLink(REFIT_VOLUME
*Volume
, CHAR16
*Path
, EFI_FILE_INFO
*DirEntry
) {
1228 EFI_FILE_HANDLE FileHandle
;
1229 EFI_FILE_INFO
*FileInfo
= NULL
;
1231 UINTN FileSize2
= 0;
1234 FileName
= StrDuplicate(Path
);
1235 MergeStrings(&FileName
, DirEntry
->FileName
, L
'\\');
1236 CleanUpPathNameSlashes(FileName
);
1238 Status
= refit_call5_wrapper(Volume
->RootDir
->Open
, Volume
->RootDir
, &FileHandle
, FileName
, EFI_FILE_MODE_READ
, 0);
1239 if (Status
== EFI_SUCCESS
) {
1240 FileInfo
= LibFileInfo(FileHandle
);
1241 if (FileInfo
!= NULL
)
1242 FileSize2
= FileInfo
->FileSize
;
1245 MyFreePool(FileName
);
1246 MyFreePool(FileInfo
);
1248 return (DirEntry
->FileSize
!= FileSize2
);
1249 } // BOOLEAN IsSymbolicLink()
1251 // Returns TRUE if a file with the same name as the original but with
1252 // ".efi.signed" is also present in the same directory. Ubuntu is using
1253 // this filename as a signed version of the original unsigned kernel, and
1254 // there's no point in cluttering the display with two kernels that will
1255 // behave identically on non-SB systems, or when one will fail when SB
1257 static BOOLEAN
HasSignedCounterpart(IN REFIT_VOLUME
*Volume
, IN CHAR16
*Path
, IN CHAR16
*Filename
) {
1258 CHAR16
*NewFile
= NULL
;
1259 BOOLEAN retval
= FALSE
;
1261 MergeStrings(&NewFile
, Path
, 0);
1262 MergeStrings(&NewFile
, Filename
, L
'\\');
1263 MergeStrings(&NewFile
, L
".efi.signed", 0);
1264 if (NewFile
!= NULL
) {
1265 CleanUpPathNameSlashes(NewFile
);
1266 if (FileExists(Volume
->RootDir
, NewFile
))
1268 MyFreePool(NewFile
);
1272 } // BOOLEAN HasSignedCounterpart()
1274 // Scan an individual directory for EFI boot loader files and, if found,
1275 // add them to the list. Exception: Ignores FALLBACK_FULLNAME, which is picked
1276 // up in ScanEfiFiles(). Sorts the entries within the loader directory so that
1277 // the most recent one appears first in the list.
1278 // Returns TRUE if a duplicate for FALLBACK_FILENAME was found, FALSE if not.
1279 static BOOLEAN
ScanLoaderDir(IN REFIT_VOLUME
*Volume
, IN CHAR16
*Path
, IN CHAR16
*Pattern
)
1282 REFIT_DIR_ITER DirIter
;
1283 EFI_FILE_INFO
*DirEntry
;
1284 CHAR16 FileName
[256], *Extension
;
1285 struct LOADER_LIST
*LoaderList
= NULL
, *NewLoader
;
1286 BOOLEAN FoundFallbackDuplicate
= FALSE
;
1288 if ((!SelfDirPath
|| !Path
|| ((StriCmp(Path
, SelfDirPath
) == 0) && (Volume
->DeviceHandle
!= SelfVolume
->DeviceHandle
)) ||
1289 (StriCmp(Path
, SelfDirPath
) != 0)) && (ShouldScan(Volume
, Path
))) {
1290 // look through contents of the directory
1291 DirIterOpen(Volume
->RootDir
, Path
, &DirIter
);
1292 while (DirIterNext(&DirIter
, 2, Pattern
, &DirEntry
)) {
1293 Extension
= FindExtension(DirEntry
->FileName
);
1294 if (DirEntry
->FileName
[0] == '.' ||
1295 StriCmp(Extension
, L
".icns") == 0 ||
1296 StriCmp(Extension
, L
".png") == 0 ||
1297 (StriCmp(DirEntry
->FileName
, FALLBACK_BASENAME
) == 0 && (StriCmp(Path
, L
"EFI\\BOOT") == 0)) ||
1298 StriSubCmp(L
"shell", DirEntry
->FileName
) ||
1299 IsSymbolicLink(Volume
, Path
, DirEntry
) || /* is symbolic link */
1300 HasSignedCounterpart(Volume
, Path
, DirEntry
->FileName
) || /* a file with same name plus ".efi.signed" is present */
1301 FilenameIn(Volume
, Path
, DirEntry
->FileName
, GlobalConfig
.DontScanFiles
))
1302 continue; // skip this
1305 SPrint(FileName
, 255, L
"\\%s\\%s", Path
, DirEntry
->FileName
);
1307 SPrint(FileName
, 255, L
"\\%s", DirEntry
->FileName
);
1308 CleanUpPathNameSlashes(FileName
);
1310 if(!IsValidLoader(Volume
->RootDir
, FileName
))
1313 NewLoader
= AllocateZeroPool(sizeof(struct LOADER_LIST
));
1314 if (NewLoader
!= NULL
) {
1315 NewLoader
->FileName
= StrDuplicate(FileName
);
1316 NewLoader
->TimeStamp
= DirEntry
->ModificationTime
;
1317 LoaderList
= AddLoaderListEntry(LoaderList
, NewLoader
);
1318 if (DuplicatesFallback(Volume
, FileName
))
1319 FoundFallbackDuplicate
= TRUE
;
1321 MyFreePool(Extension
);
1324 NewLoader
= LoaderList
;
1325 while (NewLoader
!= NULL
) {
1326 AddLoaderEntry(NewLoader
->FileName
, NULL
, Volume
);
1327 NewLoader
= NewLoader
->NextEntry
;
1330 CleanUpLoaderList(LoaderList
);
1331 Status
= DirIterClose(&DirIter
);
1332 // NOTE: EFI_INVALID_PARAMETER really is an error that should be reported;
1333 // but I've gotten reports from users who are getting this error occasionally
1334 // and I can't find anything wrong or reproduce the problem, so I'm putting
1335 // it down to buggy EFI implementations and ignoring that particular error....
1336 if ((Status
!= EFI_NOT_FOUND
) && (Status
!= EFI_INVALID_PARAMETER
)) {
1338 SPrint(FileName
, 255, L
"while scanning the %s directory", Path
);
1340 StrCpy(FileName
, L
"while scanning the root directory");
1341 CheckError(Status
, FileName
);
1342 } // if (Status != EFI_NOT_FOUND)
1343 } // if not scanning a blacklisted directory
1345 return FoundFallbackDuplicate
;
1346 } /* static VOID ScanLoaderDir() */
1348 static VOID
ScanEfiFiles(REFIT_VOLUME
*Volume
) {
1350 REFIT_DIR_ITER EfiDirIter
;
1351 EFI_FILE_INFO
*EfiDirEntry
;
1352 CHAR16 FileName
[256], *Directory
= NULL
, *MatchPatterns
, *VolName
= NULL
, *SelfPath
;
1354 BOOLEAN ScanFallbackLoader
= TRUE
;
1355 BOOLEAN FoundBRBackup
= FALSE
;
1357 if ((Volume
->RootDir
!= NULL
) && (Volume
->VolName
!= NULL
) && (Volume
->IsReadable
)) {
1358 MatchPatterns
= StrDuplicate(LOADER_MATCH_PATTERNS
);
1359 if (GlobalConfig
.ScanAllLinux
)
1360 MergeStrings(&MatchPatterns
, LINUX_MATCH_PATTERNS
, L
',');
1362 // check for Mac OS X boot loader
1363 if (ShouldScan(Volume
, L
"System\\Library\\CoreServices")) {
1364 StrCpy(FileName
, MACOSX_LOADER_PATH
);
1365 if (FileExists(Volume
->RootDir
, FileName
) && !FilenameIn(Volume
, Directory
, L
"boot.efi", GlobalConfig
.DontScanFiles
)) {
1366 AddLoaderEntry(FileName
, L
"Mac OS X", Volume
);
1367 if (DuplicatesFallback(Volume
, FileName
))
1368 ScanFallbackLoader
= FALSE
;
1372 StrCpy(FileName
, L
"System\\Library\\CoreServices\\xom.efi");
1373 if (FileExists(Volume
->RootDir
, FileName
) && !FilenameIn(Volume
, Directory
, L
"boot.efi", GlobalConfig
.DontScanFiles
)) {
1374 AddLoaderEntry(FileName
, L
"Windows XP (XoM)", Volume
);
1375 if (DuplicatesFallback(Volume
, FileName
))
1376 ScanFallbackLoader
= FALSE
;
1378 } // if should scan Mac directory
1380 // check for Microsoft boot loader/menu
1381 if (ShouldScan(Volume
, L
"EFI\\Microsoft\\Boot")) {
1382 StrCpy(FileName
, L
"EFI\\Microsoft\\Boot\\bkpbootmgfw.efi");
1383 if (FileExists(Volume
->RootDir
, FileName
) && !FilenameIn(Volume
, Directory
, L
"bkpbootmgfw.efi",
1384 GlobalConfig
.DontScanFiles
)) {
1385 AddLoaderEntry(FileName
, L
"Microsoft EFI boot (Boot Repair backup)", Volume
);
1386 FoundBRBackup
= TRUE
;
1387 if (DuplicatesFallback(Volume
, FileName
))
1388 ScanFallbackLoader
= FALSE
;
1390 StrCpy(FileName
, L
"EFI\\Microsoft\\Boot\\bootmgfw.efi");
1391 if (FileExists(Volume
->RootDir
, FileName
) && !FilenameIn(Volume
, Directory
, L
"bootmgfw.efi", GlobalConfig
.DontScanFiles
)) {
1393 AddLoaderEntry(FileName
, L
"Supposed Microsoft EFI boot (probably GRUB)", Volume
);
1395 AddLoaderEntry(FileName
, L
"Microsoft EFI boot", Volume
);
1396 if (DuplicatesFallback(Volume
, FileName
))
1397 ScanFallbackLoader
= FALSE
;
1401 // scan the root directory for EFI executables
1402 if (ScanLoaderDir(Volume
, L
"\\", MatchPatterns
))
1403 ScanFallbackLoader
= FALSE
;
1405 // scan subdirectories of the EFI directory (as per the standard)
1406 DirIterOpen(Volume
->RootDir
, L
"EFI", &EfiDirIter
);
1407 while (DirIterNext(&EfiDirIter
, 1, NULL
, &EfiDirEntry
)) {
1408 if (StriCmp(EfiDirEntry
->FileName
, L
"tools") == 0 || EfiDirEntry
->FileName
[0] == '.')
1409 continue; // skip this, doesn't contain boot loaders or is scanned later
1410 SPrint(FileName
, 255, L
"EFI\\%s", EfiDirEntry
->FileName
);
1411 if (ScanLoaderDir(Volume
, FileName
, MatchPatterns
))
1412 ScanFallbackLoader
= FALSE
;
1414 Status
= DirIterClose(&EfiDirIter
);
1415 if (Status
!= EFI_NOT_FOUND
)
1416 CheckError(Status
, L
"while scanning the EFI directory");
1418 // Scan user-specified (or additional default) directories....
1420 while ((Directory
= FindCommaDelimited(GlobalConfig
.AlsoScan
, i
++)) != NULL
) {
1421 if (ShouldScan(Volume
, Directory
)) {
1422 SplitVolumeAndFilename(&Directory
, &VolName
);
1423 CleanUpPathNameSlashes(Directory
);
1424 Length
= StrLen(Directory
);
1425 if ((Length
> 0) && ScanLoaderDir(Volume
, Directory
, MatchPatterns
))
1426 ScanFallbackLoader
= FALSE
;
1427 MyFreePool(VolName
);
1428 } // if should scan dir
1429 MyFreePool(Directory
);
1432 // Don't scan the fallback loader if it's on the same volume and a duplicate of rEFInd itself....
1433 SelfPath
= DevicePathToStr(SelfLoadedImage
->FilePath
);
1434 CleanUpPathNameSlashes(SelfPath
);
1435 if ((Volume
->DeviceHandle
== SelfLoadedImage
->DeviceHandle
) && DuplicatesFallback(Volume
, SelfPath
))
1436 ScanFallbackLoader
= FALSE
;
1438 // If not a duplicate & if it exists & if it's not us, create an entry
1439 // for the fallback boot loader
1440 if (ScanFallbackLoader
&& FileExists(Volume
->RootDir
, FALLBACK_FULLNAME
) && ShouldScan(Volume
, L
"EFI\\BOOT"))
1441 AddLoaderEntry(FALLBACK_FULLNAME
, L
"Fallback boot loader", Volume
);
1443 } // static VOID ScanEfiFiles()
1445 // Scan internal disks for valid EFI boot loaders....
1446 static VOID
ScanInternal(VOID
) {
1449 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1450 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_INTERNAL
) {
1451 ScanEfiFiles(Volumes
[VolumeIndex
]);
1454 } // static VOID ScanInternal()
1456 // Scan external disks for valid EFI boot loaders....
1457 static VOID
ScanExternal(VOID
) {
1460 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1461 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_EXTERNAL
) {
1462 ScanEfiFiles(Volumes
[VolumeIndex
]);
1465 } // static VOID ScanExternal()
1467 // Scan internal disks for valid EFI boot loaders....
1468 static VOID
ScanOptical(VOID
) {
1471 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1472 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_OPTICAL
) {
1473 ScanEfiFiles(Volumes
[VolumeIndex
]);
1476 } // static VOID ScanOptical()
1479 // legacy boot functions
1482 static EFI_STATUS
ActivateMbrPartition(IN EFI_BLOCK_IO
*BlockIO
, IN UINTN PartitionIndex
)
1485 UINT8 SectorBuffer
[512];
1486 MBR_PARTITION_INFO
*MbrTable
, *EMbrTable
;
1487 UINT32 ExtBase
, ExtCurrent
, NextExtCurrent
;
1488 UINTN LogicalPartitionIndex
= 4;
1490 BOOLEAN HaveBootCode
;
1493 Status
= refit_call5_wrapper(BlockIO
->ReadBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, 0, 512, SectorBuffer
);
1494 if (EFI_ERROR(Status
))
1496 if (*((UINT16
*)(SectorBuffer
+ 510)) != 0xaa55)
1497 return EFI_NOT_FOUND
; // safety measure #1
1499 // add boot code if necessary
1500 HaveBootCode
= FALSE
;
1501 for (i
= 0; i
< MBR_BOOTCODE_SIZE
; i
++) {
1502 if (SectorBuffer
[i
] != 0) {
1503 HaveBootCode
= TRUE
;
1507 if (!HaveBootCode
) {
1508 // no boot code found in the MBR, add the syslinux MBR code
1509 SetMem(SectorBuffer
, MBR_BOOTCODE_SIZE
, 0);
1510 CopyMem(SectorBuffer
, syslinux_mbr
, SYSLINUX_MBR_SIZE
);
1513 // set the partition active
1514 MbrTable
= (MBR_PARTITION_INFO
*)(SectorBuffer
+ 446);
1516 for (i
= 0; i
< 4; i
++) {
1517 if (MbrTable
[i
].Flags
!= 0x00 && MbrTable
[i
].Flags
!= 0x80)
1518 return EFI_NOT_FOUND
; // safety measure #2
1519 if (i
== PartitionIndex
)
1520 MbrTable
[i
].Flags
= 0x80;
1521 else if (PartitionIndex
>= 4 && IS_EXTENDED_PART_TYPE(MbrTable
[i
].Type
)) {
1522 MbrTable
[i
].Flags
= 0x80;
1523 ExtBase
= MbrTable
[i
].StartLBA
;
1525 MbrTable
[i
].Flags
= 0x00;
1529 Status
= refit_call5_wrapper(BlockIO
->WriteBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, 0, 512, SectorBuffer
);
1530 if (EFI_ERROR(Status
))
1533 if (PartitionIndex
>= 4) {
1534 // we have to activate a logical partition, so walk the EMBR chain
1536 // NOTE: ExtBase was set above while looking at the MBR table
1537 for (ExtCurrent
= ExtBase
; ExtCurrent
; ExtCurrent
= NextExtCurrent
) {
1538 // read current EMBR
1539 Status
= refit_call5_wrapper(BlockIO
->ReadBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, ExtCurrent
, 512, SectorBuffer
);
1540 if (EFI_ERROR(Status
))
1542 if (*((UINT16
*)(SectorBuffer
+ 510)) != 0xaa55)
1543 return EFI_NOT_FOUND
; // safety measure #3
1545 // scan EMBR, set appropriate partition active
1546 EMbrTable
= (MBR_PARTITION_INFO
*)(SectorBuffer
+ 446);
1548 for (i
= 0; i
< 4; i
++) {
1549 if (EMbrTable
[i
].Flags
!= 0x00 && EMbrTable
[i
].Flags
!= 0x80)
1550 return EFI_NOT_FOUND
; // safety measure #4
1551 if (EMbrTable
[i
].StartLBA
== 0 || EMbrTable
[i
].Size
== 0)
1553 if (IS_EXTENDED_PART_TYPE(EMbrTable
[i
].Type
)) {
1554 // link to next EMBR
1555 NextExtCurrent
= ExtBase
+ EMbrTable
[i
].StartLBA
;
1556 EMbrTable
[i
].Flags
= (PartitionIndex
>= LogicalPartitionIndex
) ? 0x80 : 0x00;
1559 // logical partition
1560 EMbrTable
[i
].Flags
= (PartitionIndex
== LogicalPartitionIndex
) ? 0x80 : 0x00;
1561 LogicalPartitionIndex
++;
1565 // write current EMBR
1566 Status
= refit_call5_wrapper(BlockIO
->WriteBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, ExtCurrent
, 512, SectorBuffer
);
1567 if (EFI_ERROR(Status
))
1570 if (PartitionIndex
< LogicalPartitionIndex
)
1571 break; // stop the loop, no need to touch further EMBRs
1577 } /* static EFI_STATUS ActivateMbrPartition() */
1579 // early 2006 Core Duo / Core Solo models
1580 static UINT8 LegacyLoaderDevicePath1Data
[] = {
1581 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1582 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1583 0xFF, 0xFF, 0xF9, 0xFF, 0x00, 0x00, 0x00, 0x00,
1584 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1585 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1586 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1588 // mid-2006 Mac Pro (and probably other Core 2 models)
1589 static UINT8 LegacyLoaderDevicePath2Data
[] = {
1590 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1591 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1592 0xFF, 0xFF, 0xF7, 0xFF, 0x00, 0x00, 0x00, 0x00,
1593 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1594 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1595 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1597 // mid-2007 MBP ("Santa Rosa" based models)
1598 static UINT8 LegacyLoaderDevicePath3Data
[] = {
1599 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1600 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1601 0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
1602 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1603 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1604 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1607 static UINT8 LegacyLoaderDevicePath4Data
[] = {
1608 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1609 0x00, 0x00, 0xC0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1610 0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
1611 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1612 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1613 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1615 // late-2008 MB/MBP (NVidia chipset)
1616 static UINT8 LegacyLoaderDevicePath5Data
[] = {
1617 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1618 0x00, 0x40, 0xCB, 0xFF, 0x00, 0x00, 0x00, 0x00,
1619 0xFF, 0xBF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
1620 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1621 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1622 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1625 static EFI_DEVICE_PATH
*LegacyLoaderList
[] = {
1626 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath1Data
,
1627 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath2Data
,
1628 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath3Data
,
1629 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath4Data
,
1630 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath5Data
,
1634 #define MAX_DISCOVERED_PATHS (16)
1636 static VOID
StartLegacy(IN LEGACY_ENTRY
*Entry
, IN CHAR16
*SelectionName
)
1639 EG_IMAGE
*BootLogoImage
;
1640 UINTN ErrorInStep
= 0;
1641 EFI_DEVICE_PATH
*DiscoveredPathList
[MAX_DISCOVERED_PATHS
];
1643 BeginExternalScreen(TRUE
, L
"Booting Legacy OS (Mac mode)");
1645 BootLogoImage
= LoadOSIcon(Entry
->Volume
->OSIconName
, L
"legacy", TRUE
);
1646 if (BootLogoImage
!= NULL
)
1647 BltImageAlpha(BootLogoImage
,
1648 (UGAWidth
- BootLogoImage
->Width
) >> 1,
1649 (UGAHeight
- BootLogoImage
->Height
) >> 1,
1650 &StdBackgroundPixel
);
1652 if (Entry
->Volume
->IsMbrPartition
) {
1653 ActivateMbrPartition(Entry
->Volume
->WholeDiskBlockIO
, Entry
->Volume
->MbrPartitionIndex
);
1656 ExtractLegacyLoaderPaths(DiscoveredPathList
, MAX_DISCOVERED_PATHS
, LegacyLoaderList
);
1658 StoreLoaderName(SelectionName
);
1659 Status
= StartEFIImageList(DiscoveredPathList
, Entry
->LoadOptions
, TYPE_LEGACY
, L
"legacy loader", 0, &ErrorInStep
, TRUE
, FALSE
);
1660 if (Status
== EFI_NOT_FOUND
) {
1661 if (ErrorInStep
== 1) {
1662 Print(L
"\nPlease make sure that you have the latest firmware update installed.\n");
1663 } else if (ErrorInStep
== 3) {
1664 Print(L
"\nThe firmware refused to boot from the selected volume. Note that external\n"
1665 L
"hard drives are not well-supported by Apple's firmware for legacy OS booting.\n");
1668 FinishExternalScreen();
1669 } /* static VOID StartLegacy() */
1671 // Start a device on a non-Mac using the EFI_LEGACY_BIOS_PROTOCOL
1672 static VOID
StartLegacyUEFI(LEGACY_ENTRY
*Entry
, CHAR16
*SelectionName
)
1674 BeginExternalScreen(TRUE
, L
"Booting Legacy OS (UEFI mode)");
1675 StoreLoaderName(SelectionName
);
1677 BdsLibConnectDevicePath (Entry
->BdsOption
->DevicePath
);
1678 BdsLibDoLegacyBoot(Entry
->BdsOption
);
1680 // If we get here, it means that there was a failure....
1681 Print(L
"Failure booting legacy (BIOS) OS.");
1683 FinishExternalScreen();
1684 } // static VOID StartLegacyUEFI()
1686 static LEGACY_ENTRY
* AddLegacyEntry(IN CHAR16
*LoaderTitle
, IN REFIT_VOLUME
*Volume
)
1688 LEGACY_ENTRY
*Entry
, *SubEntry
;
1689 REFIT_MENU_SCREEN
*SubScreen
;
1690 CHAR16
*VolDesc
, *LegacyTitle
;
1691 CHAR16 ShortcutLetter
= 0;
1693 if (LoaderTitle
== NULL
) {
1694 if (Volume
->OSName
!= NULL
) {
1695 LoaderTitle
= Volume
->OSName
;
1696 if (LoaderTitle
[0] == 'W' || LoaderTitle
[0] == 'L')
1697 ShortcutLetter
= LoaderTitle
[0];
1699 LoaderTitle
= L
"Legacy OS";
1701 if (Volume
->VolName
!= NULL
)
1702 VolDesc
= Volume
->VolName
;
1704 VolDesc
= (Volume
->DiskKind
== DISK_KIND_OPTICAL
) ? L
"CD" : L
"HD";
1706 LegacyTitle
= AllocateZeroPool(256 * sizeof(CHAR16
));
1707 if (LegacyTitle
!= NULL
)
1708 SPrint(LegacyTitle
, 255, L
"Boot %s from %s", LoaderTitle
, VolDesc
);
1709 if (IsInSubstring(LegacyTitle
, GlobalConfig
.DontScanVolumes
)) {
1710 MyFreePool(LegacyTitle
);
1714 // prepare the menu entry
1715 Entry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1716 Entry
->me
.Title
= LegacyTitle
;
1717 Entry
->me
.Tag
= TAG_LEGACY
;
1719 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1720 Entry
->me
.Image
= LoadOSIcon(Volume
->OSIconName
, L
"legacy", FALSE
);
1721 Entry
->me
.BadgeImage
= Volume
->VolBadgeImage
;
1722 Entry
->Volume
= Volume
;
1723 Entry
->LoadOptions
= (Volume
->DiskKind
== DISK_KIND_OPTICAL
) ? L
"CD" :
1724 ((Volume
->DiskKind
== DISK_KIND_EXTERNAL
) ? L
"USB" : L
"HD");
1725 Entry
->Enabled
= TRUE
;
1727 // create the submenu
1728 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
1729 SubScreen
->Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1730 SPrint(SubScreen
->Title
, 255, L
"Boot Options for %s on %s", LoaderTitle
, VolDesc
);
1731 SubScreen
->TitleImage
= Entry
->me
.Image
;
1732 SubScreen
->Hint1
= StrDuplicate(SUBSCREEN_HINT1
);
1733 if (GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_EDITOR
) {
1734 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2_NO_EDITOR
);
1736 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2
);
1740 SubEntry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1741 SubEntry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1742 SPrint(SubEntry
->me
.Title
, 255, L
"Boot %s", LoaderTitle
);
1743 SubEntry
->me
.Tag
= TAG_LEGACY
;
1744 SubEntry
->Volume
= Entry
->Volume
;
1745 SubEntry
->LoadOptions
= Entry
->LoadOptions
;
1746 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
1748 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
1749 Entry
->me
.SubScreen
= SubScreen
;
1750 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1752 } /* static LEGACY_ENTRY * AddLegacyEntry() */
1755 // default volume badge icon based on disk kind
1756 static EG_IMAGE
* GetDiskBadge(IN UINTN DiskType
) {
1757 EG_IMAGE
* Badge
= NULL
;
1761 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_INTERNAL
);
1764 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_EXTERNAL
);
1767 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_OPTICAL
);
1771 } // static EG_IMAGE * GetDiskBadge()
1774 Create a rEFInd boot option from a Legacy BIOS protocol option.
1776 static LEGACY_ENTRY
* AddLegacyEntryUEFI(BDS_COMMON_OPTION
*BdsOption
, IN UINT16 DiskType
)
1778 LEGACY_ENTRY
*Entry
, *SubEntry
;
1779 REFIT_MENU_SCREEN
*SubScreen
;
1780 CHAR16 ShortcutLetter
= 0;
1781 CHAR16
*LegacyDescription
= StrDuplicate(BdsOption
->Description
);
1783 if (IsInSubstring(LegacyDescription
, GlobalConfig
.DontScanVolumes
))
1786 // Remove stray spaces, since many EFIs produce descriptions with lots of
1787 // extra spaces, especially at the end; this throws off centering of the
1788 // description on the screen....
1789 LimitStringLength(LegacyDescription
, 100);
1791 // prepare the menu entry
1792 Entry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1793 Entry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1794 SPrint(Entry
->me
.Title
, 255, L
"Boot legacy target %s", LegacyDescription
);
1795 Entry
->me
.Tag
= TAG_LEGACY_UEFI
;
1797 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1798 Entry
->me
.Image
= LoadOSIcon(L
"legacy", L
"legacy", TRUE
);
1799 Entry
->LoadOptions
= (DiskType
== BBS_CDROM
) ? L
"CD" :
1800 ((DiskType
== BBS_USB
) ? L
"USB" : L
"HD");
1801 Entry
->me
.BadgeImage
= GetDiskBadge(DiskType
);
1802 Entry
->BdsOption
= BdsOption
;
1803 Entry
->Enabled
= TRUE
;
1805 // create the submenu
1806 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
1807 SubScreen
->Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1808 SPrint(SubScreen
->Title
, 255, L
"No boot options for legacy target");
1809 SubScreen
->TitleImage
= Entry
->me
.Image
;
1810 SubScreen
->Hint1
= StrDuplicate(SUBSCREEN_HINT1
);
1811 if (GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_EDITOR
) {
1812 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2_NO_EDITOR
);
1814 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2
);
1818 SubEntry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1819 SubEntry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1820 SPrint(SubEntry
->me
.Title
, 255, L
"Boot %s", LegacyDescription
);
1821 SubEntry
->me
.Tag
= TAG_LEGACY_UEFI
;
1822 Entry
->BdsOption
= BdsOption
;
1823 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
1825 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
1826 Entry
->me
.SubScreen
= SubScreen
;
1827 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1829 } /* static LEGACY_ENTRY * AddLegacyEntryUEFI() */
1832 Scan for legacy BIOS targets on machines that implement EFI_LEGACY_BIOS_PROTOCOL.
1833 In testing, protocol has not been implemented on Macs but has been
1834 implemented on several Dell PCs and an ASUS motherboard.
1835 Restricts output to disks of the specified DiskType.
1837 static VOID
ScanLegacyUEFI(IN UINTN DiskType
)
1840 EFI_LEGACY_BIOS_PROTOCOL
*LegacyBios
;
1841 UINT16
*BootOrder
= NULL
;
1843 CHAR16 BootOption
[10];
1844 UINTN BootOrderSize
= 0;
1846 BDS_COMMON_OPTION
*BdsOption
;
1847 LIST_ENTRY TempList
;
1848 BBS_BBS_DEVICE_PATH
*BbsDevicePath
= NULL
;
1849 BOOLEAN SearchingForUsb
= FALSE
;
1851 InitializeListHead (&TempList
);
1852 ZeroMem (Buffer
, sizeof (Buffer
));
1854 // If LegacyBios protocol is not implemented on this platform, then
1855 //we do not support this type of legacy boot on this machine.
1856 Status
= refit_call3_wrapper(gBS
->LocateProtocol
, &gEfiLegacyBootProtocolGuid
, NULL
, (VOID
**) &LegacyBios
);
1857 if (EFI_ERROR (Status
))
1860 // EFI calls USB drives BBS_HARDDRIVE, but we want to distinguish them,
1861 // so we set DiskType inappropriately elsewhere in the program and
1862 // "translate" it here.
1863 if (DiskType
== BBS_USB
) {
1864 DiskType
= BBS_HARDDISK
;
1865 SearchingForUsb
= TRUE
;
1868 // Grab the boot order
1869 BootOrder
= BdsLibGetVariableAndSize(L
"BootOrder", &gEfiGlobalVariableGuid
, &BootOrderSize
);
1870 if (BootOrder
== NULL
) {
1875 while (Index
< BootOrderSize
/ sizeof (UINT16
))
1877 // Grab each boot option variable from the boot order, and convert
1878 // the variable into a BDS boot option
1879 UnicodeSPrint (BootOption
, sizeof (BootOption
), L
"Boot%04x", BootOrder
[Index
]);
1880 BdsOption
= BdsLibVariableToOption (&TempList
, BootOption
);
1882 if (BdsOption
!= NULL
) {
1883 BbsDevicePath
= (BBS_BBS_DEVICE_PATH
*)BdsOption
->DevicePath
;
1884 // Only add the entry if it is of a requested type (e.g. USB, HD)
1885 // Two checks necessary because some systems return EFI boot loaders
1886 // with a DeviceType value that would inappropriately include them
1887 // as legacy loaders....
1888 if ((BbsDevicePath
->DeviceType
== DiskType
) && (BdsOption
->DevicePath
->Type
== DEVICE_TYPE_BIOS
)) {
1889 // USB flash drives appear as hard disks with certain media flags set.
1890 // Look for this, and if present, pass it on with the (technically
1891 // incorrect, but internally useful) BBS_TYPE_USB flag set.
1892 if (DiskType
== BBS_HARDDISK
) {
1893 if (SearchingForUsb
&& (BbsDevicePath
->StatusFlag
& (BBS_MEDIA_PRESENT
| BBS_MEDIA_MAYBE_PRESENT
))) {
1894 AddLegacyEntryUEFI(BdsOption
, BBS_USB
);
1895 } else if (!SearchingForUsb
&& !(BbsDevicePath
->StatusFlag
& (BBS_MEDIA_PRESENT
| BBS_MEDIA_MAYBE_PRESENT
))) {
1896 AddLegacyEntryUEFI(BdsOption
, DiskType
);
1899 AddLegacyEntryUEFI(BdsOption
, DiskType
);
1902 } // if (BdsOption != NULL)
1905 } /* static VOID ScanLegacyUEFI() */
1907 static VOID
ScanLegacyVolume(REFIT_VOLUME
*Volume
, UINTN VolumeIndex
) {
1909 BOOLEAN ShowVolume
, HideIfOthersFound
;
1912 HideIfOthersFound
= FALSE
;
1913 if (Volume
->IsAppleLegacy
) {
1915 HideIfOthersFound
= TRUE
;
1916 } else if (Volume
->HasBootCode
) {
1918 if (Volume
->BlockIO
== Volume
->WholeDiskBlockIO
&&
1919 Volume
->BlockIOOffset
== 0 &&
1920 Volume
->OSName
== NULL
)
1921 // this is a whole disk (MBR) entry; hide if we have entries for partitions
1922 HideIfOthersFound
= TRUE
;
1924 if (HideIfOthersFound
) {
1925 // check for other bootable entries on the same disk
1926 for (VolumeIndex2
= 0; VolumeIndex2
< VolumesCount
; VolumeIndex2
++) {
1927 if (VolumeIndex2
!= VolumeIndex
&& Volumes
[VolumeIndex2
]->HasBootCode
&&
1928 Volumes
[VolumeIndex2
]->WholeDiskBlockIO
== Volume
->WholeDiskBlockIO
)
1934 AddLegacyEntry(NULL
, Volume
);
1935 } // static VOID ScanLegacyVolume()
1937 // Scan attached optical discs for legacy (BIOS) boot code
1938 // and add anything found to the list....
1939 static VOID
ScanLegacyDisc(VOID
)
1942 REFIT_VOLUME
*Volume
;
1944 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
) {
1945 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1946 Volume
= Volumes
[VolumeIndex
];
1947 if (Volume
->DiskKind
== DISK_KIND_OPTICAL
)
1948 ScanLegacyVolume(Volume
, VolumeIndex
);
1950 } else if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
1951 ScanLegacyUEFI(BBS_CDROM
);
1953 } /* static VOID ScanLegacyDisc() */
1955 // Scan internal hard disks for legacy (BIOS) boot code
1956 // and add anything found to the list....
1957 static VOID
ScanLegacyInternal(VOID
)
1960 REFIT_VOLUME
*Volume
;
1962 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
) {
1963 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1964 Volume
= Volumes
[VolumeIndex
];
1965 if (Volume
->DiskKind
== DISK_KIND_INTERNAL
)
1966 ScanLegacyVolume(Volume
, VolumeIndex
);
1968 } else if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
1969 // TODO: This actually picks up USB flash drives, too; try to find
1970 // a way to differentiate the two....
1971 ScanLegacyUEFI(BBS_HARDDISK
);
1973 } /* static VOID ScanLegacyInternal() */
1975 // Scan external disks for legacy (BIOS) boot code
1976 // and add anything found to the list....
1977 static VOID
ScanLegacyExternal(VOID
)
1980 REFIT_VOLUME
*Volume
;
1982 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
) {
1983 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1984 Volume
= Volumes
[VolumeIndex
];
1985 if (Volume
->DiskKind
== DISK_KIND_EXTERNAL
)
1986 ScanLegacyVolume(Volume
, VolumeIndex
);
1988 } else if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
1989 // TODO: This actually doesn't do anything useful; leaving in hopes of
1990 // fixing it later....
1991 ScanLegacyUEFI(BBS_USB
);
1993 } /* static VOID ScanLegacyExternal() */
1996 // pre-boot tool functions
1999 static VOID
StartTool(IN LOADER_ENTRY
*Entry
)
2001 BeginExternalScreen(Entry
->UseGraphicsMode
, Entry
->me
.Title
+ 6); // assumes "Start <title>" as assigned below
2002 StoreLoaderName(Entry
->me
.Title
);
2003 StartEFIImage(Entry
->DevicePath
, Entry
->LoadOptions
, TYPE_EFI
,
2004 Basename(Entry
->LoaderPath
), Entry
->OSType
, NULL
, TRUE
, FALSE
);
2005 FinishExternalScreen();
2006 } /* static VOID StartTool() */
2008 static LOADER_ENTRY
* AddToolEntry(EFI_HANDLE DeviceHandle
, IN CHAR16
*LoaderPath
, IN CHAR16
*LoaderTitle
, IN EG_IMAGE
*Image
,
2009 IN CHAR16 ShortcutLetter
, IN BOOLEAN UseGraphicsMode
)
2011 LOADER_ENTRY
*Entry
;
2012 CHAR16
*TitleStr
= NULL
;
2014 Entry
= AllocateZeroPool(sizeof(LOADER_ENTRY
));
2016 TitleStr
= PoolPrint(L
"Start %s", LoaderTitle
);
2017 Entry
->me
.Title
= TitleStr
;
2018 Entry
->me
.Tag
= TAG_TOOL
;
2020 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
2021 Entry
->me
.Image
= Image
;
2022 Entry
->LoaderPath
= (LoaderPath
) ? StrDuplicate(LoaderPath
) : NULL
;
2023 Entry
->DevicePath
= FileDevicePath(DeviceHandle
, Entry
->LoaderPath
);
2024 Entry
->UseGraphicsMode
= UseGraphicsMode
;
2026 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
2028 } /* static LOADER_ENTRY * AddToolEntry() */
2031 // pre-boot driver functions
2034 static UINTN
ScanDriverDir(IN CHAR16
*Path
)
2037 REFIT_DIR_ITER DirIter
;
2039 EFI_FILE_INFO
*DirEntry
;
2040 CHAR16 FileName
[256];
2042 CleanUpPathNameSlashes(Path
);
2043 // look through contents of the directory
2044 DirIterOpen(SelfRootDir
, Path
, &DirIter
);
2045 while (DirIterNext(&DirIter
, 2, LOADER_MATCH_PATTERNS
, &DirEntry
)) {
2046 if (DirEntry
->FileName
[0] == '.')
2047 continue; // skip this
2049 SPrint(FileName
, 255, L
"%s\\%s", Path
, DirEntry
->FileName
);
2051 Status
= StartEFIImage(FileDevicePath(SelfLoadedImage
->DeviceHandle
, FileName
),
2052 L
"", TYPE_EFI
, DirEntry
->FileName
, 0, NULL
, FALSE
, TRUE
);
2054 Status
= DirIterClose(&DirIter
);
2055 if (Status
!= EFI_NOT_FOUND
) {
2056 SPrint(FileName
, 255, L
"while scanning the %s directory", Path
);
2057 CheckError(Status
, FileName
);
2062 #ifdef __MAKEWITH_GNUEFI
2063 static EFI_STATUS
ConnectAllDriversToAllControllers(VOID
)
2066 UINTN AllHandleCount
;
2067 EFI_HANDLE
*AllHandleBuffer
;
2070 EFI_HANDLE
*HandleBuffer
;
2076 Status
= LibLocateHandle(AllHandles
,
2081 if (EFI_ERROR(Status
))
2084 for (Index
= 0; Index
< AllHandleCount
; Index
++) {
2086 // Scan the handle database
2088 Status
= LibScanHandleDatabase(NULL
,
2090 AllHandleBuffer
[Index
],
2095 if (EFI_ERROR (Status
))
2099 if (HandleType
[Index
] & EFI_HANDLE_TYPE_DRIVER_BINDING_HANDLE
)
2101 if (HandleType
[Index
] & EFI_HANDLE_TYPE_IMAGE_HANDLE
)
2106 for (HandleIndex
= 0; HandleIndex
< HandleCount
; HandleIndex
++) {
2107 if (HandleType
[HandleIndex
] & EFI_HANDLE_TYPE_PARENT_HANDLE
)
2112 if (HandleType
[Index
] & EFI_HANDLE_TYPE_DEVICE_HANDLE
) {
2113 Status
= refit_call4_wrapper(BS
->ConnectController
,
2114 AllHandleBuffer
[Index
],
2122 MyFreePool (HandleBuffer
);
2123 MyFreePool (HandleType
);
2127 MyFreePool (AllHandleBuffer
);
2129 } /* EFI_STATUS ConnectAllDriversToAllControllers() */
2131 static EFI_STATUS
ConnectAllDriversToAllControllers(VOID
) {
2132 BdsLibConnectAllDriversToAllControllers();
2137 // Load all EFI drivers from rEFInd's "drivers" subdirectory and from the
2138 // directories specified by the user in the "scan_driver_dirs" configuration
2140 static VOID
LoadDrivers(VOID
)
2142 CHAR16
*Directory
, *SelfDirectory
;
2143 UINTN i
= 0, Length
, NumFound
= 0;
2145 // load drivers from the subdirectories of rEFInd's home directory specified
2146 // in the DRIVER_DIRS constant.
2147 while ((Directory
= FindCommaDelimited(DRIVER_DIRS
, i
++)) != NULL
) {
2148 SelfDirectory
= SelfDirPath
? StrDuplicate(SelfDirPath
) : NULL
;
2149 CleanUpPathNameSlashes(SelfDirectory
);
2150 MergeStrings(&SelfDirectory
, Directory
, L
'\\');
2151 NumFound
+= ScanDriverDir(SelfDirectory
);
2152 MyFreePool(Directory
);
2153 MyFreePool(SelfDirectory
);
2156 // Scan additional user-specified driver directories....
2158 while ((Directory
= FindCommaDelimited(GlobalConfig
.DriverDirs
, i
++)) != NULL
) {
2159 CleanUpPathNameSlashes(Directory
);
2160 Length
= StrLen(Directory
);
2162 NumFound
+= ScanDriverDir(Directory
);
2164 MyFreePool(Directory
);
2167 // connect all devices
2169 ConnectAllDriversToAllControllers();
2170 } /* static VOID LoadDrivers() */
2172 // Determine what (if any) type of legacy (BIOS) boot support is available
2173 static VOID
FindLegacyBootType(VOID
) {
2175 EFI_LEGACY_BIOS_PROTOCOL
*LegacyBios
;
2177 GlobalConfig
.LegacyType
= LEGACY_TYPE_NONE
;
2179 // UEFI-style legacy BIOS support is available only with some EFI implementations....
2180 Status
= refit_call3_wrapper(gBS
->LocateProtocol
, &gEfiLegacyBootProtocolGuid
, NULL
, (VOID
**) &LegacyBios
);
2181 if (!EFI_ERROR (Status
))
2182 GlobalConfig
.LegacyType
= LEGACY_TYPE_UEFI
;
2184 // Macs have their own system. If the firmware vendor code contains the
2185 // string "Apple", assume it's available. Note that this overrides the
2186 // UEFI type, and might yield false positives if the vendor string
2187 // contains "Apple" as part of something bigger, so this isn't 100%
2189 if (StriSubCmp(L
"Apple", ST
->FirmwareVendor
))
2190 GlobalConfig
.LegacyType
= LEGACY_TYPE_MAC
;
2191 } // static VOID FindLegacyBootType
2193 // Warn the user if legacy OS scans are enabled but the firmware can't support them....
2194 static VOID
WarnIfLegacyProblems(VOID
) {
2195 BOOLEAN found
= FALSE
;
2198 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_NONE
) {
2200 if (GlobalConfig
.ScanFor
[i
] == 'h' || GlobalConfig
.ScanFor
[i
] == 'b' || GlobalConfig
.ScanFor
[i
] == 'c' ||
2201 GlobalConfig
.ScanFor
[i
] == 'H' || GlobalConfig
.ScanFor
[i
] == 'B' || GlobalConfig
.ScanFor
[i
] == 'C')
2204 } while ((i
< NUM_SCAN_OPTIONS
) && (!found
));
2207 Print(L
"NOTE: refind.conf's 'scanfor' line specifies scanning for one or more legacy\n");
2208 Print(L
"(BIOS) boot options; however, this is not possible because your computer lacks\n");
2209 Print(L
"the necessary Compatibility Support Module (CSM) support or that support is\n");
2210 Print(L
"disabled in your firmware.\n");
2213 } // if no legacy support
2214 } // static VOID WarnIfLegacyProblems()
2216 // Locates boot loaders. NOTE: This assumes that GlobalConfig.LegacyType is set correctly.
2217 static VOID
ScanForBootloaders(VOID
) {
2220 BOOLEAN ScanForLegacy
= FALSE
;
2222 // Determine up-front if we'll be scanning for legacy loaders....
2223 for (i
= 0; i
< NUM_SCAN_OPTIONS
; i
++) {
2224 s
= GlobalConfig
.ScanFor
[i
];
2225 if ((s
== 'c') || (s
== 'C') || (s
== 'h') || (s
== 'H') || (s
== 'b') || (s
== 'B'))
2226 ScanForLegacy
= TRUE
;
2229 // If UEFI & scanning for legacy loaders & deep legacy scan, update NVRAM boot manager list
2230 if ((GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) && ScanForLegacy
&& GlobalConfig
.DeepLegacyScan
) {
2231 BdsDeleteAllInvalidLegacyBootOptions();
2232 BdsAddNonExistingLegacyBootOptions();
2235 // scan for loaders and tools, add them to the menu
2236 for (i
= 0; i
< NUM_SCAN_OPTIONS
; i
++) {
2237 switch(GlobalConfig
.ScanFor
[i
]) {
2242 ScanLegacyInternal();
2245 ScanLegacyExternal();
2248 ScanUserConfigured(GlobalConfig
.ConfigFilename
);
2262 // assign shortcut keys
2263 for (i
= 0; i
< MainMenu
.EntryCount
&& MainMenu
.Entries
[i
]->Row
== 0 && i
< 9; i
++)
2264 MainMenu
.Entries
[i
]->ShortcutDigit
= (CHAR16
)('1' + i
);
2266 // wait for user ACK when there were errors
2267 FinishTextScreen(FALSE
);
2268 } // static VOID ScanForBootloaders()
2270 // Locate a single tool from the specified Locations using one of the
2271 // specified Names and add it to the menu.
2272 static VOID
FindTool(CHAR16
*Locations
, CHAR16
*Names
, CHAR16
*Description
, UINTN Icon
) {
2273 UINTN j
= 0, k
, VolumeIndex
;
2274 CHAR16
*DirName
, *FileName
, *PathName
, FullDescription
[256];
2276 while ((DirName
= FindCommaDelimited(Locations
, j
++)) != NULL
) {
2278 while ((FileName
= FindCommaDelimited(Names
, k
++)) != NULL
) {
2279 PathName
= StrDuplicate(DirName
);
2280 MergeStrings(&PathName
, FileName
, (StriCmp(PathName
, L
"\\") == 0) ? 0 : L
'\\');
2281 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
2282 if ((Volumes
[VolumeIndex
]->RootDir
!= NULL
) && (FileExists(Volumes
[VolumeIndex
]->RootDir
, PathName
))) {
2283 SPrint(FullDescription
, 255, L
"%s at %s on %s", Description
, PathName
, Volumes
[VolumeIndex
]->VolName
);
2284 AddToolEntry(Volumes
[VolumeIndex
]->DeviceHandle
, PathName
, FullDescription
, BuiltinIcon(Icon
), 'S', FALSE
);
2287 MyFreePool(PathName
);
2288 MyFreePool(FileName
);
2290 MyFreePool(DirName
);
2291 } // while Locations
2292 } // VOID FindTool()
2294 // Add the second-row tags containing built-in and external tools (EFI shell,
2296 static VOID
ScanForTools(VOID
) {
2297 CHAR16
*FileName
= NULL
, *VolName
= NULL
, *MokLocations
, Description
[256];
2298 REFIT_MENU_ENTRY
*TempMenuEntry
;
2299 UINTN i
, j
, VolumeIndex
;
2303 MokLocations
= StrDuplicate(MOK_LOCATIONS
);
2304 if (MokLocations
!= NULL
)
2305 MergeStrings(&MokLocations
, SelfDirPath
, L
',');
2307 for (i
= 0; i
< NUM_TOOLS
; i
++) {
2308 switch(GlobalConfig
.ShowTools
[i
]) {
2309 // NOTE: Be sure that FileName is NULL at the end of each case.
2311 TempMenuEntry
= CopyMenuEntry(&MenuEntryShutdown
);
2312 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_SHUTDOWN
);
2313 AddMenuEntry(&MainMenu
, TempMenuEntry
);
2317 TempMenuEntry
= CopyMenuEntry(&MenuEntryReset
);
2318 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_RESET
);
2319 AddMenuEntry(&MainMenu
, TempMenuEntry
);
2323 TempMenuEntry
= CopyMenuEntry(&MenuEntryAbout
);
2324 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT
);
2325 AddMenuEntry(&MainMenu
, TempMenuEntry
);
2329 TempMenuEntry
= CopyMenuEntry(&MenuEntryExit
);
2330 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_EXIT
);
2331 AddMenuEntry(&MainMenu
, TempMenuEntry
);
2335 if (EfivarGetRaw(&GlobalGuid
, L
"OsIndicationsSupported", &b
, &j
) == EFI_SUCCESS
) {
2337 if (osind
& EFI_OS_INDICATIONS_BOOT_TO_FW_UI
) {
2338 TempMenuEntry
= CopyMenuEntry(&MenuEntryFirmware
);
2339 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_FIRMWARE
);
2340 AddMenuEntry(&MainMenu
, TempMenuEntry
);
2347 while ((FileName
= FindCommaDelimited(SHELL_NAMES
, j
++)) != NULL
) {
2348 if (FileExists(SelfRootDir
, FileName
)) {
2349 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, L
"EFI Shell", BuiltinIcon(BUILTIN_ICON_TOOL_SHELL
),
2352 MyFreePool(FileName
);
2358 while ((FileName
= FindCommaDelimited(GPTSYNC_NAMES
, j
++)) != NULL
) {
2359 if (FileExists(SelfRootDir
, FileName
)) {
2360 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, L
"Hybrid MBR tool", BuiltinIcon(BUILTIN_ICON_TOOL_PART
),
2363 MyFreePool(FileName
);
2370 while ((FileName
= FindCommaDelimited(GDISK_NAMES
, j
++)) != NULL
) {
2371 if (FileExists(SelfRootDir
, FileName
)) {
2372 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, L
"disk partitioning tool",
2373 BuiltinIcon(BUILTIN_ICON_TOOL_PART
), 'G', FALSE
);
2375 MyFreePool(FileName
);
2380 case TAG_APPLE_RECOVERY
:
2381 FileName
= StrDuplicate(L
"\\com.apple.recovery.boot\\boot.efi");
2382 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
2383 if ((Volumes
[VolumeIndex
]->RootDir
!= NULL
) && (FileExists(Volumes
[VolumeIndex
]->RootDir
, FileName
))) {
2384 SPrint(Description
, 255, L
"Apple Recovery on %s", Volumes
[VolumeIndex
]->VolName
);
2385 AddToolEntry(Volumes
[VolumeIndex
]->DeviceHandle
, FileName
, Description
,
2386 BuiltinIcon(BUILTIN_ICON_TOOL_APPLE_RESCUE
), 'R', TRUE
);
2389 MyFreePool(FileName
);
2393 case TAG_WINDOWS_RECOVERY
:
2395 while ((FileName
= FindCommaDelimited(GlobalConfig
.WindowsRecoveryFiles
, j
++)) != NULL
) {
2396 SplitVolumeAndFilename(&FileName
, &VolName
);
2397 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
2398 if ((Volumes
[VolumeIndex
]->RootDir
!= NULL
) && (FileExists(Volumes
[VolumeIndex
]->RootDir
, FileName
)) &&
2399 ((VolName
== NULL
) || (StriCmp(VolName
, Volumes
[VolumeIndex
]->VolName
) == 0))) {
2400 SPrint(Description
, 255, L
"Microsoft Recovery on %s", Volumes
[VolumeIndex
]->VolName
);
2401 AddToolEntry(Volumes
[VolumeIndex
]->DeviceHandle
, FileName
, Description
,
2402 BuiltinIcon(BUILTIN_ICON_TOOL_WINDOWS_RESCUE
), 'R', TRUE
);
2406 MyFreePool(FileName
);
2408 MyFreePool(VolName
);
2413 FindTool(MokLocations
, MOK_NAMES
, L
"MOK utility", BUILTIN_ICON_TOOL_MOK_TOOL
);
2417 FindTool(MEMTEST_LOCATIONS
, MEMTEST_NAMES
, L
"Memory test utility", BUILTIN_ICON_TOOL_MEMTEST
);
2422 } // static VOID ScanForTools
2424 // Rescan for boot loaders
2425 static VOID
RescanAll(BOOLEAN DisplayMessage
) {
2433 egDisplayMessage(L
"Scanning for new boot loaders; please wait....", &BGColor
);
2434 FreeList((VOID
***) &(MainMenu
.Entries
), &MainMenu
.EntryCount
);
2435 MainMenu
.Entries
= NULL
;
2436 MainMenu
.EntryCount
= 0;
2437 ReadConfig(GlobalConfig
.ConfigFilename
);
2438 ConnectAllDriversToAllControllers();
2440 ScanForBootloaders();
2443 } // VOID RescanAll()
2445 #ifdef __MAKEWITH_TIANO
2447 // Minimal initialization function
2448 static VOID
InitializeLib(IN EFI_HANDLE ImageHandle
, IN EFI_SYSTEM_TABLE
*SystemTable
) {
2450 // gImageHandle = ImageHandle;
2451 gBS
= SystemTable
->BootServices
;
2452 // gRS = SystemTable->RuntimeServices;
2453 gRT
= SystemTable
->RuntimeServices
; // Some BDS functions need gRT to be set
2454 EfiGetSystemConfigurationTable (&gEfiDxeServicesTableGuid
, (VOID
**) &gDS
);
2456 // InitializeConsoleSim();
2461 // Set up our own Secure Boot extensions....
2462 // Returns TRUE on success, FALSE otherwise
2463 static BOOLEAN
SecureBootSetup(VOID
) {
2465 BOOLEAN Success
= FALSE
;
2467 if (secure_mode() && ShimLoaded()) {
2468 Status
= security_policy_install();
2469 if (Status
== EFI_SUCCESS
) {
2472 Print(L
"Failed to install MOK Secure Boot extensions");
2476 } // VOID SecureBootSetup()
2478 // Remove our own Secure Boot extensions....
2479 // Returns TRUE on success, FALSE otherwise
2480 static BOOLEAN
SecureBootUninstall(VOID
) {
2482 BOOLEAN Success
= TRUE
;
2484 if (secure_mode()) {
2485 Status
= security_policy_uninstall();
2486 if (Status
!= EFI_SUCCESS
) {
2488 BeginTextScreen(L
"Secure Boot Policy Failure");
2489 Print(L
"Failed to uninstall MOK Secure Boot extensions; forcing a reboot.");
2491 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);
2495 } // VOID SecureBootUninstall
2497 // Sets the global configuration filename; will be CONFIG_FILE_NAME unless the
2498 // "-c" command-line option is set, in which case that takes precedence.
2499 // If an error is encountered, leaves the value alone (it should be set to
2500 // CONFIG_FILE_NAME when GlobalConfig is initialized).
2501 static VOID
SetConfigFilename(EFI_HANDLE ImageHandle
) {
2502 EFI_LOADED_IMAGE
*Info
;
2503 CHAR16
*Options
, *FileName
;
2507 Status
= refit_call3_wrapper(BS
->HandleProtocol
, ImageHandle
, &LoadedImageProtocol
, (VOID
**) &Info
);
2508 if ((Status
== EFI_SUCCESS
) && (Info
->LoadOptionsSize
> 0)) {
2509 Options
= (CHAR16
*) Info
->LoadOptions
;
2510 Where
= FindSubString(L
" -c ", Options
);
2512 FileName
= StrDuplicate(&Options
[Where
+ 4]);
2513 Where
= FindSubString(L
" ", FileName
);
2515 FileName
[Where
] = L
'\0';
2517 if (FileExists(SelfDir
, FileName
)) {
2518 GlobalConfig
.ConfigFilename
= FileName
;
2520 Print(L
"Specified configuration file (%s) doesn't exist; using\n'refind.conf' default\n", FileName
);
2521 MyFreePool(FileName
);
2525 } // VOID SetConfigFilename()
2532 efi_main (EFI_HANDLE ImageHandle
, EFI_SYSTEM_TABLE
*SystemTable
)
2535 BOOLEAN MainLoopRunning
= TRUE
;
2536 BOOLEAN MokProtocol
;
2537 REFIT_MENU_ENTRY
*ChosenEntry
;
2539 CHAR16
*SelectionName
= NULL
;
2543 InitializeLib(ImageHandle
, SystemTable
);
2544 Status
= InitRefitLib(ImageHandle
);
2545 if (EFI_ERROR(Status
))
2548 // read configuration
2549 CopyMem(GlobalConfig
.ScanFor
, "ieom ", NUM_SCAN_OPTIONS
);
2550 FindLegacyBootType();
2551 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
)
2552 CopyMem(GlobalConfig
.ScanFor
, "ihebocm ", NUM_SCAN_OPTIONS
);
2553 SetConfigFilename(ImageHandle
);
2554 ReadConfig(GlobalConfig
.ConfigFilename
);
2557 WarnIfLegacyProblems();
2558 MainMenu
.TimeoutSeconds
= GlobalConfig
.Timeout
;
2560 // disable EFI watchdog timer
2561 refit_call4_wrapper(BS
->SetWatchdogTimer
, 0x0000, 0x0000, 0x0000, NULL
);
2563 // further bootstrap (now with config available)
2564 MokProtocol
= SecureBootSetup();
2567 ScanForBootloaders();
2571 if (GlobalConfig
.ScanDelay
> 0) {
2576 if (GlobalConfig
.ScanDelay
> 1)
2577 egDisplayMessage(L
"Pausing before disk scan; please wait....", &BGColor
);
2578 for (i
= 0; i
< GlobalConfig
.ScanDelay
; i
++)
2579 refit_call1_wrapper(BS
->Stall
, 1000000);
2580 RescanAll(GlobalConfig
.ScanDelay
> 1);
2583 if (GlobalConfig
.DefaultSelection
)
2584 SelectionName
= StrDuplicate(GlobalConfig
.DefaultSelection
);
2586 while (MainLoopRunning
) {
2587 MenuExit
= RunMainMenu(&MainMenu
, &SelectionName
, &ChosenEntry
);
2589 // The Escape key triggers a re-scan operation....
2590 if (MenuExit
== MENU_EXIT_ESCAPE
) {
2596 switch (ChosenEntry
->Tag
) {
2598 case TAG_REBOOT
: // Reboot
2600 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);
2601 MainLoopRunning
= FALSE
; // just in case we get this far
2604 case TAG_SHUTDOWN
: // Shut Down
2606 refit_call4_wrapper(RT
->ResetSystem
, EfiResetShutdown
, EFI_SUCCESS
, 0, NULL
);
2607 MainLoopRunning
= FALSE
; // just in case we get this far
2610 case TAG_ABOUT
: // About rEFInd
2614 case TAG_LOADER
: // Boot OS via .EFI loader
2615 StartLoader((LOADER_ENTRY
*)ChosenEntry
, SelectionName
);
2618 case TAG_LEGACY
: // Boot legacy OS
2619 StartLegacy((LEGACY_ENTRY
*)ChosenEntry
, SelectionName
);
2622 case TAG_LEGACY_UEFI
: // Boot a legacy OS on a non-Mac
2623 StartLegacyUEFI((LEGACY_ENTRY
*)ChosenEntry
, SelectionName
);
2626 case TAG_TOOL
: // Start a EFI tool
2627 StartTool((LOADER_ENTRY
*)ChosenEntry
);
2630 case TAG_EXIT
: // Terminate rEFInd
2631 if ((MokProtocol
) && !SecureBootUninstall()) {
2632 MainLoopRunning
= FALSE
; // just in case we get this far
2634 BeginTextScreen(L
" ");
2639 case TAG_FIRMWARE
: // Reboot into firmware's user interface
2640 RebootIntoFirmware();
2646 // If we end up here, things have gone wrong. Try to reboot, and if that
2647 // fails, go into an endless loop.
2648 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);