3 * Main code for the boot menu
5 * Copyright (c) 2006-2010 Christoph Pfisterer
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions are
12 * * Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
15 * * Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the
20 * * Neither the name of Christoph Pfisterer nor the names of the
21 * contributors may be used to endorse or promote products derived
22 * from this software without specific prior written permission.
24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
26 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
27 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
28 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
29 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
30 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
31 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
32 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
33 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
34 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37 * Modifications copyright (c) 2012-2014 Roderick W. Smith
39 * Modifications distributed under the terms of the GNU General Public
40 * License (GPL) version 3 (GPLv3), a copy of which must be distributed
41 * with this source code or binaries made from it.
53 #include "security_policy.h"
54 #include "../include/Handle.h"
55 #include "../include/refit_call_wrapper.h"
56 #include "driver_support.h"
57 #include "../include/syslinux_mbr.h"
59 #ifdef __MAKEWITH_GNUEFI
60 #ifndef EFI_SECURITY_VIOLATION
61 #define EFI_SECURITY_VIOLATION EFIERR (26)
65 #include "../EfiLib/BdsHelper.h"
66 #include "../EfiLib/legacy.h"
68 #ifndef EFI_OS_INDICATIONS_BOOT_TO_FW_UI
69 #define EFI_OS_INDICATIONS_BOOT_TO_FW_UI 0x0000000000000001ULL
72 #ifdef __MAKEWITH_TIANO
73 #define LibLocateHandle gBS->LocateHandleBuffer
79 #define MACOSX_LOADER_PATH L"System\\Library\\CoreServices\\boot.efi"
81 #define SHELL_NAMES L"\\EFI\\tools\\shell.efi,\\EFI\\tools\\shellx64.efi,\\shell.efi,\\shellx64.efi"
82 #define GPTSYNC_NAMES L"\\EFI\\tools\\gptsync.efi,\\EFI\\tools\\gptsync_x64.efi"
83 #define GDISK_NAMES L"\\EFI\\tools\\gdisk.efi,\\EFI\\tools\\gdisk_x64.efi"
84 #define 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 // Default hint text for program-launch submenus
128 #define SUBSCREEN_HINT1 L"Use arrow keys to move cursor; Enter to boot;"
129 #define SUBSCREEN_HINT2 L"Insert or F2 to edit options; Esc to return to main menu"
130 #define SUBSCREEN_HINT2_NO_EDITOR L"Esc to return to main menu"
134 #define TYPE_LEGACY 2
136 static REFIT_MENU_ENTRY MenuEntryAbout
= { L
"About rEFInd", TAG_ABOUT
, 1, 0, 'A', NULL
, NULL
, NULL
};
137 static REFIT_MENU_ENTRY MenuEntryReset
= { L
"Reboot Computer", TAG_REBOOT
, 1, 0, 'R', NULL
, NULL
, NULL
};
138 static REFIT_MENU_ENTRY MenuEntryShutdown
= { L
"Shut Down Computer", TAG_SHUTDOWN
, 1, 0, 'U', NULL
, NULL
, NULL
};
139 static REFIT_MENU_ENTRY MenuEntryReturn
= { L
"Return to Main Menu", TAG_RETURN
, 1, 0, 0, NULL
, NULL
, NULL
};
140 static REFIT_MENU_ENTRY MenuEntryExit
= { L
"Exit rEFInd", TAG_EXIT
, 1, 0, 0, NULL
, NULL
, NULL
};
141 static REFIT_MENU_ENTRY MenuEntryFirmware
= { L
"Reboot to Computer Setup Utility", TAG_FIRMWARE
, 1, 0, 0, NULL
, NULL
, NULL
};
143 static REFIT_MENU_SCREEN MainMenu
= { L
"Main Menu", NULL
, 0, NULL
, 0, NULL
, 0, L
"Automatic boot",
144 L
"Use arrow keys to move cursor; Enter to boot;",
145 L
"Insert or F2 for more options; Esc to refresh" };
146 static REFIT_MENU_SCREEN AboutMenu
= { L
"About", NULL
, 0, NULL
, 0, NULL
, 0, NULL
, L
"Press Enter to return to main menu", L
"" };
148 REFIT_CONFIG GlobalConfig
= { FALSE
, TRUE
, FALSE
, FALSE
, 0, 0, 0, DONT_CHANGE_TEXT_MODE
, 20, 0, 0, GRAPHICS_FOR_OSX
, LEGACY_TYPE_MAC
,
149 0, 0, { DEFAULT_BIG_ICON_SIZE
/ 4, DEFAULT_SMALL_ICON_SIZE
, DEFAULT_BIG_ICON_SIZE
}, BANNER_NOSCALE
,
150 NULL
, NULL
, CONFIG_FILE_NAME
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
151 { TAG_SHELL
, TAG_MEMTEST
, TAG_GDISK
, TAG_APPLE_RECOVERY
, TAG_WINDOWS_RECOVERY
, TAG_MOK_TOOL
,
152 TAG_ABOUT
, TAG_SHUTDOWN
, TAG_REBOOT
, TAG_FIRMWARE
, 0, 0, 0, 0, 0, 0, 0, 0 }
155 EFI_GUID GlobalGuid
= EFI_GLOBAL_VARIABLE
;
156 EFI_GUID RefindGuid
= REFIND_GUID_VALUE
;
158 GPT_DATA
*gPartitions
= NULL
;
160 // Structure used to hold boot loader filenames and time stamps in
161 // a linked list; used to sort entries within a directory.
165 struct LOADER_LIST
*NextEntry
;
172 static VOID
AboutrEFInd(VOID
)
174 CHAR16
*FirmwareVendor
;
176 if (AboutMenu
.EntryCount
== 0) {
177 AboutMenu
.TitleImage
= BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT
);
178 AddMenuInfoLine(&AboutMenu
, L
"rEFInd Version 0.8.3.4");
179 AddMenuInfoLine(&AboutMenu
, L
"");
180 AddMenuInfoLine(&AboutMenu
, L
"Copyright (c) 2006-2010 Christoph Pfisterer");
181 AddMenuInfoLine(&AboutMenu
, L
"Copyright (c) 2012-2014 Roderick W. Smith");
182 AddMenuInfoLine(&AboutMenu
, L
"Portions Copyright (c) Intel Corporation and others");
183 AddMenuInfoLine(&AboutMenu
, L
"Distributed under the terms of the GNU GPLv3 license");
184 AddMenuInfoLine(&AboutMenu
, L
"");
185 AddMenuInfoLine(&AboutMenu
, L
"Running on:");
186 AddMenuInfoLine(&AboutMenu
, PoolPrint(L
" EFI Revision %d.%02d", ST
->Hdr
.Revision
>> 16, ST
->Hdr
.Revision
& ((1 << 16) - 1)));
188 AddMenuInfoLine(&AboutMenu
, L
" Platform: x86 (32 bit)");
189 #elif defined(EFIX64)
190 AddMenuInfoLine(&AboutMenu
, PoolPrint(L
" Platform: x86_64 (64 bit); Secure Boot %s",
191 secure_mode() ? L
"active" : L
"inactive"));
193 AddMenuInfoLine(&AboutMenu
, L
" Platform: unknown");
195 FirmwareVendor
= StrDuplicate(ST
->FirmwareVendor
);
196 LimitStringLength(FirmwareVendor
, 65); // More than ~65 causes empty info page on 800x600 display
197 AddMenuInfoLine(&AboutMenu
, PoolPrint(L
" Firmware: %s %d.%02d", FirmwareVendor
, ST
->FirmwareRevision
>> 16,
198 ST
->FirmwareRevision
& ((1 << 16) - 1)));
199 AddMenuInfoLine(&AboutMenu
, PoolPrint(L
" Screen Output: %s", egScreenDescription()));
200 AddMenuInfoLine(&AboutMenu
, L
"");
201 #if defined(__MAKEWITH_GNUEFI)
202 AddMenuInfoLine(&AboutMenu
, L
"Built with GNU-EFI");
204 AddMenuInfoLine(&AboutMenu
, L
"Built with TianoCore EDK2");
206 AddMenuInfoLine(&AboutMenu
, L
"");
207 AddMenuInfoLine(&AboutMenu
, L
"For more information, see the rEFInd Web site:");
208 AddMenuInfoLine(&AboutMenu
, L
"http://www.rodsbooks.com/refind/");
209 AddMenuEntry(&AboutMenu
, &MenuEntryReturn
);
212 RunMenu(&AboutMenu
, NULL
);
213 } /* VOID AboutrEFInd() */
215 static VOID
WarnSecureBootError(CHAR16
*Name
, BOOLEAN Verbose
) {
217 Name
= L
"the loader";
219 refit_call2_wrapper(ST
->ConOut
->SetAttribute
, ST
->ConOut
, ATTR_ERROR
);
220 Print(L
"Secure Boot validation failure loading %s!\n", Name
);
221 refit_call2_wrapper(ST
->ConOut
->SetAttribute
, ST
->ConOut
, ATTR_BASIC
);
222 if (Verbose
&& secure_mode()) {
223 Print(L
"\nThis computer is configured with Secure Boot active, but\n%s has failed validation.\n", Name
);
224 Print(L
"\nYou can:\n * Launch another boot loader\n");
225 Print(L
" * Disable Secure Boot in your firmware\n");
226 Print(L
" * Sign %s with a machine owner key (MOK)\n", Name
);
227 Print(L
" * Use a MOK utility (often present on the second row) to add a MOK with which\n");
228 Print(L
" %s has already been signed.\n", Name
);
229 Print(L
" * Use a MOK utility to register %s (\"enroll its hash\") without\n", Name
);
230 Print(L
" signing it.\n");
231 Print(L
"\nSee http://www.rodsbooks.com/refind/secureboot.html for more information\n");
234 } // VOID WarnSecureBootError()
236 // Returns TRUE if this file is a valid EFI loader file, and is proper ARCH
237 static BOOLEAN
IsValidLoader(EFI_FILE
*RootDir
, CHAR16
*FileName
) {
238 BOOLEAN IsValid
= TRUE
;
239 #if defined (EFIX64) | defined (EFI32)
241 EFI_FILE_HANDLE FileHandle
;
243 UINTN Size
= sizeof(Header
);
245 if ((RootDir
== NULL
) || (FileName
== NULL
)) {
246 // Assume valid here, because Macs produce NULL RootDir (& maybe FileName)
247 // when launching from a Firewire drive. This should be handled better, but
248 // fix would have to be in StartEFIImageList() and/or in FindVolumeAndFilename().
252 Status
= refit_call5_wrapper(RootDir
->Open
, RootDir
, &FileHandle
, FileName
, EFI_FILE_MODE_READ
, 0);
253 if (EFI_ERROR(Status
))
256 Status
= refit_call3_wrapper(FileHandle
->Read
, FileHandle
, &Size
, Header
);
257 refit_call1_wrapper(FileHandle
->Close
, FileHandle
);
259 IsValid
= !EFI_ERROR(Status
) &&
260 Size
== sizeof(Header
) &&
261 ((Header
[0] == 'M' && Header
[1] == 'Z' &&
262 (Size
= *(UINT32
*)&Header
[0x3c]) < 0x180 &&
263 Header
[Size
] == 'P' && Header
[Size
+1] == 'E' &&
264 Header
[Size
+2] == 0 && Header
[Size
+3] == 0 &&
265 *(UINT16
*)&Header
[Size
+4] == EFI_STUB_ARCH
) ||
266 (*(UINT32
*)&Header
== FAT_ARCH
));
269 } // BOOLEAN IsValidLoader()
271 // Launch an EFI binary.
272 static EFI_STATUS
StartEFIImageList(IN EFI_DEVICE_PATH
**DevicePaths
,
273 IN CHAR16
*LoadOptions
, IN UINTN LoaderType
,
274 IN CHAR16
*ImageTitle
, IN CHAR8 OSType
,
275 OUT UINTN
*ErrorInStep
,
279 EFI_STATUS Status
, ReturnStatus
;
280 EFI_HANDLE ChildImageHandle
;
281 EFI_LOADED_IMAGE
*ChildLoadedImage
= NULL
;
282 REFIT_VOLUME
*Volume
= NULL
;
283 UINTN DevicePathIndex
;
284 CHAR16 ErrorInfo
[256];
285 CHAR16
*FullLoadOptions
= NULL
;
286 CHAR16
*Filename
= NULL
;
289 if (ErrorInStep
!= NULL
)
293 if (LoadOptions
!= NULL
) {
294 FullLoadOptions
= StrDuplicate(LoadOptions
);
295 if ((LoaderType
== TYPE_EFI
) && (OSType
== 'M')) {
296 MergeStrings(&FullLoadOptions
, L
" ", 0);
297 // NOTE: That last space is also added by the EFI shell and seems to be significant
298 // when passing options to Apple's boot.efi...
300 } // if (LoadOptions != NULL)
302 Print(L
"Starting %s\nUsing load options '%s'\n", ImageTitle
, FullLoadOptions
? FullLoadOptions
: L
"");
304 // load the image into memory (and execute it, in the case of a shim/MOK image).
305 ReturnStatus
= Status
= EFI_NOT_FOUND
; // in case the list is empty
306 for (DevicePathIndex
= 0; DevicePaths
[DevicePathIndex
] != NULL
; DevicePathIndex
++) {
307 FindVolumeAndFilename(DevicePaths
[DevicePathIndex
], &Volume
, &Filename
);
308 // Some EFIs crash if attempting to load driver for invalid architecture, so
309 // protect for this condition; but sometimes Volume comes back NULL, so provide
310 // an exception. (TODO: Handle this special condition better.)
311 if ((LoaderType
== TYPE_LEGACY
) || (Volume
== NULL
) || IsValidLoader(Volume
->RootDir
, Filename
)) {
312 if (Filename
&& (LoaderType
!= TYPE_LEGACY
)) {
313 Temp
= PoolPrint(L
"\\%s %s", Filename
, FullLoadOptions
? FullLoadOptions
: L
"");
315 MyFreePool(FullLoadOptions
);
316 FullLoadOptions
= Temp
;
320 // NOTE: Below commented-out line could be more efficient if file were read ahead of
321 // time and passed as a pre-loaded image to LoadImage(), but it doesn't work on my
322 // 32-bit Mac Mini or my 64-bit Intel box when launching a Linux kernel; the
323 // kernel returns a "Failed to handle fs_proto" error message.
324 // TODO: Track down the cause of this error and fix it, if possible.
325 // ReturnStatus = Status = refit_call6_wrapper(BS->LoadImage, FALSE, SelfImageHandle, DevicePaths[DevicePathIndex],
326 // ImageData, ImageSize, &ChildImageHandle);
327 ReturnStatus
= Status
= refit_call6_wrapper(BS
->LoadImage
, FALSE
, SelfImageHandle
, DevicePaths
[DevicePathIndex
],
328 NULL
, 0, &ChildImageHandle
);
330 Print(L
"Invalid loader file!\n");
331 ReturnStatus
= EFI_LOAD_ERROR
;
333 if (ReturnStatus
!= EFI_NOT_FOUND
) {
337 if ((Status
== EFI_ACCESS_DENIED
) || (Status
== EFI_SECURITY_VIOLATION
)) {
338 WarnSecureBootError(ImageTitle
, Verbose
);
341 SPrint(ErrorInfo
, 255, L
"while loading %s", ImageTitle
);
342 if (CheckError(Status
, ErrorInfo
)) {
343 if (ErrorInStep
!= NULL
)
348 ReturnStatus
= Status
= refit_call3_wrapper(BS
->HandleProtocol
, ChildImageHandle
, &LoadedImageProtocol
,
349 (VOID
**) &ChildLoadedImage
);
350 if (CheckError(Status
, L
"while getting a LoadedImageProtocol handle")) {
351 if (ErrorInStep
!= NULL
)
355 ChildLoadedImage
->LoadOptions
= (VOID
*)FullLoadOptions
;
356 ChildLoadedImage
->LoadOptionsSize
= FullLoadOptions
? ((UINT32
)StrLen(FullLoadOptions
) + 1) * sizeof(CHAR16
) : 0;
357 // turn control over to the image
358 // TODO: (optionally) re-enable the EFI watchdog timer!
360 // close open file handles
362 ReturnStatus
= Status
= refit_call3_wrapper(BS
->StartImage
, ChildImageHandle
, NULL
, NULL
);
364 // control returns here when the child image calls Exit()
365 SPrint(ErrorInfo
, 255, L
"returned from %s", ImageTitle
);
366 if (CheckError(Status
, ErrorInfo
)) {
367 if (ErrorInStep
!= NULL
)
371 // re-open file handles
375 // unload the image, we don't care if it works or not...
377 Status
= refit_call1_wrapper(BS
->UnloadImage
, ChildImageHandle
);
380 MyFreePool(FullLoadOptions
);
382 } /* static EFI_STATUS StartEFIImageList() */
384 static EFI_STATUS
StartEFIImage(IN EFI_DEVICE_PATH
*DevicePath
,
385 IN CHAR16
*LoadOptions
, IN UINTN LoaderType
,
386 IN CHAR16
*ImageTitle
, IN CHAR8 OSType
,
387 OUT UINTN
*ErrorInStep
,
392 EFI_DEVICE_PATH
*DevicePaths
[2];
394 DevicePaths
[0] = DevicePath
;
395 DevicePaths
[1] = NULL
;
396 return StartEFIImageList(DevicePaths
, LoadOptions
, LoaderType
, ImageTitle
, OSType
, ErrorInStep
, Verbose
, IsDriver
);
397 } /* static EFI_STATUS StartEFIImage() */
399 // From gummiboot: Reboot the computer into its built-in user interface
400 static EFI_STATUS
RebootIntoFirmware(VOID
) {
406 osind
= EFI_OS_INDICATIONS_BOOT_TO_FW_UI
;
408 err
= EfivarGetRaw(&GlobalGuid
, L
"OsIndications", &b
, &size
);
409 if (err
== EFI_SUCCESS
)
413 err
= EfivarSetRaw(&GlobalGuid
, L
"OsIndications", (CHAR8
*)&osind
, sizeof(UINT64
), TRUE
);
414 if (err
!= EFI_SUCCESS
)
417 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);
418 Print(L
"Error calling ResetSystem: %r", err
);
423 // Record the value of the loader's name/description in rEFInd's "PreviousBoot" EFI variable,
424 // if it's different from what's already stored there.
425 static VOID
StoreLoaderName(IN CHAR16
*Name
) {
427 CHAR16
*OldName
= NULL
;
431 Status
= EfivarGetRaw(&RefindGuid
, L
"PreviousBoot", (CHAR8
**) &OldName
, &Length
);
432 if ((Status
!= EFI_SUCCESS
) || (StrCmp(OldName
, Name
) != 0)) {
433 EfivarSetRaw(&RefindGuid
, L
"PreviousBoot", (CHAR8
*) Name
, StrLen(Name
) * 2 + 2, TRUE
);
437 } // VOID StoreLoaderName()
440 // EFI OS loader functions
443 // See http://www.thomas-krenn.com/en/wiki/Activating_the_Intel_VT_Virtualization_Feature
444 // for information on Intel VMX features
445 static VOID
DoEnableAndLockVMX(VOID
)
448 UINT32 low_bits
= 0, high_bits
= 0;
451 __asm__
volatile ("rdmsr" : "=a" (low_bits
), "=d" (high_bits
) : "c" (msr
));
453 // enable and lock vmx if not locked
454 if ((low_bits
& 1) == 0) {
458 __asm__
volatile ("wrmsr" : : "c" (msr
), "a" (low_bits
), "d" (high_bits
));
460 } // VOID DoEnableAndLockVMX()
462 static VOID
StartLoader(LOADER_ENTRY
*Entry
, CHAR16
*SelectionName
)
464 UINTN ErrorInStep
= 0;
466 if (GlobalConfig
.EnableAndLockVMX
) {
467 DoEnableAndLockVMX();
470 BeginExternalScreen(Entry
->UseGraphicsMode
, L
"Booting OS");
471 StoreLoaderName(SelectionName
);
472 StartEFIImage(Entry
->DevicePath
, Entry
->LoadOptions
, TYPE_EFI
,
473 Basename(Entry
->LoaderPath
), Entry
->OSType
, &ErrorInStep
, !Entry
->UseGraphicsMode
, FALSE
);
474 FinishExternalScreen();
477 // Locate an initrd or initramfs file that matches the kernel specified by LoaderPath.
478 // The matching file has a name that begins with "init" and includes the same version
479 // number string as is found in LoaderPath -- but not a longer version number string.
480 // For instance, if LoaderPath is \EFI\kernels\bzImage-3.3.0.efi, and if \EFI\kernels
481 // has a file called initramfs-3.3.0.img, this function will return the string
482 // '\EFI\kernels\initramfs-3.3.0.img'. If the directory ALSO contains the file
483 // initramfs-3.3.0-rc7.img or initramfs-13.3.0.img, those files will NOT match;
484 // however, initmine-3.3.0.img might match. (FindInitrd() returns the first match it
485 // finds.) Thus, care should be taken to avoid placing duplicate matching files in
486 // the kernel's directory.
487 // If no matching init file can be found, returns NULL.
488 static CHAR16
* FindInitrd(IN CHAR16
*LoaderPath
, IN REFIT_VOLUME
*Volume
) {
489 CHAR16
*InitrdName
= NULL
, *FileName
, *KernelVersion
, *InitrdVersion
, *Path
;
490 REFIT_DIR_ITER DirIter
;
491 EFI_FILE_INFO
*DirEntry
;
493 FileName
= Basename(LoaderPath
);
494 KernelVersion
= FindNumbers(FileName
);
495 Path
= FindPath(LoaderPath
);
497 // Add trailing backslash for root directory; necessary on some systems, but must
498 // NOT be added to all directories, since on other systems, a trailing backslash on
499 // anything but the root directory causes them to flake out!
500 if (StrLen(Path
) == 0) {
501 MergeStrings(&Path
, L
"\\", 0);
503 DirIterOpen(Volume
->RootDir
, Path
, &DirIter
);
504 // Now add a trailing backslash if it was NOT added earlier, for consistency in
505 // building the InitrdName later....
506 if ((StrLen(Path
) > 0) && (Path
[StrLen(Path
) - 1] != L
'\\'))
507 MergeStrings(&Path
, L
"\\", 0);
508 while ((DirIterNext(&DirIter
, 2, L
"init*", &DirEntry
)) && (InitrdName
== NULL
)) {
509 InitrdVersion
= FindNumbers(DirEntry
->FileName
);
510 if (KernelVersion
!= NULL
) {
511 if (StriCmp(InitrdVersion
, KernelVersion
) == 0) {
512 InitrdName
= PoolPrint(L
"%s%s", Path
, DirEntry
->FileName
);
515 if (InitrdVersion
== NULL
) {
516 InitrdName
= PoolPrint(L
"%s%s", Path
, DirEntry
->FileName
);
519 MyFreePool(InitrdVersion
);
521 DirIterClose(&DirIter
);
523 // Note: Don't FreePool(FileName), since Basename returns a pointer WITHIN the string it's passed.
524 MyFreePool(KernelVersion
);
527 } // static CHAR16 * FindInitrd()
529 LOADER_ENTRY
* AddPreparedLoaderEntry(LOADER_ENTRY
*Entry
) {
530 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
533 } // LOADER_ENTRY * AddPreparedLoaderEntry()
535 // Creates a copy of a menu screen.
536 // Returns a pointer to the copy of the menu screen.
537 static REFIT_MENU_SCREEN
* CopyMenuScreen(REFIT_MENU_SCREEN
*Entry
) {
538 REFIT_MENU_SCREEN
*NewEntry
;
541 NewEntry
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
542 if ((Entry
!= NULL
) && (NewEntry
!= NULL
)) {
543 CopyMem(NewEntry
, Entry
, sizeof(REFIT_MENU_SCREEN
));
544 NewEntry
->Title
= (Entry
->Title
) ? StrDuplicate(Entry
->Title
) : NULL
;
545 NewEntry
->TimeoutText
= (Entry
->TimeoutText
) ? StrDuplicate(Entry
->TimeoutText
) : NULL
;
546 if (Entry
->TitleImage
!= NULL
) {
547 NewEntry
->TitleImage
= AllocatePool(sizeof(EG_IMAGE
));
548 if (NewEntry
->TitleImage
!= NULL
)
549 CopyMem(NewEntry
->TitleImage
, Entry
->TitleImage
, sizeof(EG_IMAGE
));
551 NewEntry
->InfoLines
= (CHAR16
**) AllocateZeroPool(Entry
->InfoLineCount
* (sizeof(CHAR16
*)));
552 for (i
= 0; i
< Entry
->InfoLineCount
&& NewEntry
->InfoLines
; i
++) {
553 NewEntry
->InfoLines
[i
] = (Entry
->InfoLines
[i
]) ? StrDuplicate(Entry
->InfoLines
[i
]) : NULL
;
555 NewEntry
->Entries
= (REFIT_MENU_ENTRY
**) AllocateZeroPool(Entry
->EntryCount
* (sizeof (REFIT_MENU_ENTRY
*)));
556 for (i
= 0; i
< Entry
->EntryCount
&& NewEntry
->Entries
; i
++) {
557 AddMenuEntry(NewEntry
, Entry
->Entries
[i
]);
559 NewEntry
->Hint1
= (Entry
->Hint1
) ? StrDuplicate(Entry
->Hint1
) : NULL
;
560 NewEntry
->Hint2
= (Entry
->Hint2
) ? StrDuplicate(Entry
->Hint2
) : NULL
;
563 } // static REFIT_MENU_SCREEN* CopyMenuScreen()
565 // Creates a copy of a menu entry. Intended to enable moving a stack-based
566 // menu entry (such as the ones for the "reboot" and "exit" functions) to
567 // to the heap. This enables easier deletion of the whole set of menu
568 // entries when re-scanning.
569 // Returns a pointer to the copy of the menu entry.
570 static REFIT_MENU_ENTRY
* CopyMenuEntry(REFIT_MENU_ENTRY
*Entry
) {
571 REFIT_MENU_ENTRY
*NewEntry
;
573 NewEntry
= AllocateZeroPool(sizeof(REFIT_MENU_ENTRY
));
574 if ((Entry
!= NULL
) && (NewEntry
!= NULL
)) {
575 CopyMem(NewEntry
, Entry
, sizeof(REFIT_MENU_ENTRY
));
576 NewEntry
->Title
= (Entry
->Title
) ? StrDuplicate(Entry
->Title
) : NULL
;
577 if (Entry
->BadgeImage
!= NULL
) {
578 NewEntry
->BadgeImage
= AllocatePool(sizeof(EG_IMAGE
));
579 if (NewEntry
->BadgeImage
!= NULL
)
580 CopyMem(NewEntry
->BadgeImage
, Entry
->BadgeImage
, sizeof(EG_IMAGE
));
582 if (Entry
->Image
!= NULL
) {
583 NewEntry
->Image
= AllocatePool(sizeof(EG_IMAGE
));
584 if (NewEntry
->Image
!= NULL
)
585 CopyMem(NewEntry
->Image
, Entry
->Image
, sizeof(EG_IMAGE
));
587 if (Entry
->SubScreen
!= NULL
) {
588 NewEntry
->SubScreen
= CopyMenuScreen(Entry
->SubScreen
);
592 } // REFIT_MENU_ENTRY* CopyMenuEntry()
594 // Creates a new LOADER_ENTRY data structure and populates it with
595 // default values from the specified Entry, or NULL values if Entry
596 // is unspecified (NULL).
597 // Returns a pointer to the new data structure, or NULL if it
598 // couldn't be allocated
599 LOADER_ENTRY
*InitializeLoaderEntry(IN LOADER_ENTRY
*Entry
) {
600 LOADER_ENTRY
*NewEntry
= NULL
;
602 NewEntry
= AllocateZeroPool(sizeof(LOADER_ENTRY
));
603 if (NewEntry
!= NULL
) {
604 NewEntry
->me
.Title
= NULL
;
605 NewEntry
->me
.Tag
= TAG_LOADER
;
606 NewEntry
->Enabled
= TRUE
;
607 NewEntry
->UseGraphicsMode
= FALSE
;
608 NewEntry
->OSType
= 0;
610 NewEntry
->LoaderPath
= (Entry
->LoaderPath
) ? StrDuplicate(Entry
->LoaderPath
) : NULL
;
611 NewEntry
->VolName
= (Entry
->VolName
) ? StrDuplicate(Entry
->VolName
) : NULL
;
612 NewEntry
->DevicePath
= Entry
->DevicePath
;
613 NewEntry
->UseGraphicsMode
= Entry
->UseGraphicsMode
;
614 NewEntry
->LoadOptions
= (Entry
->LoadOptions
) ? StrDuplicate(Entry
->LoadOptions
) : NULL
;
615 NewEntry
->InitrdPath
= (Entry
->InitrdPath
) ? StrDuplicate(Entry
->InitrdPath
) : NULL
;
619 } // LOADER_ENTRY *InitializeLoaderEntry()
621 // Adds InitrdPath to Options, but only if Options doesn't already include an
622 // initrd= line. Done to enable overriding the default initrd selection in a
623 // refind_linux.conf file's options list.
624 // Returns a pointer to a new string. The calling function is responsible for
625 // freeing its memory.
626 static CHAR16
*AddInitrdToOptions(CHAR16
*Options
, CHAR16
*InitrdPath
) {
627 CHAR16
*NewOptions
= NULL
;
630 NewOptions
= StrDuplicate(Options
);
631 if ((InitrdPath
!= NULL
) && !StriSubCmp(L
"initrd=", Options
)) {
632 MergeStrings(&NewOptions
, L
"initrd=", L
' ');
633 MergeStrings(&NewOptions
, InitrdPath
, 0);
636 } // CHAR16 *AddInitrdToOptions()
638 // Prepare a REFIT_MENU_SCREEN data structure for a subscreen entry. This sets up
639 // the default entry that launches the boot loader using the same options as the
640 // main Entry does. Subsequent options can be added by the calling function.
641 // If a subscreen already exists in the Entry that's passed to this function,
642 // it's left unchanged and a pointer to it is returned.
643 // Returns a pointer to the new subscreen data structure, or NULL if there
644 // were problems allocating memory.
645 REFIT_MENU_SCREEN
*InitializeSubScreen(IN LOADER_ENTRY
*Entry
) {
646 CHAR16
*FileName
, *MainOptions
= NULL
;
647 REFIT_MENU_SCREEN
*SubScreen
= NULL
;
648 LOADER_ENTRY
*SubEntry
;
650 FileName
= Basename(Entry
->LoaderPath
);
651 if (Entry
->me
.SubScreen
== NULL
) { // No subscreen yet; initialize default entry....
652 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
653 if (SubScreen
!= NULL
) {
654 SubScreen
->Title
= AllocateZeroPool(sizeof(CHAR16
) * 256);
655 SPrint(SubScreen
->Title
, 255, L
"Boot Options for %s on %s",
656 (Entry
->Title
!= NULL
) ? Entry
->Title
: FileName
, Entry
->VolName
);
657 SubScreen
->TitleImage
= Entry
->me
.Image
;
659 SubEntry
= InitializeLoaderEntry(Entry
);
660 if (SubEntry
!= NULL
) {
661 SubEntry
->me
.Title
= StrDuplicate(L
"Boot using default options");
662 MainOptions
= SubEntry
->LoadOptions
;
663 SubEntry
->LoadOptions
= AddInitrdToOptions(MainOptions
, SubEntry
->InitrdPath
);
664 MyFreePool(MainOptions
);
665 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
666 } // if (SubEntry != NULL)
667 SubScreen
->Hint1
= StrDuplicate(SUBSCREEN_HINT1
);
668 if (GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_EDITOR
) {
669 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2_NO_EDITOR
);
671 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2
);
673 } // if (SubScreen != NULL)
674 } else { // existing subscreen; less initialization, and just add new entry later....
675 SubScreen
= Entry
->me
.SubScreen
;
678 } // REFIT_MENU_SCREEN *InitializeSubScreen()
680 VOID
GenerateSubScreen(LOADER_ENTRY
*Entry
, IN REFIT_VOLUME
*Volume
) {
681 REFIT_MENU_SCREEN
*SubScreen
;
682 LOADER_ENTRY
*SubEntry
;
684 CHAR16 DiagsFileName
[256];
689 // create the submenu
690 if (StrLen(Entry
->Title
) == 0) {
691 MyFreePool(Entry
->Title
);
694 SubScreen
= InitializeSubScreen(Entry
);
696 // loader-specific submenu entries
697 if (Entry
->OSType
== 'M') { // entries for Mac OS X
699 SubEntry
= InitializeLoaderEntry(Entry
);
700 if (SubEntry
!= NULL
) {
701 SubEntry
->me
.Title
= L
"Boot Mac OS X with a 64-bit kernel";
702 SubEntry
->LoadOptions
= L
"arch=x86_64";
703 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
704 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
707 SubEntry
= InitializeLoaderEntry(Entry
);
708 if (SubEntry
!= NULL
) {
709 SubEntry
->me
.Title
= L
"Boot Mac OS X with a 32-bit kernel";
710 SubEntry
->LoadOptions
= L
"arch=i386";
711 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
712 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
716 if (!(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_SINGLEUSER
)) {
717 SubEntry
= InitializeLoaderEntry(Entry
);
718 if (SubEntry
!= NULL
) {
719 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode";
720 SubEntry
->UseGraphicsMode
= FALSE
;
721 SubEntry
->LoadOptions
= L
"-v";
722 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
726 SubEntry
= InitializeLoaderEntry(Entry
);
727 if (SubEntry
!= NULL
) {
728 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode (64-bit)";
729 SubEntry
->UseGraphicsMode
= FALSE
;
730 SubEntry
->LoadOptions
= L
"-v arch=x86_64";
731 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
734 SubEntry
= InitializeLoaderEntry(Entry
);
735 if (SubEntry
!= NULL
) {
736 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode (32-bit)";
737 SubEntry
->UseGraphicsMode
= FALSE
;
738 SubEntry
->LoadOptions
= L
"-v arch=i386";
739 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
743 SubEntry
= InitializeLoaderEntry(Entry
);
744 if (SubEntry
!= NULL
) {
745 SubEntry
->me
.Title
= L
"Boot Mac OS X in single user mode";
746 SubEntry
->UseGraphicsMode
= FALSE
;
747 SubEntry
->LoadOptions
= L
"-v -s";
748 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
750 } // single-user mode allowed
752 if (!(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_SAFEMODE
)) {
753 SubEntry
= InitializeLoaderEntry(Entry
);
754 if (SubEntry
!= NULL
) {
755 SubEntry
->me
.Title
= L
"Boot Mac OS X in safe mode";
756 SubEntry
->UseGraphicsMode
= FALSE
;
757 SubEntry
->LoadOptions
= L
"-v -x";
758 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
760 } // safe mode allowed
762 // check for Apple hardware diagnostics
763 StrCpy(DiagsFileName
, L
"System\\Library\\CoreServices\\.diagnostics\\diags.efi");
764 if (FileExists(Volume
->RootDir
, DiagsFileName
) && !(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_HWTEST
)) {
765 SubEntry
= InitializeLoaderEntry(Entry
);
766 if (SubEntry
!= NULL
) {
767 SubEntry
->me
.Title
= L
"Run Apple Hardware Test";
768 MyFreePool(SubEntry
->LoaderPath
);
769 SubEntry
->LoaderPath
= StrDuplicate(DiagsFileName
);
770 SubEntry
->DevicePath
= FileDevicePath(Volume
->DeviceHandle
, SubEntry
->LoaderPath
);
771 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
772 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
774 } // if diagnostics entry found
776 } else if (Entry
->OSType
== 'L') { // entries for Linux kernels with EFI stub loaders
777 File
= ReadLinuxOptionsFile(Entry
->LoaderPath
, Volume
);
779 InitrdName
= FindInitrd(Entry
->LoaderPath
, Volume
);
780 TokenCount
= ReadTokenLine(File
, &TokenList
);
781 // first entry requires special processing, since it was initially set
782 // up with a default title but correct options by InitializeSubScreen(),
784 if ((SubScreen
->Entries
!= NULL
) && (SubScreen
->Entries
[0] != NULL
)) {
785 MyFreePool(SubScreen
->Entries
[0]->Title
);
786 SubScreen
->Entries
[0]->Title
= TokenList
[0] ? StrDuplicate(TokenList
[0]) : StrDuplicate(L
"Boot Linux");
788 FreeTokenLine(&TokenList
, &TokenCount
);
789 while ((TokenCount
= ReadTokenLine(File
, &TokenList
)) > 1) {
790 SubEntry
= InitializeLoaderEntry(Entry
);
791 SubEntry
->me
.Title
= TokenList
[0] ? StrDuplicate(TokenList
[0]) : StrDuplicate(L
"Boot Linux");
792 MyFreePool(SubEntry
->LoadOptions
);
793 SubEntry
->LoadOptions
= AddInitrdToOptions(TokenList
[1], InitrdName
);
794 FreeTokenLine(&TokenList
, &TokenCount
);
795 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_LINUX
;
796 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
798 MyFreePool(InitrdName
);
802 } else if (Entry
->OSType
== 'E') { // entries for ELILO
803 SubEntry
= InitializeLoaderEntry(Entry
);
804 if (SubEntry
!= NULL
) {
805 SubEntry
->me
.Title
= L
"Run ELILO in interactive mode";
806 SubEntry
->LoadOptions
= L
"-p";
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 17\" iMac or a 15\" MacBook Pro (*)";
814 SubEntry
->UseGraphicsMode
= TRUE
;
815 SubEntry
->LoadOptions
= L
"-d 0 i17";
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 20\" iMac (*)";
823 SubEntry
->UseGraphicsMode
= TRUE
;
824 SubEntry
->LoadOptions
= L
"-d 0 i20";
825 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
826 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
829 SubEntry
= InitializeLoaderEntry(Entry
);
830 if (SubEntry
!= NULL
) {
831 SubEntry
->me
.Title
= L
"Boot Linux for a Mac Mini (*)";
832 SubEntry
->UseGraphicsMode
= TRUE
;
833 SubEntry
->LoadOptions
= L
"-d 0 mini";
834 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
835 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
838 AddMenuInfoLine(SubScreen
, L
"NOTE: This is an example. Entries");
839 AddMenuInfoLine(SubScreen
, L
"marked with (*) may not work.");
841 } else if (Entry
->OSType
== 'X') { // entries for xom.efi
842 // by default, skip the built-in selection and boot from hard disk only
843 Entry
->LoadOptions
= L
"-s -h";
845 SubEntry
= InitializeLoaderEntry(Entry
);
846 if (SubEntry
!= NULL
) {
847 SubEntry
->me
.Title
= L
"Boot Windows from Hard Disk";
848 SubEntry
->LoadOptions
= L
"-s -h";
849 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
850 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
853 SubEntry
= InitializeLoaderEntry(Entry
);
854 if (SubEntry
!= NULL
) {
855 SubEntry
->me
.Title
= L
"Boot Windows from CD-ROM";
856 SubEntry
->LoadOptions
= L
"-s -c";
857 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
858 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
861 SubEntry
= InitializeLoaderEntry(Entry
);
862 if (SubEntry
!= NULL
) {
863 SubEntry
->me
.Title
= L
"Run XOM in text mode";
864 SubEntry
->UseGraphicsMode
= FALSE
;
865 SubEntry
->LoadOptions
= L
"-v";
866 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
867 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
869 } // entries for xom.efi
870 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
871 Entry
->me
.SubScreen
= SubScreen
;
872 } // VOID GenerateSubScreen()
874 // Returns options for a Linux kernel. Reads them from an options file in the
875 // kernel's directory; and if present, adds an initrd= option for an initial
876 // RAM disk file with the same version number as the kernel file.
877 static CHAR16
* GetMainLinuxOptions(IN CHAR16
* LoaderPath
, IN REFIT_VOLUME
*Volume
) {
878 CHAR16
*Options
= NULL
, *InitrdName
, *FullOptions
= NULL
;
880 Options
= GetFirstOptionsFromFile(LoaderPath
, Volume
);
881 InitrdName
= FindInitrd(LoaderPath
, Volume
);
882 FullOptions
= AddInitrdToOptions(Options
, InitrdName
);
885 MyFreePool(InitrdName
);
886 return (FullOptions
);
887 } // static CHAR16 * GetMainLinuxOptions()
889 // Try to guess the name of the Linux distribution & add that name to
891 static VOID
GuessLinuxDistribution(CHAR16
**OSIconName
, REFIT_VOLUME
*Volume
, CHAR16
*LoaderPath
) {
895 UINTN TokenCount
= 0;
897 // If on Linux root fs, /etc/os-release file probably has clues....
898 if (FileExists(Volume
->RootDir
, L
"etc\\os-release") &&
899 (ReadFile(Volume
->RootDir
, L
"etc\\os-release", &File
, &FileSize
) == EFI_SUCCESS
)) {
901 TokenCount
= ReadTokenLine(&File
, &TokenList
);
902 if ((TokenCount
> 1) && ((StriCmp(TokenList
[0], L
"ID") == 0) || (StriCmp(TokenList
[0], L
"NAME") == 0))) {
903 MergeStrings(OSIconName
, TokenList
[1], L
',');
905 FreeTokenLine(&TokenList
, &TokenCount
);
906 } while (TokenCount
> 0);
907 MyFreePool(File
.Buffer
);
910 // Search for clues in the kernel's filename....
911 if (StriSubCmp(L
".fc", LoaderPath
))
912 MergeStrings(OSIconName
, L
"fedora", L
',');
913 if (StriSubCmp(L
".el", LoaderPath
))
914 MergeStrings(OSIconName
, L
"redhat", L
',');
915 } // VOID GuessLinuxDistribution()
917 // Sets a few defaults for a loader entry -- mainly the icon, but also the OS type
918 // code and shortcut letter. For Linux EFI stub loaders, also sets kernel options
919 // that will (with luck) work fairly automatically.
920 VOID
SetLoaderDefaults(LOADER_ENTRY
*Entry
, CHAR16
*LoaderPath
, REFIT_VOLUME
*Volume
) {
921 CHAR16
*NameClues
, *PathOnly
, *NoExtension
, *OSIconName
= NULL
, *Temp
, *SubString
;
922 CHAR16 ShortcutLetter
= 0;
925 NameClues
= Basename(LoaderPath
);
926 PathOnly
= FindPath(LoaderPath
);
927 NoExtension
= StripEfiExtension(NameClues
);
929 if (Volume
->DiskKind
== DISK_KIND_NET
) {
930 MergeStrings(&NameClues
, Entry
->me
.Title
, L
' ');
932 // locate a custom icon for the loader
933 // Anything found here takes precedence over the "hints" in the OSIconName variable
934 if (!Entry
->me
.Image
) {
935 Entry
->me
.Image
= egLoadIconAnyType(Volume
->RootDir
, PathOnly
, NoExtension
, GlobalConfig
.IconSizes
[ICON_SIZE_BIG
]);
937 if (!Entry
->me
.Image
) {
938 Entry
->me
.Image
= egCopyImage(Volume
->VolIconImage
);
941 // Begin creating icon "hints" by using last part of directory path leading
943 Temp
= FindLastDirName(LoaderPath
);
944 MergeStrings(&OSIconName
, Temp
, L
',');
947 if (OSIconName
!= NULL
) {
948 ShortcutLetter
= OSIconName
[0];
951 // Add every "word" in the volume label, delimited by spaces, dashes (-), or
952 // underscores (_), to the list of hints to be used in searching for OS
954 if ((Volume
->VolName
) && (StrLen(Volume
->VolName
) > 0)) {
955 Temp
= SubString
= StrDuplicate(Volume
->VolName
);
957 Length
= StrLen(Temp
);
958 for (i
= 0; i
< Length
; i
++) {
959 if ((Temp
[i
] == L
' ') || (Temp
[i
] == L
'_') || (Temp
[i
] == L
'-')) {
961 if (StrLen(SubString
) > 0)
962 MergeStrings(&OSIconName
, SubString
, L
',');
963 SubString
= Temp
+ i
+ 1;
966 MergeStrings(&OSIconName
, SubString
, L
',');
970 } // if/else network boot
972 // detect specific loaders
973 if (StriSubCmp(L
"bzImage", NameClues
) || StriSubCmp(L
"vmlinuz", NameClues
)) {
974 if (Volume
->DiskKind
!= DISK_KIND_NET
) {
975 GuessLinuxDistribution(&OSIconName
, Volume
, LoaderPath
);
976 Entry
->LoadOptions
= GetMainLinuxOptions(LoaderPath
, Volume
);
978 MergeStrings(&OSIconName
, L
"linux", L
',');
980 if (ShortcutLetter
== 0)
981 ShortcutLetter
= 'L';
982 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_LINUX
;
983 } else if (StriSubCmp(L
"refit", LoaderPath
)) {
984 MergeStrings(&OSIconName
, L
"refit", L
',');
986 ShortcutLetter
= 'R';
987 } else if (StriSubCmp(L
"refind", LoaderPath
)) {
988 MergeStrings(&OSIconName
, L
"refind", L
',');
990 ShortcutLetter
= 'R';
991 } else if (StriCmp(LoaderPath
, MACOSX_LOADER_PATH
) == 0) {
992 MergeStrings(&OSIconName
, L
"mac", L
',');
994 ShortcutLetter
= 'M';
995 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
996 } else if (StriCmp(NameClues
, L
"diags.efi") == 0) {
997 MergeStrings(&OSIconName
, L
"hwtest", L
',');
998 } else if (StriCmp(NameClues
, L
"e.efi") == 0 || StriCmp(NameClues
, L
"elilo.efi") == 0 || StriSubCmp(L
"elilo", NameClues
)) {
999 MergeStrings(&OSIconName
, L
"elilo,linux", L
',');
1000 Entry
->OSType
= 'E';
1001 if (ShortcutLetter
== 0)
1002 ShortcutLetter
= 'L';
1003 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
1004 } else if (StriSubCmp(L
"grub", NameClues
)) {
1005 MergeStrings(&OSIconName
, L
"grub,linux", L
',');
1006 Entry
->OSType
= 'G';
1007 ShortcutLetter
= 'G';
1008 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_GRUB
;
1009 } else if (StriCmp(NameClues
, L
"cdboot.efi") == 0 ||
1010 StriCmp(NameClues
, L
"bootmgr.efi") == 0 ||
1011 StriCmp(NameClues
, L
"bootmgfw.efi") == 0 ||
1012 StriCmp(NameClues
, L
"bkpbootmgfw.efi") == 0) {
1013 MergeStrings(&OSIconName
, L
"win", L
',');
1014 Entry
->OSType
= 'W';
1015 ShortcutLetter
= 'W';
1016 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
1017 } else if (StriCmp(NameClues
, L
"xom.efi") == 0) {
1018 MergeStrings(&OSIconName
, L
"xom,win", L
',');
1019 Entry
->UseGraphicsMode
= TRUE
;
1020 Entry
->OSType
= 'X';
1021 ShortcutLetter
= 'W';
1022 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
1024 else if (StriSubCmp(L
"ipxe", NameClues
)) {
1025 Entry
->OSType
= 'N';
1026 ShortcutLetter
= 'N';
1027 MergeStrings(&OSIconName
, L
"network", L
',');
1030 if ((ShortcutLetter
>= 'a') && (ShortcutLetter
<= 'z'))
1031 ShortcutLetter
= ShortcutLetter
- 'a' + 'A'; // convert lowercase to uppercase
1032 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1033 if (Entry
->me
.Image
== NULL
)
1034 Entry
->me
.Image
= LoadOSIcon(OSIconName
, L
"unknown", FALSE
);
1035 MyFreePool(PathOnly
);
1036 } // VOID SetLoaderDefaults()
1038 // Add a specified EFI boot loader to the list, using automatic settings
1039 // for icons, options, etc.
1040 LOADER_ENTRY
* AddLoaderEntry(IN CHAR16
*LoaderPath
, IN CHAR16
*LoaderTitle
, IN REFIT_VOLUME
*Volume
) {
1041 LOADER_ENTRY
*Entry
;
1043 CleanUpPathNameSlashes(LoaderPath
);
1044 Entry
= InitializeLoaderEntry(NULL
);
1045 if (Entry
!= NULL
) {
1046 Entry
->Title
= StrDuplicate((LoaderTitle
!= NULL
) ? LoaderTitle
: LoaderPath
);
1047 Entry
->me
.Title
= AllocateZeroPool(sizeof(CHAR16
) * 256);
1048 // Extra space at end of Entry->me.Title enables searching on Volume->VolName even if another volume
1049 // name is identical except for something added to the end (e.g., VolB1 vs. VolB12).
1050 // Note: Volume->VolName will be NULL for network boot programs.
1051 if (Volume
->VolName
)
1052 SPrint(Entry
->me
.Title
, 255, L
"Boot %s from %s ", (LoaderTitle
!= NULL
) ? LoaderTitle
: LoaderPath
, Volume
->VolName
);
1054 SPrint(Entry
->me
.Title
, 255, L
"Boot %s ", (LoaderTitle
!= NULL
) ? LoaderTitle
: LoaderPath
);
1056 Entry
->me
.BadgeImage
= Volume
->VolBadgeImage
;
1057 if ((LoaderPath
!= NULL
) && (LoaderPath
[0] != L
'\\')) {
1058 Entry
->LoaderPath
= StrDuplicate(L
"\\");
1060 Entry
->LoaderPath
= NULL
;
1062 MergeStrings(&(Entry
->LoaderPath
), LoaderPath
, 0);
1063 Entry
->VolName
= Volume
->VolName
;
1064 Entry
->DevicePath
= FileDevicePath(Volume
->DeviceHandle
, Entry
->LoaderPath
);
1065 SetLoaderDefaults(Entry
, LoaderPath
, Volume
);
1066 GenerateSubScreen(Entry
, Volume
);
1067 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1071 } // LOADER_ENTRY * AddLoaderEntry()
1073 // Returns -1 if (Time1 < Time2), +1 if (Time1 > Time2), or 0 if
1074 // (Time1 == Time2). Precision is only to the nearest second; since
1075 // this is used for sorting boot loader entries, differences smaller
1076 // than this are likely to be meaningless (and unlikely!).
1077 INTN
TimeComp(IN EFI_TIME
*Time1
, IN EFI_TIME
*Time2
) {
1078 INT64 Time1InSeconds
, Time2InSeconds
;
1080 // Following values are overestimates; I'm assuming 31 days in every month.
1081 // This is fine for the purpose of this function, which is limited
1082 Time1InSeconds
= Time1
->Second
+ (Time1
->Minute
* 60) + (Time1
->Hour
* 3600) + (Time1
->Day
* 86400) +
1083 (Time1
->Month
* 2678400) + ((Time1
->Year
- 1998) * 32140800);
1084 Time2InSeconds
= Time2
->Second
+ (Time2
->Minute
* 60) + (Time2
->Hour
* 3600) + (Time2
->Day
* 86400) +
1085 (Time2
->Month
* 2678400) + ((Time2
->Year
- 1998) * 32140800);
1086 if (Time1InSeconds
< Time2InSeconds
)
1088 else if (Time1InSeconds
> Time2InSeconds
)
1092 } // INTN TimeComp()
1094 // Adds a loader list element, keeping it sorted by date. Returns the new
1095 // first element (the one with the most recent date).
1096 static struct LOADER_LIST
* AddLoaderListEntry(struct LOADER_LIST
*LoaderList
, struct LOADER_LIST
*NewEntry
) {
1097 struct LOADER_LIST
*LatestEntry
, *CurrentEntry
, *PrevEntry
= NULL
;
1099 LatestEntry
= CurrentEntry
= LoaderList
;
1100 if (LoaderList
== NULL
) {
1101 LatestEntry
= NewEntry
;
1103 while ((CurrentEntry
!= NULL
) && (TimeComp(&(NewEntry
->TimeStamp
), &(CurrentEntry
->TimeStamp
)) < 0)) {
1104 PrevEntry
= CurrentEntry
;
1105 CurrentEntry
= CurrentEntry
->NextEntry
;
1107 NewEntry
->NextEntry
= CurrentEntry
;
1108 if (PrevEntry
== NULL
) {
1109 LatestEntry
= NewEntry
;
1111 PrevEntry
->NextEntry
= NewEntry
;
1114 return (LatestEntry
);
1115 } // static VOID AddLoaderListEntry()
1117 // Delete the LOADER_LIST linked list
1118 static VOID
CleanUpLoaderList(struct LOADER_LIST
*LoaderList
) {
1119 struct LOADER_LIST
*Temp
;
1121 while (LoaderList
!= NULL
) {
1123 LoaderList
= LoaderList
->NextEntry
;
1124 MyFreePool(Temp
->FileName
);
1127 } // static VOID CleanUpLoaderList()
1129 // Returns FALSE if the specified file/volume matches the GlobalConfig.DontScanDirs
1130 // or GlobalConfig.DontScanVolumes specification, or if Path points to a volume
1131 // other than the one specified by Volume, or if the specified path is SelfDir.
1132 // Returns TRUE if none of these conditions is met -- that is, if the path is
1133 // eligible for scanning.
1134 static BOOLEAN
ShouldScan(REFIT_VOLUME
*Volume
, CHAR16
*Path
) {
1135 CHAR16
*VolName
= NULL
, *DontScanDir
, *PathCopy
= NULL
;
1137 BOOLEAN ScanIt
= TRUE
;
1139 if ((IsIn(Volume
->VolName
, GlobalConfig
.DontScanVolumes
)) || (IsIn(Volume
->PartName
, GlobalConfig
.DontScanVolumes
)))
1142 if ((StriCmp(Path
, SelfDirPath
) == 0) && (Volume
->DeviceHandle
== SelfVolume
->DeviceHandle
))
1145 // See if Path includes an explicit volume declaration that's NOT Volume....
1146 PathCopy
= StrDuplicate(Path
);
1147 if (SplitVolumeAndFilename(&PathCopy
, &VolName
)) {
1148 VolumeNumberToName(Volume
, &VolName
);
1149 if (VolName
&& StriCmp(VolName
, Volume
->VolName
) != 0) {
1152 } // if Path includes volume specification
1153 MyFreePool(PathCopy
);
1154 MyFreePool(VolName
);
1157 // See if Volume is in GlobalConfig.DontScanDirs....
1158 while (ScanIt
&& (DontScanDir
= FindCommaDelimited(GlobalConfig
.DontScanDirs
, i
++))) {
1159 SplitVolumeAndFilename(&DontScanDir
, &VolName
);
1160 CleanUpPathNameSlashes(DontScanDir
);
1161 VolumeNumberToName(Volume
, &VolName
);
1162 if (VolName
!= NULL
) {
1163 if ((StriCmp(VolName
, Volume
->VolName
) == 0) && (StriCmp(DontScanDir
, Path
) == 0))
1166 if (StriCmp(DontScanDir
, Path
) == 0)
1169 MyFreePool(DontScanDir
);
1170 MyFreePool(VolName
);
1176 } // BOOLEAN ShouldScan()
1178 // Returns TRUE if the file is byte-for-byte identical with the fallback file
1179 // on the volume AND if the file is not itself the fallback file; returns
1180 // FALSE if the file is not identical to the fallback file OR if the file
1181 // IS the fallback file. Intended for use in excluding the fallback boot
1182 // loader when it's a duplicate of another boot loader.
1183 static BOOLEAN
DuplicatesFallback(IN REFIT_VOLUME
*Volume
, IN CHAR16
*FileName
) {
1184 CHAR8
*FileContents
, *FallbackContents
;
1185 EFI_FILE_HANDLE FileHandle
, FallbackHandle
;
1186 EFI_FILE_INFO
*FileInfo
, *FallbackInfo
;
1187 UINTN FileSize
= 0, FallbackSize
= 0;
1189 BOOLEAN AreIdentical
= FALSE
;
1191 if (!FileExists(Volume
->RootDir
, FileName
) || !FileExists(Volume
->RootDir
, FALLBACK_FULLNAME
))
1194 CleanUpPathNameSlashes(FileName
);
1196 if (StriCmp(FileName
, FALLBACK_FULLNAME
) == 0)
1197 return FALSE
; // identical filenames, so not a duplicate....
1199 Status
= refit_call5_wrapper(Volume
->RootDir
->Open
, Volume
->RootDir
, &FileHandle
, FileName
, EFI_FILE_MODE_READ
, 0);
1200 if (Status
== EFI_SUCCESS
) {
1201 FileInfo
= LibFileInfo(FileHandle
);
1202 FileSize
= FileInfo
->FileSize
;
1207 Status
= refit_call5_wrapper(Volume
->RootDir
->Open
, Volume
->RootDir
, &FallbackHandle
, FALLBACK_FULLNAME
, EFI_FILE_MODE_READ
, 0);
1208 if (Status
== EFI_SUCCESS
) {
1209 FallbackInfo
= LibFileInfo(FallbackHandle
);
1210 FallbackSize
= FallbackInfo
->FileSize
;
1212 refit_call1_wrapper(FileHandle
->Close
, FileHandle
);
1216 if (FallbackSize
!= FileSize
) { // not same size, so can't be identical
1217 AreIdentical
= FALSE
;
1218 } else { // could be identical; do full check....
1219 FileContents
= AllocatePool(FileSize
);
1220 FallbackContents
= AllocatePool(FallbackSize
);
1221 if (FileContents
&& FallbackContents
) {
1222 Status
= refit_call3_wrapper(FileHandle
->Read
, FileHandle
, &FileSize
, FileContents
);
1223 if (Status
== EFI_SUCCESS
) {
1224 Status
= refit_call3_wrapper(FallbackHandle
->Read
, FallbackHandle
, &FallbackSize
, FallbackContents
);
1226 if (Status
== EFI_SUCCESS
) {
1227 AreIdentical
= (CompareMem(FileContents
, FallbackContents
, FileSize
) == 0);
1230 MyFreePool(FileContents
);
1231 MyFreePool(FallbackContents
);
1234 // BUG ALERT: Some systems (e.g., DUET, some Macs with large displays) crash if the
1235 // following two calls are reversed. Go figure....
1236 refit_call1_wrapper(FileHandle
->Close
, FallbackHandle
);
1237 refit_call1_wrapper(FileHandle
->Close
, FileHandle
);
1238 return AreIdentical
;
1239 } // BOOLEAN DuplicatesFallback()
1241 // Returns FALSE if two measures of file size are identical for a single file,
1242 // TRUE if not or if the file can't be opened and the other measure is non-0.
1243 // Despite the function's name, this isn't really a direct test of symbolic
1244 // link status, since EFI doesn't officially support symlinks. It does seem
1245 // to be a reliable indicator, though. (OTOH, some disk errors might cause a
1246 // file to fail to open, which would return a false positive -- but as I use
1247 // this function to exclude symbolic links from the list of boot loaders,
1248 // that would be fine, since such boot loaders wouldn't work.)
1249 static BOOLEAN
IsSymbolicLink(REFIT_VOLUME
*Volume
, CHAR16
*Path
, EFI_FILE_INFO
*DirEntry
) {
1250 EFI_FILE_HANDLE FileHandle
;
1251 EFI_FILE_INFO
*FileInfo
= NULL
;
1253 UINTN FileSize2
= 0;
1256 FileName
= StrDuplicate(Path
);
1257 MergeStrings(&FileName
, DirEntry
->FileName
, L
'\\');
1258 CleanUpPathNameSlashes(FileName
);
1260 Status
= refit_call5_wrapper(Volume
->RootDir
->Open
, Volume
->RootDir
, &FileHandle
, FileName
, EFI_FILE_MODE_READ
, 0);
1261 if (Status
== EFI_SUCCESS
) {
1262 FileInfo
= LibFileInfo(FileHandle
);
1263 if (FileInfo
!= NULL
)
1264 FileSize2
= FileInfo
->FileSize
;
1267 MyFreePool(FileName
);
1268 MyFreePool(FileInfo
);
1270 return (DirEntry
->FileSize
!= FileSize2
);
1271 } // BOOLEAN IsSymbolicLink()
1273 // Returns TRUE if a file with the same name as the original but with
1274 // ".efi.signed" is also present in the same directory. Ubuntu is using
1275 // this filename as a signed version of the original unsigned kernel, and
1276 // there's no point in cluttering the display with two kernels that will
1277 // behave identically on non-SB systems, or when one will fail when SB
1279 static BOOLEAN
HasSignedCounterpart(IN REFIT_VOLUME
*Volume
, IN CHAR16
*Path
, IN CHAR16
*Filename
) {
1280 CHAR16
*NewFile
= NULL
;
1281 BOOLEAN retval
= FALSE
;
1283 MergeStrings(&NewFile
, Path
, 0);
1284 MergeStrings(&NewFile
, Filename
, L
'\\');
1285 MergeStrings(&NewFile
, L
".efi.signed", 0);
1286 if (NewFile
!= NULL
) {
1287 CleanUpPathNameSlashes(NewFile
);
1288 if (FileExists(Volume
->RootDir
, NewFile
))
1290 MyFreePool(NewFile
);
1294 } // BOOLEAN HasSignedCounterpart()
1296 // Scan an individual directory for EFI boot loader files and, if found,
1297 // add them to the list. Exception: Ignores FALLBACK_FULLNAME, which is picked
1298 // up in ScanEfiFiles(). Sorts the entries within the loader directory so that
1299 // the most recent one appears first in the list.
1300 // Returns TRUE if a duplicate for FALLBACK_FILENAME was found, FALSE if not.
1301 static BOOLEAN
ScanLoaderDir(IN REFIT_VOLUME
*Volume
, IN CHAR16
*Path
, IN CHAR16
*Pattern
)
1304 REFIT_DIR_ITER DirIter
;
1305 EFI_FILE_INFO
*DirEntry
;
1306 CHAR16 FileName
[256], *Extension
;
1307 struct LOADER_LIST
*LoaderList
= NULL
, *NewLoader
;
1308 BOOLEAN FoundFallbackDuplicate
= FALSE
;
1310 if ((!SelfDirPath
|| !Path
|| ((StriCmp(Path
, SelfDirPath
) == 0) && (Volume
->DeviceHandle
!= SelfVolume
->DeviceHandle
)) ||
1311 (StriCmp(Path
, SelfDirPath
) != 0)) && (ShouldScan(Volume
, Path
))) {
1312 // look through contents of the directory
1313 DirIterOpen(Volume
->RootDir
, Path
, &DirIter
);
1314 while (DirIterNext(&DirIter
, 2, Pattern
, &DirEntry
)) {
1315 Extension
= FindExtension(DirEntry
->FileName
);
1316 if (DirEntry
->FileName
[0] == '.' ||
1317 StriCmp(Extension
, L
".icns") == 0 ||
1318 StriCmp(Extension
, L
".png") == 0 ||
1319 (StriCmp(DirEntry
->FileName
, FALLBACK_BASENAME
) == 0 && (StriCmp(Path
, L
"EFI\\BOOT") == 0)) ||
1320 StriSubCmp(L
"shell", DirEntry
->FileName
) ||
1321 IsSymbolicLink(Volume
, Path
, DirEntry
) || /* is symbolic link */
1322 HasSignedCounterpart(Volume
, Path
, DirEntry
->FileName
) || /* a file with same name plus ".efi.signed" is present */
1323 FilenameIn(Volume
, Path
, DirEntry
->FileName
, GlobalConfig
.DontScanFiles
))
1324 continue; // skip this
1327 SPrint(FileName
, 255, L
"\\%s\\%s", Path
, DirEntry
->FileName
);
1329 SPrint(FileName
, 255, L
"\\%s", DirEntry
->FileName
);
1330 CleanUpPathNameSlashes(FileName
);
1332 if(!IsValidLoader(Volume
->RootDir
, FileName
))
1335 NewLoader
= AllocateZeroPool(sizeof(struct LOADER_LIST
));
1336 if (NewLoader
!= NULL
) {
1337 NewLoader
->FileName
= StrDuplicate(FileName
);
1338 NewLoader
->TimeStamp
= DirEntry
->ModificationTime
;
1339 LoaderList
= AddLoaderListEntry(LoaderList
, NewLoader
);
1340 if (DuplicatesFallback(Volume
, FileName
))
1341 FoundFallbackDuplicate
= TRUE
;
1343 MyFreePool(Extension
);
1346 NewLoader
= LoaderList
;
1347 while (NewLoader
!= NULL
) {
1348 AddLoaderEntry(NewLoader
->FileName
, NULL
, Volume
);
1349 NewLoader
= NewLoader
->NextEntry
;
1352 CleanUpLoaderList(LoaderList
);
1353 Status
= DirIterClose(&DirIter
);
1354 // NOTE: EFI_INVALID_PARAMETER really is an error that should be reported;
1355 // but I've gotten reports from users who are getting this error occasionally
1356 // and I can't find anything wrong or reproduce the problem, so I'm putting
1357 // it down to buggy EFI implementations and ignoring that particular error....
1358 if ((Status
!= EFI_NOT_FOUND
) && (Status
!= EFI_INVALID_PARAMETER
)) {
1360 SPrint(FileName
, 255, L
"while scanning the %s directory", Path
);
1362 StrCpy(FileName
, L
"while scanning the root directory");
1363 CheckError(Status
, FileName
);
1364 } // if (Status != EFI_NOT_FOUND)
1365 } // if not scanning a blacklisted directory
1367 return FoundFallbackDuplicate
;
1368 } /* static VOID ScanLoaderDir() */
1370 // Run the IPXE_DISCOVER_NAME program, which obtains the IP address of the boot
1371 // server and the name of the boot file it delivers.
1372 CHAR16
* RuniPXEDiscover(EFI_HANDLE Volume
)
1375 EFI_DEVICE_PATH
*FilePath
;
1376 EFI_HANDLE iPXEHandle
;
1377 CHAR16
*boot_info
= NULL
;
1378 UINTN boot_info_size
= 0;
1380 FilePath
= FileDevicePath (Volume
, IPXE_DISCOVER_NAME
);
1381 Status
= refit_call6_wrapper(BS
->LoadImage
, FALSE
, SelfImageHandle
, FilePath
,
1382 NULL
, 0, &iPXEHandle
);
1386 Status
= refit_call3_wrapper(BS
->StartImage
, iPXEHandle
, &boot_info_size
, &boot_info
);
1389 } // RuniPXEDiscover()
1391 // Scan for network (PXE) boot servers. This function relies on the presence
1392 // of the IPXE_DISCOVER_NAME and IPXE_NAME program files on the volume from
1393 // which rEFInd launched. As of December 6, 2014, these tools aren't entirely
1394 // reliable. See BUILDING.txt for information on building them.
1395 static VOID
ScanNetboot() {
1396 CHAR16
*iPXEFileName
= IPXE_NAME
;
1398 REFIT_VOLUME
*NetVolume
;
1400 if (FileExists(SelfVolume
->RootDir
, IPXE_DISCOVER_NAME
) &&
1401 FileExists(SelfVolume
->RootDir
, IPXE_NAME
) &&
1402 IsValidLoader(SelfVolume
->RootDir
, IPXE_DISCOVER_NAME
) &&
1403 IsValidLoader(SelfVolume
->RootDir
, IPXE_NAME
)) {
1404 Location
= RuniPXEDiscover(SelfVolume
->DeviceHandle
);
1405 if (Location
!= NULL
&& FileExists(SelfVolume
->RootDir
, iPXEFileName
)) {
1406 NetVolume
= AllocatePool(sizeof(REFIT_VOLUME
));
1407 CopyMem(NetVolume
, SelfVolume
, sizeof(REFIT_VOLUME
));
1408 NetVolume
->DiskKind
= DISK_KIND_NET
;
1409 NetVolume
->VolBadgeImage
= BuiltinIcon(BUILTIN_ICON_VOL_NET
);
1410 NetVolume
->PartName
= NetVolume
->VolName
= NULL
;
1411 AddLoaderEntry(iPXEFileName
, Location
, NetVolume
);
1412 MyFreePool(NetVolume
);
1413 } // if support files exist and are valid
1415 } // VOID ScanNetBoot()
1417 static VOID
ScanEfiFiles(REFIT_VOLUME
*Volume
) {
1419 REFIT_DIR_ITER EfiDirIter
;
1420 EFI_FILE_INFO
*EfiDirEntry
;
1421 CHAR16 FileName
[256], *Directory
= NULL
, *MatchPatterns
, *VolName
= NULL
, *SelfPath
;
1423 BOOLEAN ScanFallbackLoader
= TRUE
;
1424 BOOLEAN FoundBRBackup
= FALSE
;
1426 if ((Volume
->RootDir
!= NULL
) && (Volume
->VolName
!= NULL
) && (Volume
->IsReadable
)) {
1427 MatchPatterns
= StrDuplicate(LOADER_MATCH_PATTERNS
);
1428 if (GlobalConfig
.ScanAllLinux
)
1429 MergeStrings(&MatchPatterns
, LINUX_MATCH_PATTERNS
, L
',');
1431 // check for Mac OS X boot loader
1432 if (ShouldScan(Volume
, L
"System\\Library\\CoreServices")) {
1433 StrCpy(FileName
, MACOSX_LOADER_PATH
);
1434 if (FileExists(Volume
->RootDir
, FileName
) && !FilenameIn(Volume
, Directory
, L
"boot.efi", GlobalConfig
.DontScanFiles
)) {
1435 AddLoaderEntry(FileName
, L
"Mac OS X", Volume
);
1436 if (DuplicatesFallback(Volume
, FileName
))
1437 ScanFallbackLoader
= FALSE
;
1441 StrCpy(FileName
, L
"System\\Library\\CoreServices\\xom.efi");
1442 if (FileExists(Volume
->RootDir
, FileName
) && !FilenameIn(Volume
, Directory
, L
"boot.efi", GlobalConfig
.DontScanFiles
)) {
1443 AddLoaderEntry(FileName
, L
"Windows XP (XoM)", Volume
);
1444 if (DuplicatesFallback(Volume
, FileName
))
1445 ScanFallbackLoader
= FALSE
;
1447 } // if should scan Mac directory
1449 // check for Microsoft boot loader/menu
1450 if (ShouldScan(Volume
, L
"EFI\\Microsoft\\Boot")) {
1451 StrCpy(FileName
, L
"EFI\\Microsoft\\Boot\\bkpbootmgfw.efi");
1452 if (FileExists(Volume
->RootDir
, FileName
) && !FilenameIn(Volume
, Directory
, L
"bkpbootmgfw.efi",
1453 GlobalConfig
.DontScanFiles
)) {
1454 AddLoaderEntry(FileName
, L
"Microsoft EFI boot (Boot Repair backup)", Volume
);
1455 FoundBRBackup
= TRUE
;
1456 if (DuplicatesFallback(Volume
, FileName
))
1457 ScanFallbackLoader
= FALSE
;
1459 StrCpy(FileName
, L
"EFI\\Microsoft\\Boot\\bootmgfw.efi");
1460 if (FileExists(Volume
->RootDir
, FileName
) && !FilenameIn(Volume
, Directory
, L
"bootmgfw.efi", GlobalConfig
.DontScanFiles
)) {
1462 AddLoaderEntry(FileName
, L
"Supposed Microsoft EFI boot (probably GRUB)", Volume
);
1464 AddLoaderEntry(FileName
, L
"Microsoft EFI boot", Volume
);
1465 if (DuplicatesFallback(Volume
, FileName
))
1466 ScanFallbackLoader
= FALSE
;
1470 // scan the root directory for EFI executables
1471 if (ScanLoaderDir(Volume
, L
"\\", MatchPatterns
))
1472 ScanFallbackLoader
= FALSE
;
1474 // scan subdirectories of the EFI directory (as per the standard)
1475 DirIterOpen(Volume
->RootDir
, L
"EFI", &EfiDirIter
);
1476 while (DirIterNext(&EfiDirIter
, 1, NULL
, &EfiDirEntry
)) {
1477 if (StriCmp(EfiDirEntry
->FileName
, L
"tools") == 0 || EfiDirEntry
->FileName
[0] == '.')
1478 continue; // skip this, doesn't contain boot loaders or is scanned later
1479 SPrint(FileName
, 255, L
"EFI\\%s", EfiDirEntry
->FileName
);
1480 if (ScanLoaderDir(Volume
, FileName
, MatchPatterns
))
1481 ScanFallbackLoader
= FALSE
;
1483 Status
= DirIterClose(&EfiDirIter
);
1484 if (Status
!= EFI_NOT_FOUND
)
1485 CheckError(Status
, L
"while scanning the EFI directory");
1487 // Scan user-specified (or additional default) directories....
1489 while ((Directory
= FindCommaDelimited(GlobalConfig
.AlsoScan
, i
++)) != NULL
) {
1490 if (ShouldScan(Volume
, Directory
)) {
1491 SplitVolumeAndFilename(&Directory
, &VolName
);
1492 CleanUpPathNameSlashes(Directory
);
1493 Length
= StrLen(Directory
);
1494 if ((Length
> 0) && ScanLoaderDir(Volume
, Directory
, MatchPatterns
))
1495 ScanFallbackLoader
= FALSE
;
1496 MyFreePool(VolName
);
1497 } // if should scan dir
1498 MyFreePool(Directory
);
1501 // Don't scan the fallback loader if it's on the same volume and a duplicate of rEFInd itself....
1502 SelfPath
= DevicePathToStr(SelfLoadedImage
->FilePath
);
1503 CleanUpPathNameSlashes(SelfPath
);
1504 if ((Volume
->DeviceHandle
== SelfLoadedImage
->DeviceHandle
) && DuplicatesFallback(Volume
, SelfPath
))
1505 ScanFallbackLoader
= FALSE
;
1507 // If not a duplicate & if it exists & if it's not us, create an entry
1508 // for the fallback boot loader
1509 if (ScanFallbackLoader
&& FileExists(Volume
->RootDir
, FALLBACK_FULLNAME
) && ShouldScan(Volume
, L
"EFI\\BOOT"))
1510 AddLoaderEntry(FALLBACK_FULLNAME
, L
"Fallback boot loader", Volume
);
1512 } // static VOID ScanEfiFiles()
1514 // Scan internal disks for valid EFI boot loaders....
1515 static VOID
ScanInternal(VOID
) {
1518 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1519 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_INTERNAL
) {
1520 ScanEfiFiles(Volumes
[VolumeIndex
]);
1523 } // static VOID ScanInternal()
1525 // Scan external disks for valid EFI boot loaders....
1526 static VOID
ScanExternal(VOID
) {
1529 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1530 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_EXTERNAL
) {
1531 ScanEfiFiles(Volumes
[VolumeIndex
]);
1534 } // static VOID ScanExternal()
1536 // Scan internal disks for valid EFI boot loaders....
1537 static VOID
ScanOptical(VOID
) {
1540 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1541 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_OPTICAL
) {
1542 ScanEfiFiles(Volumes
[VolumeIndex
]);
1545 } // static VOID ScanOptical()
1548 // legacy boot functions
1551 static EFI_STATUS
ActivateMbrPartition(IN EFI_BLOCK_IO
*BlockIO
, IN UINTN PartitionIndex
)
1554 UINT8 SectorBuffer
[512];
1555 MBR_PARTITION_INFO
*MbrTable
, *EMbrTable
;
1556 UINT32 ExtBase
, ExtCurrent
, NextExtCurrent
;
1557 UINTN LogicalPartitionIndex
= 4;
1559 BOOLEAN HaveBootCode
;
1562 Status
= refit_call5_wrapper(BlockIO
->ReadBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, 0, 512, SectorBuffer
);
1563 if (EFI_ERROR(Status
))
1565 if (*((UINT16
*)(SectorBuffer
+ 510)) != 0xaa55)
1566 return EFI_NOT_FOUND
; // safety measure #1
1568 // add boot code if necessary
1569 HaveBootCode
= FALSE
;
1570 for (i
= 0; i
< MBR_BOOTCODE_SIZE
; i
++) {
1571 if (SectorBuffer
[i
] != 0) {
1572 HaveBootCode
= TRUE
;
1576 if (!HaveBootCode
) {
1577 // no boot code found in the MBR, add the syslinux MBR code
1578 SetMem(SectorBuffer
, MBR_BOOTCODE_SIZE
, 0);
1579 CopyMem(SectorBuffer
, syslinux_mbr
, SYSLINUX_MBR_SIZE
);
1582 // set the partition active
1583 MbrTable
= (MBR_PARTITION_INFO
*)(SectorBuffer
+ 446);
1585 for (i
= 0; i
< 4; i
++) {
1586 if (MbrTable
[i
].Flags
!= 0x00 && MbrTable
[i
].Flags
!= 0x80)
1587 return EFI_NOT_FOUND
; // safety measure #2
1588 if (i
== PartitionIndex
)
1589 MbrTable
[i
].Flags
= 0x80;
1590 else if (PartitionIndex
>= 4 && IS_EXTENDED_PART_TYPE(MbrTable
[i
].Type
)) {
1591 MbrTable
[i
].Flags
= 0x80;
1592 ExtBase
= MbrTable
[i
].StartLBA
;
1594 MbrTable
[i
].Flags
= 0x00;
1598 Status
= refit_call5_wrapper(BlockIO
->WriteBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, 0, 512, SectorBuffer
);
1599 if (EFI_ERROR(Status
))
1602 if (PartitionIndex
>= 4) {
1603 // we have to activate a logical partition, so walk the EMBR chain
1605 // NOTE: ExtBase was set above while looking at the MBR table
1606 for (ExtCurrent
= ExtBase
; ExtCurrent
; ExtCurrent
= NextExtCurrent
) {
1607 // read current EMBR
1608 Status
= refit_call5_wrapper(BlockIO
->ReadBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, ExtCurrent
, 512, SectorBuffer
);
1609 if (EFI_ERROR(Status
))
1611 if (*((UINT16
*)(SectorBuffer
+ 510)) != 0xaa55)
1612 return EFI_NOT_FOUND
; // safety measure #3
1614 // scan EMBR, set appropriate partition active
1615 EMbrTable
= (MBR_PARTITION_INFO
*)(SectorBuffer
+ 446);
1617 for (i
= 0; i
< 4; i
++) {
1618 if (EMbrTable
[i
].Flags
!= 0x00 && EMbrTable
[i
].Flags
!= 0x80)
1619 return EFI_NOT_FOUND
; // safety measure #4
1620 if (EMbrTable
[i
].StartLBA
== 0 || EMbrTable
[i
].Size
== 0)
1622 if (IS_EXTENDED_PART_TYPE(EMbrTable
[i
].Type
)) {
1623 // link to next EMBR
1624 NextExtCurrent
= ExtBase
+ EMbrTable
[i
].StartLBA
;
1625 EMbrTable
[i
].Flags
= (PartitionIndex
>= LogicalPartitionIndex
) ? 0x80 : 0x00;
1628 // logical partition
1629 EMbrTable
[i
].Flags
= (PartitionIndex
== LogicalPartitionIndex
) ? 0x80 : 0x00;
1630 LogicalPartitionIndex
++;
1634 // write current EMBR
1635 Status
= refit_call5_wrapper(BlockIO
->WriteBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, ExtCurrent
, 512, SectorBuffer
);
1636 if (EFI_ERROR(Status
))
1639 if (PartitionIndex
< LogicalPartitionIndex
)
1640 break; // stop the loop, no need to touch further EMBRs
1646 } /* static EFI_STATUS ActivateMbrPartition() */
1648 // early 2006 Core Duo / Core Solo models
1649 static UINT8 LegacyLoaderDevicePath1Data
[] = {
1650 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1651 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1652 0xFF, 0xFF, 0xF9, 0xFF, 0x00, 0x00, 0x00, 0x00,
1653 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1654 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1655 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1657 // mid-2006 Mac Pro (and probably other Core 2 models)
1658 static UINT8 LegacyLoaderDevicePath2Data
[] = {
1659 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1660 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1661 0xFF, 0xFF, 0xF7, 0xFF, 0x00, 0x00, 0x00, 0x00,
1662 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1663 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1664 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1666 // mid-2007 MBP ("Santa Rosa" based models)
1667 static UINT8 LegacyLoaderDevicePath3Data
[] = {
1668 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1669 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1670 0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
1671 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1672 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1673 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1676 static UINT8 LegacyLoaderDevicePath4Data
[] = {
1677 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1678 0x00, 0x00, 0xC0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1679 0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
1680 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1681 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1682 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1684 // late-2008 MB/MBP (NVidia chipset)
1685 static UINT8 LegacyLoaderDevicePath5Data
[] = {
1686 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1687 0x00, 0x40, 0xCB, 0xFF, 0x00, 0x00, 0x00, 0x00,
1688 0xFF, 0xBF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
1689 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1690 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1691 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1694 static EFI_DEVICE_PATH
*LegacyLoaderList
[] = {
1695 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath1Data
,
1696 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath2Data
,
1697 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath3Data
,
1698 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath4Data
,
1699 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath5Data
,
1703 #define MAX_DISCOVERED_PATHS (16)
1705 static VOID
StartLegacy(IN LEGACY_ENTRY
*Entry
, IN CHAR16
*SelectionName
)
1708 EG_IMAGE
*BootLogoImage
;
1709 UINTN ErrorInStep
= 0;
1710 EFI_DEVICE_PATH
*DiscoveredPathList
[MAX_DISCOVERED_PATHS
];
1712 BeginExternalScreen(TRUE
, L
"Booting Legacy OS (Mac mode)");
1714 BootLogoImage
= LoadOSIcon(Entry
->Volume
->OSIconName
, L
"legacy", TRUE
);
1715 if (BootLogoImage
!= NULL
)
1716 BltImageAlpha(BootLogoImage
,
1717 (UGAWidth
- BootLogoImage
->Width
) >> 1,
1718 (UGAHeight
- BootLogoImage
->Height
) >> 1,
1719 &StdBackgroundPixel
);
1721 if (Entry
->Volume
->IsMbrPartition
) {
1722 ActivateMbrPartition(Entry
->Volume
->WholeDiskBlockIO
, Entry
->Volume
->MbrPartitionIndex
);
1725 ExtractLegacyLoaderPaths(DiscoveredPathList
, MAX_DISCOVERED_PATHS
, LegacyLoaderList
);
1727 StoreLoaderName(SelectionName
);
1728 Status
= StartEFIImageList(DiscoveredPathList
, Entry
->LoadOptions
, TYPE_LEGACY
, L
"legacy loader", 0, &ErrorInStep
, TRUE
, FALSE
);
1729 if (Status
== EFI_NOT_FOUND
) {
1730 if (ErrorInStep
== 1) {
1731 Print(L
"\nPlease make sure that you have the latest firmware update installed.\n");
1732 } else if (ErrorInStep
== 3) {
1733 Print(L
"\nThe firmware refused to boot from the selected volume. Note that external\n"
1734 L
"hard drives are not well-supported by Apple's firmware for legacy OS booting.\n");
1737 FinishExternalScreen();
1738 } /* static VOID StartLegacy() */
1740 // Start a device on a non-Mac using the EFI_LEGACY_BIOS_PROTOCOL
1741 static VOID
StartLegacyUEFI(LEGACY_ENTRY
*Entry
, CHAR16
*SelectionName
)
1743 BeginExternalScreen(TRUE
, L
"Booting Legacy OS (UEFI mode)");
1744 StoreLoaderName(SelectionName
);
1746 BdsLibConnectDevicePath (Entry
->BdsOption
->DevicePath
);
1747 BdsLibDoLegacyBoot(Entry
->BdsOption
);
1749 // If we get here, it means that there was a failure....
1750 Print(L
"Failure booting legacy (BIOS) OS.");
1752 FinishExternalScreen();
1753 } // static VOID StartLegacyUEFI()
1755 static LEGACY_ENTRY
* AddLegacyEntry(IN CHAR16
*LoaderTitle
, IN REFIT_VOLUME
*Volume
)
1757 LEGACY_ENTRY
*Entry
, *SubEntry
;
1758 REFIT_MENU_SCREEN
*SubScreen
;
1759 CHAR16
*VolDesc
, *LegacyTitle
;
1760 CHAR16 ShortcutLetter
= 0;
1762 if (LoaderTitle
== NULL
) {
1763 if (Volume
->OSName
!= NULL
) {
1764 LoaderTitle
= Volume
->OSName
;
1765 if (LoaderTitle
[0] == 'W' || LoaderTitle
[0] == 'L')
1766 ShortcutLetter
= LoaderTitle
[0];
1768 LoaderTitle
= L
"Legacy OS";
1770 if (Volume
->VolName
!= NULL
)
1771 VolDesc
= Volume
->VolName
;
1773 VolDesc
= (Volume
->DiskKind
== DISK_KIND_OPTICAL
) ? L
"CD" : L
"HD";
1775 LegacyTitle
= AllocateZeroPool(256 * sizeof(CHAR16
));
1776 if (LegacyTitle
!= NULL
)
1777 SPrint(LegacyTitle
, 255, L
"Boot %s from %s", LoaderTitle
, VolDesc
);
1778 if (IsInSubstring(LegacyTitle
, GlobalConfig
.DontScanVolumes
)) {
1779 MyFreePool(LegacyTitle
);
1783 // prepare the menu entry
1784 Entry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1785 Entry
->me
.Title
= LegacyTitle
;
1786 Entry
->me
.Tag
= TAG_LEGACY
;
1788 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1789 Entry
->me
.Image
= LoadOSIcon(Volume
->OSIconName
, L
"legacy", FALSE
);
1790 Entry
->me
.BadgeImage
= Volume
->VolBadgeImage
;
1791 Entry
->Volume
= Volume
;
1792 Entry
->LoadOptions
= (Volume
->DiskKind
== DISK_KIND_OPTICAL
) ? L
"CD" :
1793 ((Volume
->DiskKind
== DISK_KIND_EXTERNAL
) ? L
"USB" : L
"HD");
1794 Entry
->Enabled
= TRUE
;
1796 // create the submenu
1797 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
1798 SubScreen
->Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1799 SPrint(SubScreen
->Title
, 255, L
"Boot Options for %s on %s", LoaderTitle
, VolDesc
);
1800 SubScreen
->TitleImage
= Entry
->me
.Image
;
1801 SubScreen
->Hint1
= StrDuplicate(SUBSCREEN_HINT1
);
1802 if (GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_EDITOR
) {
1803 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2_NO_EDITOR
);
1805 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2
);
1809 SubEntry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1810 SubEntry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1811 SPrint(SubEntry
->me
.Title
, 255, L
"Boot %s", LoaderTitle
);
1812 SubEntry
->me
.Tag
= TAG_LEGACY
;
1813 SubEntry
->Volume
= Entry
->Volume
;
1814 SubEntry
->LoadOptions
= Entry
->LoadOptions
;
1815 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
1817 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
1818 Entry
->me
.SubScreen
= SubScreen
;
1819 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1821 } /* static LEGACY_ENTRY * AddLegacyEntry() */
1824 // default volume badge icon based on disk kind
1825 static EG_IMAGE
* GetDiskBadge(IN UINTN DiskType
) {
1826 EG_IMAGE
* Badge
= NULL
;
1830 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_INTERNAL
);
1833 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_EXTERNAL
);
1836 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_OPTICAL
);
1840 } // static EG_IMAGE * GetDiskBadge()
1843 Create a rEFInd boot option from a Legacy BIOS protocol option.
1845 static LEGACY_ENTRY
* AddLegacyEntryUEFI(BDS_COMMON_OPTION
*BdsOption
, IN UINT16 DiskType
)
1847 LEGACY_ENTRY
*Entry
, *SubEntry
;
1848 REFIT_MENU_SCREEN
*SubScreen
;
1849 CHAR16 ShortcutLetter
= 0;
1850 CHAR16
*LegacyDescription
= StrDuplicate(BdsOption
->Description
);
1852 if (IsInSubstring(LegacyDescription
, GlobalConfig
.DontScanVolumes
))
1855 // Remove stray spaces, since many EFIs produce descriptions with lots of
1856 // extra spaces, especially at the end; this throws off centering of the
1857 // description on the screen....
1858 LimitStringLength(LegacyDescription
, 100);
1860 // prepare the menu entry
1861 Entry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1862 Entry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1863 SPrint(Entry
->me
.Title
, 255, L
"Boot legacy target %s", LegacyDescription
);
1864 Entry
->me
.Tag
= TAG_LEGACY_UEFI
;
1866 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1867 Entry
->me
.Image
= LoadOSIcon(L
"legacy", L
"legacy", TRUE
);
1868 Entry
->LoadOptions
= (DiskType
== BBS_CDROM
) ? L
"CD" :
1869 ((DiskType
== BBS_USB
) ? L
"USB" : L
"HD");
1870 Entry
->me
.BadgeImage
= GetDiskBadge(DiskType
);
1871 Entry
->BdsOption
= BdsOption
;
1872 Entry
->Enabled
= TRUE
;
1874 // create the submenu
1875 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
1876 SubScreen
->Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1877 SPrint(SubScreen
->Title
, 255, L
"No boot options for legacy target");
1878 SubScreen
->TitleImage
= Entry
->me
.Image
;
1879 SubScreen
->Hint1
= StrDuplicate(SUBSCREEN_HINT1
);
1880 if (GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_EDITOR
) {
1881 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2_NO_EDITOR
);
1883 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2
);
1887 SubEntry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1888 SubEntry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1889 SPrint(SubEntry
->me
.Title
, 255, L
"Boot %s", LegacyDescription
);
1890 SubEntry
->me
.Tag
= TAG_LEGACY_UEFI
;
1891 Entry
->BdsOption
= BdsOption
;
1892 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
1894 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
1895 Entry
->me
.SubScreen
= SubScreen
;
1896 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1898 } /* static LEGACY_ENTRY * AddLegacyEntryUEFI() */
1901 Scan for legacy BIOS targets on machines that implement EFI_LEGACY_BIOS_PROTOCOL.
1902 In testing, protocol has not been implemented on Macs but has been
1903 implemented on several Dell PCs and an ASUS motherboard.
1904 Restricts output to disks of the specified DiskType.
1906 static VOID
ScanLegacyUEFI(IN UINTN DiskType
)
1909 EFI_LEGACY_BIOS_PROTOCOL
*LegacyBios
;
1910 UINT16
*BootOrder
= NULL
;
1912 CHAR16 BootOption
[10];
1913 UINTN BootOrderSize
= 0;
1915 BDS_COMMON_OPTION
*BdsOption
;
1916 LIST_ENTRY TempList
;
1917 BBS_BBS_DEVICE_PATH
*BbsDevicePath
= NULL
;
1918 BOOLEAN SearchingForUsb
= FALSE
;
1920 InitializeListHead (&TempList
);
1921 ZeroMem (Buffer
, sizeof (Buffer
));
1923 // If LegacyBios protocol is not implemented on this platform, then
1924 //we do not support this type of legacy boot on this machine.
1925 Status
= refit_call3_wrapper(gBS
->LocateProtocol
, &gEfiLegacyBootProtocolGuid
, NULL
, (VOID
**) &LegacyBios
);
1926 if (EFI_ERROR (Status
))
1929 // EFI calls USB drives BBS_HARDDRIVE, but we want to distinguish them,
1930 // so we set DiskType inappropriately elsewhere in the program and
1931 // "translate" it here.
1932 if (DiskType
== BBS_USB
) {
1933 DiskType
= BBS_HARDDISK
;
1934 SearchingForUsb
= TRUE
;
1937 // Grab the boot order
1938 BootOrder
= BdsLibGetVariableAndSize(L
"BootOrder", &gEfiGlobalVariableGuid
, &BootOrderSize
);
1939 if (BootOrder
== NULL
) {
1944 while (Index
< BootOrderSize
/ sizeof (UINT16
))
1946 // Grab each boot option variable from the boot order, and convert
1947 // the variable into a BDS boot option
1948 UnicodeSPrint (BootOption
, sizeof (BootOption
), L
"Boot%04x", BootOrder
[Index
]);
1949 BdsOption
= BdsLibVariableToOption (&TempList
, BootOption
);
1951 if (BdsOption
!= NULL
) {
1952 BbsDevicePath
= (BBS_BBS_DEVICE_PATH
*)BdsOption
->DevicePath
;
1953 // Only add the entry if it is of a requested type (e.g. USB, HD)
1954 // Two checks necessary because some systems return EFI boot loaders
1955 // with a DeviceType value that would inappropriately include them
1956 // as legacy loaders....
1957 if ((BbsDevicePath
->DeviceType
== DiskType
) && (BdsOption
->DevicePath
->Type
== DEVICE_TYPE_BIOS
)) {
1958 // USB flash drives appear as hard disks with certain media flags set.
1959 // Look for this, and if present, pass it on with the (technically
1960 // incorrect, but internally useful) BBS_TYPE_USB flag set.
1961 if (DiskType
== BBS_HARDDISK
) {
1962 if (SearchingForUsb
&& (BbsDevicePath
->StatusFlag
& (BBS_MEDIA_PRESENT
| BBS_MEDIA_MAYBE_PRESENT
))) {
1963 AddLegacyEntryUEFI(BdsOption
, BBS_USB
);
1964 } else if (!SearchingForUsb
&& !(BbsDevicePath
->StatusFlag
& (BBS_MEDIA_PRESENT
| BBS_MEDIA_MAYBE_PRESENT
))) {
1965 AddLegacyEntryUEFI(BdsOption
, DiskType
);
1968 AddLegacyEntryUEFI(BdsOption
, DiskType
);
1971 } // if (BdsOption != NULL)
1974 } /* static VOID ScanLegacyUEFI() */
1976 static VOID
ScanLegacyVolume(REFIT_VOLUME
*Volume
, UINTN VolumeIndex
) {
1978 BOOLEAN ShowVolume
, HideIfOthersFound
;
1981 HideIfOthersFound
= FALSE
;
1982 if (Volume
->IsAppleLegacy
) {
1984 HideIfOthersFound
= TRUE
;
1985 } else if (Volume
->HasBootCode
) {
1987 if (Volume
->BlockIO
== Volume
->WholeDiskBlockIO
&&
1988 Volume
->BlockIOOffset
== 0 &&
1989 Volume
->OSName
== NULL
)
1990 // this is a whole disk (MBR) entry; hide if we have entries for partitions
1991 HideIfOthersFound
= TRUE
;
1993 if (HideIfOthersFound
) {
1994 // check for other bootable entries on the same disk
1995 for (VolumeIndex2
= 0; VolumeIndex2
< VolumesCount
; VolumeIndex2
++) {
1996 if (VolumeIndex2
!= VolumeIndex
&& Volumes
[VolumeIndex2
]->HasBootCode
&&
1997 Volumes
[VolumeIndex2
]->WholeDiskBlockIO
== Volume
->WholeDiskBlockIO
)
2003 AddLegacyEntry(NULL
, Volume
);
2004 } // static VOID ScanLegacyVolume()
2006 // Scan attached optical discs for legacy (BIOS) boot code
2007 // and add anything found to the list....
2008 static VOID
ScanLegacyDisc(VOID
)
2011 REFIT_VOLUME
*Volume
;
2013 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
) {
2014 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
2015 Volume
= Volumes
[VolumeIndex
];
2016 if (Volume
->DiskKind
== DISK_KIND_OPTICAL
)
2017 ScanLegacyVolume(Volume
, VolumeIndex
);
2019 } else if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
2020 ScanLegacyUEFI(BBS_CDROM
);
2022 } /* static VOID ScanLegacyDisc() */
2024 // Scan internal hard disks for legacy (BIOS) boot code
2025 // and add anything found to the list....
2026 static VOID
ScanLegacyInternal(VOID
)
2029 REFIT_VOLUME
*Volume
;
2031 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
) {
2032 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
2033 Volume
= Volumes
[VolumeIndex
];
2034 if (Volume
->DiskKind
== DISK_KIND_INTERNAL
)
2035 ScanLegacyVolume(Volume
, VolumeIndex
);
2037 } else if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
2038 // TODO: This actually picks up USB flash drives, too; try to find
2039 // a way to differentiate the two....
2040 ScanLegacyUEFI(BBS_HARDDISK
);
2042 } /* static VOID ScanLegacyInternal() */
2044 // Scan external disks for legacy (BIOS) boot code
2045 // and add anything found to the list....
2046 static VOID
ScanLegacyExternal(VOID
)
2049 REFIT_VOLUME
*Volume
;
2051 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
) {
2052 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
2053 Volume
= Volumes
[VolumeIndex
];
2054 if (Volume
->DiskKind
== DISK_KIND_EXTERNAL
)
2055 ScanLegacyVolume(Volume
, VolumeIndex
);
2057 } else if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
2058 // TODO: This actually doesn't do anything useful; leaving in hopes of
2059 // fixing it later....
2060 ScanLegacyUEFI(BBS_USB
);
2062 } /* static VOID ScanLegacyExternal() */
2065 // pre-boot tool functions
2068 static VOID
StartTool(IN LOADER_ENTRY
*Entry
)
2070 BeginExternalScreen(Entry
->UseGraphicsMode
, Entry
->me
.Title
+ 6); // assumes "Start <title>" as assigned below
2071 StoreLoaderName(Entry
->me
.Title
);
2072 StartEFIImage(Entry
->DevicePath
, Entry
->LoadOptions
, TYPE_EFI
,
2073 Basename(Entry
->LoaderPath
), Entry
->OSType
, NULL
, TRUE
, FALSE
);
2074 FinishExternalScreen();
2075 } /* static VOID StartTool() */
2077 static LOADER_ENTRY
* AddToolEntry(EFI_HANDLE DeviceHandle
, IN CHAR16
*LoaderPath
, IN CHAR16
*LoaderTitle
, IN EG_IMAGE
*Image
,
2078 IN CHAR16 ShortcutLetter
, IN BOOLEAN UseGraphicsMode
)
2080 LOADER_ENTRY
*Entry
;
2081 CHAR16
*TitleStr
= NULL
;
2083 Entry
= AllocateZeroPool(sizeof(LOADER_ENTRY
));
2085 TitleStr
= PoolPrint(L
"Start %s", LoaderTitle
);
2086 Entry
->me
.Title
= TitleStr
;
2087 Entry
->me
.Tag
= TAG_TOOL
;
2089 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
2090 Entry
->me
.Image
= Image
;
2091 Entry
->LoaderPath
= (LoaderPath
) ? StrDuplicate(LoaderPath
) : NULL
;
2092 Entry
->DevicePath
= FileDevicePath(DeviceHandle
, Entry
->LoaderPath
);
2093 Entry
->UseGraphicsMode
= UseGraphicsMode
;
2095 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
2097 } /* static LOADER_ENTRY * AddToolEntry() */
2100 // pre-boot driver functions
2103 static UINTN
ScanDriverDir(IN CHAR16
*Path
)
2106 REFIT_DIR_ITER DirIter
;
2108 EFI_FILE_INFO
*DirEntry
;
2109 CHAR16 FileName
[256];
2111 CleanUpPathNameSlashes(Path
);
2112 // look through contents of the directory
2113 DirIterOpen(SelfRootDir
, Path
, &DirIter
);
2114 while (DirIterNext(&DirIter
, 2, LOADER_MATCH_PATTERNS
, &DirEntry
)) {
2115 if (DirEntry
->FileName
[0] == '.')
2116 continue; // skip this
2118 SPrint(FileName
, 255, L
"%s\\%s", Path
, DirEntry
->FileName
);
2120 Status
= StartEFIImage(FileDevicePath(SelfLoadedImage
->DeviceHandle
, FileName
),
2121 L
"", TYPE_EFI
, DirEntry
->FileName
, 0, NULL
, FALSE
, TRUE
);
2123 Status
= DirIterClose(&DirIter
);
2124 if (Status
!= EFI_NOT_FOUND
) {
2125 SPrint(FileName
, 255, L
"while scanning the %s directory", Path
);
2126 CheckError(Status
, FileName
);
2131 #ifdef __MAKEWITH_GNUEFI
2132 static EFI_STATUS
ConnectAllDriversToAllControllers(VOID
)
2135 UINTN AllHandleCount
;
2136 EFI_HANDLE
*AllHandleBuffer
;
2139 EFI_HANDLE
*HandleBuffer
;
2145 Status
= LibLocateHandle(AllHandles
,
2150 if (EFI_ERROR(Status
))
2153 for (Index
= 0; Index
< AllHandleCount
; Index
++) {
2155 // Scan the handle database
2157 Status
= LibScanHandleDatabase(NULL
,
2159 AllHandleBuffer
[Index
],
2164 if (EFI_ERROR (Status
))
2168 if (HandleType
[Index
] & EFI_HANDLE_TYPE_DRIVER_BINDING_HANDLE
)
2170 if (HandleType
[Index
] & EFI_HANDLE_TYPE_IMAGE_HANDLE
)
2175 for (HandleIndex
= 0; HandleIndex
< HandleCount
; HandleIndex
++) {
2176 if (HandleType
[HandleIndex
] & EFI_HANDLE_TYPE_PARENT_HANDLE
)
2181 if (HandleType
[Index
] & EFI_HANDLE_TYPE_DEVICE_HANDLE
) {
2182 Status
= refit_call4_wrapper(BS
->ConnectController
,
2183 AllHandleBuffer
[Index
],
2191 MyFreePool (HandleBuffer
);
2192 MyFreePool (HandleType
);
2196 MyFreePool (AllHandleBuffer
);
2198 } /* EFI_STATUS ConnectAllDriversToAllControllers() */
2200 static EFI_STATUS
ConnectAllDriversToAllControllers(VOID
) {
2201 BdsLibConnectAllDriversToAllControllers();
2206 // Load all EFI drivers from rEFInd's "drivers" subdirectory and from the
2207 // directories specified by the user in the "scan_driver_dirs" configuration
2209 static VOID
LoadDrivers(VOID
)
2211 CHAR16
*Directory
, *SelfDirectory
;
2212 UINTN i
= 0, Length
, NumFound
= 0;
2214 // load drivers from the subdirectories of rEFInd's home directory specified
2215 // in the DRIVER_DIRS constant.
2216 while ((Directory
= FindCommaDelimited(DRIVER_DIRS
, i
++)) != NULL
) {
2217 SelfDirectory
= SelfDirPath
? StrDuplicate(SelfDirPath
) : NULL
;
2218 CleanUpPathNameSlashes(SelfDirectory
);
2219 MergeStrings(&SelfDirectory
, Directory
, L
'\\');
2220 NumFound
+= ScanDriverDir(SelfDirectory
);
2221 MyFreePool(Directory
);
2222 MyFreePool(SelfDirectory
);
2225 // Scan additional user-specified driver directories....
2227 while ((Directory
= FindCommaDelimited(GlobalConfig
.DriverDirs
, i
++)) != NULL
) {
2228 CleanUpPathNameSlashes(Directory
);
2229 Length
= StrLen(Directory
);
2231 NumFound
+= ScanDriverDir(Directory
);
2233 MyFreePool(Directory
);
2236 // connect all devices
2238 ConnectAllDriversToAllControllers();
2239 } /* static VOID LoadDrivers() */
2241 // Determine what (if any) type of legacy (BIOS) boot support is available
2242 static VOID
FindLegacyBootType(VOID
) {
2244 EFI_LEGACY_BIOS_PROTOCOL
*LegacyBios
;
2246 GlobalConfig
.LegacyType
= LEGACY_TYPE_NONE
;
2248 // UEFI-style legacy BIOS support is available only with some EFI implementations....
2249 Status
= refit_call3_wrapper(gBS
->LocateProtocol
, &gEfiLegacyBootProtocolGuid
, NULL
, (VOID
**) &LegacyBios
);
2250 if (!EFI_ERROR (Status
))
2251 GlobalConfig
.LegacyType
= LEGACY_TYPE_UEFI
;
2253 // Macs have their own system. If the firmware vendor code contains the
2254 // string "Apple", assume it's available. Note that this overrides the
2255 // UEFI type, and might yield false positives if the vendor string
2256 // contains "Apple" as part of something bigger, so this isn't 100%
2258 if (StriSubCmp(L
"Apple", ST
->FirmwareVendor
))
2259 GlobalConfig
.LegacyType
= LEGACY_TYPE_MAC
;
2260 } // static VOID FindLegacyBootType
2262 // Warn the user if legacy OS scans are enabled but the firmware can't support them....
2263 static VOID
WarnIfLegacyProblems(VOID
) {
2264 BOOLEAN found
= FALSE
;
2267 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_NONE
) {
2269 if (GlobalConfig
.ScanFor
[i
] == 'h' || GlobalConfig
.ScanFor
[i
] == 'b' || GlobalConfig
.ScanFor
[i
] == 'c' ||
2270 GlobalConfig
.ScanFor
[i
] == 'H' || GlobalConfig
.ScanFor
[i
] == 'B' || GlobalConfig
.ScanFor
[i
] == 'C')
2273 } while ((i
< NUM_SCAN_OPTIONS
) && (!found
));
2276 Print(L
"NOTE: refind.conf's 'scanfor' line specifies scanning for one or more legacy\n");
2277 Print(L
"(BIOS) boot options; however, this is not possible because your computer lacks\n");
2278 Print(L
"the necessary Compatibility Support Module (CSM) support or that support is\n");
2279 Print(L
"disabled in your firmware.\n");
2282 } // if no legacy support
2283 } // static VOID WarnIfLegacyProblems()
2285 // Locates boot loaders. NOTE: This assumes that GlobalConfig.LegacyType is set correctly.
2286 static VOID
ScanForBootloaders(VOID
) {
2289 BOOLEAN ScanForLegacy
= FALSE
;
2291 // Determine up-front if we'll be scanning for legacy loaders....
2292 for (i
= 0; i
< NUM_SCAN_OPTIONS
; i
++) {
2293 s
= GlobalConfig
.ScanFor
[i
];
2294 if ((s
== 'c') || (s
== 'C') || (s
== 'h') || (s
== 'H') || (s
== 'b') || (s
== 'B'))
2295 ScanForLegacy
= TRUE
;
2298 // If UEFI & scanning for legacy loaders & deep legacy scan, update NVRAM boot manager list
2299 if ((GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) && ScanForLegacy
&& GlobalConfig
.DeepLegacyScan
) {
2300 BdsDeleteAllInvalidLegacyBootOptions();
2301 BdsAddNonExistingLegacyBootOptions();
2304 // scan for loaders and tools, add them to the menu
2305 for (i
= 0; i
< NUM_SCAN_OPTIONS
; i
++) {
2306 switch(GlobalConfig
.ScanFor
[i
]) {
2311 ScanLegacyInternal();
2314 ScanLegacyExternal();
2317 ScanUserConfigured(GlobalConfig
.ConfigFilename
);
2334 // assign shortcut keys
2335 for (i
= 0; i
< MainMenu
.EntryCount
&& MainMenu
.Entries
[i
]->Row
== 0 && i
< 9; i
++)
2336 MainMenu
.Entries
[i
]->ShortcutDigit
= (CHAR16
)('1' + i
);
2338 // wait for user ACK when there were errors
2339 FinishTextScreen(FALSE
);
2340 } // static VOID ScanForBootloaders()
2342 // Locate a single tool from the specified Locations using one of the
2343 // specified Names and add it to the menu.
2344 static VOID
FindTool(CHAR16
*Locations
, CHAR16
*Names
, CHAR16
*Description
, UINTN Icon
) {
2345 UINTN j
= 0, k
, VolumeIndex
;
2346 CHAR16
*DirName
, *FileName
, *PathName
, FullDescription
[256];
2348 while ((DirName
= FindCommaDelimited(Locations
, j
++)) != NULL
) {
2350 while ((FileName
= FindCommaDelimited(Names
, k
++)) != NULL
) {
2351 PathName
= StrDuplicate(DirName
);
2352 MergeStrings(&PathName
, FileName
, (StriCmp(PathName
, L
"\\") == 0) ? 0 : L
'\\');
2353 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
2354 if ((Volumes
[VolumeIndex
]->RootDir
!= NULL
) && (FileExists(Volumes
[VolumeIndex
]->RootDir
, PathName
))) {
2355 SPrint(FullDescription
, 255, L
"%s at %s on %s", Description
, PathName
, Volumes
[VolumeIndex
]->VolName
);
2356 AddToolEntry(Volumes
[VolumeIndex
]->DeviceHandle
, PathName
, FullDescription
, BuiltinIcon(Icon
), 'S', FALSE
);
2359 MyFreePool(PathName
);
2360 MyFreePool(FileName
);
2362 MyFreePool(DirName
);
2363 } // while Locations
2364 } // VOID FindTool()
2366 // Add the second-row tags containing built-in and external tools (EFI shell,
2368 static VOID
ScanForTools(VOID
) {
2369 CHAR16
*FileName
= NULL
, *VolName
= NULL
, *MokLocations
, Description
[256];
2370 REFIT_MENU_ENTRY
*TempMenuEntry
;
2371 UINTN i
, j
, VolumeIndex
;
2375 MokLocations
= StrDuplicate(MOK_LOCATIONS
);
2376 if (MokLocations
!= NULL
)
2377 MergeStrings(&MokLocations
, SelfDirPath
, L
',');
2379 for (i
= 0; i
< NUM_TOOLS
; i
++) {
2380 switch(GlobalConfig
.ShowTools
[i
]) {
2381 // NOTE: Be sure that FileName is NULL at the end of each case.
2383 TempMenuEntry
= CopyMenuEntry(&MenuEntryShutdown
);
2384 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_SHUTDOWN
);
2385 AddMenuEntry(&MainMenu
, TempMenuEntry
);
2389 TempMenuEntry
= CopyMenuEntry(&MenuEntryReset
);
2390 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_RESET
);
2391 AddMenuEntry(&MainMenu
, TempMenuEntry
);
2395 TempMenuEntry
= CopyMenuEntry(&MenuEntryAbout
);
2396 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT
);
2397 AddMenuEntry(&MainMenu
, TempMenuEntry
);
2401 TempMenuEntry
= CopyMenuEntry(&MenuEntryExit
);
2402 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_EXIT
);
2403 AddMenuEntry(&MainMenu
, TempMenuEntry
);
2407 if (EfivarGetRaw(&GlobalGuid
, L
"OsIndicationsSupported", &b
, &j
) == EFI_SUCCESS
) {
2409 if (osind
& EFI_OS_INDICATIONS_BOOT_TO_FW_UI
) {
2410 TempMenuEntry
= CopyMenuEntry(&MenuEntryFirmware
);
2411 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_FIRMWARE
);
2412 AddMenuEntry(&MainMenu
, TempMenuEntry
);
2419 while ((FileName
= FindCommaDelimited(SHELL_NAMES
, j
++)) != NULL
) {
2420 if (FileExists(SelfRootDir
, FileName
)) {
2421 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, L
"EFI Shell", BuiltinIcon(BUILTIN_ICON_TOOL_SHELL
),
2424 MyFreePool(FileName
);
2430 while ((FileName
= FindCommaDelimited(GPTSYNC_NAMES
, j
++)) != NULL
) {
2431 if (FileExists(SelfRootDir
, FileName
)) {
2432 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, L
"Hybrid MBR tool", BuiltinIcon(BUILTIN_ICON_TOOL_PART
),
2435 MyFreePool(FileName
);
2442 while ((FileName
= FindCommaDelimited(GDISK_NAMES
, j
++)) != NULL
) {
2443 if (FileExists(SelfRootDir
, FileName
)) {
2444 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, L
"disk partitioning tool",
2445 BuiltinIcon(BUILTIN_ICON_TOOL_PART
), 'G', FALSE
);
2447 MyFreePool(FileName
);
2454 while ((FileName
= FindCommaDelimited(NETBOOT_NAMES
, j
++)) != NULL
) {
2455 if (FileExists(SelfRootDir
, FileName
)) {
2456 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, L
"Netboot",
2457 BuiltinIcon(BUILTIN_ICON_TOOL_NETBOOT
), 'N', FALSE
);
2459 MyFreePool(FileName
);
2464 case TAG_APPLE_RECOVERY
:
2465 FileName
= StrDuplicate(L
"\\com.apple.recovery.boot\\boot.efi");
2466 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
2467 if ((Volumes
[VolumeIndex
]->RootDir
!= NULL
) && (FileExists(Volumes
[VolumeIndex
]->RootDir
, FileName
))) {
2468 SPrint(Description
, 255, L
"Apple Recovery on %s", Volumes
[VolumeIndex
]->VolName
);
2469 AddToolEntry(Volumes
[VolumeIndex
]->DeviceHandle
, FileName
, Description
,
2470 BuiltinIcon(BUILTIN_ICON_TOOL_APPLE_RESCUE
), 'R', TRUE
);
2473 MyFreePool(FileName
);
2477 case TAG_WINDOWS_RECOVERY
:
2479 while ((FileName
= FindCommaDelimited(GlobalConfig
.WindowsRecoveryFiles
, j
++)) != NULL
) {
2480 SplitVolumeAndFilename(&FileName
, &VolName
);
2481 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
2482 if ((Volumes
[VolumeIndex
]->RootDir
!= NULL
) && (FileExists(Volumes
[VolumeIndex
]->RootDir
, FileName
)) &&
2483 ((VolName
== NULL
) || (StriCmp(VolName
, Volumes
[VolumeIndex
]->VolName
) == 0))) {
2484 SPrint(Description
, 255, L
"Microsoft Recovery on %s", Volumes
[VolumeIndex
]->VolName
);
2485 AddToolEntry(Volumes
[VolumeIndex
]->DeviceHandle
, FileName
, Description
,
2486 BuiltinIcon(BUILTIN_ICON_TOOL_WINDOWS_RESCUE
), 'R', TRUE
);
2490 MyFreePool(FileName
);
2492 MyFreePool(VolName
);
2497 FindTool(MokLocations
, MOK_NAMES
, L
"MOK utility", BUILTIN_ICON_TOOL_MOK_TOOL
);
2501 FindTool(MEMTEST_LOCATIONS
, MEMTEST_NAMES
, L
"Memory test utility", BUILTIN_ICON_TOOL_MEMTEST
);
2506 } // static VOID ScanForTools
2508 // Rescan for boot loaders
2509 static VOID
RescanAll(BOOLEAN DisplayMessage
) {
2517 egDisplayMessage(L
"Scanning for new boot loaders; please wait....", &BGColor
);
2518 FreeList((VOID
***) &(MainMenu
.Entries
), &MainMenu
.EntryCount
);
2519 MainMenu
.Entries
= NULL
;
2520 MainMenu
.EntryCount
= 0;
2521 ReadConfig(GlobalConfig
.ConfigFilename
);
2522 ConnectAllDriversToAllControllers();
2524 ScanForBootloaders();
2527 } // VOID RescanAll()
2529 #ifdef __MAKEWITH_TIANO
2531 // Minimal initialization function
2532 static VOID
InitializeLib(IN EFI_HANDLE ImageHandle
, IN EFI_SYSTEM_TABLE
*SystemTable
) {
2534 // gImageHandle = ImageHandle;
2535 gBS
= SystemTable
->BootServices
;
2536 // gRS = SystemTable->RuntimeServices;
2537 gRT
= SystemTable
->RuntimeServices
; // Some BDS functions need gRT to be set
2538 EfiGetSystemConfigurationTable (&gEfiDxeServicesTableGuid
, (VOID
**) &gDS
);
2540 // InitializeConsoleSim();
2545 // Set up our own Secure Boot extensions....
2546 // Returns TRUE on success, FALSE otherwise
2547 static BOOLEAN
SecureBootSetup(VOID
) {
2549 BOOLEAN Success
= FALSE
;
2551 if (secure_mode() && ShimLoaded()) {
2552 Status
= security_policy_install();
2553 if (Status
== EFI_SUCCESS
) {
2556 Print(L
"Failed to install MOK Secure Boot extensions");
2560 } // VOID SecureBootSetup()
2562 // Remove our own Secure Boot extensions....
2563 // Returns TRUE on success, FALSE otherwise
2564 static BOOLEAN
SecureBootUninstall(VOID
) {
2566 BOOLEAN Success
= TRUE
;
2568 if (secure_mode()) {
2569 Status
= security_policy_uninstall();
2570 if (Status
!= EFI_SUCCESS
) {
2572 BeginTextScreen(L
"Secure Boot Policy Failure");
2573 Print(L
"Failed to uninstall MOK Secure Boot extensions; forcing a reboot.");
2575 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);
2579 } // VOID SecureBootUninstall
2581 // Sets the global configuration filename; will be CONFIG_FILE_NAME unless the
2582 // "-c" command-line option is set, in which case that takes precedence.
2583 // If an error is encountered, leaves the value alone (it should be set to
2584 // CONFIG_FILE_NAME when GlobalConfig is initialized).
2585 static VOID
SetConfigFilename(EFI_HANDLE ImageHandle
) {
2586 EFI_LOADED_IMAGE
*Info
;
2587 CHAR16
*Options
, *FileName
;
2591 Status
= refit_call3_wrapper(BS
->HandleProtocol
, ImageHandle
, &LoadedImageProtocol
, (VOID
**) &Info
);
2592 if ((Status
== EFI_SUCCESS
) && (Info
->LoadOptionsSize
> 0)) {
2593 Options
= (CHAR16
*) Info
->LoadOptions
;
2594 Where
= FindSubString(L
" -c ", Options
);
2596 FileName
= StrDuplicate(&Options
[Where
+ 4]);
2597 Where
= FindSubString(L
" ", FileName
);
2599 FileName
[Where
] = L
'\0';
2601 if (FileExists(SelfDir
, FileName
)) {
2602 GlobalConfig
.ConfigFilename
= FileName
;
2604 Print(L
"Specified configuration file (%s) doesn't exist; using\n'refind.conf' default\n", FileName
);
2605 MyFreePool(FileName
);
2609 } // VOID SetConfigFilename()
2616 efi_main (EFI_HANDLE ImageHandle
, EFI_SYSTEM_TABLE
*SystemTable
)
2619 BOOLEAN MainLoopRunning
= TRUE
;
2620 BOOLEAN MokProtocol
;
2621 REFIT_MENU_ENTRY
*ChosenEntry
;
2623 CHAR16
*SelectionName
= NULL
;
2627 InitializeLib(ImageHandle
, SystemTable
);
2628 Status
= InitRefitLib(ImageHandle
);
2629 if (EFI_ERROR(Status
))
2632 // read configuration
2633 CopyMem(GlobalConfig
.ScanFor
, "ieom ", NUM_SCAN_OPTIONS
);
2634 FindLegacyBootType();
2635 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
)
2636 CopyMem(GlobalConfig
.ScanFor
, "ihebocm ", NUM_SCAN_OPTIONS
);
2637 SetConfigFilename(ImageHandle
);
2638 ReadConfig(GlobalConfig
.ConfigFilename
);
2641 WarnIfLegacyProblems();
2642 MainMenu
.TimeoutSeconds
= GlobalConfig
.Timeout
;
2644 // disable EFI watchdog timer
2645 refit_call4_wrapper(BS
->SetWatchdogTimer
, 0x0000, 0x0000, 0x0000, NULL
);
2647 // further bootstrap (now with config available)
2648 MokProtocol
= SecureBootSetup();
2651 ScanForBootloaders();
2655 if (GlobalConfig
.ScanDelay
> 0) {
2660 if (GlobalConfig
.ScanDelay
> 1)
2661 egDisplayMessage(L
"Pausing before disk scan; please wait....", &BGColor
);
2662 for (i
= 0; i
< GlobalConfig
.ScanDelay
; i
++)
2663 refit_call1_wrapper(BS
->Stall
, 1000000);
2664 RescanAll(GlobalConfig
.ScanDelay
> 1);
2667 if (GlobalConfig
.DefaultSelection
)
2668 SelectionName
= StrDuplicate(GlobalConfig
.DefaultSelection
);
2670 while (MainLoopRunning
) {
2671 MenuExit
= RunMainMenu(&MainMenu
, &SelectionName
, &ChosenEntry
);
2673 // The Escape key triggers a re-scan operation....
2674 if (MenuExit
== MENU_EXIT_ESCAPE
) {
2680 switch (ChosenEntry
->Tag
) {
2682 case TAG_REBOOT
: // Reboot
2684 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);
2685 MainLoopRunning
= FALSE
; // just in case we get this far
2688 case TAG_SHUTDOWN
: // Shut Down
2690 refit_call4_wrapper(RT
->ResetSystem
, EfiResetShutdown
, EFI_SUCCESS
, 0, NULL
);
2691 MainLoopRunning
= FALSE
; // just in case we get this far
2694 case TAG_ABOUT
: // About rEFInd
2698 case TAG_LOADER
: // Boot OS via .EFI loader
2699 StartLoader((LOADER_ENTRY
*)ChosenEntry
, SelectionName
);
2702 case TAG_LEGACY
: // Boot legacy OS
2703 StartLegacy((LEGACY_ENTRY
*)ChosenEntry
, SelectionName
);
2706 case TAG_LEGACY_UEFI
: // Boot a legacy OS on a non-Mac
2707 StartLegacyUEFI((LEGACY_ENTRY
*)ChosenEntry
, SelectionName
);
2710 case TAG_TOOL
: // Start a EFI tool
2711 StartTool((LOADER_ENTRY
*)ChosenEntry
);
2714 case TAG_EXIT
: // Terminate rEFInd
2715 if ((MokProtocol
) && !SecureBootUninstall()) {
2716 MainLoopRunning
= FALSE
; // just in case we get this far
2718 BeginTextScreen(L
" ");
2723 case TAG_FIRMWARE
: // Reboot into firmware's user interface
2724 RebootIntoFirmware();
2730 // If we end up here, things have gone wrong. Try to reboot, and if that
2731 // fails, go into an endless loop.
2732 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);