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-2017 Roderick W. Smith
39 * Modifications distributed under the terms of the GNU General Public
40 * License (GPL) version 3 (GPLv3), or (at your option) any later version.
44 * This program is free software: you can redistribute it and/or modify
45 * it under the terms of the GNU General Public License as published by
46 * the Free Software Foundation, either version 3 of the License, or
47 * (at your option) any later version.
49 * This program is distributed in the hope that it will be useful,
50 * but WITHOUT ANY WARRANTY; without even the implied warranty of
51 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
52 * GNU General Public License for more details.
54 * You should have received a copy of the GNU General Public License
55 * along with this program. If not, see <http://www.gnu.org/licenses/>.
68 #include "mystrings.h"
69 #include "security_policy.h"
70 #include "driver_support.h"
71 #include "../include/Handle.h"
72 #include "../include/refit_call_wrapper.h"
73 #include "../EfiLib/BdsHelper.h"
74 #include "../EfiLib/legacy.h"
76 #ifdef __MAKEWITH_GNUEFI
77 #ifndef EFI_SECURITY_VIOLATION
78 #define EFI_SECURITY_VIOLATION EFIERR (26)
82 #ifndef EFI_OS_INDICATIONS_BOOT_TO_FW_UI
83 #define EFI_OS_INDICATIONS_BOOT_TO_FW_UI 0x0000000000000001ULL
86 #ifdef __MAKEWITH_TIANO
87 #define LibLocateHandle gBS->LocateHandleBuffer
93 #define MACOSX_LOADER_DIR L"System\\Library\\CoreServices"
94 #define MACOSX_LOADER_PATH ( MACOSX_LOADER_DIR L"\\boot.efi" )
96 #define SHELL_NAMES L"\\EFI\\tools\\shell.efi,\\EFI\\tools\\shellx64.efi,\\shell.efi,\\shellx64.efi"
97 #define GPTSYNC_NAMES L"\\EFI\\tools\\gptsync.efi,\\EFI\\tools\\gptsync_x64.efi"
98 #define GDISK_NAMES L"\\EFI\\tools\\gdisk.efi,\\EFI\\tools\\gdisk_x64.efi"
99 #define NETBOOT_NAMES L"\\EFI\\tools\\ipxe.efi"
100 #define MEMTEST_NAMES L"memtest86.efi,memtest86_x64.efi,memtest86x64.efi,bootx64.efi"
101 #define FALLBACK_FULLNAME L"EFI\\BOOT\\bootx64.efi"
102 #define FALLBACK_BASENAME L"bootx64.efi"
103 #define EFI_STUB_ARCH 0x8664
104 EFI_GUID gFreedesktopRootGuid
= { 0x4f68bce3, 0xe8cd, 0x4db1, { 0x96, 0xe7, 0xfb, 0xca, 0xf9, 0x84, 0xb7, 0x09 }};
105 #elif defined (EFI32)
106 #define SHELL_NAMES L"\\EFI\\tools\\shell.efi,\\EFI\\tools\\shellia32.efi,\\shell.efi,\\shellia32.efi"
107 #define GPTSYNC_NAMES L"\\EFI\\tools\\gptsync.efi,\\EFI\\tools\\gptsync_ia32.efi"
108 #define GDISK_NAMES L"\\EFI\\tools\\gdisk.efi,\\EFI\\tools\\gdisk_ia32.efi"
109 #define NETBOOT_NAMES L"\\EFI\\tools\\ipxe.efi"
110 #define MEMTEST_NAMES L"memtest86.efi,memtest86_ia32.efi,memtest86ia32.efi,bootia32.efi"
111 #define FALLBACK_FULLNAME L"EFI\\BOOT\\bootia32.efi"
112 #define FALLBACK_BASENAME L"bootia32.efi"
113 #define EFI_STUB_ARCH 0x014c
114 EFI_GUID gFreedesktopRootGuid
= { 0x44479540, 0xf297, 0x41b2, { 0x9a, 0xf7, 0xd1, 0x31, 0xd5, 0xf0, 0x45, 0x8a }};
115 #elif defined (EFIAARCH64)
116 #define SHELL_NAMES L"\\EFI\\tools\\shell.efi,\\EFI\\tools\\shellaa64.efi,\\shell.efi,\\shellaa64.efi"
117 #define GPTSYNC_NAMES L"\\EFI\\tools\\gptsync.efi,\\EFI\\tools\\gptsync_aa64.efi"
118 #define GDISK_NAMES L"\\EFI\\tools\\gdisk.efi,\\EFI\\tools\\gdisk_aa64.efi"
119 #define NETBOOT_NAMES L"\\EFI\\tools\\ipxe.efi"
120 #define MEMTEST_NAMES L"memtest86.efi,memtest86_aa64.efi,memtest86aa64.efi,bootaa64.efi"
121 #define FALLBACK_FULLNAME L"EFI\\BOOT\\bootaa64.efi"
122 #define FALLBACK_BASENAME L"bootaa64.efi"
123 #define EFI_STUB_ARCH 0xaa64
124 EFI_GUID gFreedesktopRootGuid
= { 0xb921b045, 0x1df0, 0x41c3, { 0xaf, 0x44, 0x4c, 0x6f, 0x28, 0x0d, 0x3f, 0xae }};
126 #define SHELL_NAMES L"\\EFI\\tools\\shell.efi,\\shell.efi"
127 #define GPTSYNC_NAMES L"\\EFI\\tools\\gptsync.efi"
128 #define GDISK_NAMES L"\\EFI\\tools\\gdisk.efi"
129 #define NETBOOT_NAMES L"\\EFI\\tools\\ipxe.efi"
130 #define MEMTEST_NAMES L"memtest86.efi"
131 #define DRIVER_DIRS L"drivers"
132 #define FALLBACK_FULLNAME L"EFI\\BOOT\\boot.efi" /* Not really correct */
133 #define FALLBACK_BASENAME L"boot.efi" /* Not really correct */
134 // Below is GUID for ARM32
135 EFI_GUID gFreedesktopRootGuid
= { 0x69dad710, 0x2ce4, 0x4e3c, { 0xb1, 0x6c, 0x21, 0xa1, 0xd4, 0x9a, 0xbe, 0xd3 }};
137 #define FAT_ARCH 0x0ef1fab9 /* ID for Apple "fat" binary */
139 #define IPXE_DISCOVER_NAME L"\\efi\\tools\\ipxe_discover.efi"
140 #define IPXE_NAME L"\\efi\\tools\\ipxe.efi"
142 // Patterns that identify Linux kernels. Added to the loader match pattern when the
143 // scan_all_linux_kernels option is set in the configuration file. Causes kernels WITHOUT
144 // a ".efi" extension to be found when scanning for boot loaders.
145 #define LINUX_MATCH_PATTERNS L"vmlinuz*,bzImage*,kernel*"
147 // Maximum length of a text string in certain menus
148 #define MAX_LINE_LENGTH 65
150 static REFIT_MENU_ENTRY MenuEntryAbout
= { L
"About rEFInd", TAG_ABOUT
, 1, 0, 'A', NULL
, NULL
, NULL
};
151 static REFIT_MENU_ENTRY MenuEntryReset
= { L
"Reboot Computer", TAG_REBOOT
, 1, 0, 'R', NULL
, NULL
, NULL
};
152 static REFIT_MENU_ENTRY MenuEntryShutdown
= { L
"Shut Down Computer", TAG_SHUTDOWN
, 1, 0, 'U', NULL
, NULL
, NULL
};
153 REFIT_MENU_ENTRY MenuEntryReturn
= { L
"Return to Main Menu", TAG_RETURN
, 1, 0, 0, NULL
, NULL
, NULL
};
154 static REFIT_MENU_ENTRY MenuEntryExit
= { L
"Exit rEFInd", TAG_EXIT
, 1, 0, 0, NULL
, NULL
, NULL
};
155 static REFIT_MENU_ENTRY MenuEntryFirmware
= { L
"Reboot to Computer Setup Utility", TAG_FIRMWARE
, 1, 0, 0, NULL
, NULL
, NULL
};
156 static REFIT_MENU_ENTRY MenuEntryRotateCsr
= { L
"Change SIP Policy", TAG_CSR_ROTATE
, 1, 0, 0, NULL
, NULL
, NULL
};
158 REFIT_MENU_SCREEN MainMenu
= { L
"Main Menu", NULL
, 0, NULL
, 0, NULL
, 0, L
"Automatic boot",
159 L
"Use arrow keys to move cursor; Enter to boot;",
160 L
"Insert, Tab, or F2 for more options; Esc or Backspace to refresh" };
161 static REFIT_MENU_SCREEN AboutMenu
= { L
"About", NULL
, 0, NULL
, 0, NULL
, 0, NULL
, L
"Press Enter to return to main menu", L
"" };
163 REFIT_CONFIG GlobalConfig
= { FALSE
, TRUE
, FALSE
, FALSE
, TRUE
, FALSE
, 0, 0, 0, DONT_CHANGE_TEXT_MODE
,
164 20, 0, 0, GRAPHICS_FOR_OSX
, LEGACY_TYPE_MAC
,
165 0, 0, { DEFAULT_BIG_ICON_SIZE
/ 4, DEFAULT_SMALL_ICON_SIZE
, DEFAULT_BIG_ICON_SIZE
},
166 BANNER_NOSCALE
, NULL
, NULL
, NULL
, NULL
, CONFIG_FILE_NAME
, NULL
, NULL
, NULL
, NULL
,
167 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
168 { TAG_SHELL
, TAG_MEMTEST
, TAG_GDISK
, TAG_APPLE_RECOVERY
, TAG_WINDOWS_RECOVERY
,
169 TAG_MOK_TOOL
, TAG_ABOUT
, TAG_SHUTDOWN
, TAG_REBOOT
, TAG_FIRMWARE
, TAG_FWUPDATE_TOOL
,
170 0, 0, 0, 0, 0, 0, 0, 0 }
173 EFI_GUID GlobalGuid
= EFI_GLOBAL_VARIABLE
;
174 EFI_GUID RefindGuid
= REFIND_GUID_VALUE
;
176 GPT_DATA
*gPartitions
= NULL
;
178 // Structure used to hold boot loader filenames and time stamps in
179 // a linked list; used to sort entries within a directory.
183 struct LOADER_LIST
*NextEntry
;
190 static VOID
AboutrEFInd(VOID
)
192 CHAR16
*FirmwareVendor
;
195 if (AboutMenu
.EntryCount
== 0) {
196 AboutMenu
.TitleImage
= BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT
);
197 AddMenuInfoLine(&AboutMenu
, PoolPrint(L
"rEFInd Version %s", REFIND_VERSION
));
198 AddMenuInfoLine(&AboutMenu
, L
"");
199 AddMenuInfoLine(&AboutMenu
, L
"Copyright (c) 2006-2010 Christoph Pfisterer");
200 AddMenuInfoLine(&AboutMenu
, L
"Copyright (c) 2012-2016 Roderick W. Smith");
201 AddMenuInfoLine(&AboutMenu
, L
"Portions Copyright (c) Intel Corporation and others");
202 AddMenuInfoLine(&AboutMenu
, L
"Distributed under the terms of the GNU GPLv3 license");
203 AddMenuInfoLine(&AboutMenu
, L
"");
204 AddMenuInfoLine(&AboutMenu
, L
"Running on:");
205 AddMenuInfoLine(&AboutMenu
, PoolPrint(L
" EFI Revision %d.%02d", ST
->Hdr
.Revision
>> 16, ST
->Hdr
.Revision
& ((1 << 16) - 1)));
207 AddMenuInfoLine(&AboutMenu
, PoolPrint(L
" Platform: x86 (32 bit); Secure Boot %s",
208 secure_mode() ? L
"active" : L
"inactive"));
209 #elif defined(EFIX64)
210 AddMenuInfoLine(&AboutMenu
, PoolPrint(L
" Platform: x86_64 (64 bit); Secure Boot %s",
211 secure_mode() ? L
"active" : L
"inactive"));
212 #elif defined(EFIAARCH64)
213 AddMenuInfoLine(&AboutMenu
, PoolPrint(L
" Platform: ARM (64 bit); Secure Boot %s",
214 secure_mode() ? L
"active" : L
"inactive"));
216 AddMenuInfoLine(&AboutMenu
, L
" Platform: unknown");
218 if (GetCsrStatus(&CsrStatus
) == EFI_SUCCESS
) {
219 RecordgCsrStatus(CsrStatus
, FALSE
);
220 AddMenuInfoLine(&AboutMenu
, gCsrStatus
);
222 FirmwareVendor
= StrDuplicate(ST
->FirmwareVendor
);
223 LimitStringLength(FirmwareVendor
, MAX_LINE_LENGTH
); // More than ~65 causes empty info page on 800x600 display
224 AddMenuInfoLine(&AboutMenu
, PoolPrint(L
" Firmware: %s %d.%02d", FirmwareVendor
, ST
->FirmwareRevision
>> 16,
225 ST
->FirmwareRevision
& ((1 << 16) - 1)));
226 AddMenuInfoLine(&AboutMenu
, PoolPrint(L
" Screen Output: %s", egScreenDescription()));
227 AddMenuInfoLine(&AboutMenu
, L
"");
228 #if defined(__MAKEWITH_GNUEFI)
229 AddMenuInfoLine(&AboutMenu
, L
"Built with GNU-EFI");
231 AddMenuInfoLine(&AboutMenu
, L
"Built with TianoCore EDK2");
233 AddMenuInfoLine(&AboutMenu
, L
"");
234 AddMenuInfoLine(&AboutMenu
, L
"For more information, see the rEFInd Web site:");
235 AddMenuInfoLine(&AboutMenu
, L
"http://www.rodsbooks.com/refind/");
236 AddMenuEntry(&AboutMenu
, &MenuEntryReturn
);
239 RunMenu(&AboutMenu
, NULL
);
240 } /* VOID AboutrEFInd() */
242 static VOID
WarnSecureBootError(CHAR16
*Name
, BOOLEAN Verbose
) {
244 Name
= L
"the loader";
246 refit_call2_wrapper(ST
->ConOut
->SetAttribute
, ST
->ConOut
, ATTR_ERROR
);
247 Print(L
"Secure Boot validation failure loading %s!\n", Name
);
248 refit_call2_wrapper(ST
->ConOut
->SetAttribute
, ST
->ConOut
, ATTR_BASIC
);
249 if (Verbose
&& secure_mode()) {
250 Print(L
"\nThis computer is configured with Secure Boot active, but\n%s has failed validation.\n", Name
);
251 Print(L
"\nYou can:\n * Launch another boot loader\n");
252 Print(L
" * Disable Secure Boot in your firmware\n");
253 Print(L
" * Sign %s with a machine owner key (MOK)\n", Name
);
254 Print(L
" * Use a MOK utility (often present on the second row) to add a MOK with which\n");
255 Print(L
" %s has already been signed.\n", Name
);
256 Print(L
" * Use a MOK utility to register %s (\"enroll its hash\") without\n", Name
);
257 Print(L
" signing it.\n");
258 Print(L
"\nSee http://www.rodsbooks.com/refind/secureboot.html for more information\n");
261 } // VOID WarnSecureBootError()
263 // Returns TRUE if this file is a valid EFI loader file, and is proper ARCH
264 static BOOLEAN
IsValidLoader(EFI_FILE
*RootDir
, CHAR16
*FileName
) {
265 BOOLEAN IsValid
= TRUE
;
266 #if defined (EFIX64) | defined (EFI32) | defined (EFIAARCH64)
268 EFI_FILE_HANDLE FileHandle
;
270 UINTN Size
= sizeof(Header
);
272 if ((RootDir
== NULL
) || (FileName
== NULL
)) {
273 // Assume valid here, because Macs produce NULL RootDir (& maybe FileName)
274 // when launching from a Firewire drive. This should be handled better, but
275 // fix would have to be in StartEFIImageList() and/or in FindVolumeAndFilename().
279 Status
= refit_call5_wrapper(RootDir
->Open
, RootDir
, &FileHandle
, FileName
, EFI_FILE_MODE_READ
, 0);
280 if (EFI_ERROR(Status
))
283 Status
= refit_call3_wrapper(FileHandle
->Read
, FileHandle
, &Size
, Header
);
284 refit_call1_wrapper(FileHandle
->Close
, FileHandle
);
286 IsValid
= !EFI_ERROR(Status
) &&
287 Size
== sizeof(Header
) &&
288 ((Header
[0] == 'M' && Header
[1] == 'Z' &&
289 (Size
= *(UINT32
*)&Header
[0x3c]) < 0x180 &&
290 Header
[Size
] == 'P' && Header
[Size
+1] == 'E' &&
291 Header
[Size
+2] == 0 && Header
[Size
+3] == 0 &&
292 *(UINT16
*)&Header
[Size
+4] == EFI_STUB_ARCH
) ||
293 (*(UINT32
*)&Header
== FAT_ARCH
));
296 } // BOOLEAN IsValidLoader()
298 // Launch an EFI binary.
299 EFI_STATUS
StartEFIImageList(IN EFI_DEVICE_PATH
**DevicePaths
,
300 IN CHAR16
*LoadOptions
, IN UINTN LoaderType
,
301 IN CHAR16
*ImageTitle
, IN CHAR8 OSType
,
302 OUT UINTN
*ErrorInStep
,
306 EFI_STATUS Status
, ReturnStatus
;
307 EFI_HANDLE ChildImageHandle
, ChildImageHandle2
;
308 EFI_LOADED_IMAGE
*ChildLoadedImage
= NULL
;
309 REFIT_VOLUME
*Volume
= NULL
;
310 UINTN DevicePathIndex
;
311 CHAR16 ErrorInfo
[256];
312 CHAR16
*FullLoadOptions
= NULL
;
313 CHAR16
*Filename
= NULL
;
316 if (ErrorInStep
!= NULL
)
320 if (LoadOptions
!= NULL
) {
321 FullLoadOptions
= StrDuplicate(LoadOptions
);
322 if ((LoaderType
== TYPE_EFI
) && (OSType
== 'M')) {
323 MergeStrings(&FullLoadOptions
, L
" ", 0);
324 // NOTE: That last space is also added by the EFI shell and seems to be significant
325 // when passing options to Apple's boot.efi...
327 } // if (LoadOptions != NULL)
329 Print(L
"Starting %s\nUsing load options '%s'\n", ImageTitle
, FullLoadOptions
? FullLoadOptions
: L
"");
331 // load the image into memory
332 ReturnStatus
= Status
= EFI_NOT_FOUND
; // in case the list is empty
333 for (DevicePathIndex
= 0; DevicePaths
[DevicePathIndex
] != NULL
; DevicePathIndex
++) {
334 FindVolumeAndFilename(DevicePaths
[DevicePathIndex
], &Volume
, &Filename
);
335 // Some EFIs crash if attempting to load driver for invalid architecture, so
336 // protect for this condition; but sometimes Volume comes back NULL, so provide
337 // an exception. (TODO: Handle this special condition better.)
338 if ((LoaderType
== TYPE_LEGACY
) || (Volume
== NULL
) || IsValidLoader(Volume
->RootDir
, Filename
)) {
339 if (Filename
&& (LoaderType
!= TYPE_LEGACY
)) {
340 Temp
= PoolPrint(L
"\\%s %s", Filename
, FullLoadOptions
? FullLoadOptions
: L
"");
342 MyFreePool(FullLoadOptions
);
343 FullLoadOptions
= Temp
;
347 // NOTE: Below commented-out line could be more efficient if file were read ahead of
348 // time and passed as a pre-loaded image to LoadImage(), but it doesn't work on my
349 // 32-bit Mac Mini or my 64-bit Intel box when launching a Linux kernel; the
350 // kernel returns a "Failed to handle fs_proto" error message.
351 // TODO: Track down the cause of this error and fix it, if possible.
352 // ReturnStatus = Status = refit_call6_wrapper(BS->LoadImage, FALSE, SelfImageHandle, DevicePaths[DevicePathIndex],
353 // ImageData, ImageSize, &ChildImageHandle);
354 ReturnStatus
= Status
= refit_call6_wrapper(BS
->LoadImage
, FALSE
, SelfImageHandle
, DevicePaths
[DevicePathIndex
],
355 NULL
, 0, &ChildImageHandle
);
356 if (secure_mode() && ShimLoaded()) {
357 // Load ourself into memory. This is a trick to work around a bug in Shim 0.8,
358 // which ties itself into the BS->LoadImage() and BS->StartImage() functions and
359 // then unregisters itself from the EFI system table when its replacement
360 // StartImage() function is called *IF* the previous LoadImage() was for the same
361 // program. The result is that rEFInd can validate only the first program it
362 // launches (often a filesystem driver). Loading a second program (rEFInd itself,
363 // here, to keep it smaller than a kernel) works around this problem. See the
364 // replacements.c file in Shim, and especially its start_image() function, for
365 // the source of the problem.
366 // NOTE: This doesn't check the return status or handle errors. It could
367 // conceivably do weird things if, say, rEFInd were on a USB drive that the
368 // user pulls before launching a program.
369 refit_call6_wrapper(BS
->LoadImage
, FALSE
, SelfImageHandle
, GlobalConfig
.SelfDevicePath
,
370 NULL
, 0, &ChildImageHandle2
);
373 Print(L
"Invalid loader file!\n");
374 ReturnStatus
= EFI_LOAD_ERROR
;
376 if (ReturnStatus
!= EFI_NOT_FOUND
) {
380 if ((Status
== EFI_ACCESS_DENIED
) || (Status
== EFI_SECURITY_VIOLATION
)) {
381 WarnSecureBootError(ImageTitle
, Verbose
);
384 SPrint(ErrorInfo
, 255, L
"while loading %s", ImageTitle
);
385 if (CheckError(Status
, ErrorInfo
)) {
386 if (ErrorInStep
!= NULL
)
391 ReturnStatus
= Status
= refit_call3_wrapper(BS
->HandleProtocol
, ChildImageHandle
, &LoadedImageProtocol
,
392 (VOID
**) &ChildLoadedImage
);
393 if (CheckError(Status
, L
"while getting a LoadedImageProtocol handle")) {
394 if (ErrorInStep
!= NULL
)
398 ChildLoadedImage
->LoadOptions
= (VOID
*)FullLoadOptions
;
399 ChildLoadedImage
->LoadOptionsSize
= FullLoadOptions
? ((UINT32
)StrLen(FullLoadOptions
) + 1) * sizeof(CHAR16
) : 0;
400 // turn control over to the image
401 // TODO: (optionally) re-enable the EFI watchdog timer!
403 // close open file handles
405 ReturnStatus
= Status
= refit_call3_wrapper(BS
->StartImage
, ChildImageHandle
, NULL
, NULL
);
407 // control returns here when the child image calls Exit()
408 SPrint(ErrorInfo
, 255, L
"returned from %s", ImageTitle
);
409 if (CheckError(Status
, ErrorInfo
)) {
410 if (ErrorInStep
!= NULL
)
414 // Below should have no effect on most systems, but works
415 // around bug with some EFIs that prevents filesystem drivers
416 // from binding to partitions.
417 ConnectFilesystemDriver(ChildImageHandle
);
420 // re-open file handles
424 // unload the image, we don't care if it works or not...
426 Status
= refit_call1_wrapper(BS
->UnloadImage
, ChildImageHandle
);
429 MyFreePool(FullLoadOptions
);
431 } /* EFI_STATUS StartEFIImageList() */
433 EFI_STATUS
StartEFIImage(IN EFI_DEVICE_PATH
*DevicePath
,
434 IN CHAR16
*LoadOptions
, IN UINTN LoaderType
,
435 IN CHAR16
*ImageTitle
, IN CHAR8 OSType
,
436 OUT UINTN
*ErrorInStep
,
440 EFI_DEVICE_PATH
*DevicePaths
[2];
442 DevicePaths
[0] = DevicePath
;
443 DevicePaths
[1] = NULL
;
444 return StartEFIImageList(DevicePaths
, LoadOptions
, LoaderType
, ImageTitle
, OSType
, ErrorInStep
, Verbose
, IsDriver
);
445 } /* static EFI_STATUS StartEFIImage() */
447 // From gummiboot: Reboot the computer into its built-in user interface
448 static EFI_STATUS
RebootIntoFirmware(VOID
) {
454 osind
= EFI_OS_INDICATIONS_BOOT_TO_FW_UI
;
456 err
= EfivarGetRaw(&GlobalGuid
, L
"OsIndications", &b
, &size
);
457 if (err
== EFI_SUCCESS
)
461 err
= EfivarSetRaw(&GlobalGuid
, L
"OsIndications", (CHAR8
*)&osind
, sizeof(UINT64
), TRUE
);
462 if (err
!= EFI_SUCCESS
)
465 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);
466 Print(L
"Error calling ResetSystem: %r", err
);
471 // Record the value of the loader's name/description in rEFInd's "PreviousBoot" EFI variable,
472 // if it's different from what's already stored there.
473 VOID
StoreLoaderName(IN CHAR16
*Name
) {
475 CHAR16
*OldName
= NULL
;
479 Status
= EfivarGetRaw(&RefindGuid
, L
"PreviousBoot", (CHAR8
**) &OldName
, &Length
);
480 if ((Status
!= EFI_SUCCESS
) || (StrCmp(OldName
, Name
) != 0)) {
481 EfivarSetRaw(&RefindGuid
, L
"PreviousBoot", (CHAR8
*) Name
, StrLen(Name
) * 2 + 2, TRUE
);
485 } // VOID StoreLoaderName()
488 // EFI OS loader functions
491 // See http://www.thomas-krenn.com/en/wiki/Activating_the_Intel_VT_Virtualization_Feature
492 // for information on Intel VMX features
493 static VOID
DoEnableAndLockVMX(VOID
)
495 #if defined (EFIX64) | defined (EFI32)
497 UINT32 low_bits
= 0, high_bits
= 0;
500 __asm__
volatile ("rdmsr" : "=a" (low_bits
), "=d" (high_bits
) : "c" (msr
));
502 // enable and lock vmx if not locked
503 if ((low_bits
& 1) == 0) {
507 __asm__
volatile ("wrmsr" : : "c" (msr
), "a" (low_bits
), "d" (high_bits
));
510 } // VOID DoEnableAndLockVMX()
512 static VOID
StartLoader(LOADER_ENTRY
*Entry
, CHAR16
*SelectionName
)
514 UINTN ErrorInStep
= 0;
516 if (GlobalConfig
.EnableAndLockVMX
) {
517 DoEnableAndLockVMX();
520 BeginExternalScreen(Entry
->UseGraphicsMode
, L
"Booting OS");
521 StoreLoaderName(SelectionName
);
522 StartEFIImage(Entry
->DevicePath
, Entry
->LoadOptions
, TYPE_EFI
,
523 Basename(Entry
->LoaderPath
), Entry
->OSType
, &ErrorInStep
, !Entry
->UseGraphicsMode
, FALSE
);
524 FinishExternalScreen();
527 // Locate an initrd or initramfs file that matches the kernel specified by LoaderPath.
528 // The matching file has a name that begins with "init" and includes the same version
529 // number string as is found in LoaderPath -- but not a longer version number string.
530 // For instance, if LoaderPath is \EFI\kernels\bzImage-3.3.0.efi, and if \EFI\kernels
531 // has a file called initramfs-3.3.0.img, this function will return the string
532 // '\EFI\kernels\initramfs-3.3.0.img'. If the directory ALSO contains the file
533 // initramfs-3.3.0-rc7.img or initramfs-13.3.0.img, those files will NOT match.
534 // If more than one initrd file matches the extracted version string, the one
535 // that matches more characters AFTER (actually, from the start of) the version
537 // If no matching init file can be found, returns NULL.
538 static CHAR16
* FindInitrd(IN CHAR16
*LoaderPath
, IN REFIT_VOLUME
*Volume
) {
539 CHAR16
*InitrdName
= NULL
, *FileName
, *KernelVersion
, *InitrdVersion
, *Path
;
540 CHAR16
*KernelPostNum
, *InitrdPostNum
;
541 UINTN MaxSharedChars
, SharedChars
;
542 STRING_LIST
*InitrdNames
= NULL
, *FinalInitrdName
= NULL
, *CurrentInitrdName
= NULL
, *MaxSharedInitrd
;
543 REFIT_DIR_ITER DirIter
;
544 EFI_FILE_INFO
*DirEntry
;
546 FileName
= Basename(LoaderPath
);
547 KernelVersion
= FindNumbers(FileName
);
548 Path
= FindPath(LoaderPath
);
550 // Add trailing backslash for root directory; necessary on some systems, but must
551 // NOT be added to all directories, since on other systems, a trailing backslash on
552 // anything but the root directory causes them to flake out!
553 if (StrLen(Path
) == 0) {
554 MergeStrings(&Path
, L
"\\", 0);
556 DirIterOpen(Volume
->RootDir
, Path
, &DirIter
);
557 // Now add a trailing backslash if it was NOT added earlier, for consistency in
558 // building the InitrdName later....
559 if ((StrLen(Path
) > 0) && (Path
[StrLen(Path
) - 1] != L
'\\'))
560 MergeStrings(&Path
, L
"\\", 0);
561 while (DirIterNext(&DirIter
, 2, L
"init*", &DirEntry
)) {
562 InitrdVersion
= FindNumbers(DirEntry
->FileName
);
563 if (((KernelVersion
!= NULL
) && (MyStriCmp(InitrdVersion
, KernelVersion
))) ||
564 ((KernelVersion
== NULL
) && (InitrdVersion
== NULL
))) {
565 CurrentInitrdName
= AllocateZeroPool(sizeof(STRING_LIST
));
566 if (InitrdNames
== NULL
)
567 InitrdNames
= FinalInitrdName
= CurrentInitrdName
;
568 if (CurrentInitrdName
) {
569 CurrentInitrdName
->Value
= PoolPrint(L
"%s%s", Path
, DirEntry
->FileName
);
570 if (CurrentInitrdName
!= FinalInitrdName
) {
571 FinalInitrdName
->Next
= CurrentInitrdName
;
572 FinalInitrdName
= CurrentInitrdName
;
578 if (InitrdNames
->Next
== NULL
) {
579 InitrdName
= StrDuplicate(InitrdNames
-> Value
);
581 MaxSharedInitrd
= CurrentInitrdName
= InitrdNames
;
583 while (CurrentInitrdName
!= NULL
) {
584 KernelPostNum
= MyStrStr(LoaderPath
, KernelVersion
);
585 InitrdPostNum
= MyStrStr(CurrentInitrdName
->Value
, KernelVersion
);
586 SharedChars
= NumCharsInCommon(KernelPostNum
, InitrdPostNum
);
587 if (SharedChars
> MaxSharedChars
&& SharedChars
> 0) {
588 MaxSharedChars
= SharedChars
;
589 MaxSharedInitrd
= CurrentInitrdName
;
591 // TODO: Compute number of shared characters & compare with max.
592 CurrentInitrdName
= CurrentInitrdName
->Next
;
595 InitrdName
= StrDuplicate(MaxSharedInitrd
->Value
);
598 DeleteStringList(InitrdNames
);
600 // Note: Don't FreePool(FileName), since Basename returns a pointer WITHIN the string it's passed.
601 MyFreePool(KernelVersion
);
604 } // static CHAR16 * FindInitrd()
606 LOADER_ENTRY
* AddPreparedLoaderEntry(LOADER_ENTRY
*Entry
) {
607 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
610 } // LOADER_ENTRY * AddPreparedLoaderEntry()
612 // Creates a copy of a menu screen.
613 // Returns a pointer to the copy of the menu screen.
614 static REFIT_MENU_SCREEN
* CopyMenuScreen(REFIT_MENU_SCREEN
*Entry
) {
615 REFIT_MENU_SCREEN
*NewEntry
;
618 NewEntry
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
619 if ((Entry
!= NULL
) && (NewEntry
!= NULL
)) {
620 CopyMem(NewEntry
, Entry
, sizeof(REFIT_MENU_SCREEN
));
621 NewEntry
->Title
= (Entry
->Title
) ? StrDuplicate(Entry
->Title
) : NULL
;
622 NewEntry
->TimeoutText
= (Entry
->TimeoutText
) ? StrDuplicate(Entry
->TimeoutText
) : NULL
;
623 if (Entry
->TitleImage
!= NULL
) {
624 NewEntry
->TitleImage
= AllocatePool(sizeof(EG_IMAGE
));
625 if (NewEntry
->TitleImage
!= NULL
)
626 CopyMem(NewEntry
->TitleImage
, Entry
->TitleImage
, sizeof(EG_IMAGE
));
628 NewEntry
->InfoLines
= (CHAR16
**) AllocateZeroPool(Entry
->InfoLineCount
* (sizeof(CHAR16
*)));
629 for (i
= 0; i
< Entry
->InfoLineCount
&& NewEntry
->InfoLines
; i
++) {
630 NewEntry
->InfoLines
[i
] = (Entry
->InfoLines
[i
]) ? StrDuplicate(Entry
->InfoLines
[i
]) : NULL
;
632 NewEntry
->Entries
= (REFIT_MENU_ENTRY
**) AllocateZeroPool(Entry
->EntryCount
* (sizeof (REFIT_MENU_ENTRY
*)));
633 for (i
= 0; i
< Entry
->EntryCount
&& NewEntry
->Entries
; i
++) {
634 AddMenuEntry(NewEntry
, Entry
->Entries
[i
]);
636 NewEntry
->Hint1
= (Entry
->Hint1
) ? StrDuplicate(Entry
->Hint1
) : NULL
;
637 NewEntry
->Hint2
= (Entry
->Hint2
) ? StrDuplicate(Entry
->Hint2
) : NULL
;
640 } // static REFIT_MENU_SCREEN* CopyMenuScreen()
642 // Creates a copy of a menu entry. Intended to enable moving a stack-based
643 // menu entry (such as the ones for the "reboot" and "exit" functions) to
644 // to the heap. This enables easier deletion of the whole set of menu
645 // entries when re-scanning.
646 // Returns a pointer to the copy of the menu entry.
647 static REFIT_MENU_ENTRY
* CopyMenuEntry(REFIT_MENU_ENTRY
*Entry
) {
648 REFIT_MENU_ENTRY
*NewEntry
;
650 NewEntry
= AllocateZeroPool(sizeof(REFIT_MENU_ENTRY
));
651 if ((Entry
!= NULL
) && (NewEntry
!= NULL
)) {
652 CopyMem(NewEntry
, Entry
, sizeof(REFIT_MENU_ENTRY
));
653 NewEntry
->Title
= (Entry
->Title
) ? StrDuplicate(Entry
->Title
) : NULL
;
654 if (Entry
->BadgeImage
!= NULL
) {
655 NewEntry
->BadgeImage
= AllocatePool(sizeof(EG_IMAGE
));
656 if (NewEntry
->BadgeImage
!= NULL
)
657 CopyMem(NewEntry
->BadgeImage
, Entry
->BadgeImage
, sizeof(EG_IMAGE
));
659 if (Entry
->Image
!= NULL
) {
660 NewEntry
->Image
= AllocatePool(sizeof(EG_IMAGE
));
661 if (NewEntry
->Image
!= NULL
)
662 CopyMem(NewEntry
->Image
, Entry
->Image
, sizeof(EG_IMAGE
));
664 if (Entry
->SubScreen
!= NULL
) {
665 NewEntry
->SubScreen
= CopyMenuScreen(Entry
->SubScreen
);
669 } // REFIT_MENU_ENTRY* CopyMenuEntry()
671 // Creates a new LOADER_ENTRY data structure and populates it with
672 // default values from the specified Entry, or NULL values if Entry
673 // is unspecified (NULL).
674 // Returns a pointer to the new data structure, or NULL if it
675 // couldn't be allocated
676 LOADER_ENTRY
*InitializeLoaderEntry(IN LOADER_ENTRY
*Entry
) {
677 LOADER_ENTRY
*NewEntry
= NULL
;
679 NewEntry
= AllocateZeroPool(sizeof(LOADER_ENTRY
));
680 if (NewEntry
!= NULL
) {
681 NewEntry
->me
.Title
= NULL
;
682 NewEntry
->me
.Tag
= TAG_LOADER
;
683 NewEntry
->Enabled
= TRUE
;
684 NewEntry
->UseGraphicsMode
= FALSE
;
685 NewEntry
->OSType
= 0;
687 NewEntry
->LoaderPath
= (Entry
->LoaderPath
) ? StrDuplicate(Entry
->LoaderPath
) : NULL
;
688 NewEntry
->VolName
= (Entry
->VolName
) ? StrDuplicate(Entry
->VolName
) : NULL
;
689 NewEntry
->DevicePath
= Entry
->DevicePath
;
690 NewEntry
->UseGraphicsMode
= Entry
->UseGraphicsMode
;
691 NewEntry
->LoadOptions
= (Entry
->LoadOptions
) ? StrDuplicate(Entry
->LoadOptions
) : NULL
;
692 NewEntry
->InitrdPath
= (Entry
->InitrdPath
) ? StrDuplicate(Entry
->InitrdPath
) : NULL
;
696 } // LOADER_ENTRY *InitializeLoaderEntry()
698 // Adds InitrdPath to Options, but only if Options doesn't already include an
699 // initrd= line. Done to enable overriding the default initrd selection in a
700 // refind_linux.conf file's options list.
701 // Returns a pointer to a new string. The calling function is responsible for
702 // freeing its memory.
703 static CHAR16
*AddInitrdToOptions(CHAR16
*Options
, CHAR16
*InitrdPath
) {
704 CHAR16
*NewOptions
= NULL
;
707 NewOptions
= StrDuplicate(Options
);
708 if ((InitrdPath
!= NULL
) && !StriSubCmp(L
"initrd=", Options
)) {
709 MergeStrings(&NewOptions
, L
"initrd=", L
' ');
710 MergeStrings(&NewOptions
, InitrdPath
, 0);
713 } // CHAR16 *AddInitrdToOptions()
715 // Prepare a REFIT_MENU_SCREEN data structure for a subscreen entry. This sets up
716 // the default entry that launches the boot loader using the same options as the
717 // main Entry does. Subsequent options can be added by the calling function.
718 // If a subscreen already exists in the Entry that's passed to this function,
719 // it's left unchanged and a pointer to it is returned.
720 // Returns a pointer to the new subscreen data structure, or NULL if there
721 // were problems allocating memory.
722 REFIT_MENU_SCREEN
*InitializeSubScreen(IN LOADER_ENTRY
*Entry
) {
723 CHAR16
*FileName
, *MainOptions
= NULL
;
724 REFIT_MENU_SCREEN
*SubScreen
= NULL
;
725 LOADER_ENTRY
*SubEntry
;
727 FileName
= Basename(Entry
->LoaderPath
);
728 if (Entry
->me
.SubScreen
== NULL
) { // No subscreen yet; initialize default entry....
729 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
730 if (SubScreen
!= NULL
) {
731 SubScreen
->Title
= AllocateZeroPool(sizeof(CHAR16
) * 256);
732 SPrint(SubScreen
->Title
, 255, L
"Boot Options for %s on %s",
733 (Entry
->Title
!= NULL
) ? Entry
->Title
: FileName
, Entry
->VolName
);
734 SubScreen
->TitleImage
= Entry
->me
.Image
;
736 SubEntry
= InitializeLoaderEntry(Entry
);
737 if (SubEntry
!= NULL
) {
738 SubEntry
->me
.Title
= StrDuplicate(L
"Boot using default options");
739 MainOptions
= SubEntry
->LoadOptions
;
740 SubEntry
->LoadOptions
= AddInitrdToOptions(MainOptions
, SubEntry
->InitrdPath
);
741 MyFreePool(MainOptions
);
742 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
743 } // if (SubEntry != NULL)
744 SubScreen
->Hint1
= StrDuplicate(SUBSCREEN_HINT1
);
745 if (GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_EDITOR
) {
746 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2_NO_EDITOR
);
748 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2
);
750 } // if (SubScreen != NULL)
751 } else { // existing subscreen; less initialization, and just add new entry later....
752 SubScreen
= Entry
->me
.SubScreen
;
755 } // REFIT_MENU_SCREEN *InitializeSubScreen()
757 VOID
GenerateSubScreen(LOADER_ENTRY
*Entry
, IN REFIT_VOLUME
*Volume
, IN BOOLEAN GenerateReturn
) {
758 REFIT_MENU_SCREEN
*SubScreen
;
759 LOADER_ENTRY
*SubEntry
;
760 CHAR16
*InitrdName
, *KernelVersion
= NULL
;
761 CHAR16 DiagsFileName
[256];
766 // create the submenu
767 if (StrLen(Entry
->Title
) == 0) {
768 MyFreePool(Entry
->Title
);
771 SubScreen
= InitializeSubScreen(Entry
);
773 // loader-specific submenu entries
774 if (Entry
->OSType
== 'M') { // entries for Mac OS X
776 SubEntry
= InitializeLoaderEntry(Entry
);
777 if (SubEntry
!= NULL
) {
778 SubEntry
->me
.Title
= L
"Boot Mac OS X with a 64-bit kernel";
779 SubEntry
->LoadOptions
= L
"arch=x86_64";
780 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
781 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
784 SubEntry
= InitializeLoaderEntry(Entry
);
785 if (SubEntry
!= NULL
) {
786 SubEntry
->me
.Title
= L
"Boot Mac OS X with a 32-bit kernel";
787 SubEntry
->LoadOptions
= L
"arch=i386";
788 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
789 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
793 if (!(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_SINGLEUSER
)) {
794 SubEntry
= InitializeLoaderEntry(Entry
);
795 if (SubEntry
!= NULL
) {
796 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode";
797 SubEntry
->UseGraphicsMode
= FALSE
;
798 SubEntry
->LoadOptions
= L
"-v";
799 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
803 SubEntry
= InitializeLoaderEntry(Entry
);
804 if (SubEntry
!= NULL
) {
805 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode (64-bit)";
806 SubEntry
->UseGraphicsMode
= FALSE
;
807 SubEntry
->LoadOptions
= L
"-v arch=x86_64";
808 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
811 SubEntry
= InitializeLoaderEntry(Entry
);
812 if (SubEntry
!= NULL
) {
813 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode (32-bit)";
814 SubEntry
->UseGraphicsMode
= FALSE
;
815 SubEntry
->LoadOptions
= L
"-v arch=i386";
816 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
820 SubEntry
= InitializeLoaderEntry(Entry
);
821 if (SubEntry
!= NULL
) {
822 SubEntry
->me
.Title
= L
"Boot Mac OS X in single user mode";
823 SubEntry
->UseGraphicsMode
= FALSE
;
824 SubEntry
->LoadOptions
= L
"-v -s";
825 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
827 } // single-user mode allowed
829 if (!(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_SAFEMODE
)) {
830 SubEntry
= InitializeLoaderEntry(Entry
);
831 if (SubEntry
!= NULL
) {
832 SubEntry
->me
.Title
= L
"Boot Mac OS X in safe mode";
833 SubEntry
->UseGraphicsMode
= FALSE
;
834 SubEntry
->LoadOptions
= L
"-v -x";
835 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
837 } // safe mode allowed
839 // check for Apple hardware diagnostics
840 StrCpy(DiagsFileName
, L
"System\\Library\\CoreServices\\.diagnostics\\diags.efi");
841 if (FileExists(Volume
->RootDir
, DiagsFileName
) && !(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_HWTEST
)) {
842 SubEntry
= InitializeLoaderEntry(Entry
);
843 if (SubEntry
!= NULL
) {
844 SubEntry
->me
.Title
= L
"Run Apple Hardware Test";
845 MyFreePool(SubEntry
->LoaderPath
);
846 SubEntry
->LoaderPath
= StrDuplicate(DiagsFileName
);
847 SubEntry
->DevicePath
= FileDevicePath(Volume
->DeviceHandle
, SubEntry
->LoaderPath
);
848 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
849 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
851 } // if diagnostics entry found
853 } else if (Entry
->OSType
== 'L') { // entries for Linux kernels with EFI stub loaders
854 File
= ReadLinuxOptionsFile(Entry
->LoaderPath
, Volume
);
856 InitrdName
= FindInitrd(Entry
->LoaderPath
, Volume
);
857 TokenCount
= ReadTokenLine(File
, &TokenList
);
858 KernelVersion
= FindNumbers(Entry
->LoaderPath
);
859 ReplaceSubstring(&(TokenList
[1]), KERNEL_VERSION
, KernelVersion
);
860 // first entry requires special processing, since it was initially set
861 // up with a default title but correct options by InitializeSubScreen(),
863 if ((TokenCount
> 1) && (SubScreen
->Entries
!= NULL
) && (SubScreen
->Entries
[0] != NULL
)) {
864 MyFreePool(SubScreen
->Entries
[0]->Title
);
865 SubScreen
->Entries
[0]->Title
= TokenList
[0] ? StrDuplicate(TokenList
[0]) : StrDuplicate(L
"Boot Linux");
867 FreeTokenLine(&TokenList
, &TokenCount
);
868 while ((TokenCount
= ReadTokenLine(File
, &TokenList
)) > 1) {
869 ReplaceSubstring(&(TokenList
[1]), KERNEL_VERSION
, KernelVersion
);
870 SubEntry
= InitializeLoaderEntry(Entry
);
871 SubEntry
->me
.Title
= TokenList
[0] ? StrDuplicate(TokenList
[0]) : StrDuplicate(L
"Boot Linux");
872 MyFreePool(SubEntry
->LoadOptions
);
873 SubEntry
->LoadOptions
= AddInitrdToOptions(TokenList
[1], InitrdName
);
874 FreeTokenLine(&TokenList
, &TokenCount
);
875 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_LINUX
;
876 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
878 MyFreePool(InitrdName
);
882 } else if (Entry
->OSType
== 'E') { // entries for ELILO
883 SubEntry
= InitializeLoaderEntry(Entry
);
884 if (SubEntry
!= NULL
) {
885 SubEntry
->me
.Title
= L
"Run ELILO in interactive mode";
886 SubEntry
->LoadOptions
= L
"-p";
887 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
888 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
891 SubEntry
= InitializeLoaderEntry(Entry
);
892 if (SubEntry
!= NULL
) {
893 SubEntry
->me
.Title
= L
"Boot Linux for a 17\" iMac or a 15\" MacBook Pro (*)";
894 SubEntry
->LoadOptions
= L
"-d 0 i17";
895 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
896 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
899 SubEntry
= InitializeLoaderEntry(Entry
);
900 if (SubEntry
!= NULL
) {
901 SubEntry
->me
.Title
= L
"Boot Linux for a 20\" iMac (*)";
902 SubEntry
->LoadOptions
= L
"-d 0 i20";
903 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
904 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
907 SubEntry
= InitializeLoaderEntry(Entry
);
908 if (SubEntry
!= NULL
) {
909 SubEntry
->me
.Title
= L
"Boot Linux for a Mac Mini (*)";
910 SubEntry
->LoadOptions
= L
"-d 0 mini";
911 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
912 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
915 AddMenuInfoLine(SubScreen
, L
"NOTE: This is an example. Entries");
916 AddMenuInfoLine(SubScreen
, L
"marked with (*) may not work.");
918 } else if (Entry
->OSType
== 'X') { // entries for xom.efi
919 // by default, skip the built-in selection and boot from hard disk only
920 Entry
->LoadOptions
= L
"-s -h";
922 SubEntry
= InitializeLoaderEntry(Entry
);
923 if (SubEntry
!= NULL
) {
924 SubEntry
->me
.Title
= L
"Boot Windows from Hard Disk";
925 SubEntry
->LoadOptions
= L
"-s -h";
926 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
927 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
930 SubEntry
= InitializeLoaderEntry(Entry
);
931 if (SubEntry
!= NULL
) {
932 SubEntry
->me
.Title
= L
"Boot Windows from CD-ROM";
933 SubEntry
->LoadOptions
= L
"-s -c";
934 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
935 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
938 SubEntry
= InitializeLoaderEntry(Entry
);
939 if (SubEntry
!= NULL
) {
940 SubEntry
->me
.Title
= L
"Run XOM in text mode";
941 SubEntry
->LoadOptions
= L
"-v";
942 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
943 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
945 } // entries for xom.efi
947 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
948 Entry
->me
.SubScreen
= SubScreen
;
949 MyFreePool(KernelVersion
);
950 } // VOID GenerateSubScreen()
952 // Returns options for a Linux kernel. Reads them from an options file in the
953 // kernel's directory; and if present, adds an initrd= option for an initial
954 // RAM disk file with the same version number as the kernel file.
955 static CHAR16
* GetMainLinuxOptions(IN CHAR16
* LoaderPath
, IN REFIT_VOLUME
*Volume
) {
956 CHAR16
*Options
= NULL
, *InitrdName
, *FullOptions
= NULL
, *KernelVersion
;
958 Options
= GetFirstOptionsFromFile(LoaderPath
, Volume
);
959 InitrdName
= FindInitrd(LoaderPath
, Volume
);
960 KernelVersion
= FindNumbers(InitrdName
);
961 ReplaceSubstring(&Options
, KERNEL_VERSION
, KernelVersion
);
962 FullOptions
= AddInitrdToOptions(Options
, InitrdName
);
965 MyFreePool(InitrdName
);
966 MyFreePool(KernelVersion
);
967 return (FullOptions
);
968 } // static CHAR16 * GetMainLinuxOptions()
970 // Read the specified file and add values of "ID", "NAME", or "DISTRIB_ID" tokens to
971 // OSIconName list. Intended for adding Linux distribution clues gleaned from
972 // /etc/lsb-release and /etc/os-release files.
973 static VOID
ParseReleaseFile(CHAR16
**OSIconName
, REFIT_VOLUME
*Volume
, CHAR16
*FileName
) {
977 UINTN TokenCount
= 0;
979 if ((Volume
== NULL
) || (FileName
== NULL
) || (OSIconName
== NULL
) || (*OSIconName
== NULL
))
982 if (FileExists(Volume
->RootDir
, FileName
) &&
983 (ReadFile(Volume
->RootDir
, FileName
, &File
, &FileSize
) == EFI_SUCCESS
)) {
985 TokenCount
= ReadTokenLine(&File
, &TokenList
);
986 if ((TokenCount
> 1) && (MyStriCmp(TokenList
[0], L
"ID") ||
987 MyStriCmp(TokenList
[0], L
"NAME") ||
988 MyStriCmp(TokenList
[0], L
"DISTRIB_ID"))) {
989 MergeWords(OSIconName
, TokenList
[1], L
',');
991 FreeTokenLine(&TokenList
, &TokenCount
);
992 } while (TokenCount
> 0);
993 MyFreePool(File
.Buffer
);
995 } // VOID ParseReleaseFile()
997 // Try to guess the name of the Linux distribution & add that name to
999 static VOID
GuessLinuxDistribution(CHAR16
**OSIconName
, REFIT_VOLUME
*Volume
, CHAR16
*LoaderPath
) {
1000 // If on Linux root fs, /etc/os-release or /etc/lsb-release file probably has clues....
1001 ParseReleaseFile(OSIconName
, Volume
, L
"etc\\lsb-release");
1002 ParseReleaseFile(OSIconName
, Volume
, L
"etc\\os-release");
1004 // Search for clues in the kernel's filename....
1005 if (StriSubCmp(L
".fc", LoaderPath
))
1006 MergeStrings(OSIconName
, L
"fedora", L
',');
1007 if (StriSubCmp(L
".el", LoaderPath
))
1008 MergeStrings(OSIconName
, L
"redhat", L
',');
1009 } // VOID GuessLinuxDistribution()
1011 // Sets a few defaults for a loader entry -- mainly the icon, but also the OS type
1012 // code and shortcut letter. For Linux EFI stub loaders, also sets kernel options
1013 // that will (with luck) work fairly automatically.
1014 VOID
SetLoaderDefaults(LOADER_ENTRY
*Entry
, CHAR16
*LoaderPath
, REFIT_VOLUME
*Volume
) {
1015 CHAR16
*NameClues
, *PathOnly
, *NoExtension
, *OSIconName
= NULL
, *Temp
;
1016 CHAR16 ShortcutLetter
= 0;
1018 NameClues
= Basename(LoaderPath
);
1019 PathOnly
= FindPath(LoaderPath
);
1020 NoExtension
= StripEfiExtension(NameClues
);
1022 if (Volume
->DiskKind
== DISK_KIND_NET
) {
1023 MergeStrings(&NameClues
, Entry
->me
.Title
, L
' ');
1025 // locate a custom icon for the loader
1026 // Anything found here takes precedence over the "hints" in the OSIconName variable
1027 if (!Entry
->me
.Image
) {
1028 Entry
->me
.Image
= egLoadIconAnyType(Volume
->RootDir
, PathOnly
, NoExtension
, GlobalConfig
.IconSizes
[ICON_SIZE_BIG
]);
1030 if (!Entry
->me
.Image
) {
1031 Entry
->me
.Image
= egCopyImage(Volume
->VolIconImage
);
1034 // Begin creating icon "hints" by using last part of directory path leading
1036 Temp
= FindLastDirName(LoaderPath
);
1037 MergeStrings(&OSIconName
, Temp
, L
',');
1040 if (OSIconName
!= NULL
) {
1041 ShortcutLetter
= OSIconName
[0];
1044 // Add every "word" in the volume label, delimited by spaces, dashes (-), or
1045 // underscores (_), to the list of hints to be used in searching for OS
1047 MergeWords(&OSIconName
, Volume
->VolName
, L
',');
1048 } // if/else network boot
1050 // detect specific loaders
1051 if (StriSubCmp(L
"bzImage", NameClues
) || StriSubCmp(L
"vmlinuz", NameClues
) || StriSubCmp(L
"kernel", NameClues
)) {
1052 if (Volume
->DiskKind
!= DISK_KIND_NET
) {
1053 GuessLinuxDistribution(&OSIconName
, Volume
, LoaderPath
);
1054 Entry
->LoadOptions
= GetMainLinuxOptions(LoaderPath
, Volume
);
1056 MergeStrings(&OSIconName
, L
"linux", L
',');
1057 Entry
->OSType
= 'L';
1058 if (ShortcutLetter
== 0)
1059 ShortcutLetter
= 'L';
1060 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_LINUX
;
1061 } else if (StriSubCmp(L
"refit", LoaderPath
)) {
1062 MergeStrings(&OSIconName
, L
"refit", L
',');
1063 Entry
->OSType
= 'R';
1064 ShortcutLetter
= 'R';
1065 } else if (StriSubCmp(L
"refind", LoaderPath
)) {
1066 MergeStrings(&OSIconName
, L
"refind", L
',');
1067 Entry
->OSType
= 'R';
1068 ShortcutLetter
= 'R';
1069 } else if (MyStriCmp(LoaderPath
, MACOSX_LOADER_PATH
)) {
1070 MergeStrings(&OSIconName
, L
"mac", L
',');
1071 Entry
->OSType
= 'M';
1072 ShortcutLetter
= 'M';
1073 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
1074 } else if (MyStriCmp(NameClues
, L
"diags.efi")) {
1075 MergeStrings(&OSIconName
, L
"hwtest", L
',');
1076 } else if (MyStriCmp(NameClues
, L
"e.efi") || MyStriCmp(NameClues
, L
"elilo.efi") || StriSubCmp(L
"elilo", NameClues
)) {
1077 MergeStrings(&OSIconName
, L
"elilo,linux", L
',');
1078 Entry
->OSType
= 'E';
1079 if (ShortcutLetter
== 0)
1080 ShortcutLetter
= 'L';
1081 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
1082 } else if (StriSubCmp(L
"grub", NameClues
)) {
1083 MergeStrings(&OSIconName
, L
"grub,linux", L
',');
1084 Entry
->OSType
= 'G';
1085 ShortcutLetter
= 'G';
1086 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_GRUB
;
1087 } else if (MyStriCmp(NameClues
, L
"cdboot.efi") ||
1088 MyStriCmp(NameClues
, L
"bootmgr.efi") ||
1089 MyStriCmp(NameClues
, L
"bootmgfw.efi") ||
1090 MyStriCmp(NameClues
, L
"bkpbootmgfw.efi")) {
1091 MergeStrings(&OSIconName
, L
"win8", L
',');
1092 Entry
->OSType
= 'W';
1093 ShortcutLetter
= 'W';
1094 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
1095 } else if (MyStriCmp(NameClues
, L
"xom.efi")) {
1096 MergeStrings(&OSIconName
, L
"xom,win,win8", L
',');
1097 Entry
->OSType
= 'X';
1098 ShortcutLetter
= 'W';
1099 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
1101 else if (StriSubCmp(L
"ipxe", NameClues
)) {
1102 Entry
->OSType
= 'N';
1103 ShortcutLetter
= 'N';
1104 MergeStrings(&OSIconName
, L
"network", L
',');
1107 if ((ShortcutLetter
>= 'a') && (ShortcutLetter
<= 'z'))
1108 ShortcutLetter
= ShortcutLetter
- 'a' + 'A'; // convert lowercase to uppercase
1109 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1110 if (Entry
->me
.Image
== NULL
)
1111 Entry
->me
.Image
= LoadOSIcon(OSIconName
, L
"unknown", FALSE
);
1112 MyFreePool(PathOnly
);
1113 } // VOID SetLoaderDefaults()
1115 // Add a specified EFI boot loader to the list, using automatic settings
1116 // for icons, options, etc.
1117 static LOADER_ENTRY
* AddLoaderEntry(IN CHAR16
*LoaderPath
, IN CHAR16
*LoaderTitle
, IN REFIT_VOLUME
*Volume
, IN BOOLEAN SubScreenReturn
) {
1118 LOADER_ENTRY
*Entry
;
1120 CleanUpPathNameSlashes(LoaderPath
);
1121 Entry
= InitializeLoaderEntry(NULL
);
1122 if (Entry
!= NULL
) {
1123 Entry
->Title
= StrDuplicate((LoaderTitle
!= NULL
) ? LoaderTitle
: LoaderPath
);
1124 Entry
->me
.Title
= AllocateZeroPool(sizeof(CHAR16
) * 256);
1125 // Extra space at end of Entry->me.Title enables searching on Volume->VolName even if another volume
1126 // name is identical except for something added to the end (e.g., VolB1 vs. VolB12).
1127 // Note: Volume->VolName will be NULL for network boot programs.
1128 if (Volume
->VolName
)
1129 SPrint(Entry
->me
.Title
, 255, L
"Boot %s from %s ", (LoaderTitle
!= NULL
) ? LoaderTitle
: LoaderPath
, Volume
->VolName
);
1131 SPrint(Entry
->me
.Title
, 255, L
"Boot %s ", (LoaderTitle
!= NULL
) ? LoaderTitle
: LoaderPath
);
1133 Entry
->me
.BadgeImage
= Volume
->VolBadgeImage
;
1134 if ((LoaderPath
!= NULL
) && (LoaderPath
[0] != L
'\\')) {
1135 Entry
->LoaderPath
= StrDuplicate(L
"\\");
1137 Entry
->LoaderPath
= NULL
;
1139 MergeStrings(&(Entry
->LoaderPath
), LoaderPath
, 0);
1140 Entry
->VolName
= Volume
->VolName
;
1141 Entry
->DevicePath
= FileDevicePath(Volume
->DeviceHandle
, Entry
->LoaderPath
);
1142 SetLoaderDefaults(Entry
, LoaderPath
, Volume
);
1143 GenerateSubScreen(Entry
, Volume
, SubScreenReturn
);
1144 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1148 } // LOADER_ENTRY * AddLoaderEntry()
1150 // Add a Linux kernel as a submenu entry for another (pre-existing) Linux kernel entry.
1151 static VOID
AddKernelToSubmenu(LOADER_ENTRY
* TargetLoader
, CHAR16
*FileName
, REFIT_VOLUME
*Volume
) {
1153 CHAR16
**TokenList
= NULL
, *InitrdName
, *SubmenuName
= NULL
, *VolName
= NULL
;
1154 CHAR16
*Path
= NULL
, *Title
, *KernelVersion
;
1155 REFIT_MENU_SCREEN
*SubScreen
;
1156 LOADER_ENTRY
*SubEntry
;
1159 KernelVersion
= FindNumbers(FileName
);
1160 File
= ReadLinuxOptionsFile(TargetLoader
->LoaderPath
, Volume
);
1162 SubScreen
= TargetLoader
->me
.SubScreen
;
1163 InitrdName
= FindInitrd(FileName
, Volume
);
1164 while ((TokenCount
= ReadTokenLine(File
, &TokenList
)) > 1) {
1165 ReplaceSubstring(&(TokenList
[1]), KERNEL_VERSION
, KernelVersion
);
1166 SubEntry
= InitializeLoaderEntry(TargetLoader
);
1167 SplitPathName(FileName
, &VolName
, &Path
, &SubmenuName
);
1168 MergeStrings(&SubmenuName
, L
": ", '\0');
1169 MergeStrings(&SubmenuName
, TokenList
[0] ? StrDuplicate(TokenList
[0]) : StrDuplicate(L
"Boot Linux"), '\0');
1170 Title
= StrDuplicate(SubmenuName
);
1171 LimitStringLength(Title
, MAX_LINE_LENGTH
);
1172 SubEntry
->me
.Title
= Title
;
1173 MyFreePool(SubEntry
->LoadOptions
);
1174 SubEntry
->LoadOptions
= AddInitrdToOptions(TokenList
[1], InitrdName
);
1175 MyFreePool(SubEntry
->LoaderPath
);
1176 SubEntry
->LoaderPath
= StrDuplicate(FileName
);
1177 CleanUpPathNameSlashes(SubEntry
->LoaderPath
);
1178 SubEntry
->DevicePath
= FileDevicePath(Volume
->DeviceHandle
, SubEntry
->LoaderPath
);
1179 FreeTokenLine(&TokenList
, &TokenCount
);
1180 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_LINUX
;
1181 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
1183 MyFreePool(VolName
);
1185 MyFreePool(SubmenuName
);
1186 MyFreePool(InitrdName
);
1189 MyFreePool(KernelVersion
);
1190 } // static VOID AddKernelToSubmenu()
1192 // Returns -1 if (Time1 < Time2), +1 if (Time1 > Time2), or 0 if
1193 // (Time1 == Time2). Precision is only to the nearest second; since
1194 // this is used for sorting boot loader entries, differences smaller
1195 // than this are likely to be meaningless (and unlikely!).
1196 INTN
TimeComp(IN EFI_TIME
*Time1
, IN EFI_TIME
*Time2
) {
1197 INT64 Time1InSeconds
, Time2InSeconds
;
1199 // Following values are overestimates; I'm assuming 31 days in every month.
1200 // This is fine for the purpose of this function, which is limited
1201 Time1InSeconds
= Time1
->Second
+ (Time1
->Minute
* 60) + (Time1
->Hour
* 3600) + (Time1
->Day
* 86400) +
1202 (Time1
->Month
* 2678400) + ((Time1
->Year
- 1998) * 32140800);
1203 Time2InSeconds
= Time2
->Second
+ (Time2
->Minute
* 60) + (Time2
->Hour
* 3600) + (Time2
->Day
* 86400) +
1204 (Time2
->Month
* 2678400) + ((Time2
->Year
- 1998) * 32140800);
1205 if (Time1InSeconds
< Time2InSeconds
)
1207 else if (Time1InSeconds
> Time2InSeconds
)
1211 } // INTN TimeComp()
1213 // Adds a loader list element, keeping it sorted by date. EXCEPTION: Fedora's rescue
1214 // kernel, which begins with "vmlinuz-0-rescue," should not be at the top of the list,
1215 // since that will make it the default if kernel folding is enabled, so float it to
1217 // Returns the new first element (the one with the most recent date).
1218 static struct LOADER_LIST
* AddLoaderListEntry(struct LOADER_LIST
*LoaderList
, struct LOADER_LIST
*NewEntry
) {
1219 struct LOADER_LIST
*LatestEntry
, *CurrentEntry
, *PrevEntry
= NULL
;
1220 BOOLEAN LinuxRescue
= FALSE
;
1222 LatestEntry
= CurrentEntry
= LoaderList
;
1223 if (LoaderList
== NULL
) {
1224 LatestEntry
= NewEntry
;
1226 if (StriSubCmp(L
"vmlinuz-0-rescue", NewEntry
->FileName
))
1228 while ((CurrentEntry
!= NULL
) && (LinuxRescue
|| (TimeComp(&(NewEntry
->TimeStamp
), &(CurrentEntry
->TimeStamp
)) < 0))) {
1229 PrevEntry
= CurrentEntry
;
1230 CurrentEntry
= CurrentEntry
->NextEntry
;
1232 NewEntry
->NextEntry
= CurrentEntry
;
1233 if (PrevEntry
== NULL
) {
1234 LatestEntry
= NewEntry
;
1236 PrevEntry
->NextEntry
= NewEntry
;
1239 return (LatestEntry
);
1240 } // static VOID AddLoaderListEntry()
1242 // Delete the LOADER_LIST linked list
1243 static VOID
CleanUpLoaderList(struct LOADER_LIST
*LoaderList
) {
1244 struct LOADER_LIST
*Temp
;
1246 while (LoaderList
!= NULL
) {
1248 LoaderList
= LoaderList
->NextEntry
;
1249 MyFreePool(Temp
->FileName
);
1252 } // static VOID CleanUpLoaderList()
1254 // Returns FALSE if the specified file/volume matches the GlobalConfig.DontScanDirs
1255 // or GlobalConfig.DontScanVolumes specification, or if Path points to a volume
1256 // other than the one specified by Volume, or if the specified path is SelfDir.
1257 // Returns TRUE if none of these conditions is met -- that is, if the path is
1258 // eligible for scanning.
1259 static BOOLEAN
ShouldScan(REFIT_VOLUME
*Volume
, CHAR16
*Path
) {
1260 CHAR16
*VolName
= NULL
, *DontScanDir
, *PathCopy
= NULL
;
1262 BOOLEAN ScanIt
= TRUE
;
1264 if ((IsIn(Volume
->VolName
, GlobalConfig
.DontScanVolumes
)) || (IsIn(Volume
->PartName
, GlobalConfig
.DontScanVolumes
)))
1267 if (MyStriCmp(Path
, SelfDirPath
) && (Volume
->DeviceHandle
== SelfVolume
->DeviceHandle
))
1270 // See if Path includes an explicit volume declaration that's NOT Volume....
1271 PathCopy
= StrDuplicate(Path
);
1272 if (SplitVolumeAndFilename(&PathCopy
, &VolName
)) {
1273 VolumeNumberToName(Volume
, &VolName
);
1274 if (VolName
&& !MyStriCmp(VolName
, Volume
->VolName
)) {
1277 } // if Path includes volume specification
1278 MyFreePool(PathCopy
);
1279 MyFreePool(VolName
);
1282 // See if Volume is in GlobalConfig.DontScanDirs....
1283 while (ScanIt
&& (DontScanDir
= FindCommaDelimited(GlobalConfig
.DontScanDirs
, i
++))) {
1284 SplitVolumeAndFilename(&DontScanDir
, &VolName
);
1285 CleanUpPathNameSlashes(DontScanDir
);
1286 VolumeNumberToName(Volume
, &VolName
);
1287 if (VolName
!= NULL
) {
1288 if (MyStriCmp(VolName
, Volume
->VolName
) && MyStriCmp(DontScanDir
, Path
))
1291 if (MyStriCmp(DontScanDir
, Path
))
1294 MyFreePool(DontScanDir
);
1295 MyFreePool(VolName
);
1301 } // BOOLEAN ShouldScan()
1303 // Returns TRUE if the file is byte-for-byte identical with the fallback file
1304 // on the volume AND if the file is not itself the fallback file; returns
1305 // FALSE if the file is not identical to the fallback file OR if the file
1306 // IS the fallback file. Intended for use in excluding the fallback boot
1307 // loader when it's a duplicate of another boot loader.
1308 static BOOLEAN
DuplicatesFallback(IN REFIT_VOLUME
*Volume
, IN CHAR16
*FileName
) {
1309 CHAR8
*FileContents
, *FallbackContents
;
1310 EFI_FILE_HANDLE FileHandle
, FallbackHandle
;
1311 EFI_FILE_INFO
*FileInfo
, *FallbackInfo
;
1312 UINTN FileSize
= 0, FallbackSize
= 0;
1314 BOOLEAN AreIdentical
= FALSE
;
1316 if (!FileExists(Volume
->RootDir
, FileName
) || !FileExists(Volume
->RootDir
, FALLBACK_FULLNAME
))
1319 CleanUpPathNameSlashes(FileName
);
1321 if (MyStriCmp(FileName
, FALLBACK_FULLNAME
))
1322 return FALSE
; // identical filenames, so not a duplicate....
1324 Status
= refit_call5_wrapper(Volume
->RootDir
->Open
, Volume
->RootDir
, &FileHandle
, FileName
, EFI_FILE_MODE_READ
, 0);
1325 if (Status
== EFI_SUCCESS
) {
1326 FileInfo
= LibFileInfo(FileHandle
);
1327 FileSize
= FileInfo
->FileSize
;
1332 Status
= refit_call5_wrapper(Volume
->RootDir
->Open
, Volume
->RootDir
, &FallbackHandle
, FALLBACK_FULLNAME
, EFI_FILE_MODE_READ
, 0);
1333 if (Status
== EFI_SUCCESS
) {
1334 FallbackInfo
= LibFileInfo(FallbackHandle
);
1335 FallbackSize
= FallbackInfo
->FileSize
;
1337 refit_call1_wrapper(FileHandle
->Close
, FileHandle
);
1341 if (FallbackSize
== FileSize
) { // could be identical; do full check....
1342 FileContents
= AllocatePool(FileSize
);
1343 FallbackContents
= AllocatePool(FallbackSize
);
1344 if (FileContents
&& FallbackContents
) {
1345 Status
= refit_call3_wrapper(FileHandle
->Read
, FileHandle
, &FileSize
, FileContents
);
1346 if (Status
== EFI_SUCCESS
) {
1347 Status
= refit_call3_wrapper(FallbackHandle
->Read
, FallbackHandle
, &FallbackSize
, FallbackContents
);
1349 if (Status
== EFI_SUCCESS
) {
1350 AreIdentical
= (CompareMem(FileContents
, FallbackContents
, FileSize
) == 0);
1353 MyFreePool(FileContents
);
1354 MyFreePool(FallbackContents
);
1357 // BUG ALERT: Some systems (e.g., DUET, some Macs with large displays) crash if the
1358 // following two calls are reversed. Go figure....
1359 refit_call1_wrapper(FileHandle
->Close
, FallbackHandle
);
1360 refit_call1_wrapper(FileHandle
->Close
, FileHandle
);
1361 return AreIdentical
;
1362 } // BOOLEAN DuplicatesFallback()
1364 // Returns FALSE if two measures of file size are identical for a single file,
1365 // TRUE if not or if the file can't be opened and the other measure is non-0.
1366 // Despite the function's name, this isn't really a direct test of symbolic
1367 // link status, since EFI doesn't officially support symlinks. It does seem
1368 // to be a reliable indicator, though. (OTOH, some disk errors might cause a
1369 // file to fail to open, which would return a false positive -- but as I use
1370 // this function to exclude symbolic links from the list of boot loaders,
1371 // that would be fine, since such boot loaders wouldn't work.)
1372 // CAUTION: *FullName MUST be properly cleaned up (via CleanUpPathNameSlashes())
1373 static BOOLEAN
IsSymbolicLink(REFIT_VOLUME
*Volume
, CHAR16
*FullName
, EFI_FILE_INFO
*DirEntry
) {
1374 EFI_FILE_HANDLE FileHandle
;
1375 EFI_FILE_INFO
*FileInfo
= NULL
;
1377 UINTN FileSize2
= 0;
1379 Status
= refit_call5_wrapper(Volume
->RootDir
->Open
, Volume
->RootDir
, &FileHandle
, FullName
, EFI_FILE_MODE_READ
, 0);
1380 if (Status
== EFI_SUCCESS
) {
1381 FileInfo
= LibFileInfo(FileHandle
);
1382 if (FileInfo
!= NULL
)
1383 FileSize2
= FileInfo
->FileSize
;
1386 MyFreePool(FileInfo
);
1388 return (DirEntry
->FileSize
!= FileSize2
);
1389 } // BOOLEAN IsSymbolicLink()
1391 // Returns TRUE if a file with the same name as the original but with
1392 // ".efi.signed" is also present in the same directory. Ubuntu is using
1393 // this filename as a signed version of the original unsigned kernel, and
1394 // there's no point in cluttering the display with two kernels that will
1395 // behave identically on non-SB systems, or when one will fail when SB
1397 // CAUTION: *FullName MUST be properly cleaned up (via CleanUpPathNameSlashes())
1398 static BOOLEAN
HasSignedCounterpart(IN REFIT_VOLUME
*Volume
, IN CHAR16
*FullName
) {
1399 CHAR16
*NewFile
= NULL
;
1400 BOOLEAN retval
= FALSE
;
1402 MergeStrings(&NewFile
, FullName
, 0);
1403 MergeStrings(&NewFile
, L
".efi.signed", 0);
1404 if (NewFile
!= NULL
) {
1405 if (FileExists(Volume
->RootDir
, NewFile
))
1407 MyFreePool(NewFile
);
1411 } // BOOLEAN HasSignedCounterpart()
1413 // Scan an individual directory for EFI boot loader files and, if found,
1414 // add them to the list. Exception: Ignores FALLBACK_FULLNAME, which is picked
1415 // up in ScanEfiFiles(). Sorts the entries within the loader directory so that
1416 // the most recent one appears first in the list.
1417 // Returns TRUE if a duplicate for FALLBACK_FILENAME was found, FALSE if not.
1418 static BOOLEAN
ScanLoaderDir(IN REFIT_VOLUME
*Volume
, IN CHAR16
*Path
, IN CHAR16
*Pattern
)
1421 REFIT_DIR_ITER DirIter
;
1422 EFI_FILE_INFO
*DirEntry
;
1423 CHAR16 Message
[256], *Extension
, *FullName
;
1424 struct LOADER_LIST
*LoaderList
= NULL
, *NewLoader
;
1425 LOADER_ENTRY
*FirstKernel
= NULL
, *LatestEntry
= NULL
;
1426 BOOLEAN FoundFallbackDuplicate
= FALSE
, IsLinux
= FALSE
, InSelfPath
;
1428 InSelfPath
= MyStriCmp(Path
, SelfDirPath
);
1429 if ((!SelfDirPath
|| !Path
|| (InSelfPath
&& (Volume
->DeviceHandle
!= SelfVolume
->DeviceHandle
)) ||
1430 (!InSelfPath
)) && (ShouldScan(Volume
, Path
))) {
1431 // look through contents of the directory
1432 DirIterOpen(Volume
->RootDir
, Path
, &DirIter
);
1433 while (DirIterNext(&DirIter
, 2, Pattern
, &DirEntry
)) {
1434 Extension
= FindExtension(DirEntry
->FileName
);
1435 FullName
= StrDuplicate(Path
);
1436 MergeStrings(&FullName
, DirEntry
->FileName
, L
'\\');
1437 CleanUpPathNameSlashes(FullName
);
1438 if (DirEntry
->FileName
[0] == '.' ||
1439 MyStriCmp(Extension
, L
".icns") ||
1440 MyStriCmp(Extension
, L
".png") ||
1441 (MyStriCmp(DirEntry
->FileName
, FALLBACK_BASENAME
) && (MyStriCmp(Path
, L
"EFI\\BOOT"))) ||
1442 FilenameIn(Volume
, Path
, DirEntry
->FileName
, SHELL_NAMES
) ||
1443 IsSymbolicLink(Volume
, FullName
, DirEntry
) || /* is symbolic link */
1444 HasSignedCounterpart(Volume
, FullName
) || /* a file with same name plus ".efi.signed" is present */
1445 FilenameIn(Volume
, Path
, DirEntry
->FileName
, GlobalConfig
.DontScanFiles
) ||
1446 !IsValidLoader(Volume
->RootDir
, FullName
)) {
1447 continue; // skip this
1450 NewLoader
= AllocateZeroPool(sizeof(struct LOADER_LIST
));
1451 if (NewLoader
!= NULL
) {
1452 NewLoader
->FileName
= StrDuplicate(FullName
);
1453 NewLoader
->TimeStamp
= DirEntry
->ModificationTime
;
1454 LoaderList
= AddLoaderListEntry(LoaderList
, NewLoader
);
1455 if (DuplicatesFallback(Volume
, FullName
))
1456 FoundFallbackDuplicate
= TRUE
;
1458 MyFreePool(Extension
);
1459 MyFreePool(FullName
);
1462 NewLoader
= LoaderList
;
1463 while (NewLoader
!= NULL
) {
1464 IsLinux
= (StriSubCmp(L
"bzImage", NewLoader
->FileName
) ||
1465 StriSubCmp(L
"vmlinuz", NewLoader
->FileName
) ||
1466 StriSubCmp(L
"kernel", NewLoader
->FileName
));
1467 if ((FirstKernel
!= NULL
) && IsLinux
&& GlobalConfig
.FoldLinuxKernels
) {
1468 AddKernelToSubmenu(FirstKernel
, NewLoader
->FileName
, Volume
);
1470 LatestEntry
= AddLoaderEntry(NewLoader
->FileName
, NULL
, Volume
, !(IsLinux
&& GlobalConfig
.FoldLinuxKernels
));
1471 if (IsLinux
&& (FirstKernel
== NULL
))
1472 FirstKernel
= LatestEntry
;
1474 NewLoader
= NewLoader
->NextEntry
;
1476 if ((NewLoader
!= NULL
) && (FirstKernel
!= NULL
) && IsLinux
&& GlobalConfig
.FoldLinuxKernels
)
1477 AddMenuEntry(FirstKernel
->me
.SubScreen
, &MenuEntryReturn
);
1479 CleanUpLoaderList(LoaderList
);
1480 Status
= DirIterClose(&DirIter
);
1481 // NOTE: EFI_INVALID_PARAMETER really is an error that should be reported;
1482 // but I've gotten reports from users who are getting this error occasionally
1483 // and I can't find anything wrong or reproduce the problem, so I'm putting
1484 // it down to buggy EFI implementations and ignoring that particular error....
1485 if ((Status
!= EFI_NOT_FOUND
) && (Status
!= EFI_INVALID_PARAMETER
)) {
1487 SPrint(Message
, 255, L
"while scanning the %s directory", Path
);
1489 StrCpy(Message
, L
"while scanning the root directory");
1490 CheckError(Status
, Message
);
1491 } // if (Status != EFI_NOT_FOUND)
1492 } // if not scanning a blacklisted directory
1494 return FoundFallbackDuplicate
;
1495 } /* static VOID ScanLoaderDir() */
1497 // Run the IPXE_DISCOVER_NAME program, which obtains the IP address of the boot
1498 // server and the name of the boot file it delivers.
1499 CHAR16
* RuniPXEDiscover(EFI_HANDLE Volume
)
1502 EFI_DEVICE_PATH
*FilePath
;
1503 EFI_HANDLE iPXEHandle
;
1504 CHAR16
*boot_info
= NULL
;
1505 UINTN boot_info_size
= 0;
1507 FilePath
= FileDevicePath (Volume
, IPXE_DISCOVER_NAME
);
1508 Status
= refit_call6_wrapper(BS
->LoadImage
, FALSE
, SelfImageHandle
, FilePath
, NULL
, 0, &iPXEHandle
);
1512 Status
= refit_call3_wrapper(BS
->StartImage
, iPXEHandle
, &boot_info_size
, &boot_info
);
1515 } // RuniPXEDiscover()
1517 // Scan for network (PXE) boot servers. This function relies on the presence
1518 // of the IPXE_DISCOVER_NAME and IPXE_NAME program files on the volume from
1519 // which rEFInd launched. As of December 6, 2014, these tools aren't entirely
1520 // reliable. See BUILDING.txt for information on building them.
1521 static VOID
ScanNetboot() {
1522 CHAR16
*iPXEFileName
= IPXE_NAME
;
1524 REFIT_VOLUME
*NetVolume
;
1526 if (FileExists(SelfVolume
->RootDir
, IPXE_DISCOVER_NAME
) &&
1527 FileExists(SelfVolume
->RootDir
, IPXE_NAME
) &&
1528 IsValidLoader(SelfVolume
->RootDir
, IPXE_DISCOVER_NAME
) &&
1529 IsValidLoader(SelfVolume
->RootDir
, IPXE_NAME
)) {
1530 Location
= RuniPXEDiscover(SelfVolume
->DeviceHandle
);
1531 if (Location
!= NULL
&& FileExists(SelfVolume
->RootDir
, iPXEFileName
)) {
1532 NetVolume
= AllocatePool(sizeof(REFIT_VOLUME
));
1533 CopyMem(NetVolume
, SelfVolume
, sizeof(REFIT_VOLUME
));
1534 NetVolume
->DiskKind
= DISK_KIND_NET
;
1535 NetVolume
->VolBadgeImage
= BuiltinIcon(BUILTIN_ICON_VOL_NET
);
1536 NetVolume
->PartName
= NetVolume
->VolName
= NULL
;
1537 AddLoaderEntry(iPXEFileName
, Location
, NetVolume
, TRUE
);
1538 MyFreePool(NetVolume
);
1539 } // if support files exist and are valid
1541 } // VOID ScanNetBoot()
1543 static VOID
ScanEfiFiles(REFIT_VOLUME
*Volume
) {
1545 REFIT_DIR_ITER EfiDirIter
;
1546 EFI_FILE_INFO
*EfiDirEntry
;
1547 CHAR16 FileName
[256], *Directory
= NULL
, *MatchPatterns
, *VolName
= NULL
, *SelfPath
;
1549 BOOLEAN ScanFallbackLoader
= TRUE
;
1550 BOOLEAN FoundBRBackup
= FALSE
;
1552 if (Volume
&& (Volume
->RootDir
!= NULL
) && (Volume
->VolName
!= NULL
) && (Volume
->IsReadable
)) {
1553 MatchPatterns
= StrDuplicate(LOADER_MATCH_PATTERNS
);
1554 if (GlobalConfig
.ScanAllLinux
)
1555 MergeStrings(&MatchPatterns
, LINUX_MATCH_PATTERNS
, L
',');
1557 // check for Mac OS X boot loader
1558 if (ShouldScan(Volume
, MACOSX_LOADER_DIR
)) {
1559 StrCpy(FileName
, MACOSX_LOADER_PATH
);
1560 if (FileExists(Volume
->RootDir
, FileName
) && !FilenameIn(Volume
, MACOSX_LOADER_DIR
, L
"boot.efi", GlobalConfig
.DontScanFiles
)) {
1561 AddLoaderEntry(FileName
, L
"Mac OS X", Volume
, TRUE
);
1562 if (DuplicatesFallback(Volume
, FileName
))
1563 ScanFallbackLoader
= FALSE
;
1567 StrCpy(FileName
, L
"System\\Library\\CoreServices\\xom.efi");
1568 if (FileExists(Volume
->RootDir
, FileName
) && !FilenameIn(Volume
, MACOSX_LOADER_DIR
, L
"xom.efi", GlobalConfig
.DontScanFiles
)) {
1569 AddLoaderEntry(FileName
, L
"Windows XP (XoM)", Volume
, TRUE
);
1570 if (DuplicatesFallback(Volume
, FileName
))
1571 ScanFallbackLoader
= FALSE
;
1573 } // if should scan Mac directory
1575 // check for Microsoft boot loader/menu
1576 if (ShouldScan(Volume
, L
"EFI\\Microsoft\\Boot")) {
1577 StrCpy(FileName
, L
"EFI\\Microsoft\\Boot\\bkpbootmgfw.efi");
1578 if (FileExists(Volume
->RootDir
, FileName
) && !FilenameIn(Volume
, L
"EFI\\Microsoft\\Boot", L
"bkpbootmgfw.efi",
1579 GlobalConfig
.DontScanFiles
)) {
1580 AddLoaderEntry(FileName
, L
"Microsoft EFI boot (Boot Repair backup)", Volume
, TRUE
);
1581 FoundBRBackup
= TRUE
;
1582 if (DuplicatesFallback(Volume
, FileName
))
1583 ScanFallbackLoader
= FALSE
;
1585 StrCpy(FileName
, L
"EFI\\Microsoft\\Boot\\bootmgfw.efi");
1586 if (FileExists(Volume
->RootDir
, FileName
) &&
1587 !FilenameIn(Volume
, L
"EFI\\Microsoft\\Boot", L
"bootmgfw.efi", GlobalConfig
.DontScanFiles
)) {
1589 AddLoaderEntry(FileName
, L
"Supposed Microsoft EFI boot (probably GRUB)", Volume
, TRUE
);
1591 AddLoaderEntry(FileName
, L
"Microsoft EFI boot", Volume
, TRUE
);
1592 if (DuplicatesFallback(Volume
, FileName
))
1593 ScanFallbackLoader
= FALSE
;
1597 // scan the root directory for EFI executables
1598 if (ScanLoaderDir(Volume
, L
"\\", MatchPatterns
))
1599 ScanFallbackLoader
= FALSE
;
1601 // scan subdirectories of the EFI directory (as per the standard)
1602 DirIterOpen(Volume
->RootDir
, L
"EFI", &EfiDirIter
);
1603 while (DirIterNext(&EfiDirIter
, 1, NULL
, &EfiDirEntry
)) {
1604 if (MyStriCmp(EfiDirEntry
->FileName
, L
"tools") || EfiDirEntry
->FileName
[0] == '.')
1605 continue; // skip this, doesn't contain boot loaders or is scanned later
1606 SPrint(FileName
, 255, L
"EFI\\%s", EfiDirEntry
->FileName
);
1607 if (ScanLoaderDir(Volume
, FileName
, MatchPatterns
))
1608 ScanFallbackLoader
= FALSE
;
1610 Status
= DirIterClose(&EfiDirIter
);
1611 if ((Status
!= EFI_NOT_FOUND
) && (Status
!= EFI_INVALID_PARAMETER
))
1612 CheckError(Status
, L
"while scanning the EFI directory");
1614 // Scan user-specified (or additional default) directories....
1616 while ((Directory
= FindCommaDelimited(GlobalConfig
.AlsoScan
, i
++)) != NULL
) {
1617 if (ShouldScan(Volume
, Directory
)) {
1618 SplitVolumeAndFilename(&Directory
, &VolName
);
1619 CleanUpPathNameSlashes(Directory
);
1620 Length
= StrLen(Directory
);
1621 if ((Length
> 0) && ScanLoaderDir(Volume
, Directory
, MatchPatterns
))
1622 ScanFallbackLoader
= FALSE
;
1623 MyFreePool(VolName
);
1624 } // if should scan dir
1625 MyFreePool(Directory
);
1628 // Don't scan the fallback loader if it's on the same volume and a duplicate of rEFInd itself....
1629 SelfPath
= DevicePathToStr(SelfLoadedImage
->FilePath
);
1630 CleanUpPathNameSlashes(SelfPath
);
1631 if ((Volume
->DeviceHandle
== SelfLoadedImage
->DeviceHandle
) && DuplicatesFallback(Volume
, SelfPath
))
1632 ScanFallbackLoader
= FALSE
;
1634 // If not a duplicate & if it exists & if it's not us, create an entry
1635 // for the fallback boot loader
1636 if (ScanFallbackLoader
&& FileExists(Volume
->RootDir
, FALLBACK_FULLNAME
) && ShouldScan(Volume
, L
"EFI\\BOOT")) {
1637 AddLoaderEntry(FALLBACK_FULLNAME
, L
"Fallback boot loader", Volume
, TRUE
);
1640 } // static VOID ScanEfiFiles()
1642 // Scan internal disks for valid EFI boot loaders....
1643 static VOID
ScanInternal(VOID
) {
1646 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1647 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_INTERNAL
) {
1648 ScanEfiFiles(Volumes
[VolumeIndex
]);
1651 } // static VOID ScanInternal()
1653 // Scan external disks for valid EFI boot loaders....
1654 static VOID
ScanExternal(VOID
) {
1657 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1658 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_EXTERNAL
) {
1659 ScanEfiFiles(Volumes
[VolumeIndex
]);
1662 } // static VOID ScanExternal()
1664 // Scan internal disks for valid EFI boot loaders....
1665 static VOID
ScanOptical(VOID
) {
1668 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1669 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_OPTICAL
) {
1670 ScanEfiFiles(Volumes
[VolumeIndex
]);
1673 } // static VOID ScanOptical()
1675 // default volume badge icon based on disk kind
1676 EG_IMAGE
* GetDiskBadge(IN UINTN DiskType
) {
1677 EG_IMAGE
* Badge
= NULL
;
1681 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_INTERNAL
);
1684 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_EXTERNAL
);
1687 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_OPTICAL
);
1691 } // EG_IMAGE * GetDiskBadge()
1694 // pre-boot tool functions
1697 static VOID
StartTool(IN LOADER_ENTRY
*Entry
)
1699 BeginExternalScreen(Entry
->UseGraphicsMode
, Entry
->me
.Title
+ 6); // assumes "Start <title>" as assigned below
1700 StoreLoaderName(Entry
->me
.Title
);
1701 StartEFIImage(Entry
->DevicePath
, Entry
->LoadOptions
, TYPE_EFI
,
1702 Basename(Entry
->LoaderPath
), Entry
->OSType
, NULL
, TRUE
, FALSE
);
1703 FinishExternalScreen();
1704 } /* static VOID StartTool() */
1706 static LOADER_ENTRY
* AddToolEntry(EFI_HANDLE DeviceHandle
, IN CHAR16
*LoaderPath
, IN CHAR16
*LoaderTitle
, IN EG_IMAGE
*Image
,
1707 IN CHAR16 ShortcutLetter
, IN BOOLEAN UseGraphicsMode
)
1709 LOADER_ENTRY
*Entry
;
1710 CHAR16
*TitleStr
= NULL
;
1712 Entry
= AllocateZeroPool(sizeof(LOADER_ENTRY
));
1714 TitleStr
= PoolPrint(L
"Start %s", LoaderTitle
);
1715 Entry
->me
.Title
= TitleStr
;
1716 Entry
->me
.Tag
= TAG_TOOL
;
1718 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1719 Entry
->me
.Image
= Image
;
1720 Entry
->LoaderPath
= (LoaderPath
) ? StrDuplicate(LoaderPath
) : NULL
;
1721 Entry
->DevicePath
= FileDevicePath(DeviceHandle
, Entry
->LoaderPath
);
1722 Entry
->UseGraphicsMode
= UseGraphicsMode
;
1724 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1726 } /* static LOADER_ENTRY * AddToolEntry() */
1728 // Locates boot loaders. NOTE: This assumes that GlobalConfig.LegacyType is set correctly.
1729 static VOID
ScanForBootloaders(VOID
) {
1732 BOOLEAN ScanForLegacy
= FALSE
;
1734 // Determine up-front if we'll be scanning for legacy loaders....
1735 for (i
= 0; i
< NUM_SCAN_OPTIONS
; i
++) {
1736 s
= GlobalConfig
.ScanFor
[i
];
1737 if ((s
== 'c') || (s
== 'C') || (s
== 'h') || (s
== 'H') || (s
== 'b') || (s
== 'B'))
1738 ScanForLegacy
= TRUE
;
1741 // If UEFI & scanning for legacy loaders & deep legacy scan, update NVRAM boot manager list
1742 if ((GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) && ScanForLegacy
&& GlobalConfig
.DeepLegacyScan
) {
1743 BdsDeleteAllInvalidLegacyBootOptions();
1744 BdsAddNonExistingLegacyBootOptions();
1747 // scan for loaders and tools, add them to the menu
1748 for (i
= 0; i
< NUM_SCAN_OPTIONS
; i
++) {
1749 switch(GlobalConfig
.ScanFor
[i
]) {
1754 ScanLegacyInternal();
1757 ScanLegacyExternal();
1760 ScanUserConfigured(GlobalConfig
.ConfigFilename
);
1777 // assign shortcut keys
1778 for (i
= 0; i
< MainMenu
.EntryCount
&& MainMenu
.Entries
[i
]->Row
== 0 && i
< 9; i
++)
1779 MainMenu
.Entries
[i
]->ShortcutDigit
= (CHAR16
)('1' + i
);
1781 // wait for user ACK when there were errors
1782 FinishTextScreen(FALSE
);
1783 } // static VOID ScanForBootloaders()
1785 // Locate a single tool from the specified Locations using one of the
1786 // specified Names and add it to the menu.
1787 static VOID
FindTool(CHAR16
*Locations
, CHAR16
*Names
, CHAR16
*Description
, UINTN Icon
) {
1788 UINTN j
= 0, k
, VolumeIndex
;
1789 CHAR16
*DirName
, *FileName
, *PathName
, FullDescription
[256];
1791 while ((DirName
= FindCommaDelimited(Locations
, j
++)) != NULL
) {
1793 while ((FileName
= FindCommaDelimited(Names
, k
++)) != NULL
) {
1794 PathName
= StrDuplicate(DirName
);
1795 MergeStrings(&PathName
, FileName
, MyStriCmp(PathName
, L
"\\") ? 0 : L
'\\');
1796 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1797 if ((Volumes
[VolumeIndex
]->RootDir
!= NULL
) &&
1798 (FileExists(Volumes
[VolumeIndex
]->RootDir
, PathName
)) &&
1799 IsValidLoader(Volumes
[VolumeIndex
]->RootDir
, PathName
)) {
1800 SPrint(FullDescription
, 255, L
"%s at %s on %s", Description
, PathName
, Volumes
[VolumeIndex
]->VolName
);
1801 AddToolEntry(Volumes
[VolumeIndex
]->DeviceHandle
, PathName
, FullDescription
, BuiltinIcon(Icon
), 'S', FALSE
);
1804 MyFreePool(PathName
);
1805 MyFreePool(FileName
);
1807 MyFreePool(DirName
);
1808 } // while Locations
1809 } // VOID FindTool()
1811 // Add the second-row tags containing built-in and external tools (EFI shell,
1813 static VOID
ScanForTools(VOID
) {
1814 CHAR16
*FileName
= NULL
, *VolName
= NULL
, *MokLocations
, Description
[256];
1815 REFIT_MENU_ENTRY
*TempMenuEntry
;
1816 UINTN i
, j
, VolumeIndex
;
1821 MokLocations
= StrDuplicate(MOK_LOCATIONS
);
1822 if (MokLocations
!= NULL
)
1823 MergeStrings(&MokLocations
, SelfDirPath
, L
',');
1825 for (i
= 0; i
< NUM_TOOLS
; i
++) {
1826 switch(GlobalConfig
.ShowTools
[i
]) {
1827 // NOTE: Be sure that FileName is NULL at the end of each case.
1829 TempMenuEntry
= CopyMenuEntry(&MenuEntryShutdown
);
1830 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_SHUTDOWN
);
1831 AddMenuEntry(&MainMenu
, TempMenuEntry
);
1835 TempMenuEntry
= CopyMenuEntry(&MenuEntryReset
);
1836 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_RESET
);
1837 AddMenuEntry(&MainMenu
, TempMenuEntry
);
1841 TempMenuEntry
= CopyMenuEntry(&MenuEntryAbout
);
1842 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT
);
1843 AddMenuEntry(&MainMenu
, TempMenuEntry
);
1847 TempMenuEntry
= CopyMenuEntry(&MenuEntryExit
);
1848 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_EXIT
);
1849 AddMenuEntry(&MainMenu
, TempMenuEntry
);
1853 if (EfivarGetRaw(&GlobalGuid
, L
"OsIndicationsSupported", &b
, &j
) == EFI_SUCCESS
) {
1855 if (osind
& EFI_OS_INDICATIONS_BOOT_TO_FW_UI
) {
1856 TempMenuEntry
= CopyMenuEntry(&MenuEntryFirmware
);
1857 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_FIRMWARE
);
1858 AddMenuEntry(&MainMenu
, TempMenuEntry
);
1865 while ((FileName
= FindCommaDelimited(SHELL_NAMES
, j
++)) != NULL
) {
1866 if (FileExists(SelfRootDir
, FileName
) && IsValidLoader(SelfRootDir
, FileName
)) {
1867 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, L
"EFI Shell", BuiltinIcon(BUILTIN_ICON_TOOL_SHELL
),
1870 MyFreePool(FileName
);
1876 while ((FileName
= FindCommaDelimited(GPTSYNC_NAMES
, j
++)) != NULL
) {
1877 if (FileExists(SelfRootDir
, FileName
) && IsValidLoader(SelfRootDir
, FileName
)) {
1878 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, L
"Hybrid MBR tool", BuiltinIcon(BUILTIN_ICON_TOOL_PART
),
1881 MyFreePool(FileName
);
1888 while ((FileName
= FindCommaDelimited(GDISK_NAMES
, j
++)) != NULL
) {
1889 if (FileExists(SelfRootDir
, FileName
) && IsValidLoader(SelfRootDir
, FileName
)) {
1890 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, L
"disk partitioning tool",
1891 BuiltinIcon(BUILTIN_ICON_TOOL_PART
), 'G', FALSE
);
1893 MyFreePool(FileName
);
1900 while ((FileName
= FindCommaDelimited(NETBOOT_NAMES
, j
++)) != NULL
) {
1901 if (FileExists(SelfRootDir
, FileName
) && IsValidLoader(SelfRootDir
, FileName
)) {
1902 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, L
"Netboot",
1903 BuiltinIcon(BUILTIN_ICON_TOOL_NETBOOT
), 'N', FALSE
);
1905 MyFreePool(FileName
);
1910 case TAG_APPLE_RECOVERY
:
1911 FileName
= StrDuplicate(L
"\\com.apple.recovery.boot\\boot.efi");
1912 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1913 if ((Volumes
[VolumeIndex
]->RootDir
!= NULL
) &&
1914 (FileExists(Volumes
[VolumeIndex
]->RootDir
, FileName
)) &&
1915 IsValidLoader(Volumes
[VolumeIndex
]->RootDir
, FileName
)) {
1916 SPrint(Description
, 255, L
"Apple Recovery on %s", Volumes
[VolumeIndex
]->VolName
);
1917 AddToolEntry(Volumes
[VolumeIndex
]->DeviceHandle
, FileName
, Description
,
1918 BuiltinIcon(BUILTIN_ICON_TOOL_APPLE_RESCUE
), 'R', TRUE
);
1921 MyFreePool(FileName
);
1925 case TAG_WINDOWS_RECOVERY
:
1927 while ((FileName
= FindCommaDelimited(GlobalConfig
.WindowsRecoveryFiles
, j
++)) != NULL
) {
1928 SplitVolumeAndFilename(&FileName
, &VolName
);
1929 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1930 if ((Volumes
[VolumeIndex
]->RootDir
!= NULL
) &&
1931 (FileExists(Volumes
[VolumeIndex
]->RootDir
, FileName
)) &&
1932 IsValidLoader(Volumes
[VolumeIndex
]->RootDir
, FileName
) &&
1933 ((VolName
== NULL
) || MyStriCmp(VolName
, Volumes
[VolumeIndex
]->VolName
))) {
1934 SPrint(Description
, 255, L
"Microsoft Recovery on %s", Volumes
[VolumeIndex
]->VolName
);
1935 AddToolEntry(Volumes
[VolumeIndex
]->DeviceHandle
, FileName
, Description
,
1936 BuiltinIcon(BUILTIN_ICON_TOOL_WINDOWS_RESCUE
), 'R', TRUE
);
1940 MyFreePool(FileName
);
1942 MyFreePool(VolName
);
1947 FindTool(MokLocations
, MOK_NAMES
, L
"MOK utility", BUILTIN_ICON_TOOL_MOK_TOOL
);
1950 case TAG_FWUPDATE_TOOL
:
1951 FindTool(MokLocations
, FWUPDATE_NAMES
, L
"firmware update utility", BUILTIN_ICON_TOOL_FWUPDATE
);
1954 case TAG_CSR_ROTATE
:
1955 if ((GetCsrStatus(&CsrValue
) == EFI_SUCCESS
) && (GlobalConfig
.CsrValues
)) {
1956 TempMenuEntry
= CopyMenuEntry(&MenuEntryRotateCsr
);
1957 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_CSR_ROTATE
);
1958 AddMenuEntry(&MainMenu
, TempMenuEntry
);
1963 FindTool(MEMTEST_LOCATIONS
, MEMTEST_NAMES
, L
"Memory test utility", BUILTIN_ICON_TOOL_MEMTEST
);
1968 } // static VOID ScanForTools
1970 // Rescan for boot loaders
1971 static VOID
RescanAll(BOOLEAN DisplayMessage
) {
1979 egDisplayMessage(L
"Scanning for new boot loaders; please wait....", &BGColor
);
1980 FreeList((VOID
***) &(MainMenu
.Entries
), &MainMenu
.EntryCount
);
1981 MainMenu
.Entries
= NULL
;
1982 MainMenu
.EntryCount
= 0;
1983 ConnectAllDriversToAllControllers();
1985 ReadConfig(GlobalConfig
.ConfigFilename
);
1987 ScanForBootloaders();
1990 } // VOID RescanAll()
1992 #ifdef __MAKEWITH_TIANO
1994 // Minimal initialization function
1995 static VOID
InitializeLib(IN EFI_HANDLE ImageHandle
, IN EFI_SYSTEM_TABLE
*SystemTable
) {
1997 // gImageHandle = ImageHandle;
1998 gBS
= SystemTable
->BootServices
;
1999 // gRS = SystemTable->RuntimeServices;
2000 gRT
= SystemTable
->RuntimeServices
; // Some BDS functions need gRT to be set
2001 EfiGetSystemConfigurationTable (&gEfiDxeServicesTableGuid
, (VOID
**) &gDS
);
2006 // Set up our own Secure Boot extensions....
2007 // Returns TRUE on success, FALSE otherwise
2008 static BOOLEAN
SecureBootSetup(VOID
) {
2010 BOOLEAN Success
= FALSE
;
2012 if (secure_mode() && ShimLoaded()) {
2013 Status
= security_policy_install();
2014 if (Status
== EFI_SUCCESS
) {
2017 Print(L
"Failed to install MOK Secure Boot extensions");
2022 } // VOID SecureBootSetup()
2024 // Remove our own Secure Boot extensions....
2025 // Returns TRUE on success, FALSE otherwise
2026 static BOOLEAN
SecureBootUninstall(VOID
) {
2028 BOOLEAN Success
= TRUE
;
2030 if (secure_mode()) {
2031 Status
= security_policy_uninstall();
2032 if (Status
!= EFI_SUCCESS
) {
2034 BeginTextScreen(L
"Secure Boot Policy Failure");
2035 Print(L
"Failed to uninstall MOK Secure Boot extensions; forcing a reboot.");
2037 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);
2041 } // VOID SecureBootUninstall
2043 // Sets the global configuration filename; will be CONFIG_FILE_NAME unless the
2044 // "-c" command-line option is set, in which case that takes precedence.
2045 // If an error is encountered, leaves the value alone (it should be set to
2046 // CONFIG_FILE_NAME when GlobalConfig is initialized).
2047 static VOID
SetConfigFilename(EFI_HANDLE ImageHandle
) {
2048 EFI_LOADED_IMAGE
*Info
;
2049 CHAR16
*Options
, *FileName
, *SubString
;
2052 Status
= refit_call3_wrapper(BS
->HandleProtocol
, ImageHandle
, &LoadedImageProtocol
, (VOID
**) &Info
);
2053 if ((Status
== EFI_SUCCESS
) && (Info
->LoadOptionsSize
> 0)) {
2054 Options
= (CHAR16
*) Info
->LoadOptions
;
2055 SubString
= MyStrStr(Options
, L
" -c ");
2057 FileName
= StrDuplicate(&SubString
[4]);
2059 LimitStringLength(FileName
, 256);
2062 if (FileExists(SelfDir
, FileName
)) {
2063 GlobalConfig
.ConfigFilename
= FileName
;
2065 Print(L
"Specified configuration file (%s) doesn't exist; using\n'refind.conf' default\n", FileName
);
2066 MyFreePool(FileName
);
2070 } // VOID SetConfigFilename()
2077 efi_main (EFI_HANDLE ImageHandle
, EFI_SYSTEM_TABLE
*SystemTable
)
2080 BOOLEAN MainLoopRunning
= TRUE
;
2081 BOOLEAN MokProtocol
;
2082 REFIT_MENU_ENTRY
*ChosenEntry
;
2084 CHAR16
*SelectionName
= NULL
;
2088 InitializeLib(ImageHandle
, SystemTable
);
2089 Status
= InitRefitLib(ImageHandle
);
2090 if (EFI_ERROR(Status
))
2093 // read configuration
2094 CopyMem(GlobalConfig
.ScanFor
, "ieom ", NUM_SCAN_OPTIONS
);
2095 FindLegacyBootType();
2096 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
)
2097 CopyMem(GlobalConfig
.ScanFor
, "ihebocm ", NUM_SCAN_OPTIONS
);
2098 SetConfigFilename(ImageHandle
);
2099 MokProtocol
= SecureBootSetup();
2101 ScanVolumes(); // Do before ReadConfig() because it needs SelfVolume->VolName
2102 ReadConfig(GlobalConfig
.ConfigFilename
);
2105 if (GlobalConfig
.SpoofOSXVersion
&& GlobalConfig
.SpoofOSXVersion
[0] != L
'\0')
2109 WarnIfLegacyProblems();
2110 MainMenu
.TimeoutSeconds
= GlobalConfig
.Timeout
;
2112 // disable EFI watchdog timer
2113 refit_call4_wrapper(BS
->SetWatchdogTimer
, 0x0000, 0x0000, 0x0000, NULL
);
2115 // further bootstrap (now with config available)
2116 ScanForBootloaders();
2120 if (GlobalConfig
.ScanDelay
> 0) {
2125 if (GlobalConfig
.ScanDelay
> 1)
2126 egDisplayMessage(L
"Pausing before disk scan; please wait....", &BGColor
);
2127 for (i
= 0; i
< GlobalConfig
.ScanDelay
; i
++)
2128 refit_call1_wrapper(BS
->Stall
, 1000000);
2129 RescanAll(GlobalConfig
.ScanDelay
> 1);
2132 if (GlobalConfig
.DefaultSelection
)
2133 SelectionName
= StrDuplicate(GlobalConfig
.DefaultSelection
);
2135 while (MainLoopRunning
) {
2136 MenuExit
= RunMainMenu(&MainMenu
, &SelectionName
, &ChosenEntry
);
2138 // The Escape key triggers a re-scan operation....
2139 if (MenuExit
== MENU_EXIT_ESCAPE
) {
2145 switch (ChosenEntry
->Tag
) {
2147 case TAG_REBOOT
: // Reboot
2149 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);
2150 MainLoopRunning
= FALSE
; // just in case we get this far
2153 case TAG_SHUTDOWN
: // Shut Down
2155 refit_call4_wrapper(RT
->ResetSystem
, EfiResetShutdown
, EFI_SUCCESS
, 0, NULL
);
2156 MainLoopRunning
= FALSE
; // just in case we get this far
2159 case TAG_ABOUT
: // About rEFInd
2163 case TAG_LOADER
: // Boot OS via .EFI loader
2164 StartLoader((LOADER_ENTRY
*)ChosenEntry
, SelectionName
);
2167 case TAG_LEGACY
: // Boot legacy OS
2168 StartLegacy((LEGACY_ENTRY
*)ChosenEntry
, SelectionName
);
2171 case TAG_LEGACY_UEFI
: // Boot a legacy OS on a non-Mac
2172 StartLegacyUEFI((LEGACY_ENTRY
*)ChosenEntry
, SelectionName
);
2175 case TAG_TOOL
: // Start a EFI tool
2176 StartTool((LOADER_ENTRY
*)ChosenEntry
);
2179 case TAG_EXIT
: // Terminate rEFInd
2180 if ((MokProtocol
) && !SecureBootUninstall()) {
2181 MainLoopRunning
= FALSE
; // just in case we get this far
2183 BeginTextScreen(L
" ");
2188 case TAG_FIRMWARE
: // Reboot into firmware's user interface
2189 RebootIntoFirmware();
2192 case TAG_CSR_ROTATE
:
2199 // If we end up here, things have gone wrong. Try to reboot, and if that
2200 // fails, go into an endless loop.
2201 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);