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-2015 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.
54 #include "security_policy.h"
55 #include "driver_support.h"
56 #include "../include/Handle.h"
57 #include "../include/refit_call_wrapper.h"
58 #include "../EfiLib/BdsHelper.h"
59 #include "../EfiLib/legacy.h"
61 #ifdef __MAKEWITH_GNUEFI
62 #ifndef EFI_SECURITY_VIOLATION
63 #define EFI_SECURITY_VIOLATION EFIERR (26)
67 #ifndef EFI_OS_INDICATIONS_BOOT_TO_FW_UI
68 #define EFI_OS_INDICATIONS_BOOT_TO_FW_UI 0x0000000000000001ULL
71 #ifdef __MAKEWITH_TIANO
72 #define LibLocateHandle gBS->LocateHandleBuffer
78 #define MACOSX_LOADER_DIR L"System\\Library\\CoreServices"
79 #define MACOSX_LOADER_PATH ( MACOSX_LOADER_DIR L"\\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 NETBOOT_NAMES L"\\EFI\\tools\\ipxe.efi"
85 #define MEMTEST_NAMES L"memtest86.efi,memtest86_x64.efi,memtest86x64.efi,bootx64.efi"
86 #define DRIVER_DIRS L"drivers,drivers_x64"
87 #define FALLBACK_FULLNAME L"EFI\\BOOT\\bootx64.efi"
88 #define FALLBACK_BASENAME L"bootx64.efi"
89 #define EFI_STUB_ARCH 0x8664
91 #define SHELL_NAMES L"\\EFI\\tools\\shell.efi,\\EFI\\tools\\shellia32.efi,\\shell.efi,\\shellia32.efi"
92 #define GPTSYNC_NAMES L"\\EFI\\tools\\gptsync.efi,\\EFI\\tools\\gptsync_ia32.efi"
93 #define GDISK_NAMES L"\\EFI\\tools\\gdisk.efi,\\EFI\\tools\\gdisk_ia32.efi"
94 #define NETBOOT_NAMES L"\\EFI\\tools\\ipxe.efi"
95 #define MEMTEST_NAMES L"memtest86.efi,memtest86_ia32.efi,memtest86ia32.efi,bootia32.efi"
96 #define DRIVER_DIRS L"drivers,drivers_ia32"
97 #define FALLBACK_FULLNAME L"EFI\\BOOT\\bootia32.efi"
98 #define FALLBACK_BASENAME L"bootia32.efi"
99 #define EFI_STUB_ARCH 0x014c
101 #define SHELL_NAMES L"\\EFI\\tools\\shell.efi,\\shell.efi"
102 #define GPTSYNC_NAMES L"\\EFI\\tools\\gptsync.efi"
103 #define GDISK_NAMES L"\\EFI\\tools\\gdisk.efi"
104 #define NETBOOT_NAMES L"\\EFI\\tools\\ipxe.efi"
105 #define MEMTEST_NAMES L"memtest86.efi"
106 #define DRIVER_DIRS L"drivers"
107 #define FALLBACK_FULLNAME L"EFI\\BOOT\\boot.efi" /* Not really correct */
108 #define FALLBACK_BASENAME L"boot.efi" /* Not really correct */
110 #define FAT_ARCH 0x0ef1fab9 /* ID for Apple "fat" binary */
112 #define IPXE_DISCOVER_NAME L"\\efi\\tools\\ipxe_discover.efi"
113 #define IPXE_NAME L"\\efi\\tools\\ipxe.efi"
115 // Filename patterns that identify EFI boot loaders. Note that a single case (either L"*.efi" or
116 // L"*.EFI") is fine for most systems; but Gigabyte's buggy Hybrid EFI does a case-sensitive
117 // comparison when it should do a case-insensitive comparison, so I'm doubling this up. It does
118 // no harm on other computers, AFAIK. In theory, every case variation should be done for
119 // completeness, but that's ridiculous....
120 #define LOADER_MATCH_PATTERNS L"*.efi,*.EFI"
122 // Patterns that identify Linux kernels. Added to the loader match pattern when the
123 // scan_all_linux_kernels option is set in the configuration file. Causes kernels WITHOUT
124 // a ".efi" extension to be found when scanning for boot loaders.
125 #define LINUX_MATCH_PATTERNS L"vmlinuz*,bzImage*"
127 static REFIT_MENU_ENTRY MenuEntryAbout
= { L
"About rEFInd", TAG_ABOUT
, 1, 0, 'A', NULL
, NULL
, NULL
};
128 static REFIT_MENU_ENTRY MenuEntryReset
= { L
"Reboot Computer", TAG_REBOOT
, 1, 0, 'R', NULL
, NULL
, NULL
};
129 static REFIT_MENU_ENTRY MenuEntryShutdown
= { L
"Shut Down Computer", TAG_SHUTDOWN
, 1, 0, 'U', NULL
, NULL
, NULL
};
130 REFIT_MENU_ENTRY MenuEntryReturn
= { L
"Return to Main Menu", TAG_RETURN
, 1, 0, 0, NULL
, NULL
, NULL
};
131 static REFIT_MENU_ENTRY MenuEntryExit
= { L
"Exit rEFInd", TAG_EXIT
, 1, 0, 0, NULL
, NULL
, NULL
};
132 static REFIT_MENU_ENTRY MenuEntryFirmware
= { L
"Reboot to Computer Setup Utility", TAG_FIRMWARE
, 1, 0, 0, NULL
, NULL
, NULL
};
134 REFIT_MENU_SCREEN MainMenu
= { L
"Main Menu", NULL
, 0, NULL
, 0, NULL
, 0, L
"Automatic boot",
135 L
"Use arrow keys to move cursor; Enter to boot;",
136 L
"Insert or F2 for more options; Esc to refresh" };
137 static REFIT_MENU_SCREEN AboutMenu
= { L
"About", NULL
, 0, NULL
, 0, NULL
, 0, NULL
, L
"Press Enter to return to main menu", L
"" };
139 REFIT_CONFIG GlobalConfig
= { FALSE
, TRUE
, FALSE
, FALSE
, 0, 0, 0, DONT_CHANGE_TEXT_MODE
, 20, 0, 0, GRAPHICS_FOR_OSX
, LEGACY_TYPE_MAC
,
140 0, 0, { DEFAULT_BIG_ICON_SIZE
/ 4, DEFAULT_SMALL_ICON_SIZE
, DEFAULT_BIG_ICON_SIZE
}, BANNER_NOSCALE
,
141 NULL
, NULL
, CONFIG_FILE_NAME
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
142 { TAG_SHELL
, TAG_MEMTEST
, TAG_GDISK
, TAG_APPLE_RECOVERY
, TAG_WINDOWS_RECOVERY
, TAG_MOK_TOOL
,
143 TAG_ABOUT
, TAG_SHUTDOWN
, TAG_REBOOT
, TAG_FIRMWARE
, 0, 0, 0, 0, 0, 0, 0, 0 }
146 EFI_GUID GlobalGuid
= EFI_GLOBAL_VARIABLE
;
147 EFI_GUID RefindGuid
= REFIND_GUID_VALUE
;
149 GPT_DATA
*gPartitions
= NULL
;
151 // Structure used to hold boot loader filenames and time stamps in
152 // a linked list; used to sort entries within a directory.
156 struct LOADER_LIST
*NextEntry
;
163 static VOID
AboutrEFInd(VOID
)
165 CHAR16
*FirmwareVendor
;
167 if (AboutMenu
.EntryCount
== 0) {
168 AboutMenu
.TitleImage
= BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT
);
169 AddMenuInfoLine(&AboutMenu
, L
"rEFInd Version 0.8.6");
170 AddMenuInfoLine(&AboutMenu
, L
"");
171 AddMenuInfoLine(&AboutMenu
, L
"Copyright (c) 2006-2010 Christoph Pfisterer");
172 AddMenuInfoLine(&AboutMenu
, L
"Copyright (c) 2012-2015 Roderick W. Smith");
173 AddMenuInfoLine(&AboutMenu
, L
"Portions Copyright (c) Intel Corporation and others");
174 AddMenuInfoLine(&AboutMenu
, L
"Distributed under the terms of the GNU GPLv3 license");
175 AddMenuInfoLine(&AboutMenu
, L
"");
176 AddMenuInfoLine(&AboutMenu
, L
"Running on:");
177 AddMenuInfoLine(&AboutMenu
, PoolPrint(L
" EFI Revision %d.%02d", ST
->Hdr
.Revision
>> 16, ST
->Hdr
.Revision
& ((1 << 16) - 1)));
179 AddMenuInfoLine(&AboutMenu
, L
" Platform: x86 (32 bit)");
180 #elif defined(EFIX64)
181 AddMenuInfoLine(&AboutMenu
, PoolPrint(L
" Platform: x86_64 (64 bit); Secure Boot %s",
182 secure_mode() ? L
"active" : L
"inactive"));
184 AddMenuInfoLine(&AboutMenu
, L
" Platform: unknown");
186 FirmwareVendor
= StrDuplicate(ST
->FirmwareVendor
);
187 LimitStringLength(FirmwareVendor
, 65); // More than ~65 causes empty info page on 800x600 display
188 AddMenuInfoLine(&AboutMenu
, PoolPrint(L
" Firmware: %s %d.%02d", FirmwareVendor
, ST
->FirmwareRevision
>> 16,
189 ST
->FirmwareRevision
& ((1 << 16) - 1)));
190 AddMenuInfoLine(&AboutMenu
, PoolPrint(L
" Screen Output: %s", egScreenDescription()));
191 AddMenuInfoLine(&AboutMenu
, L
"");
192 #if defined(__MAKEWITH_GNUEFI)
193 AddMenuInfoLine(&AboutMenu
, L
"Built with GNU-EFI");
195 AddMenuInfoLine(&AboutMenu
, L
"Built with TianoCore EDK2");
197 AddMenuInfoLine(&AboutMenu
, L
"");
198 AddMenuInfoLine(&AboutMenu
, L
"For more information, see the rEFInd Web site:");
199 AddMenuInfoLine(&AboutMenu
, L
"http://www.rodsbooks.com/refind/");
200 AddMenuEntry(&AboutMenu
, &MenuEntryReturn
);
203 RunMenu(&AboutMenu
, NULL
);
204 } /* VOID AboutrEFInd() */
206 static VOID
WarnSecureBootError(CHAR16
*Name
, BOOLEAN Verbose
) {
208 Name
= L
"the loader";
210 refit_call2_wrapper(ST
->ConOut
->SetAttribute
, ST
->ConOut
, ATTR_ERROR
);
211 Print(L
"Secure Boot validation failure loading %s!\n", Name
);
212 refit_call2_wrapper(ST
->ConOut
->SetAttribute
, ST
->ConOut
, ATTR_BASIC
);
213 if (Verbose
&& secure_mode()) {
214 Print(L
"\nThis computer is configured with Secure Boot active, but\n%s has failed validation.\n", Name
);
215 Print(L
"\nYou can:\n * Launch another boot loader\n");
216 Print(L
" * Disable Secure Boot in your firmware\n");
217 Print(L
" * Sign %s with a machine owner key (MOK)\n", Name
);
218 Print(L
" * Use a MOK utility (often present on the second row) to add a MOK with which\n");
219 Print(L
" %s has already been signed.\n", Name
);
220 Print(L
" * Use a MOK utility to register %s (\"enroll its hash\") without\n", Name
);
221 Print(L
" signing it.\n");
222 Print(L
"\nSee http://www.rodsbooks.com/refind/secureboot.html for more information\n");
225 } // VOID WarnSecureBootError()
227 // Returns TRUE if this file is a valid EFI loader file, and is proper ARCH
228 static BOOLEAN
IsValidLoader(EFI_FILE
*RootDir
, CHAR16
*FileName
) {
229 BOOLEAN IsValid
= TRUE
;
230 #if defined (EFIX64) | defined (EFI32)
232 EFI_FILE_HANDLE FileHandle
;
234 UINTN Size
= sizeof(Header
);
236 if ((RootDir
== NULL
) || (FileName
== NULL
)) {
237 // Assume valid here, because Macs produce NULL RootDir (& maybe FileName)
238 // when launching from a Firewire drive. This should be handled better, but
239 // fix would have to be in StartEFIImageList() and/or in FindVolumeAndFilename().
243 Status
= refit_call5_wrapper(RootDir
->Open
, RootDir
, &FileHandle
, FileName
, EFI_FILE_MODE_READ
, 0);
244 if (EFI_ERROR(Status
))
247 Status
= refit_call3_wrapper(FileHandle
->Read
, FileHandle
, &Size
, Header
);
248 refit_call1_wrapper(FileHandle
->Close
, FileHandle
);
250 IsValid
= !EFI_ERROR(Status
) &&
251 Size
== sizeof(Header
) &&
252 ((Header
[0] == 'M' && Header
[1] == 'Z' &&
253 (Size
= *(UINT32
*)&Header
[0x3c]) < 0x180 &&
254 Header
[Size
] == 'P' && Header
[Size
+1] == 'E' &&
255 Header
[Size
+2] == 0 && Header
[Size
+3] == 0 &&
256 *(UINT16
*)&Header
[Size
+4] == EFI_STUB_ARCH
) ||
257 (*(UINT32
*)&Header
== FAT_ARCH
));
260 } // BOOLEAN IsValidLoader()
262 // Launch an EFI binary.
263 EFI_STATUS
StartEFIImageList(IN EFI_DEVICE_PATH
**DevicePaths
,
264 IN CHAR16
*LoadOptions
, IN UINTN LoaderType
,
265 IN CHAR16
*ImageTitle
, IN CHAR8 OSType
,
266 OUT UINTN
*ErrorInStep
,
270 EFI_STATUS Status
, ReturnStatus
;
271 EFI_HANDLE ChildImageHandle
;
272 EFI_LOADED_IMAGE
*ChildLoadedImage
= NULL
;
273 REFIT_VOLUME
*Volume
= NULL
;
274 UINTN DevicePathIndex
;
275 CHAR16 ErrorInfo
[256];
276 CHAR16
*FullLoadOptions
= NULL
;
277 CHAR16
*Filename
= NULL
;
280 if (ErrorInStep
!= NULL
)
284 if (LoadOptions
!= NULL
) {
285 FullLoadOptions
= StrDuplicate(LoadOptions
);
286 if ((LoaderType
== TYPE_EFI
) && (OSType
== 'M')) {
287 MergeStrings(&FullLoadOptions
, L
" ", 0);
288 // NOTE: That last space is also added by the EFI shell and seems to be significant
289 // when passing options to Apple's boot.efi...
291 } // if (LoadOptions != NULL)
293 Print(L
"Starting %s\nUsing load options '%s'\n", ImageTitle
, FullLoadOptions
? FullLoadOptions
: L
"");
295 // load the image into memory (and execute it, in the case of a shim/MOK image).
296 ReturnStatus
= Status
= EFI_NOT_FOUND
; // in case the list is empty
297 for (DevicePathIndex
= 0; DevicePaths
[DevicePathIndex
] != NULL
; DevicePathIndex
++) {
298 FindVolumeAndFilename(DevicePaths
[DevicePathIndex
], &Volume
, &Filename
);
299 // Some EFIs crash if attempting to load driver for invalid architecture, so
300 // protect for this condition; but sometimes Volume comes back NULL, so provide
301 // an exception. (TODO: Handle this special condition better.)
302 if ((LoaderType
== TYPE_LEGACY
) || (Volume
== NULL
) || IsValidLoader(Volume
->RootDir
, Filename
)) {
303 if (Filename
&& (LoaderType
!= TYPE_LEGACY
)) {
304 Temp
= PoolPrint(L
"\\%s %s", Filename
, FullLoadOptions
? FullLoadOptions
: L
"");
306 MyFreePool(FullLoadOptions
);
307 FullLoadOptions
= Temp
;
311 // NOTE: Below commented-out line could be more efficient if file were read ahead of
312 // time and passed as a pre-loaded image to LoadImage(), but it doesn't work on my
313 // 32-bit Mac Mini or my 64-bit Intel box when launching a Linux kernel; the
314 // kernel returns a "Failed to handle fs_proto" error message.
315 // TODO: Track down the cause of this error and fix it, if possible.
316 // ReturnStatus = Status = refit_call6_wrapper(BS->LoadImage, FALSE, SelfImageHandle, DevicePaths[DevicePathIndex],
317 // ImageData, ImageSize, &ChildImageHandle);
318 ReturnStatus
= Status
= refit_call6_wrapper(BS
->LoadImage
, FALSE
, SelfImageHandle
, DevicePaths
[DevicePathIndex
],
319 NULL
, 0, &ChildImageHandle
);
321 Print(L
"Invalid loader file!\n");
322 ReturnStatus
= EFI_LOAD_ERROR
;
324 if (ReturnStatus
!= EFI_NOT_FOUND
) {
328 if ((Status
== EFI_ACCESS_DENIED
) || (Status
== EFI_SECURITY_VIOLATION
)) {
329 WarnSecureBootError(ImageTitle
, Verbose
);
332 SPrint(ErrorInfo
, 255, L
"while loading %s", ImageTitle
);
333 if (CheckError(Status
, ErrorInfo
)) {
334 if (ErrorInStep
!= NULL
)
339 ReturnStatus
= Status
= refit_call3_wrapper(BS
->HandleProtocol
, ChildImageHandle
, &LoadedImageProtocol
,
340 (VOID
**) &ChildLoadedImage
);
341 if (CheckError(Status
, L
"while getting a LoadedImageProtocol handle")) {
342 if (ErrorInStep
!= NULL
)
346 ChildLoadedImage
->LoadOptions
= (VOID
*)FullLoadOptions
;
347 ChildLoadedImage
->LoadOptionsSize
= FullLoadOptions
? ((UINT32
)StrLen(FullLoadOptions
) + 1) * sizeof(CHAR16
) : 0;
348 // turn control over to the image
349 // TODO: (optionally) re-enable the EFI watchdog timer!
351 // close open file handles
353 ReturnStatus
= Status
= refit_call3_wrapper(BS
->StartImage
, ChildImageHandle
, NULL
, NULL
);
355 // control returns here when the child image calls Exit()
356 SPrint(ErrorInfo
, 255, L
"returned from %s", ImageTitle
);
357 if (CheckError(Status
, ErrorInfo
)) {
358 if (ErrorInStep
!= NULL
)
362 // re-open file handles
366 // unload the image, we don't care if it works or not...
368 Status
= refit_call1_wrapper(BS
->UnloadImage
, ChildImageHandle
);
371 MyFreePool(FullLoadOptions
);
373 } /* EFI_STATUS StartEFIImageList() */
375 static EFI_STATUS
StartEFIImage(IN EFI_DEVICE_PATH
*DevicePath
,
376 IN CHAR16
*LoadOptions
, IN UINTN LoaderType
,
377 IN CHAR16
*ImageTitle
, IN CHAR8 OSType
,
378 OUT UINTN
*ErrorInStep
,
383 EFI_DEVICE_PATH
*DevicePaths
[2];
385 DevicePaths
[0] = DevicePath
;
386 DevicePaths
[1] = NULL
;
387 return StartEFIImageList(DevicePaths
, LoadOptions
, LoaderType
, ImageTitle
, OSType
, ErrorInStep
, Verbose
, IsDriver
);
388 } /* static EFI_STATUS StartEFIImage() */
390 // From gummiboot: Reboot the computer into its built-in user interface
391 static EFI_STATUS
RebootIntoFirmware(VOID
) {
397 osind
= EFI_OS_INDICATIONS_BOOT_TO_FW_UI
;
399 err
= EfivarGetRaw(&GlobalGuid
, L
"OsIndications", &b
, &size
);
400 if (err
== EFI_SUCCESS
)
404 err
= EfivarSetRaw(&GlobalGuid
, L
"OsIndications", (CHAR8
*)&osind
, sizeof(UINT64
), TRUE
);
405 if (err
!= EFI_SUCCESS
)
408 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);
409 Print(L
"Error calling ResetSystem: %r", err
);
414 // Record the value of the loader's name/description in rEFInd's "PreviousBoot" EFI variable,
415 // if it's different from what's already stored there.
416 VOID
StoreLoaderName(IN CHAR16
*Name
) {
418 CHAR16
*OldName
= NULL
;
422 Status
= EfivarGetRaw(&RefindGuid
, L
"PreviousBoot", (CHAR8
**) &OldName
, &Length
);
423 if ((Status
!= EFI_SUCCESS
) || (StrCmp(OldName
, Name
) != 0)) {
424 EfivarSetRaw(&RefindGuid
, L
"PreviousBoot", (CHAR8
*) Name
, StrLen(Name
) * 2 + 2, TRUE
);
428 } // VOID StoreLoaderName()
431 // EFI OS loader functions
434 // See http://www.thomas-krenn.com/en/wiki/Activating_the_Intel_VT_Virtualization_Feature
435 // for information on Intel VMX features
436 static VOID
DoEnableAndLockVMX(VOID
)
439 UINT32 low_bits
= 0, high_bits
= 0;
442 __asm__
volatile ("rdmsr" : "=a" (low_bits
), "=d" (high_bits
) : "c" (msr
));
444 // enable and lock vmx if not locked
445 if ((low_bits
& 1) == 0) {
449 __asm__
volatile ("wrmsr" : : "c" (msr
), "a" (low_bits
), "d" (high_bits
));
451 } // VOID DoEnableAndLockVMX()
453 static VOID
StartLoader(LOADER_ENTRY
*Entry
, CHAR16
*SelectionName
)
455 UINTN ErrorInStep
= 0;
457 if (GlobalConfig
.EnableAndLockVMX
) {
458 DoEnableAndLockVMX();
461 BeginExternalScreen(Entry
->UseGraphicsMode
, L
"Booting OS");
462 StoreLoaderName(SelectionName
);
463 StartEFIImage(Entry
->DevicePath
, Entry
->LoadOptions
, TYPE_EFI
,
464 Basename(Entry
->LoaderPath
), Entry
->OSType
, &ErrorInStep
, !Entry
->UseGraphicsMode
, FALSE
);
465 FinishExternalScreen();
468 // Locate an initrd or initramfs file that matches the kernel specified by LoaderPath.
469 // The matching file has a name that begins with "init" and includes the same version
470 // number string as is found in LoaderPath -- but not a longer version number string.
471 // For instance, if LoaderPath is \EFI\kernels\bzImage-3.3.0.efi, and if \EFI\kernels
472 // has a file called initramfs-3.3.0.img, this function will return the string
473 // '\EFI\kernels\initramfs-3.3.0.img'. If the directory ALSO contains the file
474 // initramfs-3.3.0-rc7.img or initramfs-13.3.0.img, those files will NOT match;
475 // however, initmine-3.3.0.img might match. (FindInitrd() returns the first match it
476 // finds.) Thus, care should be taken to avoid placing duplicate matching files in
477 // the kernel's directory.
478 // If no matching init file can be found, returns NULL.
479 static CHAR16
* FindInitrd(IN CHAR16
*LoaderPath
, IN REFIT_VOLUME
*Volume
) {
480 CHAR16
*InitrdName
= NULL
, *FileName
, *KernelVersion
, *InitrdVersion
, *Path
;
481 REFIT_DIR_ITER DirIter
;
482 EFI_FILE_INFO
*DirEntry
;
484 FileName
= Basename(LoaderPath
);
485 KernelVersion
= FindNumbers(FileName
);
486 Path
= FindPath(LoaderPath
);
488 // Add trailing backslash for root directory; necessary on some systems, but must
489 // NOT be added to all directories, since on other systems, a trailing backslash on
490 // anything but the root directory causes them to flake out!
491 if (StrLen(Path
) == 0) {
492 MergeStrings(&Path
, L
"\\", 0);
494 DirIterOpen(Volume
->RootDir
, Path
, &DirIter
);
495 // Now add a trailing backslash if it was NOT added earlier, for consistency in
496 // building the InitrdName later....
497 if ((StrLen(Path
) > 0) && (Path
[StrLen(Path
) - 1] != L
'\\'))
498 MergeStrings(&Path
, L
"\\", 0);
499 while ((DirIterNext(&DirIter
, 2, L
"init*", &DirEntry
)) && (InitrdName
== NULL
)) {
500 InitrdVersion
= FindNumbers(DirEntry
->FileName
);
501 if (KernelVersion
!= NULL
) {
502 if (StriCmp(InitrdVersion
, KernelVersion
) == 0) {
503 InitrdName
= PoolPrint(L
"%s%s", Path
, DirEntry
->FileName
);
506 if (InitrdVersion
== NULL
) {
507 InitrdName
= PoolPrint(L
"%s%s", Path
, DirEntry
->FileName
);
510 MyFreePool(InitrdVersion
);
512 DirIterClose(&DirIter
);
514 // Note: Don't FreePool(FileName), since Basename returns a pointer WITHIN the string it's passed.
515 MyFreePool(KernelVersion
);
518 } // static CHAR16 * FindInitrd()
520 LOADER_ENTRY
* AddPreparedLoaderEntry(LOADER_ENTRY
*Entry
) {
521 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
524 } // LOADER_ENTRY * AddPreparedLoaderEntry()
526 // Creates a copy of a menu screen.
527 // Returns a pointer to the copy of the menu screen.
528 static REFIT_MENU_SCREEN
* CopyMenuScreen(REFIT_MENU_SCREEN
*Entry
) {
529 REFIT_MENU_SCREEN
*NewEntry
;
532 NewEntry
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
533 if ((Entry
!= NULL
) && (NewEntry
!= NULL
)) {
534 CopyMem(NewEntry
, Entry
, sizeof(REFIT_MENU_SCREEN
));
535 NewEntry
->Title
= (Entry
->Title
) ? StrDuplicate(Entry
->Title
) : NULL
;
536 NewEntry
->TimeoutText
= (Entry
->TimeoutText
) ? StrDuplicate(Entry
->TimeoutText
) : NULL
;
537 if (Entry
->TitleImage
!= NULL
) {
538 NewEntry
->TitleImage
= AllocatePool(sizeof(EG_IMAGE
));
539 if (NewEntry
->TitleImage
!= NULL
)
540 CopyMem(NewEntry
->TitleImage
, Entry
->TitleImage
, sizeof(EG_IMAGE
));
542 NewEntry
->InfoLines
= (CHAR16
**) AllocateZeroPool(Entry
->InfoLineCount
* (sizeof(CHAR16
*)));
543 for (i
= 0; i
< Entry
->InfoLineCount
&& NewEntry
->InfoLines
; i
++) {
544 NewEntry
->InfoLines
[i
] = (Entry
->InfoLines
[i
]) ? StrDuplicate(Entry
->InfoLines
[i
]) : NULL
;
546 NewEntry
->Entries
= (REFIT_MENU_ENTRY
**) AllocateZeroPool(Entry
->EntryCount
* (sizeof (REFIT_MENU_ENTRY
*)));
547 for (i
= 0; i
< Entry
->EntryCount
&& NewEntry
->Entries
; i
++) {
548 AddMenuEntry(NewEntry
, Entry
->Entries
[i
]);
550 NewEntry
->Hint1
= (Entry
->Hint1
) ? StrDuplicate(Entry
->Hint1
) : NULL
;
551 NewEntry
->Hint2
= (Entry
->Hint2
) ? StrDuplicate(Entry
->Hint2
) : NULL
;
554 } // static REFIT_MENU_SCREEN* CopyMenuScreen()
556 // Creates a copy of a menu entry. Intended to enable moving a stack-based
557 // menu entry (such as the ones for the "reboot" and "exit" functions) to
558 // to the heap. This enables easier deletion of the whole set of menu
559 // entries when re-scanning.
560 // Returns a pointer to the copy of the menu entry.
561 static REFIT_MENU_ENTRY
* CopyMenuEntry(REFIT_MENU_ENTRY
*Entry
) {
562 REFIT_MENU_ENTRY
*NewEntry
;
564 NewEntry
= AllocateZeroPool(sizeof(REFIT_MENU_ENTRY
));
565 if ((Entry
!= NULL
) && (NewEntry
!= NULL
)) {
566 CopyMem(NewEntry
, Entry
, sizeof(REFIT_MENU_ENTRY
));
567 NewEntry
->Title
= (Entry
->Title
) ? StrDuplicate(Entry
->Title
) : NULL
;
568 if (Entry
->BadgeImage
!= NULL
) {
569 NewEntry
->BadgeImage
= AllocatePool(sizeof(EG_IMAGE
));
570 if (NewEntry
->BadgeImage
!= NULL
)
571 CopyMem(NewEntry
->BadgeImage
, Entry
->BadgeImage
, sizeof(EG_IMAGE
));
573 if (Entry
->Image
!= NULL
) {
574 NewEntry
->Image
= AllocatePool(sizeof(EG_IMAGE
));
575 if (NewEntry
->Image
!= NULL
)
576 CopyMem(NewEntry
->Image
, Entry
->Image
, sizeof(EG_IMAGE
));
578 if (Entry
->SubScreen
!= NULL
) {
579 NewEntry
->SubScreen
= CopyMenuScreen(Entry
->SubScreen
);
583 } // REFIT_MENU_ENTRY* CopyMenuEntry()
585 // Creates a new LOADER_ENTRY data structure and populates it with
586 // default values from the specified Entry, or NULL values if Entry
587 // is unspecified (NULL).
588 // Returns a pointer to the new data structure, or NULL if it
589 // couldn't be allocated
590 LOADER_ENTRY
*InitializeLoaderEntry(IN LOADER_ENTRY
*Entry
) {
591 LOADER_ENTRY
*NewEntry
= NULL
;
593 NewEntry
= AllocateZeroPool(sizeof(LOADER_ENTRY
));
594 if (NewEntry
!= NULL
) {
595 NewEntry
->me
.Title
= NULL
;
596 NewEntry
->me
.Tag
= TAG_LOADER
;
597 NewEntry
->Enabled
= TRUE
;
598 NewEntry
->UseGraphicsMode
= FALSE
;
599 NewEntry
->OSType
= 0;
601 NewEntry
->LoaderPath
= (Entry
->LoaderPath
) ? StrDuplicate(Entry
->LoaderPath
) : NULL
;
602 NewEntry
->VolName
= (Entry
->VolName
) ? StrDuplicate(Entry
->VolName
) : NULL
;
603 NewEntry
->DevicePath
= Entry
->DevicePath
;
604 NewEntry
->UseGraphicsMode
= Entry
->UseGraphicsMode
;
605 NewEntry
->LoadOptions
= (Entry
->LoadOptions
) ? StrDuplicate(Entry
->LoadOptions
) : NULL
;
606 NewEntry
->InitrdPath
= (Entry
->InitrdPath
) ? StrDuplicate(Entry
->InitrdPath
) : NULL
;
610 } // LOADER_ENTRY *InitializeLoaderEntry()
612 // Adds InitrdPath to Options, but only if Options doesn't already include an
613 // initrd= line. Done to enable overriding the default initrd selection in a
614 // refind_linux.conf file's options list.
615 // Returns a pointer to a new string. The calling function is responsible for
616 // freeing its memory.
617 static CHAR16
*AddInitrdToOptions(CHAR16
*Options
, CHAR16
*InitrdPath
) {
618 CHAR16
*NewOptions
= NULL
;
621 NewOptions
= StrDuplicate(Options
);
622 if ((InitrdPath
!= NULL
) && !StriSubCmp(L
"initrd=", Options
)) {
623 MergeStrings(&NewOptions
, L
"initrd=", L
' ');
624 MergeStrings(&NewOptions
, InitrdPath
, 0);
627 } // CHAR16 *AddInitrdToOptions()
629 // Prepare a REFIT_MENU_SCREEN data structure for a subscreen entry. This sets up
630 // the default entry that launches the boot loader using the same options as the
631 // main Entry does. Subsequent options can be added by the calling function.
632 // If a subscreen already exists in the Entry that's passed to this function,
633 // it's left unchanged and a pointer to it is returned.
634 // Returns a pointer to the new subscreen data structure, or NULL if there
635 // were problems allocating memory.
636 REFIT_MENU_SCREEN
*InitializeSubScreen(IN LOADER_ENTRY
*Entry
) {
637 CHAR16
*FileName
, *MainOptions
= NULL
;
638 REFIT_MENU_SCREEN
*SubScreen
= NULL
;
639 LOADER_ENTRY
*SubEntry
;
641 FileName
= Basename(Entry
->LoaderPath
);
642 if (Entry
->me
.SubScreen
== NULL
) { // No subscreen yet; initialize default entry....
643 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
644 if (SubScreen
!= NULL
) {
645 SubScreen
->Title
= AllocateZeroPool(sizeof(CHAR16
) * 256);
646 SPrint(SubScreen
->Title
, 255, L
"Boot Options for %s on %s",
647 (Entry
->Title
!= NULL
) ? Entry
->Title
: FileName
, Entry
->VolName
);
648 SubScreen
->TitleImage
= Entry
->me
.Image
;
650 SubEntry
= InitializeLoaderEntry(Entry
);
651 if (SubEntry
!= NULL
) {
652 SubEntry
->me
.Title
= StrDuplicate(L
"Boot using default options");
653 MainOptions
= SubEntry
->LoadOptions
;
654 SubEntry
->LoadOptions
= AddInitrdToOptions(MainOptions
, SubEntry
->InitrdPath
);
655 MyFreePool(MainOptions
);
656 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
657 } // if (SubEntry != NULL)
658 SubScreen
->Hint1
= StrDuplicate(SUBSCREEN_HINT1
);
659 if (GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_EDITOR
) {
660 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2_NO_EDITOR
);
662 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2
);
664 } // if (SubScreen != NULL)
665 } else { // existing subscreen; less initialization, and just add new entry later....
666 SubScreen
= Entry
->me
.SubScreen
;
669 } // REFIT_MENU_SCREEN *InitializeSubScreen()
671 VOID
GenerateSubScreen(LOADER_ENTRY
*Entry
, IN REFIT_VOLUME
*Volume
) {
672 REFIT_MENU_SCREEN
*SubScreen
;
673 LOADER_ENTRY
*SubEntry
;
675 CHAR16 DiagsFileName
[256];
680 // create the submenu
681 if (StrLen(Entry
->Title
) == 0) {
682 MyFreePool(Entry
->Title
);
685 SubScreen
= InitializeSubScreen(Entry
);
687 // loader-specific submenu entries
688 if (Entry
->OSType
== 'M') { // entries for Mac OS X
690 SubEntry
= InitializeLoaderEntry(Entry
);
691 if (SubEntry
!= NULL
) {
692 SubEntry
->me
.Title
= L
"Boot Mac OS X with a 64-bit kernel";
693 SubEntry
->LoadOptions
= L
"arch=x86_64";
694 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
695 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
698 SubEntry
= InitializeLoaderEntry(Entry
);
699 if (SubEntry
!= NULL
) {
700 SubEntry
->me
.Title
= L
"Boot Mac OS X with a 32-bit kernel";
701 SubEntry
->LoadOptions
= L
"arch=i386";
702 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
703 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
707 if (!(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_SINGLEUSER
)) {
708 SubEntry
= InitializeLoaderEntry(Entry
);
709 if (SubEntry
!= NULL
) {
710 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode";
711 SubEntry
->UseGraphicsMode
= FALSE
;
712 SubEntry
->LoadOptions
= L
"-v";
713 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
717 SubEntry
= InitializeLoaderEntry(Entry
);
718 if (SubEntry
!= NULL
) {
719 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode (64-bit)";
720 SubEntry
->UseGraphicsMode
= FALSE
;
721 SubEntry
->LoadOptions
= L
"-v arch=x86_64";
722 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
725 SubEntry
= InitializeLoaderEntry(Entry
);
726 if (SubEntry
!= NULL
) {
727 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode (32-bit)";
728 SubEntry
->UseGraphicsMode
= FALSE
;
729 SubEntry
->LoadOptions
= L
"-v arch=i386";
730 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
734 SubEntry
= InitializeLoaderEntry(Entry
);
735 if (SubEntry
!= NULL
) {
736 SubEntry
->me
.Title
= L
"Boot Mac OS X in single user mode";
737 SubEntry
->UseGraphicsMode
= FALSE
;
738 SubEntry
->LoadOptions
= L
"-v -s";
739 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
741 } // single-user mode allowed
743 if (!(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_SAFEMODE
)) {
744 SubEntry
= InitializeLoaderEntry(Entry
);
745 if (SubEntry
!= NULL
) {
746 SubEntry
->me
.Title
= L
"Boot Mac OS X in safe mode";
747 SubEntry
->UseGraphicsMode
= FALSE
;
748 SubEntry
->LoadOptions
= L
"-v -x";
749 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
751 } // safe mode allowed
753 // check for Apple hardware diagnostics
754 StrCpy(DiagsFileName
, L
"System\\Library\\CoreServices\\.diagnostics\\diags.efi");
755 if (FileExists(Volume
->RootDir
, DiagsFileName
) && !(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_HWTEST
)) {
756 SubEntry
= InitializeLoaderEntry(Entry
);
757 if (SubEntry
!= NULL
) {
758 SubEntry
->me
.Title
= L
"Run Apple Hardware Test";
759 MyFreePool(SubEntry
->LoaderPath
);
760 SubEntry
->LoaderPath
= StrDuplicate(DiagsFileName
);
761 SubEntry
->DevicePath
= FileDevicePath(Volume
->DeviceHandle
, SubEntry
->LoaderPath
);
762 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
763 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
765 } // if diagnostics entry found
767 } else if (Entry
->OSType
== 'L') { // entries for Linux kernels with EFI stub loaders
768 File
= ReadLinuxOptionsFile(Entry
->LoaderPath
, Volume
);
770 InitrdName
= FindInitrd(Entry
->LoaderPath
, Volume
);
771 TokenCount
= ReadTokenLine(File
, &TokenList
);
772 // first entry requires special processing, since it was initially set
773 // up with a default title but correct options by InitializeSubScreen(),
775 if ((SubScreen
->Entries
!= NULL
) && (SubScreen
->Entries
[0] != NULL
)) {
776 MyFreePool(SubScreen
->Entries
[0]->Title
);
777 SubScreen
->Entries
[0]->Title
= TokenList
[0] ? StrDuplicate(TokenList
[0]) : StrDuplicate(L
"Boot Linux");
779 FreeTokenLine(&TokenList
, &TokenCount
);
780 while ((TokenCount
= ReadTokenLine(File
, &TokenList
)) > 1) {
781 SubEntry
= InitializeLoaderEntry(Entry
);
782 SubEntry
->me
.Title
= TokenList
[0] ? StrDuplicate(TokenList
[0]) : StrDuplicate(L
"Boot Linux");
783 MyFreePool(SubEntry
->LoadOptions
);
784 SubEntry
->LoadOptions
= AddInitrdToOptions(TokenList
[1], InitrdName
);
785 FreeTokenLine(&TokenList
, &TokenCount
);
786 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_LINUX
;
787 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
789 MyFreePool(InitrdName
);
793 } else if (Entry
->OSType
== 'E') { // entries for ELILO
794 SubEntry
= InitializeLoaderEntry(Entry
);
795 if (SubEntry
!= NULL
) {
796 SubEntry
->me
.Title
= L
"Run ELILO in interactive mode";
797 SubEntry
->LoadOptions
= L
"-p";
798 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
799 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
802 SubEntry
= InitializeLoaderEntry(Entry
);
803 if (SubEntry
!= NULL
) {
804 SubEntry
->me
.Title
= L
"Boot Linux for a 17\" iMac or a 15\" MacBook Pro (*)";
805 SubEntry
->UseGraphicsMode
= TRUE
;
806 SubEntry
->LoadOptions
= L
"-d 0 i17";
807 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
808 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
811 SubEntry
= InitializeLoaderEntry(Entry
);
812 if (SubEntry
!= NULL
) {
813 SubEntry
->me
.Title
= L
"Boot Linux for a 20\" iMac (*)";
814 SubEntry
->UseGraphicsMode
= TRUE
;
815 SubEntry
->LoadOptions
= L
"-d 0 i20";
816 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
817 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
820 SubEntry
= InitializeLoaderEntry(Entry
);
821 if (SubEntry
!= NULL
) {
822 SubEntry
->me
.Title
= L
"Boot Linux for a Mac Mini (*)";
823 SubEntry
->UseGraphicsMode
= TRUE
;
824 SubEntry
->LoadOptions
= L
"-d 0 mini";
825 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
826 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
829 AddMenuInfoLine(SubScreen
, L
"NOTE: This is an example. Entries");
830 AddMenuInfoLine(SubScreen
, L
"marked with (*) may not work.");
832 } else if (Entry
->OSType
== 'X') { // entries for xom.efi
833 // by default, skip the built-in selection and boot from hard disk only
834 Entry
->LoadOptions
= L
"-s -h";
836 SubEntry
= InitializeLoaderEntry(Entry
);
837 if (SubEntry
!= NULL
) {
838 SubEntry
->me
.Title
= L
"Boot Windows from Hard Disk";
839 SubEntry
->LoadOptions
= L
"-s -h";
840 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
841 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
844 SubEntry
= InitializeLoaderEntry(Entry
);
845 if (SubEntry
!= NULL
) {
846 SubEntry
->me
.Title
= L
"Boot Windows from CD-ROM";
847 SubEntry
->LoadOptions
= L
"-s -c";
848 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
849 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
852 SubEntry
= InitializeLoaderEntry(Entry
);
853 if (SubEntry
!= NULL
) {
854 SubEntry
->me
.Title
= L
"Run XOM in text mode";
855 SubEntry
->UseGraphicsMode
= FALSE
;
856 SubEntry
->LoadOptions
= L
"-v";
857 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
858 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
860 } // entries for xom.efi
861 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
862 Entry
->me
.SubScreen
= SubScreen
;
863 } // VOID GenerateSubScreen()
865 // Returns options for a Linux kernel. Reads them from an options file in the
866 // kernel's directory; and if present, adds an initrd= option for an initial
867 // RAM disk file with the same version number as the kernel file.
868 static CHAR16
* GetMainLinuxOptions(IN CHAR16
* LoaderPath
, IN REFIT_VOLUME
*Volume
) {
869 CHAR16
*Options
= NULL
, *InitrdName
, *FullOptions
= NULL
;
871 Options
= GetFirstOptionsFromFile(LoaderPath
, Volume
);
872 InitrdName
= FindInitrd(LoaderPath
, Volume
);
873 FullOptions
= AddInitrdToOptions(Options
, InitrdName
);
876 MyFreePool(InitrdName
);
877 return (FullOptions
);
878 } // static CHAR16 * GetMainLinuxOptions()
880 // Try to guess the name of the Linux distribution & add that name to
882 static VOID
GuessLinuxDistribution(CHAR16
**OSIconName
, REFIT_VOLUME
*Volume
, CHAR16
*LoaderPath
) {
886 UINTN TokenCount
= 0;
888 // If on Linux root fs, /etc/os-release file probably has clues....
889 if (FileExists(Volume
->RootDir
, L
"etc\\os-release") &&
890 (ReadFile(Volume
->RootDir
, L
"etc\\os-release", &File
, &FileSize
) == EFI_SUCCESS
)) {
892 TokenCount
= ReadTokenLine(&File
, &TokenList
);
893 if ((TokenCount
> 1) && ((StriCmp(TokenList
[0], L
"ID") == 0) || (StriCmp(TokenList
[0], L
"NAME") == 0))) {
894 MergeStrings(OSIconName
, TokenList
[1], L
',');
896 FreeTokenLine(&TokenList
, &TokenCount
);
897 } while (TokenCount
> 0);
898 MyFreePool(File
.Buffer
);
901 // Search for clues in the kernel's filename....
902 if (StriSubCmp(L
".fc", LoaderPath
))
903 MergeStrings(OSIconName
, L
"fedora", L
',');
904 if (StriSubCmp(L
".el", LoaderPath
))
905 MergeStrings(OSIconName
, L
"redhat", L
',');
906 } // VOID GuessLinuxDistribution()
908 // Sets a few defaults for a loader entry -- mainly the icon, but also the OS type
909 // code and shortcut letter. For Linux EFI stub loaders, also sets kernel options
910 // that will (with luck) work fairly automatically.
911 VOID
SetLoaderDefaults(LOADER_ENTRY
*Entry
, CHAR16
*LoaderPath
, REFIT_VOLUME
*Volume
) {
912 CHAR16
*NameClues
, *PathOnly
, *NoExtension
, *OSIconName
= NULL
, *Temp
, *SubString
;
913 CHAR16 ShortcutLetter
= 0;
916 NameClues
= Basename(LoaderPath
);
917 PathOnly
= FindPath(LoaderPath
);
918 NoExtension
= StripEfiExtension(NameClues
);
920 if (Volume
->DiskKind
== DISK_KIND_NET
) {
921 MergeStrings(&NameClues
, Entry
->me
.Title
, L
' ');
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
',');
961 } // if/else network boot
963 // detect specific loaders
964 if (StriSubCmp(L
"bzImage", NameClues
) || StriSubCmp(L
"vmlinuz", NameClues
)) {
965 if (Volume
->DiskKind
!= DISK_KIND_NET
) {
966 GuessLinuxDistribution(&OSIconName
, Volume
, LoaderPath
);
967 Entry
->LoadOptions
= GetMainLinuxOptions(LoaderPath
, Volume
);
969 MergeStrings(&OSIconName
, L
"linux", L
',');
971 if (ShortcutLetter
== 0)
972 ShortcutLetter
= 'L';
973 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_LINUX
;
974 } else if (StriSubCmp(L
"refit", LoaderPath
)) {
975 MergeStrings(&OSIconName
, L
"refit", L
',');
977 ShortcutLetter
= 'R';
978 } else if (StriSubCmp(L
"refind", LoaderPath
)) {
979 MergeStrings(&OSIconName
, L
"refind", L
',');
981 ShortcutLetter
= 'R';
982 } else if (StriCmp(LoaderPath
, MACOSX_LOADER_PATH
) == 0) {
983 MergeStrings(&OSIconName
, L
"mac", L
',');
985 ShortcutLetter
= 'M';
986 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
987 } else if (StriCmp(NameClues
, L
"diags.efi") == 0) {
988 MergeStrings(&OSIconName
, L
"hwtest", L
',');
989 } else if (StriCmp(NameClues
, L
"e.efi") == 0 || StriCmp(NameClues
, L
"elilo.efi") == 0 || StriSubCmp(L
"elilo", NameClues
)) {
990 MergeStrings(&OSIconName
, L
"elilo,linux", L
',');
992 if (ShortcutLetter
== 0)
993 ShortcutLetter
= 'L';
994 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
995 } else if (StriSubCmp(L
"grub", NameClues
)) {
996 MergeStrings(&OSIconName
, L
"grub,linux", L
',');
998 ShortcutLetter
= 'G';
999 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_GRUB
;
1000 } else if (StriCmp(NameClues
, L
"cdboot.efi") == 0 ||
1001 StriCmp(NameClues
, L
"bootmgr.efi") == 0 ||
1002 StriCmp(NameClues
, L
"bootmgfw.efi") == 0 ||
1003 StriCmp(NameClues
, L
"bkpbootmgfw.efi") == 0) {
1004 MergeStrings(&OSIconName
, L
"win8", L
',');
1005 Entry
->OSType
= 'W';
1006 ShortcutLetter
= 'W';
1007 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
1008 } else if (StriCmp(NameClues
, L
"xom.efi") == 0) {
1009 MergeStrings(&OSIconName
, L
"xom,win,win8", L
',');
1010 Entry
->UseGraphicsMode
= TRUE
;
1011 Entry
->OSType
= 'X';
1012 ShortcutLetter
= 'W';
1013 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
1015 else if (StriSubCmp(L
"ipxe", NameClues
)) {
1016 Entry
->OSType
= 'N';
1017 ShortcutLetter
= 'N';
1018 MergeStrings(&OSIconName
, L
"network", L
',');
1021 if ((ShortcutLetter
>= 'a') && (ShortcutLetter
<= 'z'))
1022 ShortcutLetter
= ShortcutLetter
- 'a' + 'A'; // convert lowercase to uppercase
1023 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1024 if (Entry
->me
.Image
== NULL
)
1025 Entry
->me
.Image
= LoadOSIcon(OSIconName
, L
"unknown", FALSE
);
1026 MyFreePool(PathOnly
);
1027 } // VOID SetLoaderDefaults()
1029 // Add a specified EFI boot loader to the list, using automatic settings
1030 // for icons, options, etc.
1031 LOADER_ENTRY
* AddLoaderEntry(IN CHAR16
*LoaderPath
, IN CHAR16
*LoaderTitle
, IN REFIT_VOLUME
*Volume
) {
1032 LOADER_ENTRY
*Entry
;
1034 CleanUpPathNameSlashes(LoaderPath
);
1035 Entry
= InitializeLoaderEntry(NULL
);
1036 if (Entry
!= NULL
) {
1037 Entry
->Title
= StrDuplicate((LoaderTitle
!= NULL
) ? LoaderTitle
: LoaderPath
);
1038 Entry
->me
.Title
= AllocateZeroPool(sizeof(CHAR16
) * 256);
1039 // Extra space at end of Entry->me.Title enables searching on Volume->VolName even if another volume
1040 // name is identical except for something added to the end (e.g., VolB1 vs. VolB12).
1041 // Note: Volume->VolName will be NULL for network boot programs.
1042 if (Volume
->VolName
)
1043 SPrint(Entry
->me
.Title
, 255, L
"Boot %s from %s ", (LoaderTitle
!= NULL
) ? LoaderTitle
: LoaderPath
, Volume
->VolName
);
1045 SPrint(Entry
->me
.Title
, 255, L
"Boot %s ", (LoaderTitle
!= NULL
) ? LoaderTitle
: LoaderPath
);
1047 Entry
->me
.BadgeImage
= Volume
->VolBadgeImage
;
1048 if ((LoaderPath
!= NULL
) && (LoaderPath
[0] != L
'\\')) {
1049 Entry
->LoaderPath
= StrDuplicate(L
"\\");
1051 Entry
->LoaderPath
= NULL
;
1053 MergeStrings(&(Entry
->LoaderPath
), LoaderPath
, 0);
1054 Entry
->VolName
= Volume
->VolName
;
1055 Entry
->DevicePath
= FileDevicePath(Volume
->DeviceHandle
, Entry
->LoaderPath
);
1056 SetLoaderDefaults(Entry
, LoaderPath
, Volume
);
1057 GenerateSubScreen(Entry
, Volume
);
1058 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1062 } // LOADER_ENTRY * AddLoaderEntry()
1064 // Returns -1 if (Time1 < Time2), +1 if (Time1 > Time2), or 0 if
1065 // (Time1 == Time2). Precision is only to the nearest second; since
1066 // this is used for sorting boot loader entries, differences smaller
1067 // than this are likely to be meaningless (and unlikely!).
1068 INTN
TimeComp(IN EFI_TIME
*Time1
, IN EFI_TIME
*Time2
) {
1069 INT64 Time1InSeconds
, Time2InSeconds
;
1071 // Following values are overestimates; I'm assuming 31 days in every month.
1072 // This is fine for the purpose of this function, which is limited
1073 Time1InSeconds
= Time1
->Second
+ (Time1
->Minute
* 60) + (Time1
->Hour
* 3600) + (Time1
->Day
* 86400) +
1074 (Time1
->Month
* 2678400) + ((Time1
->Year
- 1998) * 32140800);
1075 Time2InSeconds
= Time2
->Second
+ (Time2
->Minute
* 60) + (Time2
->Hour
* 3600) + (Time2
->Day
* 86400) +
1076 (Time2
->Month
* 2678400) + ((Time2
->Year
- 1998) * 32140800);
1077 if (Time1InSeconds
< Time2InSeconds
)
1079 else if (Time1InSeconds
> Time2InSeconds
)
1083 } // INTN TimeComp()
1085 // Adds a loader list element, keeping it sorted by date. Returns the new
1086 // first element (the one with the most recent date).
1087 static struct LOADER_LIST
* AddLoaderListEntry(struct LOADER_LIST
*LoaderList
, struct LOADER_LIST
*NewEntry
) {
1088 struct LOADER_LIST
*LatestEntry
, *CurrentEntry
, *PrevEntry
= NULL
;
1090 LatestEntry
= CurrentEntry
= LoaderList
;
1091 if (LoaderList
== NULL
) {
1092 LatestEntry
= NewEntry
;
1094 while ((CurrentEntry
!= NULL
) && (TimeComp(&(NewEntry
->TimeStamp
), &(CurrentEntry
->TimeStamp
)) < 0)) {
1095 PrevEntry
= CurrentEntry
;
1096 CurrentEntry
= CurrentEntry
->NextEntry
;
1098 NewEntry
->NextEntry
= CurrentEntry
;
1099 if (PrevEntry
== NULL
) {
1100 LatestEntry
= NewEntry
;
1102 PrevEntry
->NextEntry
= NewEntry
;
1105 return (LatestEntry
);
1106 } // static VOID AddLoaderListEntry()
1108 // Delete the LOADER_LIST linked list
1109 static VOID
CleanUpLoaderList(struct LOADER_LIST
*LoaderList
) {
1110 struct LOADER_LIST
*Temp
;
1112 while (LoaderList
!= NULL
) {
1114 LoaderList
= LoaderList
->NextEntry
;
1115 MyFreePool(Temp
->FileName
);
1118 } // static VOID CleanUpLoaderList()
1120 // Returns FALSE if the specified file/volume matches the GlobalConfig.DontScanDirs
1121 // or GlobalConfig.DontScanVolumes specification, or if Path points to a volume
1122 // other than the one specified by Volume, or if the specified path is SelfDir.
1123 // Returns TRUE if none of these conditions is met -- that is, if the path is
1124 // eligible for scanning.
1125 static BOOLEAN
ShouldScan(REFIT_VOLUME
*Volume
, CHAR16
*Path
) {
1126 CHAR16
*VolName
= NULL
, *DontScanDir
, *PathCopy
= NULL
;
1128 BOOLEAN ScanIt
= TRUE
;
1130 if ((IsIn(Volume
->VolName
, GlobalConfig
.DontScanVolumes
)) || (IsIn(Volume
->PartName
, GlobalConfig
.DontScanVolumes
)))
1133 if ((StriCmp(Path
, SelfDirPath
) == 0) && (Volume
->DeviceHandle
== SelfVolume
->DeviceHandle
))
1136 // See if Path includes an explicit volume declaration that's NOT Volume....
1137 PathCopy
= StrDuplicate(Path
);
1138 if (SplitVolumeAndFilename(&PathCopy
, &VolName
)) {
1139 VolumeNumberToName(Volume
, &VolName
);
1140 if (VolName
&& StriCmp(VolName
, Volume
->VolName
) != 0) {
1143 } // if Path includes volume specification
1144 MyFreePool(PathCopy
);
1145 MyFreePool(VolName
);
1148 // See if Volume is in GlobalConfig.DontScanDirs....
1149 while (ScanIt
&& (DontScanDir
= FindCommaDelimited(GlobalConfig
.DontScanDirs
, i
++))) {
1150 SplitVolumeAndFilename(&DontScanDir
, &VolName
);
1151 CleanUpPathNameSlashes(DontScanDir
);
1152 VolumeNumberToName(Volume
, &VolName
);
1153 if (VolName
!= NULL
) {
1154 if ((StriCmp(VolName
, Volume
->VolName
) == 0) && (StriCmp(DontScanDir
, Path
) == 0))
1157 if (StriCmp(DontScanDir
, Path
) == 0)
1160 MyFreePool(DontScanDir
);
1161 MyFreePool(VolName
);
1167 } // BOOLEAN ShouldScan()
1169 // Returns TRUE if the file is byte-for-byte identical with the fallback file
1170 // on the volume AND if the file is not itself the fallback file; returns
1171 // FALSE if the file is not identical to the fallback file OR if the file
1172 // IS the fallback file. Intended for use in excluding the fallback boot
1173 // loader when it's a duplicate of another boot loader.
1174 static BOOLEAN
DuplicatesFallback(IN REFIT_VOLUME
*Volume
, IN CHAR16
*FileName
) {
1175 CHAR8
*FileContents
, *FallbackContents
;
1176 EFI_FILE_HANDLE FileHandle
, FallbackHandle
;
1177 EFI_FILE_INFO
*FileInfo
, *FallbackInfo
;
1178 UINTN FileSize
= 0, FallbackSize
= 0;
1180 BOOLEAN AreIdentical
= FALSE
;
1182 if (!FileExists(Volume
->RootDir
, FileName
) || !FileExists(Volume
->RootDir
, FALLBACK_FULLNAME
))
1185 CleanUpPathNameSlashes(FileName
);
1187 if (StriCmp(FileName
, FALLBACK_FULLNAME
) == 0)
1188 return FALSE
; // identical filenames, so not a duplicate....
1190 Status
= refit_call5_wrapper(Volume
->RootDir
->Open
, Volume
->RootDir
, &FileHandle
, FileName
, EFI_FILE_MODE_READ
, 0);
1191 if (Status
== EFI_SUCCESS
) {
1192 FileInfo
= LibFileInfo(FileHandle
);
1193 FileSize
= FileInfo
->FileSize
;
1198 Status
= refit_call5_wrapper(Volume
->RootDir
->Open
, Volume
->RootDir
, &FallbackHandle
, FALLBACK_FULLNAME
, EFI_FILE_MODE_READ
, 0);
1199 if (Status
== EFI_SUCCESS
) {
1200 FallbackInfo
= LibFileInfo(FallbackHandle
);
1201 FallbackSize
= FallbackInfo
->FileSize
;
1203 refit_call1_wrapper(FileHandle
->Close
, FileHandle
);
1207 if (FallbackSize
!= FileSize
) { // not same size, so can't be identical
1208 AreIdentical
= FALSE
;
1209 } else { // could be identical; do full check....
1210 FileContents
= AllocatePool(FileSize
);
1211 FallbackContents
= AllocatePool(FallbackSize
);
1212 if (FileContents
&& FallbackContents
) {
1213 Status
= refit_call3_wrapper(FileHandle
->Read
, FileHandle
, &FileSize
, FileContents
);
1214 if (Status
== EFI_SUCCESS
) {
1215 Status
= refit_call3_wrapper(FallbackHandle
->Read
, FallbackHandle
, &FallbackSize
, FallbackContents
);
1217 if (Status
== EFI_SUCCESS
) {
1218 AreIdentical
= (CompareMem(FileContents
, FallbackContents
, FileSize
) == 0);
1221 MyFreePool(FileContents
);
1222 MyFreePool(FallbackContents
);
1225 // BUG ALERT: Some systems (e.g., DUET, some Macs with large displays) crash if the
1226 // following two calls are reversed. Go figure....
1227 refit_call1_wrapper(FileHandle
->Close
, FallbackHandle
);
1228 refit_call1_wrapper(FileHandle
->Close
, FileHandle
);
1229 return AreIdentical
;
1230 } // BOOLEAN DuplicatesFallback()
1232 // Returns FALSE if two measures of file size are identical for a single file,
1233 // TRUE if not or if the file can't be opened and the other measure is non-0.
1234 // Despite the function's name, this isn't really a direct test of symbolic
1235 // link status, since EFI doesn't officially support symlinks. It does seem
1236 // to be a reliable indicator, though. (OTOH, some disk errors might cause a
1237 // file to fail to open, which would return a false positive -- but as I use
1238 // this function to exclude symbolic links from the list of boot loaders,
1239 // that would be fine, since such boot loaders wouldn't work.)
1240 static BOOLEAN
IsSymbolicLink(REFIT_VOLUME
*Volume
, CHAR16
*Path
, EFI_FILE_INFO
*DirEntry
) {
1241 EFI_FILE_HANDLE FileHandle
;
1242 EFI_FILE_INFO
*FileInfo
= NULL
;
1244 UINTN FileSize2
= 0;
1247 FileName
= StrDuplicate(Path
);
1248 MergeStrings(&FileName
, DirEntry
->FileName
, L
'\\');
1249 CleanUpPathNameSlashes(FileName
);
1251 Status
= refit_call5_wrapper(Volume
->RootDir
->Open
, Volume
->RootDir
, &FileHandle
, FileName
, EFI_FILE_MODE_READ
, 0);
1252 if (Status
== EFI_SUCCESS
) {
1253 FileInfo
= LibFileInfo(FileHandle
);
1254 if (FileInfo
!= NULL
)
1255 FileSize2
= FileInfo
->FileSize
;
1258 MyFreePool(FileName
);
1259 MyFreePool(FileInfo
);
1261 return (DirEntry
->FileSize
!= FileSize2
);
1262 } // BOOLEAN IsSymbolicLink()
1264 // Returns TRUE if a file with the same name as the original but with
1265 // ".efi.signed" is also present in the same directory. Ubuntu is using
1266 // this filename as a signed version of the original unsigned kernel, and
1267 // there's no point in cluttering the display with two kernels that will
1268 // behave identically on non-SB systems, or when one will fail when SB
1270 static BOOLEAN
HasSignedCounterpart(IN REFIT_VOLUME
*Volume
, IN CHAR16
*Path
, IN CHAR16
*Filename
) {
1271 CHAR16
*NewFile
= NULL
;
1272 BOOLEAN retval
= FALSE
;
1274 MergeStrings(&NewFile
, Path
, 0);
1275 MergeStrings(&NewFile
, Filename
, L
'\\');
1276 MergeStrings(&NewFile
, L
".efi.signed", 0);
1277 if (NewFile
!= NULL
) {
1278 CleanUpPathNameSlashes(NewFile
);
1279 if (FileExists(Volume
->RootDir
, NewFile
))
1281 MyFreePool(NewFile
);
1285 } // BOOLEAN HasSignedCounterpart()
1287 // Scan an individual directory for EFI boot loader files and, if found,
1288 // add them to the list. Exception: Ignores FALLBACK_FULLNAME, which is picked
1289 // up in ScanEfiFiles(). Sorts the entries within the loader directory so that
1290 // the most recent one appears first in the list.
1291 // Returns TRUE if a duplicate for FALLBACK_FILENAME was found, FALSE if not.
1292 static BOOLEAN
ScanLoaderDir(IN REFIT_VOLUME
*Volume
, IN CHAR16
*Path
, IN CHAR16
*Pattern
)
1295 REFIT_DIR_ITER DirIter
;
1296 EFI_FILE_INFO
*DirEntry
;
1297 CHAR16 FileName
[256], *Extension
;
1298 struct LOADER_LIST
*LoaderList
= NULL
, *NewLoader
;
1299 BOOLEAN FoundFallbackDuplicate
= FALSE
;
1301 if ((!SelfDirPath
|| !Path
|| ((StriCmp(Path
, SelfDirPath
) == 0) && (Volume
->DeviceHandle
!= SelfVolume
->DeviceHandle
)) ||
1302 (StriCmp(Path
, SelfDirPath
) != 0)) && (ShouldScan(Volume
, Path
))) {
1303 // look through contents of the directory
1304 DirIterOpen(Volume
->RootDir
, Path
, &DirIter
);
1305 while (DirIterNext(&DirIter
, 2, Pattern
, &DirEntry
)) {
1306 Extension
= FindExtension(DirEntry
->FileName
);
1307 if (DirEntry
->FileName
[0] == '.' ||
1308 StriCmp(Extension
, L
".icns") == 0 ||
1309 StriCmp(Extension
, L
".png") == 0 ||
1310 (StriCmp(DirEntry
->FileName
, FALLBACK_BASENAME
) == 0 && (StriCmp(Path
, L
"EFI\\BOOT") == 0)) ||
1311 StriSubCmp(L
"shell", DirEntry
->FileName
) ||
1312 IsSymbolicLink(Volume
, Path
, DirEntry
) || /* is symbolic link */
1313 HasSignedCounterpart(Volume
, Path
, DirEntry
->FileName
) || /* a file with same name plus ".efi.signed" is present */
1314 FilenameIn(Volume
, Path
, DirEntry
->FileName
, GlobalConfig
.DontScanFiles
))
1315 continue; // skip this
1318 SPrint(FileName
, 255, L
"\\%s\\%s", Path
, DirEntry
->FileName
);
1320 SPrint(FileName
, 255, L
"\\%s", DirEntry
->FileName
);
1321 CleanUpPathNameSlashes(FileName
);
1323 if(!IsValidLoader(Volume
->RootDir
, FileName
))
1326 NewLoader
= AllocateZeroPool(sizeof(struct LOADER_LIST
));
1327 if (NewLoader
!= NULL
) {
1328 NewLoader
->FileName
= StrDuplicate(FileName
);
1329 NewLoader
->TimeStamp
= DirEntry
->ModificationTime
;
1330 LoaderList
= AddLoaderListEntry(LoaderList
, NewLoader
);
1331 if (DuplicatesFallback(Volume
, FileName
))
1332 FoundFallbackDuplicate
= TRUE
;
1334 MyFreePool(Extension
);
1337 NewLoader
= LoaderList
;
1338 while (NewLoader
!= NULL
) {
1339 AddLoaderEntry(NewLoader
->FileName
, NULL
, Volume
);
1340 NewLoader
= NewLoader
->NextEntry
;
1343 CleanUpLoaderList(LoaderList
);
1344 Status
= DirIterClose(&DirIter
);
1345 // NOTE: EFI_INVALID_PARAMETER really is an error that should be reported;
1346 // but I've gotten reports from users who are getting this error occasionally
1347 // and I can't find anything wrong or reproduce the problem, so I'm putting
1348 // it down to buggy EFI implementations and ignoring that particular error....
1349 if ((Status
!= EFI_NOT_FOUND
) && (Status
!= EFI_INVALID_PARAMETER
)) {
1351 SPrint(FileName
, 255, L
"while scanning the %s directory", Path
);
1353 StrCpy(FileName
, L
"while scanning the root directory");
1354 CheckError(Status
, FileName
);
1355 } // if (Status != EFI_NOT_FOUND)
1356 } // if not scanning a blacklisted directory
1358 return FoundFallbackDuplicate
;
1359 } /* static VOID ScanLoaderDir() */
1361 // Run the IPXE_DISCOVER_NAME program, which obtains the IP address of the boot
1362 // server and the name of the boot file it delivers.
1363 CHAR16
* RuniPXEDiscover(EFI_HANDLE Volume
)
1366 EFI_DEVICE_PATH
*FilePath
;
1367 EFI_HANDLE iPXEHandle
;
1368 CHAR16
*boot_info
= NULL
;
1369 UINTN boot_info_size
= 0;
1371 FilePath
= FileDevicePath (Volume
, IPXE_DISCOVER_NAME
);
1372 Status
= refit_call6_wrapper(BS
->LoadImage
, FALSE
, SelfImageHandle
, FilePath
,
1373 NULL
, 0, &iPXEHandle
);
1377 Status
= refit_call3_wrapper(BS
->StartImage
, iPXEHandle
, &boot_info_size
, &boot_info
);
1380 } // RuniPXEDiscover()
1382 // Scan for network (PXE) boot servers. This function relies on the presence
1383 // of the IPXE_DISCOVER_NAME and IPXE_NAME program files on the volume from
1384 // which rEFInd launched. As of December 6, 2014, these tools aren't entirely
1385 // reliable. See BUILDING.txt for information on building them.
1386 static VOID
ScanNetboot() {
1387 CHAR16
*iPXEFileName
= IPXE_NAME
;
1389 REFIT_VOLUME
*NetVolume
;
1391 if (FileExists(SelfVolume
->RootDir
, IPXE_DISCOVER_NAME
) &&
1392 FileExists(SelfVolume
->RootDir
, IPXE_NAME
) &&
1393 IsValidLoader(SelfVolume
->RootDir
, IPXE_DISCOVER_NAME
) &&
1394 IsValidLoader(SelfVolume
->RootDir
, IPXE_NAME
)) {
1395 Location
= RuniPXEDiscover(SelfVolume
->DeviceHandle
);
1396 if (Location
!= NULL
&& FileExists(SelfVolume
->RootDir
, iPXEFileName
)) {
1397 NetVolume
= AllocatePool(sizeof(REFIT_VOLUME
));
1398 CopyMem(NetVolume
, SelfVolume
, sizeof(REFIT_VOLUME
));
1399 NetVolume
->DiskKind
= DISK_KIND_NET
;
1400 NetVolume
->VolBadgeImage
= BuiltinIcon(BUILTIN_ICON_VOL_NET
);
1401 NetVolume
->PartName
= NetVolume
->VolName
= NULL
;
1402 AddLoaderEntry(iPXEFileName
, Location
, NetVolume
);
1403 MyFreePool(NetVolume
);
1404 } // if support files exist and are valid
1406 } // VOID ScanNetBoot()
1408 static VOID
ScanEfiFiles(REFIT_VOLUME
*Volume
) {
1410 REFIT_DIR_ITER EfiDirIter
;
1411 EFI_FILE_INFO
*EfiDirEntry
;
1412 CHAR16 FileName
[256], *Directory
= NULL
, *MatchPatterns
, *VolName
= NULL
, *SelfPath
;
1414 BOOLEAN ScanFallbackLoader
= TRUE
;
1415 BOOLEAN FoundBRBackup
= FALSE
;
1417 if ((Volume
->RootDir
!= NULL
) && (Volume
->VolName
!= NULL
) && (Volume
->IsReadable
)) {
1418 MatchPatterns
= StrDuplicate(LOADER_MATCH_PATTERNS
);
1419 if (GlobalConfig
.ScanAllLinux
)
1420 MergeStrings(&MatchPatterns
, LINUX_MATCH_PATTERNS
, L
',');
1422 // check for Mac OS X boot loader
1423 if (ShouldScan(Volume
, MACOSX_LOADER_DIR
)) {
1424 StrCpy(FileName
, MACOSX_LOADER_PATH
);
1425 if (FileExists(Volume
->RootDir
, FileName
) && !FilenameIn(Volume
, MACOSX_LOADER_DIR
, L
"boot.efi", GlobalConfig
.DontScanFiles
)) {
1426 AddLoaderEntry(FileName
, L
"Mac OS X", Volume
);
1427 if (DuplicatesFallback(Volume
, FileName
))
1428 ScanFallbackLoader
= FALSE
;
1432 StrCpy(FileName
, L
"System\\Library\\CoreServices\\xom.efi");
1433 if (FileExists(Volume
->RootDir
, FileName
) && !FilenameIn(Volume
, MACOSX_LOADER_DIR
, L
"xom.efi", GlobalConfig
.DontScanFiles
)) {
1434 AddLoaderEntry(FileName
, L
"Windows XP (XoM)", Volume
);
1435 if (DuplicatesFallback(Volume
, FileName
))
1436 ScanFallbackLoader
= FALSE
;
1438 } // if should scan Mac directory
1440 // check for Microsoft boot loader/menu
1441 if (ShouldScan(Volume
, L
"EFI\\Microsoft\\Boot")) {
1442 StrCpy(FileName
, L
"EFI\\Microsoft\\Boot\\bkpbootmgfw.efi");
1443 if (FileExists(Volume
->RootDir
, FileName
) && !FilenameIn(Volume
, L
"EFI\\Microsoft\\Boot", L
"bkpbootmgfw.efi",
1444 GlobalConfig
.DontScanFiles
)) {
1445 AddLoaderEntry(FileName
, L
"Microsoft EFI boot (Boot Repair backup)", Volume
);
1446 FoundBRBackup
= TRUE
;
1447 if (DuplicatesFallback(Volume
, FileName
))
1448 ScanFallbackLoader
= FALSE
;
1450 StrCpy(FileName
, L
"EFI\\Microsoft\\Boot\\bootmgfw.efi");
1451 if (FileExists(Volume
->RootDir
, FileName
) && !FilenameIn(Volume
, L
"EFI\\Microsoft\\Boot", L
"bootmgfw.efi", GlobalConfig
.DontScanFiles
)) {
1453 AddLoaderEntry(FileName
, L
"Supposed Microsoft EFI boot (probably GRUB)", Volume
);
1455 AddLoaderEntry(FileName
, L
"Microsoft EFI boot", Volume
);
1456 if (DuplicatesFallback(Volume
, FileName
))
1457 ScanFallbackLoader
= FALSE
;
1461 // scan the root directory for EFI executables
1462 if (ScanLoaderDir(Volume
, L
"\\", MatchPatterns
))
1463 ScanFallbackLoader
= FALSE
;
1465 // scan subdirectories of the EFI directory (as per the standard)
1466 DirIterOpen(Volume
->RootDir
, L
"EFI", &EfiDirIter
);
1467 while (DirIterNext(&EfiDirIter
, 1, NULL
, &EfiDirEntry
)) {
1468 if (StriCmp(EfiDirEntry
->FileName
, L
"tools") == 0 || EfiDirEntry
->FileName
[0] == '.')
1469 continue; // skip this, doesn't contain boot loaders or is scanned later
1470 SPrint(FileName
, 255, L
"EFI\\%s", EfiDirEntry
->FileName
);
1471 if (ScanLoaderDir(Volume
, FileName
, MatchPatterns
))
1472 ScanFallbackLoader
= FALSE
;
1474 Status
= DirIterClose(&EfiDirIter
);
1475 if (Status
!= EFI_NOT_FOUND
)
1476 CheckError(Status
, L
"while scanning the EFI directory");
1478 // Scan user-specified (or additional default) directories....
1480 while ((Directory
= FindCommaDelimited(GlobalConfig
.AlsoScan
, i
++)) != NULL
) {
1481 if (ShouldScan(Volume
, Directory
)) {
1482 SplitVolumeAndFilename(&Directory
, &VolName
);
1483 CleanUpPathNameSlashes(Directory
);
1484 Length
= StrLen(Directory
);
1485 if ((Length
> 0) && ScanLoaderDir(Volume
, Directory
, MatchPatterns
))
1486 ScanFallbackLoader
= FALSE
;
1487 MyFreePool(VolName
);
1488 } // if should scan dir
1489 MyFreePool(Directory
);
1492 // Don't scan the fallback loader if it's on the same volume and a duplicate of rEFInd itself....
1493 SelfPath
= DevicePathToStr(SelfLoadedImage
->FilePath
);
1494 CleanUpPathNameSlashes(SelfPath
);
1495 if ((Volume
->DeviceHandle
== SelfLoadedImage
->DeviceHandle
) && DuplicatesFallback(Volume
, SelfPath
))
1496 ScanFallbackLoader
= FALSE
;
1498 // If not a duplicate & if it exists & if it's not us, create an entry
1499 // for the fallback boot loader
1500 if (ScanFallbackLoader
&& FileExists(Volume
->RootDir
, FALLBACK_FULLNAME
) && ShouldScan(Volume
, L
"EFI\\BOOT"))
1501 AddLoaderEntry(FALLBACK_FULLNAME
, L
"Fallback boot loader", Volume
);
1503 } // static VOID ScanEfiFiles()
1505 // Scan internal disks for valid EFI boot loaders....
1506 static VOID
ScanInternal(VOID
) {
1509 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1510 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_INTERNAL
) {
1511 ScanEfiFiles(Volumes
[VolumeIndex
]);
1514 } // static VOID ScanInternal()
1516 // Scan external disks for valid EFI boot loaders....
1517 static VOID
ScanExternal(VOID
) {
1520 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1521 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_EXTERNAL
) {
1522 ScanEfiFiles(Volumes
[VolumeIndex
]);
1525 } // static VOID ScanExternal()
1527 // Scan internal disks for valid EFI boot loaders....
1528 static VOID
ScanOptical(VOID
) {
1531 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1532 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_OPTICAL
) {
1533 ScanEfiFiles(Volumes
[VolumeIndex
]);
1536 } // static VOID ScanOptical()
1538 // default volume badge icon based on disk kind
1539 EG_IMAGE
* GetDiskBadge(IN UINTN DiskType
) {
1540 EG_IMAGE
* Badge
= NULL
;
1544 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_INTERNAL
);
1547 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_EXTERNAL
);
1550 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_OPTICAL
);
1554 } // EG_IMAGE * GetDiskBadge()
1557 // pre-boot tool functions
1560 static VOID
StartTool(IN LOADER_ENTRY
*Entry
)
1562 BeginExternalScreen(Entry
->UseGraphicsMode
, Entry
->me
.Title
+ 6); // assumes "Start <title>" as assigned below
1563 StoreLoaderName(Entry
->me
.Title
);
1564 StartEFIImage(Entry
->DevicePath
, Entry
->LoadOptions
, TYPE_EFI
,
1565 Basename(Entry
->LoaderPath
), Entry
->OSType
, NULL
, TRUE
, FALSE
);
1566 FinishExternalScreen();
1567 } /* static VOID StartTool() */
1569 static LOADER_ENTRY
* AddToolEntry(EFI_HANDLE DeviceHandle
, IN CHAR16
*LoaderPath
, IN CHAR16
*LoaderTitle
, IN EG_IMAGE
*Image
,
1570 IN CHAR16 ShortcutLetter
, IN BOOLEAN UseGraphicsMode
)
1572 LOADER_ENTRY
*Entry
;
1573 CHAR16
*TitleStr
= NULL
;
1575 Entry
= AllocateZeroPool(sizeof(LOADER_ENTRY
));
1577 TitleStr
= PoolPrint(L
"Start %s", LoaderTitle
);
1578 Entry
->me
.Title
= TitleStr
;
1579 Entry
->me
.Tag
= TAG_TOOL
;
1581 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1582 Entry
->me
.Image
= Image
;
1583 Entry
->LoaderPath
= (LoaderPath
) ? StrDuplicate(LoaderPath
) : NULL
;
1584 Entry
->DevicePath
= FileDevicePath(DeviceHandle
, Entry
->LoaderPath
);
1585 Entry
->UseGraphicsMode
= UseGraphicsMode
;
1587 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1589 } /* static LOADER_ENTRY * AddToolEntry() */
1592 // pre-boot driver functions
1595 static UINTN
ScanDriverDir(IN CHAR16
*Path
)
1598 REFIT_DIR_ITER DirIter
;
1600 EFI_FILE_INFO
*DirEntry
;
1601 CHAR16 FileName
[256];
1603 CleanUpPathNameSlashes(Path
);
1604 // look through contents of the directory
1605 DirIterOpen(SelfRootDir
, Path
, &DirIter
);
1606 while (DirIterNext(&DirIter
, 2, LOADER_MATCH_PATTERNS
, &DirEntry
)) {
1607 if (DirEntry
->FileName
[0] == '.')
1608 continue; // skip this
1610 SPrint(FileName
, 255, L
"%s\\%s", Path
, DirEntry
->FileName
);
1612 Status
= StartEFIImage(FileDevicePath(SelfLoadedImage
->DeviceHandle
, FileName
),
1613 L
"", TYPE_EFI
, DirEntry
->FileName
, 0, NULL
, FALSE
, TRUE
);
1615 Status
= DirIterClose(&DirIter
);
1616 if (Status
!= EFI_NOT_FOUND
) {
1617 SPrint(FileName
, 255, L
"while scanning the %s directory", Path
);
1618 CheckError(Status
, FileName
);
1623 #ifdef __MAKEWITH_GNUEFI
1624 static EFI_STATUS
ConnectAllDriversToAllControllers(VOID
)
1627 UINTN AllHandleCount
;
1628 EFI_HANDLE
*AllHandleBuffer
;
1631 EFI_HANDLE
*HandleBuffer
;
1637 Status
= LibLocateHandle(AllHandles
,
1642 if (EFI_ERROR(Status
))
1645 for (Index
= 0; Index
< AllHandleCount
; Index
++) {
1647 // Scan the handle database
1649 Status
= LibScanHandleDatabase(NULL
,
1651 AllHandleBuffer
[Index
],
1656 if (EFI_ERROR (Status
))
1660 if (HandleType
[Index
] & EFI_HANDLE_TYPE_DRIVER_BINDING_HANDLE
)
1662 if (HandleType
[Index
] & EFI_HANDLE_TYPE_IMAGE_HANDLE
)
1667 for (HandleIndex
= 0; HandleIndex
< HandleCount
; HandleIndex
++) {
1668 if (HandleType
[HandleIndex
] & EFI_HANDLE_TYPE_PARENT_HANDLE
)
1673 if (HandleType
[Index
] & EFI_HANDLE_TYPE_DEVICE_HANDLE
) {
1674 Status
= refit_call4_wrapper(BS
->ConnectController
,
1675 AllHandleBuffer
[Index
],
1683 MyFreePool (HandleBuffer
);
1684 MyFreePool (HandleType
);
1688 MyFreePool (AllHandleBuffer
);
1690 } /* EFI_STATUS ConnectAllDriversToAllControllers() */
1692 static EFI_STATUS
ConnectAllDriversToAllControllers(VOID
) {
1693 BdsLibConnectAllDriversToAllControllers();
1698 // Load all EFI drivers from rEFInd's "drivers" subdirectory and from the
1699 // directories specified by the user in the "scan_driver_dirs" configuration
1701 static VOID
LoadDrivers(VOID
)
1703 CHAR16
*Directory
, *SelfDirectory
;
1704 UINTN i
= 0, Length
, NumFound
= 0;
1706 // load drivers from the subdirectories of rEFInd's home directory specified
1707 // in the DRIVER_DIRS constant.
1708 while ((Directory
= FindCommaDelimited(DRIVER_DIRS
, i
++)) != NULL
) {
1709 SelfDirectory
= SelfDirPath
? StrDuplicate(SelfDirPath
) : NULL
;
1710 CleanUpPathNameSlashes(SelfDirectory
);
1711 MergeStrings(&SelfDirectory
, Directory
, L
'\\');
1712 NumFound
+= ScanDriverDir(SelfDirectory
);
1713 MyFreePool(Directory
);
1714 MyFreePool(SelfDirectory
);
1717 // Scan additional user-specified driver directories....
1719 while ((Directory
= FindCommaDelimited(GlobalConfig
.DriverDirs
, i
++)) != NULL
) {
1720 CleanUpPathNameSlashes(Directory
);
1721 Length
= StrLen(Directory
);
1723 NumFound
+= ScanDriverDir(Directory
);
1725 MyFreePool(Directory
);
1728 // connect all devices
1730 ConnectAllDriversToAllControllers();
1732 } /* static VOID LoadDrivers() */
1734 // Locates boot loaders. NOTE: This assumes that GlobalConfig.LegacyType is set correctly.
1735 static VOID
ScanForBootloaders(VOID
) {
1738 BOOLEAN ScanForLegacy
= FALSE
;
1740 // Determine up-front if we'll be scanning for legacy loaders....
1741 for (i
= 0; i
< NUM_SCAN_OPTIONS
; i
++) {
1742 s
= GlobalConfig
.ScanFor
[i
];
1743 if ((s
== 'c') || (s
== 'C') || (s
== 'h') || (s
== 'H') || (s
== 'b') || (s
== 'B'))
1744 ScanForLegacy
= TRUE
;
1747 // If UEFI & scanning for legacy loaders & deep legacy scan, update NVRAM boot manager list
1748 if ((GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) && ScanForLegacy
&& GlobalConfig
.DeepLegacyScan
) {
1749 BdsDeleteAllInvalidLegacyBootOptions();
1750 BdsAddNonExistingLegacyBootOptions();
1753 // scan for loaders and tools, add them to the menu
1754 for (i
= 0; i
< NUM_SCAN_OPTIONS
; i
++) {
1755 switch(GlobalConfig
.ScanFor
[i
]) {
1760 ScanLegacyInternal();
1763 ScanLegacyExternal();
1766 ScanUserConfigured(GlobalConfig
.ConfigFilename
);
1783 // assign shortcut keys
1784 for (i
= 0; i
< MainMenu
.EntryCount
&& MainMenu
.Entries
[i
]->Row
== 0 && i
< 9; i
++)
1785 MainMenu
.Entries
[i
]->ShortcutDigit
= (CHAR16
)('1' + i
);
1787 // wait for user ACK when there were errors
1788 FinishTextScreen(FALSE
);
1789 } // static VOID ScanForBootloaders()
1791 // Locate a single tool from the specified Locations using one of the
1792 // specified Names and add it to the menu.
1793 static VOID
FindTool(CHAR16
*Locations
, CHAR16
*Names
, CHAR16
*Description
, UINTN Icon
) {
1794 UINTN j
= 0, k
, VolumeIndex
;
1795 CHAR16
*DirName
, *FileName
, *PathName
, FullDescription
[256];
1797 while ((DirName
= FindCommaDelimited(Locations
, j
++)) != NULL
) {
1799 while ((FileName
= FindCommaDelimited(Names
, k
++)) != NULL
) {
1800 PathName
= StrDuplicate(DirName
);
1801 MergeStrings(&PathName
, FileName
, (StriCmp(PathName
, L
"\\") == 0) ? 0 : L
'\\');
1802 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1803 if ((Volumes
[VolumeIndex
]->RootDir
!= NULL
) && (FileExists(Volumes
[VolumeIndex
]->RootDir
, PathName
)) &&
1804 IsValidLoader(Volumes
[VolumeIndex
]->RootDir
, PathName
)) {
1805 SPrint(FullDescription
, 255, L
"%s at %s on %s", Description
, PathName
, Volumes
[VolumeIndex
]->VolName
);
1806 AddToolEntry(Volumes
[VolumeIndex
]->DeviceHandle
, PathName
, FullDescription
, BuiltinIcon(Icon
), 'S', FALSE
);
1809 MyFreePool(PathName
);
1810 MyFreePool(FileName
);
1812 MyFreePool(DirName
);
1813 } // while Locations
1814 } // VOID FindTool()
1816 // Add the second-row tags containing built-in and external tools (EFI shell,
1818 static VOID
ScanForTools(VOID
) {
1819 CHAR16
*FileName
= NULL
, *VolName
= NULL
, *MokLocations
, Description
[256];
1820 REFIT_MENU_ENTRY
*TempMenuEntry
;
1821 UINTN i
, j
, VolumeIndex
;
1825 MokLocations
= StrDuplicate(MOK_LOCATIONS
);
1826 if (MokLocations
!= NULL
)
1827 MergeStrings(&MokLocations
, SelfDirPath
, L
',');
1829 for (i
= 0; i
< NUM_TOOLS
; i
++) {
1830 switch(GlobalConfig
.ShowTools
[i
]) {
1831 // NOTE: Be sure that FileName is NULL at the end of each case.
1833 TempMenuEntry
= CopyMenuEntry(&MenuEntryShutdown
);
1834 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_SHUTDOWN
);
1835 AddMenuEntry(&MainMenu
, TempMenuEntry
);
1839 TempMenuEntry
= CopyMenuEntry(&MenuEntryReset
);
1840 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_RESET
);
1841 AddMenuEntry(&MainMenu
, TempMenuEntry
);
1845 TempMenuEntry
= CopyMenuEntry(&MenuEntryAbout
);
1846 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT
);
1847 AddMenuEntry(&MainMenu
, TempMenuEntry
);
1851 TempMenuEntry
= CopyMenuEntry(&MenuEntryExit
);
1852 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_EXIT
);
1853 AddMenuEntry(&MainMenu
, TempMenuEntry
);
1857 if (EfivarGetRaw(&GlobalGuid
, L
"OsIndicationsSupported", &b
, &j
) == EFI_SUCCESS
) {
1859 if (osind
& EFI_OS_INDICATIONS_BOOT_TO_FW_UI
) {
1860 TempMenuEntry
= CopyMenuEntry(&MenuEntryFirmware
);
1861 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_FIRMWARE
);
1862 AddMenuEntry(&MainMenu
, TempMenuEntry
);
1869 while ((FileName
= FindCommaDelimited(SHELL_NAMES
, j
++)) != NULL
) {
1870 if (FileExists(SelfRootDir
, FileName
) && IsValidLoader(SelfRootDir
, FileName
)) {
1871 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, L
"EFI Shell", BuiltinIcon(BUILTIN_ICON_TOOL_SHELL
),
1874 MyFreePool(FileName
);
1880 while ((FileName
= FindCommaDelimited(GPTSYNC_NAMES
, j
++)) != NULL
) {
1881 if (FileExists(SelfRootDir
, FileName
) && IsValidLoader(SelfRootDir
, FileName
)) {
1882 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, L
"Hybrid MBR tool", BuiltinIcon(BUILTIN_ICON_TOOL_PART
),
1885 MyFreePool(FileName
);
1892 while ((FileName
= FindCommaDelimited(GDISK_NAMES
, j
++)) != NULL
) {
1893 if (FileExists(SelfRootDir
, FileName
) && IsValidLoader(SelfRootDir
, FileName
)) {
1894 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, L
"disk partitioning tool",
1895 BuiltinIcon(BUILTIN_ICON_TOOL_PART
), 'G', FALSE
);
1897 MyFreePool(FileName
);
1904 while ((FileName
= FindCommaDelimited(NETBOOT_NAMES
, j
++)) != NULL
) {
1905 if (FileExists(SelfRootDir
, FileName
) && IsValidLoader(SelfRootDir
, FileName
)) {
1906 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, L
"Netboot",
1907 BuiltinIcon(BUILTIN_ICON_TOOL_NETBOOT
), 'N', FALSE
);
1909 MyFreePool(FileName
);
1914 case TAG_APPLE_RECOVERY
:
1915 FileName
= StrDuplicate(L
"\\com.apple.recovery.boot\\boot.efi");
1916 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1917 if ((Volumes
[VolumeIndex
]->RootDir
!= NULL
) && (FileExists(Volumes
[VolumeIndex
]->RootDir
, FileName
)) &&
1918 IsValidLoader(Volumes
[VolumeIndex
]->RootDir
, FileName
)) {
1919 SPrint(Description
, 255, L
"Apple Recovery on %s", Volumes
[VolumeIndex
]->VolName
);
1920 AddToolEntry(Volumes
[VolumeIndex
]->DeviceHandle
, FileName
, Description
,
1921 BuiltinIcon(BUILTIN_ICON_TOOL_APPLE_RESCUE
), 'R', TRUE
);
1924 MyFreePool(FileName
);
1928 case TAG_WINDOWS_RECOVERY
:
1930 while ((FileName
= FindCommaDelimited(GlobalConfig
.WindowsRecoveryFiles
, j
++)) != NULL
) {
1931 SplitVolumeAndFilename(&FileName
, &VolName
);
1932 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1933 if ((Volumes
[VolumeIndex
]->RootDir
!= NULL
) && (FileExists(Volumes
[VolumeIndex
]->RootDir
, FileName
)) &&
1934 IsValidLoader(Volumes
[VolumeIndex
]->RootDir
, FileName
) &&
1935 ((VolName
== NULL
) || (StriCmp(VolName
, Volumes
[VolumeIndex
]->VolName
) == 0))) {
1936 SPrint(Description
, 255, L
"Microsoft Recovery on %s", Volumes
[VolumeIndex
]->VolName
);
1937 AddToolEntry(Volumes
[VolumeIndex
]->DeviceHandle
, FileName
, Description
,
1938 BuiltinIcon(BUILTIN_ICON_TOOL_WINDOWS_RESCUE
), 'R', TRUE
);
1942 MyFreePool(FileName
);
1944 MyFreePool(VolName
);
1949 FindTool(MokLocations
, MOK_NAMES
, L
"MOK utility", BUILTIN_ICON_TOOL_MOK_TOOL
);
1953 FindTool(MEMTEST_LOCATIONS
, MEMTEST_NAMES
, L
"Memory test utility", BUILTIN_ICON_TOOL_MEMTEST
);
1958 } // static VOID ScanForTools
1960 // Rescan for boot loaders
1961 static VOID
RescanAll(BOOLEAN DisplayMessage
) {
1969 egDisplayMessage(L
"Scanning for new boot loaders; please wait....", &BGColor
);
1970 FreeList((VOID
***) &(MainMenu
.Entries
), &MainMenu
.EntryCount
);
1971 MainMenu
.Entries
= NULL
;
1972 MainMenu
.EntryCount
= 0;
1973 ReadConfig(GlobalConfig
.ConfigFilename
);
1974 ConnectAllDriversToAllControllers();
1976 ScanForBootloaders();
1979 } // VOID RescanAll()
1981 #ifdef __MAKEWITH_TIANO
1983 // Minimal initialization function
1984 static VOID
InitializeLib(IN EFI_HANDLE ImageHandle
, IN EFI_SYSTEM_TABLE
*SystemTable
) {
1986 // gImageHandle = ImageHandle;
1987 gBS
= SystemTable
->BootServices
;
1988 // gRS = SystemTable->RuntimeServices;
1989 gRT
= SystemTable
->RuntimeServices
; // Some BDS functions need gRT to be set
1990 EfiGetSystemConfigurationTable (&gEfiDxeServicesTableGuid
, (VOID
**) &gDS
);
1992 // InitializeConsoleSim();
1997 // Set up our own Secure Boot extensions....
1998 // Returns TRUE on success, FALSE otherwise
1999 static BOOLEAN
SecureBootSetup(VOID
) {
2001 BOOLEAN Success
= FALSE
;
2003 if (secure_mode() && ShimLoaded()) {
2004 Status
= security_policy_install();
2005 if (Status
== EFI_SUCCESS
) {
2008 Print(L
"Failed to install MOK Secure Boot extensions");
2012 } // VOID SecureBootSetup()
2014 // Remove our own Secure Boot extensions....
2015 // Returns TRUE on success, FALSE otherwise
2016 static BOOLEAN
SecureBootUninstall(VOID
) {
2018 BOOLEAN Success
= TRUE
;
2020 if (secure_mode()) {
2021 Status
= security_policy_uninstall();
2022 if (Status
!= EFI_SUCCESS
) {
2024 BeginTextScreen(L
"Secure Boot Policy Failure");
2025 Print(L
"Failed to uninstall MOK Secure Boot extensions; forcing a reboot.");
2027 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);
2031 } // VOID SecureBootUninstall
2033 // Sets the global configuration filename; will be CONFIG_FILE_NAME unless the
2034 // "-c" command-line option is set, in which case that takes precedence.
2035 // If an error is encountered, leaves the value alone (it should be set to
2036 // CONFIG_FILE_NAME when GlobalConfig is initialized).
2037 static VOID
SetConfigFilename(EFI_HANDLE ImageHandle
) {
2038 EFI_LOADED_IMAGE
*Info
;
2039 CHAR16
*Options
, *FileName
;
2043 Status
= refit_call3_wrapper(BS
->HandleProtocol
, ImageHandle
, &LoadedImageProtocol
, (VOID
**) &Info
);
2044 if ((Status
== EFI_SUCCESS
) && (Info
->LoadOptionsSize
> 0)) {
2045 Options
= (CHAR16
*) Info
->LoadOptions
;
2046 Where
= FindSubString(L
" -c ", Options
);
2048 FileName
= StrDuplicate(&Options
[Where
+ 4]);
2049 Where
= FindSubString(L
" ", FileName
);
2051 FileName
[Where
] = L
'\0';
2053 if (FileExists(SelfDir
, FileName
)) {
2054 GlobalConfig
.ConfigFilename
= FileName
;
2056 Print(L
"Specified configuration file (%s) doesn't exist; using\n'refind.conf' default\n", FileName
);
2057 MyFreePool(FileName
);
2061 } // VOID SetConfigFilename()
2068 efi_main (EFI_HANDLE ImageHandle
, EFI_SYSTEM_TABLE
*SystemTable
)
2071 BOOLEAN MainLoopRunning
= TRUE
;
2072 BOOLEAN MokProtocol
;
2073 REFIT_MENU_ENTRY
*ChosenEntry
;
2075 CHAR16
*SelectionName
= NULL
;
2079 InitializeLib(ImageHandle
, SystemTable
);
2080 Status
= InitRefitLib(ImageHandle
);
2081 if (EFI_ERROR(Status
))
2084 // read configuration
2085 CopyMem(GlobalConfig
.ScanFor
, "ieom ", NUM_SCAN_OPTIONS
);
2086 FindLegacyBootType();
2087 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
)
2088 CopyMem(GlobalConfig
.ScanFor
, "ihebocm ", NUM_SCAN_OPTIONS
);
2089 SetConfigFilename(ImageHandle
);
2090 ReadConfig(GlobalConfig
.ConfigFilename
);
2093 WarnIfLegacyProblems();
2094 MainMenu
.TimeoutSeconds
= GlobalConfig
.Timeout
;
2096 // disable EFI watchdog timer
2097 refit_call4_wrapper(BS
->SetWatchdogTimer
, 0x0000, 0x0000, 0x0000, NULL
);
2099 // further bootstrap (now with config available)
2100 MokProtocol
= SecureBootSetup();
2103 ScanForBootloaders();
2107 if (GlobalConfig
.ScanDelay
> 0) {
2112 if (GlobalConfig
.ScanDelay
> 1)
2113 egDisplayMessage(L
"Pausing before disk scan; please wait....", &BGColor
);
2114 for (i
= 0; i
< GlobalConfig
.ScanDelay
; i
++)
2115 refit_call1_wrapper(BS
->Stall
, 1000000);
2116 RescanAll(GlobalConfig
.ScanDelay
> 1);
2119 if (GlobalConfig
.DefaultSelection
)
2120 SelectionName
= StrDuplicate(GlobalConfig
.DefaultSelection
);
2122 while (MainLoopRunning
) {
2123 MenuExit
= RunMainMenu(&MainMenu
, &SelectionName
, &ChosenEntry
);
2125 // The Escape key triggers a re-scan operation....
2126 if (MenuExit
== MENU_EXIT_ESCAPE
) {
2132 switch (ChosenEntry
->Tag
) {
2134 case TAG_REBOOT
: // Reboot
2136 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);
2137 MainLoopRunning
= FALSE
; // just in case we get this far
2140 case TAG_SHUTDOWN
: // Shut Down
2142 refit_call4_wrapper(RT
->ResetSystem
, EfiResetShutdown
, EFI_SUCCESS
, 0, NULL
);
2143 MainLoopRunning
= FALSE
; // just in case we get this far
2146 case TAG_ABOUT
: // About rEFInd
2150 case TAG_LOADER
: // Boot OS via .EFI loader
2151 StartLoader((LOADER_ENTRY
*)ChosenEntry
, SelectionName
);
2154 case TAG_LEGACY
: // Boot legacy OS
2155 StartLegacy((LEGACY_ENTRY
*)ChosenEntry
, SelectionName
);
2158 case TAG_LEGACY_UEFI
: // Boot a legacy OS on a non-Mac
2159 StartLegacyUEFI((LEGACY_ENTRY
*)ChosenEntry
, SelectionName
);
2162 case TAG_TOOL
: // Start a EFI tool
2163 StartTool((LOADER_ENTRY
*)ChosenEntry
);
2166 case TAG_EXIT
: // Terminate rEFInd
2167 if ((MokProtocol
) && !SecureBootUninstall()) {
2168 MainLoopRunning
= FALSE
; // just in case we get this far
2170 BeginTextScreen(L
" ");
2175 case TAG_FIRMWARE
: // Reboot into firmware's user interface
2176 RebootIntoFirmware();
2182 // If we end up here, things have gone wrong. Try to reboot, and if that
2183 // fails, go into an endless loop.
2184 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);