3 * Main code for the boot menu
5 * Copyright (c) 2006-2010 Christoph Pfisterer
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions are
12 * * Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
15 * * Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the
20 * * Neither the name of Christoph Pfisterer nor the names of the
21 * contributors may be used to endorse or promote products derived
22 * from this software without specific prior written permission.
24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
26 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
27 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
28 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
29 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
30 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
31 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
32 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
33 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
34 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37 * Modifications copyright (c) 2012-2014 Roderick W. Smith
39 * Modifications distributed under the terms of the GNU General Public
40 * License (GPL) version 3 (GPLv3), a copy of which must be distributed
41 * with this source code or binaries made from it.
53 #include "security_policy.h"
54 #include "../include/Handle.h"
55 #include "../include/refit_call_wrapper.h"
56 #include "driver_support.h"
57 #include "../include/syslinux_mbr.h"
59 #ifdef __MAKEWITH_GNUEFI
60 #ifndef EFI_SECURITY_VIOLATION
61 #define EFI_SECURITY_VIOLATION EFIERR (26)
65 #include "../EfiLib/BdsHelper.h"
66 #include "../EfiLib/legacy.h"
68 #ifndef EFI_OS_INDICATIONS_BOOT_TO_FW_UI
69 #define EFI_OS_INDICATIONS_BOOT_TO_FW_UI 0x0000000000000001ULL
72 #ifdef __MAKEWITH_TIANO
73 #define LibLocateHandle gBS->LocateHandleBuffer
79 #define MACOSX_LOADER_PATH L"System\\Library\\CoreServices\\boot.efi"
81 #define SHELL_NAMES L"\\EFI\\tools\\shell.efi,\\EFI\\tools\\shellx64.efi,\\shell.efi,\\shellx64.efi"
82 #define GPTSYNC_NAMES L"\\EFI\\tools\\gptsync.efi,\\EFI\\tools\\gptsync_x64.efi"
83 #define GDISK_NAMES L"\\EFI\\tools\\gdisk.efi,\\EFI\\tools\\gdisk_x64.efi"
84 #define MEMTEST_NAMES L"memtest86.efi,memtest86_x64.efi,memtest86x64.efi,bootx64.efi"
85 #define DRIVER_DIRS L"drivers,drivers_x64"
86 #define FALLBACK_FULLNAME L"EFI\\BOOT\\bootx64.efi"
87 #define FALLBACK_BASENAME L"bootx64.efi"
88 #define EFI_STUB_ARCH 0x8664
90 #define SHELL_NAMES L"\\EFI\\tools\\shell.efi,\\EFI\\tools\\shellia32.efi,\\shell.efi,\\shellia32.efi"
91 #define GPTSYNC_NAMES L"\\EFI\\tools\\gptsync.efi,\\EFI\\tools\\gptsync_ia32.efi"
92 #define GDISK_NAMES L"\\EFI\\tools\\gdisk.efi,\\EFI\\tools\\gdisk_ia32.efi"
93 #define MEMTEST_NAMES L"memtest86.efi,memtest86_ia32.efi,memtest86ia32.efi,bootia32.efi"
94 #define DRIVER_DIRS L"drivers,drivers_ia32"
95 #define FALLBACK_FULLNAME L"EFI\\BOOT\\bootia32.efi"
96 #define FALLBACK_BASENAME L"bootia32.efi"
97 #define EFI_STUB_ARCH 0x014c
99 #define SHELL_NAMES L"\\EFI\\tools\\shell.efi,\\shell.efi"
100 #define GPTSYNC_NAMES L"\\EFI\\tools\\gptsync.efi"
101 #define GDISK_NAMES L"\\EFI\\tools\\gdisk.efi"
102 #define MEMTEST_NAMES L"memtest86.efi"
103 #define DRIVER_DIRS L"drivers"
104 #define FALLBACK_FULLNAME L"EFI\\BOOT\\boot.efi" /* Not really correct */
105 #define FALLBACK_BASENAME L"boot.efi" /* Not really correct */
107 #define FAT_ARCH 0x0ef1fab9 /* ID for Apple "fat" binary */
109 // Filename patterns that identify EFI boot loaders. Note that a single case (either L"*.efi" or
110 // L"*.EFI") is fine for most systems; but Gigabyte's buggy Hybrid EFI does a case-sensitive
111 // comparison when it should do a case-insensitive comparison, so I'm doubling this up. It does
112 // no harm on other computers, AFAIK. In theory, every case variation should be done for
113 // completeness, but that's ridiculous....
114 #define LOADER_MATCH_PATTERNS L"*.efi,*.EFI"
116 // Patterns that identify Linux kernels. Added to the loader match pattern when the
117 // scan_all_linux_kernels option is set in the configuration file. Causes kernels WITHOUT
118 // a ".efi" extension to be found when scanning for boot loaders.
119 #define LINUX_MATCH_PATTERNS L"vmlinuz*,bzImage*"
121 // Default hint text for program-launch submenus
122 #define SUBSCREEN_HINT1 L"Use arrow keys to move cursor; Enter to boot;"
123 #define SUBSCREEN_HINT2 L"Insert or F2 to edit options; Esc to return to main menu"
124 #define SUBSCREEN_HINT2_NO_EDITOR L"Esc to return to main menu"
128 #define TYPE_LEGACY 2
130 static REFIT_MENU_ENTRY MenuEntryAbout
= { L
"About rEFInd", TAG_ABOUT
, 1, 0, 'A', NULL
, NULL
, NULL
};
131 static REFIT_MENU_ENTRY MenuEntryReset
= { L
"Reboot Computer", TAG_REBOOT
, 1, 0, 'R', NULL
, NULL
, NULL
};
132 static REFIT_MENU_ENTRY MenuEntryShutdown
= { L
"Shut Down Computer", TAG_SHUTDOWN
, 1, 0, 'U', NULL
, NULL
, NULL
};
133 static REFIT_MENU_ENTRY MenuEntryReturn
= { L
"Return to Main Menu", TAG_RETURN
, 1, 0, 0, NULL
, NULL
, NULL
};
134 static REFIT_MENU_ENTRY MenuEntryExit
= { L
"Exit rEFInd", TAG_EXIT
, 1, 0, 0, NULL
, NULL
, NULL
};
135 static REFIT_MENU_ENTRY MenuEntryFirmware
= { L
"Reboot to Computer Setup Utility", TAG_FIRMWARE
, 1, 0, 0, NULL
, NULL
, NULL
};
137 static REFIT_MENU_SCREEN MainMenu
= { L
"Main Menu", NULL
, 0, NULL
, 0, NULL
, 0, L
"Automatic boot",
138 L
"Use arrow keys to move cursor; Enter to boot;",
139 L
"Insert or F2 for more options; Esc to refresh" };
140 static REFIT_MENU_SCREEN AboutMenu
= { L
"About", NULL
, 0, NULL
, 0, NULL
, 0, NULL
, L
"Press Enter to return to main menu", L
"" };
142 REFIT_CONFIG GlobalConfig
= { FALSE
, FALSE
, FALSE
, 0, 0, 0, DONT_CHANGE_TEXT_MODE
, 20, 0, 0, GRAPHICS_FOR_OSX
, LEGACY_TYPE_MAC
, 0, 0,
143 { DEFAULT_BIG_ICON_SIZE
/ 4, DEFAULT_SMALL_ICON_SIZE
, DEFAULT_BIG_ICON_SIZE
}, BANNER_NOSCALE
,
144 NULL
, NULL
, CONFIG_FILE_NAME
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
145 { TAG_SHELL
, TAG_MEMTEST
, TAG_GDISK
, TAG_APPLE_RECOVERY
, TAG_WINDOWS_RECOVERY
, TAG_MOK_TOOL
,
146 TAG_ABOUT
, TAG_SHUTDOWN
, TAG_REBOOT
, TAG_FIRMWARE
, 0, 0, 0, 0, 0, 0 }
149 EFI_GUID GlobalGuid
= EFI_GLOBAL_VARIABLE
;
150 EFI_GUID RefindGuid
= REFIND_GUID_VALUE
;
152 GPT_DATA
*gPartitions
= NULL
;
154 // Structure used to hold boot loader filenames and time stamps in
155 // a linked list; used to sort entries within a directory.
159 struct LOADER_LIST
*NextEntry
;
166 static VOID
AboutrEFInd(VOID
)
168 CHAR16
*FirmwareVendor
;
170 if (AboutMenu
.EntryCount
== 0) {
171 AboutMenu
.TitleImage
= BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT
);
172 AddMenuInfoLine(&AboutMenu
, L
"rEFInd Version 0.8.2");
173 AddMenuInfoLine(&AboutMenu
, L
"");
174 AddMenuInfoLine(&AboutMenu
, L
"Copyright (c) 2006-2010 Christoph Pfisterer");
175 AddMenuInfoLine(&AboutMenu
, L
"Copyright (c) 2012-2014 Roderick W. Smith");
176 AddMenuInfoLine(&AboutMenu
, L
"Portions Copyright (c) Intel Corporation and others");
177 AddMenuInfoLine(&AboutMenu
, L
"Distributed under the terms of the GNU GPLv3 license");
178 AddMenuInfoLine(&AboutMenu
, L
"");
179 AddMenuInfoLine(&AboutMenu
, L
"Running on:");
180 AddMenuInfoLine(&AboutMenu
, PoolPrint(L
" EFI Revision %d.%02d", ST
->Hdr
.Revision
>> 16, ST
->Hdr
.Revision
& ((1 << 16) - 1)));
182 AddMenuInfoLine(&AboutMenu
, L
" Platform: x86 (32 bit)");
183 #elif defined(EFIX64)
184 AddMenuInfoLine(&AboutMenu
, PoolPrint(L
" Platform: x86_64 (64 bit); Secure Boot %s",
185 secure_mode() ? L
"active" : L
"inactive"));
187 AddMenuInfoLine(&AboutMenu
, L
" Platform: unknown");
189 FirmwareVendor
= StrDuplicate(ST
->FirmwareVendor
);
190 LimitStringLength(FirmwareVendor
, 65); // More than ~65 causes empty info page on 800x600 display
191 AddMenuInfoLine(&AboutMenu
, PoolPrint(L
" Firmware: %s %d.%02d", FirmwareVendor
, ST
->FirmwareRevision
>> 16,
192 ST
->FirmwareRevision
& ((1 << 16) - 1)));
193 AddMenuInfoLine(&AboutMenu
, PoolPrint(L
" Screen Output: %s", egScreenDescription()));
194 AddMenuInfoLine(&AboutMenu
, L
"");
195 #if defined(__MAKEWITH_GNUEFI)
196 AddMenuInfoLine(&AboutMenu
, L
"Built with GNU-EFI");
198 AddMenuInfoLine(&AboutMenu
, L
"Built with TianoCore EDK2");
200 AddMenuInfoLine(&AboutMenu
, L
"");
201 AddMenuInfoLine(&AboutMenu
, L
"For more information, see the rEFInd Web site:");
202 AddMenuInfoLine(&AboutMenu
, L
"http://www.rodsbooks.com/refind/");
203 AddMenuEntry(&AboutMenu
, &MenuEntryReturn
);
206 RunMenu(&AboutMenu
, NULL
);
207 } /* VOID AboutrEFInd() */
209 static VOID
WarnSecureBootError(CHAR16
*Name
, BOOLEAN Verbose
) {
211 Name
= L
"the loader";
213 refit_call2_wrapper(ST
->ConOut
->SetAttribute
, ST
->ConOut
, ATTR_ERROR
);
214 Print(L
"Secure Boot validation failure loading %s!\n", Name
);
215 refit_call2_wrapper(ST
->ConOut
->SetAttribute
, ST
->ConOut
, ATTR_BASIC
);
216 if (Verbose
&& secure_mode()) {
217 Print(L
"\nThis computer is configured with Secure Boot active, but\n%s has failed validation.\n", Name
);
218 Print(L
"\nYou can:\n * Launch another boot loader\n");
219 Print(L
" * Disable Secure Boot in your firmware\n");
220 Print(L
" * Sign %s with a machine owner key (MOK)\n", Name
);
221 Print(L
" * Use a MOK utility (often present on the second row) to add a MOK with which\n");
222 Print(L
" %s has already been signed.\n", Name
);
223 Print(L
" * Use a MOK utility to register %s (\"enroll its hash\") without\n", Name
);
224 Print(L
" signing it.\n");
225 Print(L
"\nSee http://www.rodsbooks.com/refind/secureboot.html for more information\n");
228 } // VOID WarnSecureBootError()
230 // Returns TRUE if this file is a valid EFI loader file, and is proper ARCH
231 static BOOLEAN
IsValidLoader(EFI_FILE
*RootDir
, CHAR16
*FileName
) {
232 BOOLEAN IsValid
= TRUE
;
233 #if defined (EFIX64) | defined (EFI32)
235 EFI_FILE_HANDLE FileHandle
;
237 UINTN Size
= sizeof(Header
);
239 if ((RootDir
== NULL
) || (FileName
== NULL
)) {
240 // Assume valid here, because Macs produce NULL RootDir (& maybe FileName)
241 // when launching from a Firewire drive. This should be handled better, but
242 // fix would have to be in StartEFIImageList() and/or in FindVolumeAndFilename().
246 Status
= refit_call5_wrapper(RootDir
->Open
, RootDir
, &FileHandle
, FileName
, EFI_FILE_MODE_READ
, 0);
247 if (EFI_ERROR(Status
))
250 Status
= refit_call3_wrapper(FileHandle
->Read
, FileHandle
, &Size
, Header
);
251 refit_call1_wrapper(FileHandle
->Close
, FileHandle
);
253 IsValid
= !EFI_ERROR(Status
) &&
254 Size
== sizeof(Header
) &&
255 ((Header
[0] == 'M' && Header
[1] == 'Z' &&
256 (Size
= *(UINT32
*)&Header
[0x3c]) < 0x180 &&
257 Header
[Size
] == 'P' && Header
[Size
+1] == 'E' &&
258 Header
[Size
+2] == 0 && Header
[Size
+3] == 0 &&
259 *(UINT16
*)&Header
[Size
+4] == EFI_STUB_ARCH
) ||
260 (*(UINT32
*)&Header
== FAT_ARCH
));
263 } // BOOLEAN IsValidLoader()
265 // Launch an EFI binary.
266 static EFI_STATUS
StartEFIImageList(IN EFI_DEVICE_PATH
**DevicePaths
,
267 IN CHAR16
*LoadOptions
, IN UINTN LoaderType
,
268 IN CHAR16
*ImageTitle
, IN CHAR8 OSType
,
269 OUT UINTN
*ErrorInStep
,
272 EFI_STATUS Status
, ReturnStatus
;
273 EFI_HANDLE ChildImageHandle
;
274 EFI_LOADED_IMAGE
*ChildLoadedImage
= NULL
;
275 REFIT_VOLUME
*Volume
= NULL
;
276 UINTN DevicePathIndex
;
277 CHAR16 ErrorInfo
[256];
278 CHAR16
*FullLoadOptions
= NULL
;
279 CHAR16
*Filename
= NULL
;
282 if (ErrorInStep
!= NULL
)
286 if (LoadOptions
!= NULL
) {
287 FullLoadOptions
= StrDuplicate(LoadOptions
);
288 if ((LoaderType
== TYPE_EFI
) && (OSType
== 'M')) {
289 MergeStrings(&FullLoadOptions
, L
" ", 0);
290 // NOTE: That last space is also added by the EFI shell and seems to be significant
291 // when passing options to Apple's boot.efi...
293 } // if (LoadOptions != NULL)
295 Print(L
"Starting %s\nUsing load options '%s'\n", ImageTitle
, FullLoadOptions
? FullLoadOptions
: L
"");
297 // load the image into memory (and execute it, in the case of a shim/MOK image).
298 ReturnStatus
= Status
= EFI_NOT_FOUND
; // in case the list is empty
299 for (DevicePathIndex
= 0; DevicePaths
[DevicePathIndex
] != NULL
; DevicePathIndex
++) {
300 FindVolumeAndFilename(DevicePaths
[DevicePathIndex
], &Volume
, &Filename
);
301 // Some EFIs crash if attempting to load driver for invalid architecture, so
302 // protect for this condition; but sometimes Volume comes back NULL, so provide
303 // an exception. (TODO: Handle this special condition better.)
304 if ((LoaderType
== TYPE_LEGACY
) || (Volume
== NULL
) || IsValidLoader(Volume
->RootDir
, Filename
)) {
305 if (Filename
&& (LoaderType
!= TYPE_LEGACY
)) {
306 Temp
= PoolPrint(L
"\\%s %s", Filename
, FullLoadOptions
? FullLoadOptions
: L
"");
308 MyFreePool(FullLoadOptions
);
309 FullLoadOptions
= Temp
;
313 // NOTE: Below commented-out line could be more efficient if file were read ahead of
314 // time and passed as a pre-loaded image to LoadImage(), but it doesn't work on my
315 // 32-bit Mac Mini or my 64-bit Intel box when launching a Linux kernel; the
316 // kernel returns a "Failed to handle fs_proto" error message.
317 // TODO: Track down the cause of this error and fix it, if possible.
318 // ReturnStatus = Status = refit_call6_wrapper(BS->LoadImage, FALSE, SelfImageHandle, DevicePaths[DevicePathIndex],
319 // ImageData, ImageSize, &ChildImageHandle);
320 ReturnStatus
= Status
= refit_call6_wrapper(BS
->LoadImage
, FALSE
, SelfImageHandle
, DevicePaths
[DevicePathIndex
],
321 NULL
, 0, &ChildImageHandle
);
323 Print(L
"Invalid loader file!\n");
324 ReturnStatus
= EFI_LOAD_ERROR
;
326 if (ReturnStatus
!= EFI_NOT_FOUND
) {
330 if ((Status
== EFI_ACCESS_DENIED
) || (Status
== EFI_SECURITY_VIOLATION
)) {
331 WarnSecureBootError(ImageTitle
, Verbose
);
334 SPrint(ErrorInfo
, 255, L
"while loading %s", ImageTitle
);
335 if (CheckError(Status
, ErrorInfo
)) {
336 if (ErrorInStep
!= NULL
)
341 ReturnStatus
= Status
= refit_call3_wrapper(BS
->HandleProtocol
, ChildImageHandle
, &LoadedImageProtocol
,
342 (VOID
**) &ChildLoadedImage
);
343 if (CheckError(Status
, L
"while getting a LoadedImageProtocol handle")) {
344 if (ErrorInStep
!= NULL
)
348 ChildLoadedImage
->LoadOptions
= (VOID
*)FullLoadOptions
;
349 ChildLoadedImage
->LoadOptionsSize
= FullLoadOptions
? ((UINT32
)StrLen(FullLoadOptions
) + 1) * sizeof(CHAR16
) : 0;
350 // turn control over to the image
351 // TODO: (optionally) re-enable the EFI watchdog timer!
353 // close open file handles
355 ReturnStatus
= Status
= refit_call3_wrapper(BS
->StartImage
, ChildImageHandle
, NULL
, NULL
);
357 // control returns here when the child image calls Exit()
358 SPrint(ErrorInfo
, 255, L
"returned from %s", ImageTitle
);
359 if (CheckError(Status
, ErrorInfo
)) {
360 if (ErrorInStep
!= NULL
)
364 // re-open file handles
368 // unload the image, we don't care if it works or not...
369 Status
= refit_call1_wrapper(BS
->UnloadImage
, ChildImageHandle
);
372 MyFreePool(FullLoadOptions
);
374 } /* static EFI_STATUS StartEFIImageList() */
376 static EFI_STATUS
StartEFIImage(IN EFI_DEVICE_PATH
*DevicePath
,
377 IN CHAR16
*LoadOptions
, IN UINTN LoaderType
,
378 IN CHAR16
*ImageTitle
, IN CHAR8 OSType
,
379 OUT UINTN
*ErrorInStep
,
382 EFI_DEVICE_PATH
*DevicePaths
[2];
384 DevicePaths
[0] = DevicePath
;
385 DevicePaths
[1] = NULL
;
386 return StartEFIImageList(DevicePaths
, LoadOptions
, LoaderType
, ImageTitle
, OSType
, ErrorInStep
, Verbose
);
387 } /* static EFI_STATUS StartEFIImage() */
389 // From gummiboot: Reboot the computer into its built-in user interface
390 static EFI_STATUS
RebootIntoFirmware(VOID
) {
396 osind
= EFI_OS_INDICATIONS_BOOT_TO_FW_UI
;
398 err
= EfivarGetRaw(&GlobalGuid
, L
"OsIndications", &b
, &size
);
399 if (err
== EFI_SUCCESS
)
403 err
= EfivarSetRaw(&GlobalGuid
, L
"OsIndications", (CHAR8
*)&osind
, sizeof(UINT64
), TRUE
);
404 if (err
!= EFI_SUCCESS
)
407 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);
408 Print(L
"Error calling ResetSystem: %r", err
);
413 // Record the value of the loader's name/description in rEFInd's "PreviousBoot" EFI variable,
414 // if it's different from what's already stored there.
415 static VOID
StoreLoaderName(IN CHAR16
*Name
) {
417 CHAR16
*OldName
= NULL
;
421 Status
= EfivarGetRaw(&RefindGuid
, L
"PreviousBoot", (CHAR8
**) &OldName
, &Length
);
422 if ((Status
!= EFI_SUCCESS
) || (StrCmp(OldName
, Name
) != 0)) {
423 EfivarSetRaw(&RefindGuid
, L
"PreviousBoot", (CHAR8
*) Name
, StrLen(Name
) * 2 + 2, TRUE
);
427 } // VOID StoreLoaderName()
430 // EFI OS loader functions
433 static VOID
StartLoader(LOADER_ENTRY
*Entry
, CHAR16
*SelectionName
)
435 UINTN ErrorInStep
= 0;
437 BeginExternalScreen(Entry
->UseGraphicsMode
, L
"Booting OS");
438 StoreLoaderName(SelectionName
);
439 StartEFIImage(Entry
->DevicePath
, Entry
->LoadOptions
, TYPE_EFI
,
440 Basename(Entry
->LoaderPath
), Entry
->OSType
, &ErrorInStep
, !Entry
->UseGraphicsMode
);
441 FinishExternalScreen();
444 // Locate an initrd or initramfs file that matches the kernel specified by LoaderPath.
445 // The matching file has a name that begins with "init" and includes the same version
446 // number string as is found in LoaderPath -- but not a longer version number string.
447 // For instance, if LoaderPath is \EFI\kernels\bzImage-3.3.0.efi, and if \EFI\kernels
448 // has a file called initramfs-3.3.0.img, this function will return the string
449 // '\EFI\kernels\initramfs-3.3.0.img'. If the directory ALSO contains the file
450 // initramfs-3.3.0-rc7.img or initramfs-13.3.0.img, those files will NOT match;
451 // however, initmine-3.3.0.img might match. (FindInitrd() returns the first match it
452 // finds). Thus, care should be taken to avoid placing duplicate matching files in
453 // the kernel's directory.
454 // If no matching init file can be found, returns NULL.
455 static CHAR16
* FindInitrd(IN CHAR16
*LoaderPath
, IN REFIT_VOLUME
*Volume
) {
456 CHAR16
*InitrdName
= NULL
, *FileName
, *KernelVersion
, *InitrdVersion
, *Path
;
457 REFIT_DIR_ITER DirIter
;
458 EFI_FILE_INFO
*DirEntry
;
460 FileName
= Basename(LoaderPath
);
461 KernelVersion
= FindNumbers(FileName
);
462 Path
= FindPath(LoaderPath
);
464 // Add trailing backslash for root directory; necessary on some systems, but must
465 // NOT be added to all directories, since on other systems, a trailing backslash on
466 // anything but the root directory causes them to flake out!
467 if (StrLen(Path
) == 0) {
468 MergeStrings(&Path
, L
"\\", 0);
470 DirIterOpen(Volume
->RootDir
, Path
, &DirIter
);
471 // Now add a trailing backslash if it was NOT added earlier, for consistency in
472 // building the InitrdName later....
473 if ((StrLen(Path
) > 0) && (Path
[StrLen(Path
) - 1] != L
'\\'))
474 MergeStrings(&Path
, L
"\\", 0);
475 while ((DirIterNext(&DirIter
, 2, L
"init*", &DirEntry
)) && (InitrdName
== NULL
)) {
476 InitrdVersion
= FindNumbers(DirEntry
->FileName
);
477 if (KernelVersion
!= NULL
) {
478 if (StriCmp(InitrdVersion
, KernelVersion
) == 0) {
479 InitrdName
= PoolPrint(L
"%s%s", Path
, DirEntry
->FileName
);
482 if (InitrdVersion
== NULL
) {
483 InitrdName
= PoolPrint(L
"%s%s", Path
, DirEntry
->FileName
);
486 MyFreePool(InitrdVersion
);
488 DirIterClose(&DirIter
);
490 // Note: Don't FreePool(FileName), since Basename returns a pointer WITHIN the string it's passed.
491 MyFreePool(KernelVersion
);
494 } // static CHAR16 * FindInitrd()
496 LOADER_ENTRY
* AddPreparedLoaderEntry(LOADER_ENTRY
*Entry
) {
497 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
500 } // LOADER_ENTRY * AddPreparedLoaderEntry()
502 // Creates a copy of a menu screen.
503 // Returns a pointer to the copy of the menu screen.
504 static REFIT_MENU_SCREEN
* CopyMenuScreen(REFIT_MENU_SCREEN
*Entry
) {
505 REFIT_MENU_SCREEN
*NewEntry
;
508 NewEntry
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
509 if ((Entry
!= NULL
) && (NewEntry
!= NULL
)) {
510 CopyMem(NewEntry
, Entry
, sizeof(REFIT_MENU_SCREEN
));
511 NewEntry
->Title
= (Entry
->Title
) ? StrDuplicate(Entry
->Title
) : NULL
;
512 NewEntry
->TimeoutText
= (Entry
->TimeoutText
) ? StrDuplicate(Entry
->TimeoutText
) : NULL
;
513 if (Entry
->TitleImage
!= NULL
) {
514 NewEntry
->TitleImage
= AllocatePool(sizeof(EG_IMAGE
));
515 if (NewEntry
->TitleImage
!= NULL
)
516 CopyMem(NewEntry
->TitleImage
, Entry
->TitleImage
, sizeof(EG_IMAGE
));
518 NewEntry
->InfoLines
= (CHAR16
**) AllocateZeroPool(Entry
->InfoLineCount
* (sizeof(CHAR16
*)));
519 for (i
= 0; i
< Entry
->InfoLineCount
&& NewEntry
->InfoLines
; i
++) {
520 NewEntry
->InfoLines
[i
] = (Entry
->InfoLines
[i
]) ? StrDuplicate(Entry
->InfoLines
[i
]) : NULL
;
522 NewEntry
->Entries
= (REFIT_MENU_ENTRY
**) AllocateZeroPool(Entry
->EntryCount
* (sizeof (REFIT_MENU_ENTRY
*)));
523 for (i
= 0; i
< Entry
->EntryCount
&& NewEntry
->Entries
; i
++) {
524 AddMenuEntry(NewEntry
, Entry
->Entries
[i
]);
526 NewEntry
->Hint1
= (Entry
->Hint1
) ? StrDuplicate(Entry
->Hint1
) : NULL
;
527 NewEntry
->Hint2
= (Entry
->Hint2
) ? StrDuplicate(Entry
->Hint2
) : NULL
;
530 } // static REFIT_MENU_SCREEN* CopyMenuScreen()
532 // Creates a copy of a menu entry. Intended to enable moving a stack-based
533 // menu entry (such as the ones for the "reboot" and "exit" functions) to
534 // to the heap. This enables easier deletion of the whole set of menu
535 // entries when re-scanning.
536 // Returns a pointer to the copy of the menu entry.
537 static REFIT_MENU_ENTRY
* CopyMenuEntry(REFIT_MENU_ENTRY
*Entry
) {
538 REFIT_MENU_ENTRY
*NewEntry
;
540 NewEntry
= AllocateZeroPool(sizeof(REFIT_MENU_ENTRY
));
541 if ((Entry
!= NULL
) && (NewEntry
!= NULL
)) {
542 CopyMem(NewEntry
, Entry
, sizeof(REFIT_MENU_ENTRY
));
543 NewEntry
->Title
= (Entry
->Title
) ? StrDuplicate(Entry
->Title
) : NULL
;
544 if (Entry
->BadgeImage
!= NULL
) {
545 NewEntry
->BadgeImage
= AllocatePool(sizeof(EG_IMAGE
));
546 if (NewEntry
->BadgeImage
!= NULL
)
547 CopyMem(NewEntry
->BadgeImage
, Entry
->BadgeImage
, sizeof(EG_IMAGE
));
549 if (Entry
->Image
!= NULL
) {
550 NewEntry
->Image
= AllocatePool(sizeof(EG_IMAGE
));
551 if (NewEntry
->Image
!= NULL
)
552 CopyMem(NewEntry
->Image
, Entry
->Image
, sizeof(EG_IMAGE
));
554 if (Entry
->SubScreen
!= NULL
) {
555 NewEntry
->SubScreen
= CopyMenuScreen(Entry
->SubScreen
);
559 } // REFIT_MENU_ENTRY* CopyMenuEntry()
561 // Creates a new LOADER_ENTRY data structure and populates it with
562 // default values from the specified Entry, or NULL values if Entry
563 // is unspecified (NULL).
564 // Returns a pointer to the new data structure, or NULL if it
565 // couldn't be allocated
566 LOADER_ENTRY
*InitializeLoaderEntry(IN LOADER_ENTRY
*Entry
) {
567 LOADER_ENTRY
*NewEntry
= NULL
;
569 NewEntry
= AllocateZeroPool(sizeof(LOADER_ENTRY
));
570 if (NewEntry
!= NULL
) {
571 NewEntry
->me
.Title
= NULL
;
572 NewEntry
->me
.Tag
= TAG_LOADER
;
573 NewEntry
->Enabled
= TRUE
;
574 NewEntry
->UseGraphicsMode
= FALSE
;
575 NewEntry
->OSType
= 0;
577 NewEntry
->LoaderPath
= (Entry
->LoaderPath
) ? StrDuplicate(Entry
->LoaderPath
) : NULL
;
578 NewEntry
->VolName
= (Entry
->VolName
) ? StrDuplicate(Entry
->VolName
) : NULL
;
579 NewEntry
->DevicePath
= Entry
->DevicePath
;
580 NewEntry
->UseGraphicsMode
= Entry
->UseGraphicsMode
;
581 NewEntry
->LoadOptions
= (Entry
->LoadOptions
) ? StrDuplicate(Entry
->LoadOptions
) : NULL
;
582 NewEntry
->InitrdPath
= (Entry
->InitrdPath
) ? StrDuplicate(Entry
->InitrdPath
) : NULL
;
586 } // LOADER_ENTRY *InitializeLoaderEntry()
588 // Adds InitrdPath to Options, but only if Options doesn't already include an
589 // initrd= line. Done to enable overriding the default initrd selection in a
590 // refind_linux.conf file's options list.
591 // Returns a pointer to a new string. The calling function is responsible for
592 // freeing its memory.
593 static CHAR16
*AddInitrdToOptions(CHAR16
*Options
, CHAR16
*InitrdPath
) {
594 CHAR16
*NewOptions
= NULL
;
597 NewOptions
= StrDuplicate(Options
);
598 if ((InitrdPath
!= NULL
) && !StriSubCmp(L
"initrd=", Options
)) {
599 MergeStrings(&NewOptions
, L
"initrd=", L
' ');
600 MergeStrings(&NewOptions
, InitrdPath
, 0);
603 } // CHAR16 *AddInitrdToOptions()
605 // Prepare a REFIT_MENU_SCREEN data structure for a subscreen entry. This sets up
606 // the default entry that launches the boot loader using the same options as the
607 // main Entry does. Subsequent options can be added by the calling function.
608 // If a subscreen already exists in the Entry that's passed to this function,
609 // it's left unchanged and a pointer to it is returned.
610 // Returns a pointer to the new subscreen data structure, or NULL if there
611 // were problems allocating memory.
612 REFIT_MENU_SCREEN
*InitializeSubScreen(IN LOADER_ENTRY
*Entry
) {
613 CHAR16
*FileName
, *MainOptions
= NULL
;
614 REFIT_MENU_SCREEN
*SubScreen
= NULL
;
615 LOADER_ENTRY
*SubEntry
;
617 FileName
= Basename(Entry
->LoaderPath
);
618 if (Entry
->me
.SubScreen
== NULL
) { // No subscreen yet; initialize default entry....
619 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
620 if (SubScreen
!= NULL
) {
621 SubScreen
->Title
= AllocateZeroPool(sizeof(CHAR16
) * 256);
622 SPrint(SubScreen
->Title
, 255, L
"Boot Options for %s on %s",
623 (Entry
->Title
!= NULL
) ? Entry
->Title
: FileName
, Entry
->VolName
);
624 SubScreen
->TitleImage
= Entry
->me
.Image
;
626 SubEntry
= InitializeLoaderEntry(Entry
);
627 if (SubEntry
!= NULL
) {
628 SubEntry
->me
.Title
= StrDuplicate(L
"Boot using default options");
629 MainOptions
= SubEntry
->LoadOptions
;
630 SubEntry
->LoadOptions
= AddInitrdToOptions(MainOptions
, SubEntry
->InitrdPath
);
631 MyFreePool(MainOptions
);
632 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
633 } // if (SubEntry != NULL)
634 SubScreen
->Hint1
= StrDuplicate(SUBSCREEN_HINT1
);
635 if (GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_EDITOR
) {
636 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2_NO_EDITOR
);
638 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2
);
640 } // if (SubScreen != NULL)
641 } else { // existing subscreen; less initialization, and just add new entry later....
642 SubScreen
= Entry
->me
.SubScreen
;
645 } // REFIT_MENU_SCREEN *InitializeSubScreen()
647 VOID
GenerateSubScreen(LOADER_ENTRY
*Entry
, IN REFIT_VOLUME
*Volume
) {
648 REFIT_MENU_SCREEN
*SubScreen
;
649 LOADER_ENTRY
*SubEntry
;
651 CHAR16 DiagsFileName
[256];
656 // create the submenu
657 if (StrLen(Entry
->Title
) == 0) {
658 MyFreePool(Entry
->Title
);
661 SubScreen
= InitializeSubScreen(Entry
);
663 // loader-specific submenu entries
664 if (Entry
->OSType
== 'M') { // entries for Mac OS X
666 SubEntry
= InitializeLoaderEntry(Entry
);
667 if (SubEntry
!= NULL
) {
668 SubEntry
->me
.Title
= L
"Boot Mac OS X with a 64-bit kernel";
669 SubEntry
->LoadOptions
= L
"arch=x86_64";
670 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
671 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
674 SubEntry
= InitializeLoaderEntry(Entry
);
675 if (SubEntry
!= NULL
) {
676 SubEntry
->me
.Title
= L
"Boot Mac OS X with a 32-bit kernel";
677 SubEntry
->LoadOptions
= L
"arch=i386";
678 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
679 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
683 if (!(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_SINGLEUSER
)) {
684 SubEntry
= InitializeLoaderEntry(Entry
);
685 if (SubEntry
!= NULL
) {
686 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode";
687 SubEntry
->UseGraphicsMode
= FALSE
;
688 SubEntry
->LoadOptions
= L
"-v";
689 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
693 SubEntry
= InitializeLoaderEntry(Entry
);
694 if (SubEntry
!= NULL
) {
695 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode (64-bit)";
696 SubEntry
->UseGraphicsMode
= FALSE
;
697 SubEntry
->LoadOptions
= L
"-v arch=x86_64";
698 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
701 SubEntry
= InitializeLoaderEntry(Entry
);
702 if (SubEntry
!= NULL
) {
703 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode (32-bit)";
704 SubEntry
->UseGraphicsMode
= FALSE
;
705 SubEntry
->LoadOptions
= L
"-v arch=i386";
706 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
710 SubEntry
= InitializeLoaderEntry(Entry
);
711 if (SubEntry
!= NULL
) {
712 SubEntry
->me
.Title
= L
"Boot Mac OS X in single user mode";
713 SubEntry
->UseGraphicsMode
= FALSE
;
714 SubEntry
->LoadOptions
= L
"-v -s";
715 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
717 } // single-user mode allowed
719 if (!(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_SAFEMODE
)) {
720 SubEntry
= InitializeLoaderEntry(Entry
);
721 if (SubEntry
!= NULL
) {
722 SubEntry
->me
.Title
= L
"Boot Mac OS X in safe mode";
723 SubEntry
->UseGraphicsMode
= FALSE
;
724 SubEntry
->LoadOptions
= L
"-v -x";
725 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
727 } // safe mode allowed
729 // check for Apple hardware diagnostics
730 StrCpy(DiagsFileName
, L
"System\\Library\\CoreServices\\.diagnostics\\diags.efi");
731 if (FileExists(Volume
->RootDir
, DiagsFileName
) && !(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_HWTEST
)) {
732 SubEntry
= InitializeLoaderEntry(Entry
);
733 if (SubEntry
!= NULL
) {
734 SubEntry
->me
.Title
= L
"Run Apple Hardware Test";
735 MyFreePool(SubEntry
->LoaderPath
);
736 SubEntry
->LoaderPath
= StrDuplicate(DiagsFileName
);
737 SubEntry
->DevicePath
= FileDevicePath(Volume
->DeviceHandle
, SubEntry
->LoaderPath
);
738 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
739 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
741 } // if diagnostics entry found
743 } else if (Entry
->OSType
== 'L') { // entries for Linux kernels with EFI stub loaders
744 File
= ReadLinuxOptionsFile(Entry
->LoaderPath
, Volume
);
746 InitrdName
= FindInitrd(Entry
->LoaderPath
, Volume
);
747 TokenCount
= ReadTokenLine(File
, &TokenList
);
748 // first entry requires special processing, since it was initially set
749 // up with a default title but correct options by InitializeSubScreen(),
751 if ((SubScreen
->Entries
!= NULL
) && (SubScreen
->Entries
[0] != NULL
)) {
752 MyFreePool(SubScreen
->Entries
[0]->Title
);
753 SubScreen
->Entries
[0]->Title
= TokenList
[0] ? StrDuplicate(TokenList
[0]) : StrDuplicate(L
"Boot Linux");
755 FreeTokenLine(&TokenList
, &TokenCount
);
756 while ((TokenCount
= ReadTokenLine(File
, &TokenList
)) > 1) {
757 SubEntry
= InitializeLoaderEntry(Entry
);
758 SubEntry
->me
.Title
= TokenList
[0] ? StrDuplicate(TokenList
[0]) : StrDuplicate(L
"Boot Linux");
759 MyFreePool(SubEntry
->LoadOptions
);
760 SubEntry
->LoadOptions
= AddInitrdToOptions(TokenList
[1], InitrdName
);
761 FreeTokenLine(&TokenList
, &TokenCount
);
762 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_LINUX
;
763 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
765 MyFreePool(InitrdName
);
769 } else if (Entry
->OSType
== 'E') { // entries for ELILO
770 SubEntry
= InitializeLoaderEntry(Entry
);
771 if (SubEntry
!= NULL
) {
772 SubEntry
->me
.Title
= L
"Run ELILO in interactive mode";
773 SubEntry
->LoadOptions
= L
"-p";
774 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
775 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
778 SubEntry
= InitializeLoaderEntry(Entry
);
779 if (SubEntry
!= NULL
) {
780 SubEntry
->me
.Title
= L
"Boot Linux for a 17\" iMac or a 15\" MacBook Pro (*)";
781 SubEntry
->UseGraphicsMode
= TRUE
;
782 SubEntry
->LoadOptions
= L
"-d 0 i17";
783 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
784 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
787 SubEntry
= InitializeLoaderEntry(Entry
);
788 if (SubEntry
!= NULL
) {
789 SubEntry
->me
.Title
= L
"Boot Linux for a 20\" iMac (*)";
790 SubEntry
->UseGraphicsMode
= TRUE
;
791 SubEntry
->LoadOptions
= L
"-d 0 i20";
792 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
793 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
796 SubEntry
= InitializeLoaderEntry(Entry
);
797 if (SubEntry
!= NULL
) {
798 SubEntry
->me
.Title
= L
"Boot Linux for a Mac Mini (*)";
799 SubEntry
->UseGraphicsMode
= TRUE
;
800 SubEntry
->LoadOptions
= L
"-d 0 mini";
801 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
802 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
805 AddMenuInfoLine(SubScreen
, L
"NOTE: This is an example. Entries");
806 AddMenuInfoLine(SubScreen
, L
"marked with (*) may not work.");
808 } else if (Entry
->OSType
== 'X') { // entries for xom.efi
809 // by default, skip the built-in selection and boot from hard disk only
810 Entry
->LoadOptions
= L
"-s -h";
812 SubEntry
= InitializeLoaderEntry(Entry
);
813 if (SubEntry
!= NULL
) {
814 SubEntry
->me
.Title
= L
"Boot Windows from Hard Disk";
815 SubEntry
->LoadOptions
= L
"-s -h";
816 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
817 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
820 SubEntry
= InitializeLoaderEntry(Entry
);
821 if (SubEntry
!= NULL
) {
822 SubEntry
->me
.Title
= L
"Boot Windows from CD-ROM";
823 SubEntry
->LoadOptions
= L
"-s -c";
824 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
825 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
828 SubEntry
= InitializeLoaderEntry(Entry
);
829 if (SubEntry
!= NULL
) {
830 SubEntry
->me
.Title
= L
"Run XOM in text mode";
831 SubEntry
->UseGraphicsMode
= FALSE
;
832 SubEntry
->LoadOptions
= L
"-v";
833 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
834 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
836 } // entries for xom.efi
837 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
838 Entry
->me
.SubScreen
= SubScreen
;
839 } // VOID GenerateSubScreen()
841 // Returns options for a Linux kernel. Reads them from an options file in the
842 // kernel's directory; and if present, adds an initrd= option for an initial
843 // RAM disk file with the same version number as the kernel file.
844 static CHAR16
* GetMainLinuxOptions(IN CHAR16
* LoaderPath
, IN REFIT_VOLUME
*Volume
) {
845 CHAR16
*Options
= NULL
, *InitrdName
, *FullOptions
= NULL
;
847 Options
= GetFirstOptionsFromFile(LoaderPath
, Volume
);
848 InitrdName
= FindInitrd(LoaderPath
, Volume
);
849 FullOptions
= AddInitrdToOptions(Options
, InitrdName
);
852 MyFreePool(InitrdName
);
853 return (FullOptions
);
854 } // static CHAR16 * GetMainLinuxOptions()
856 // Try to guess the name of the Linux distribution & add that name to
858 static VOID
GuessLinuxDistribution(CHAR16
**OSIconName
, REFIT_VOLUME
*Volume
, CHAR16
*LoaderPath
) {
862 UINTN TokenCount
= 0;
864 // If on Linux root fs, /etc/os-release file probably has clues....
865 if (FileExists(Volume
->RootDir
, L
"etc\\os-release") &&
866 (ReadFile(Volume
->RootDir
, L
"etc\\os-release", &File
, &FileSize
) == EFI_SUCCESS
)) {
868 TokenCount
= ReadTokenLine(&File
, &TokenList
);
869 if ((TokenCount
> 1) && ((StriCmp(TokenList
[0], L
"ID") == 0) || (StriCmp(TokenList
[0], L
"NAME") == 0))) {
870 MergeStrings(OSIconName
, TokenList
[1], L
',');
872 FreeTokenLine(&TokenList
, &TokenCount
);
873 } while (TokenCount
> 0);
874 MyFreePool(File
.Buffer
);
877 // Search for clues in the kernel's filename....
878 if (StriSubCmp(L
".fc", LoaderPath
))
879 MergeStrings(OSIconName
, L
"fedora", L
',');
880 if (StriSubCmp(L
".el", LoaderPath
))
881 MergeStrings(OSIconName
, L
"redhat", L
',');
882 } // VOID GuessLinuxDistribution()
884 // Sets a few defaults for a loader entry -- mainly the icon, but also the OS type
885 // code and shortcut letter. For Linux EFI stub loaders, also sets kernel options
886 // that will (with luck) work fairly automatically.
887 VOID
SetLoaderDefaults(LOADER_ENTRY
*Entry
, CHAR16
*LoaderPath
, REFIT_VOLUME
*Volume
) {
888 CHAR16
*FileName
, *PathOnly
, *NoExtension
, *OSIconName
= NULL
, *Temp
, *SubString
;
889 CHAR16 ShortcutLetter
= 0;
892 FileName
= Basename(LoaderPath
);
893 PathOnly
= FindPath(LoaderPath
);
894 NoExtension
= StripEfiExtension(FileName
);
896 // locate a custom icon for the loader
897 // Anything found here takes precedence over the "hints" in the OSIconName variable
898 if (!Entry
->me
.Image
) {
899 Entry
->me
.Image
= egLoadIconAnyType(Volume
->RootDir
, PathOnly
, NoExtension
, GlobalConfig
.IconSizes
[ICON_SIZE_BIG
]);
901 if (!Entry
->me
.Image
) {
902 Entry
->me
.Image
= egCopyImage(Volume
->VolIconImage
);
905 // Begin creating icon "hints" by using last part of directory path leading
907 Temp
= FindLastDirName(LoaderPath
);
908 MergeStrings(&OSIconName
, Temp
, L
',');
911 if (OSIconName
!= NULL
) {
912 ShortcutLetter
= OSIconName
[0];
915 // Add every "word" in the volume label, delimited by spaces, dashes (-), or
916 // underscores (_), to the list of hints to be used in searching for OS
918 if ((Volume
->VolName
) && (StrLen(Volume
->VolName
) > 0)) {
919 Temp
= SubString
= StrDuplicate(Volume
->VolName
);
921 Length
= StrLen(Temp
);
922 for (i
= 0; i
< Length
; i
++) {
923 if ((Temp
[i
] == L
' ') || (Temp
[i
] == L
'_') || (Temp
[i
] == L
'-')) {
925 if (StrLen(SubString
) > 0)
926 MergeStrings(&OSIconName
, SubString
, L
',');
927 SubString
= Temp
+ i
+ 1;
930 MergeStrings(&OSIconName
, SubString
, L
',');
935 // detect specific loaders
936 if (StriSubCmp(L
"bzImage", FileName
) || StriSubCmp(L
"vmlinuz", FileName
)) {
937 GuessLinuxDistribution(&OSIconName
, Volume
, LoaderPath
);
938 MergeStrings(&OSIconName
, L
"linux", L
',');
940 if (ShortcutLetter
== 0)
941 ShortcutLetter
= 'L';
942 Entry
->LoadOptions
= GetMainLinuxOptions(LoaderPath
, Volume
);
943 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_LINUX
;
944 } else if (StriSubCmp(L
"refit", LoaderPath
)) {
945 MergeStrings(&OSIconName
, L
"refit", L
',');
947 ShortcutLetter
= 'R';
948 } else if (StriSubCmp(L
"refind", LoaderPath
)) {
949 MergeStrings(&OSIconName
, L
"refind", L
',');
951 ShortcutLetter
= 'R';
952 } else if (StriCmp(LoaderPath
, MACOSX_LOADER_PATH
) == 0) {
953 MergeStrings(&OSIconName
, L
"mac", L
',');
955 ShortcutLetter
= 'M';
956 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
957 } else if (StriCmp(FileName
, L
"diags.efi") == 0) {
958 MergeStrings(&OSIconName
, L
"hwtest", L
',');
959 } else if (StriCmp(FileName
, L
"e.efi") == 0 || StriCmp(FileName
, L
"elilo.efi") == 0 || StriSubCmp(L
"elilo", FileName
)) {
960 MergeStrings(&OSIconName
, L
"elilo,linux", L
',');
962 if (ShortcutLetter
== 0)
963 ShortcutLetter
= 'L';
964 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
965 } else if (StriSubCmp(L
"grub", FileName
)) {
967 ShortcutLetter
= 'G';
968 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_GRUB
;
969 } else if (StriCmp(FileName
, L
"cdboot.efi") == 0 ||
970 StriCmp(FileName
, L
"bootmgr.efi") == 0 ||
971 StriCmp(FileName
, L
"bootmgfw.efi") == 0 ||
972 StriCmp(FileName
, L
"bkpbootmgfw.efi") == 0) {
973 MergeStrings(&OSIconName
, L
"win", L
',');
975 ShortcutLetter
= 'W';
976 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
977 } else if (StriCmp(FileName
, L
"xom.efi") == 0) {
978 MergeStrings(&OSIconName
, L
"xom,win", L
',');
979 Entry
->UseGraphicsMode
= TRUE
;
981 ShortcutLetter
= 'W';
982 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
985 if ((ShortcutLetter
>= 'a') && (ShortcutLetter
<= 'z'))
986 ShortcutLetter
= ShortcutLetter
- 'a' + 'A'; // convert lowercase to uppercase
987 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
988 if (Entry
->me
.Image
== NULL
)
989 Entry
->me
.Image
= LoadOSIcon(OSIconName
, L
"unknown", FALSE
);
990 MyFreePool(PathOnly
);
991 } // VOID SetLoaderDefaults()
993 // Add a specified EFI boot loader to the list, using automatic settings
994 // for icons, options, etc.
995 LOADER_ENTRY
* AddLoaderEntry(IN CHAR16
*LoaderPath
, IN CHAR16
*LoaderTitle
, IN REFIT_VOLUME
*Volume
) {
998 CleanUpPathNameSlashes(LoaderPath
);
999 Entry
= InitializeLoaderEntry(NULL
);
1000 if (Entry
!= NULL
) {
1001 Entry
->Title
= StrDuplicate((LoaderTitle
!= NULL
) ? LoaderTitle
: LoaderPath
);
1002 Entry
->me
.Title
= AllocateZeroPool(sizeof(CHAR16
) * 256);
1003 // Extra space at end of Entry->me.Title enables searching on Volume->VolName even if another volume
1004 // name is identical except for something added to the end (e.g., VolB1 vs. VolB12).
1005 SPrint(Entry
->me
.Title
, 255, L
"Boot %s from %s ", (LoaderTitle
!= NULL
) ? LoaderTitle
: LoaderPath
, Volume
->VolName
);
1007 Entry
->me
.BadgeImage
= Volume
->VolBadgeImage
;
1008 if ((LoaderPath
!= NULL
) && (LoaderPath
[0] != L
'\\')) {
1009 Entry
->LoaderPath
= StrDuplicate(L
"\\");
1011 Entry
->LoaderPath
= NULL
;
1013 MergeStrings(&(Entry
->LoaderPath
), LoaderPath
, 0);
1014 Entry
->VolName
= Volume
->VolName
;
1015 Entry
->DevicePath
= FileDevicePath(Volume
->DeviceHandle
, Entry
->LoaderPath
);
1016 SetLoaderDefaults(Entry
, LoaderPath
, Volume
);
1017 GenerateSubScreen(Entry
, Volume
);
1018 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1022 } // LOADER_ENTRY * AddLoaderEntry()
1024 // Returns -1 if (Time1 < Time2), +1 if (Time1 > Time2), or 0 if
1025 // (Time1 == Time2). Precision is only to the nearest second; since
1026 // this is used for sorting boot loader entries, differences smaller
1027 // than this are likely to be meaningless (and unlikely!).
1028 INTN
TimeComp(IN EFI_TIME
*Time1
, IN EFI_TIME
*Time2
) {
1029 INT64 Time1InSeconds
, Time2InSeconds
;
1031 // Following values are overestimates; I'm assuming 31 days in every month.
1032 // This is fine for the purpose of this function, which is limited
1033 Time1InSeconds
= Time1
->Second
+ (Time1
->Minute
* 60) + (Time1
->Hour
* 3600) + (Time1
->Day
* 86400) +
1034 (Time1
->Month
* 2678400) + ((Time1
->Year
- 1998) * 32140800);
1035 Time2InSeconds
= Time2
->Second
+ (Time2
->Minute
* 60) + (Time2
->Hour
* 3600) + (Time2
->Day
* 86400) +
1036 (Time2
->Month
* 2678400) + ((Time2
->Year
- 1998) * 32140800);
1037 if (Time1InSeconds
< Time2InSeconds
)
1039 else if (Time1InSeconds
> Time2InSeconds
)
1043 } // INTN TimeComp()
1045 // Adds a loader list element, keeping it sorted by date. Returns the new
1046 // first element (the one with the most recent date).
1047 static struct LOADER_LIST
* AddLoaderListEntry(struct LOADER_LIST
*LoaderList
, struct LOADER_LIST
*NewEntry
) {
1048 struct LOADER_LIST
*LatestEntry
, *CurrentEntry
, *PrevEntry
= NULL
;
1050 LatestEntry
= CurrentEntry
= LoaderList
;
1051 if (LoaderList
== NULL
) {
1052 LatestEntry
= NewEntry
;
1054 while ((CurrentEntry
!= NULL
) && (TimeComp(&(NewEntry
->TimeStamp
), &(CurrentEntry
->TimeStamp
)) < 0)) {
1055 PrevEntry
= CurrentEntry
;
1056 CurrentEntry
= CurrentEntry
->NextEntry
;
1058 NewEntry
->NextEntry
= CurrentEntry
;
1059 if (PrevEntry
== NULL
) {
1060 LatestEntry
= NewEntry
;
1062 PrevEntry
->NextEntry
= NewEntry
;
1065 return (LatestEntry
);
1066 } // static VOID AddLoaderListEntry()
1068 // Delete the LOADER_LIST linked list
1069 static VOID
CleanUpLoaderList(struct LOADER_LIST
*LoaderList
) {
1070 struct LOADER_LIST
*Temp
;
1072 while (LoaderList
!= NULL
) {
1074 LoaderList
= LoaderList
->NextEntry
;
1075 MyFreePool(Temp
->FileName
);
1078 } // static VOID CleanUpLoaderList()
1080 // Returns FALSE if the specified file/volume matches the GlobalConfig.DontScanDirs
1081 // or GlobalConfig.DontScanVolumes specification, or if Path points to a volume
1082 // other than the one specified by Volume, or if the specified path is SelfDir.
1083 // Returns TRUE if none of these conditions is met -- that is, if the path is
1084 // eligible for scanning.
1085 static BOOLEAN
ShouldScan(REFIT_VOLUME
*Volume
, CHAR16
*Path
) {
1086 CHAR16
*VolName
= NULL
, *DontScanDir
, *PathCopy
= NULL
;
1088 BOOLEAN ScanIt
= TRUE
;
1090 if ((IsIn(Volume
->VolName
, GlobalConfig
.DontScanVolumes
)) || (IsIn(Volume
->PartName
, GlobalConfig
.DontScanVolumes
)))
1093 if ((StriCmp(Path
, SelfDirPath
) == 0) && (Volume
->DeviceHandle
== SelfVolume
->DeviceHandle
))
1096 // See if Path includes an explicit volume declaration that's NOT Volume....
1097 PathCopy
= StrDuplicate(Path
);
1098 if (SplitVolumeAndFilename(&PathCopy
, &VolName
)) {
1099 VolumeNumberToName(Volume
, &VolName
);
1100 if (VolName
&& StriCmp(VolName
, Volume
->VolName
) != 0) {
1103 } // if Path includes volume specification
1104 MyFreePool(PathCopy
);
1105 MyFreePool(VolName
);
1108 // See if Volume is in GlobalConfig.DontScanDirs....
1109 while (ScanIt
&& (DontScanDir
= FindCommaDelimited(GlobalConfig
.DontScanDirs
, i
++))) {
1110 SplitVolumeAndFilename(&DontScanDir
, &VolName
);
1111 CleanUpPathNameSlashes(DontScanDir
);
1112 VolumeNumberToName(Volume
, &VolName
);
1113 if (VolName
!= NULL
) {
1114 if ((StriCmp(VolName
, Volume
->VolName
) == 0) && (StriCmp(DontScanDir
, Path
) == 0))
1117 if (StriCmp(DontScanDir
, Path
) == 0)
1120 MyFreePool(DontScanDir
);
1121 MyFreePool(VolName
);
1127 } // BOOLEAN ShouldScan()
1129 // Returns TRUE if the file is byte-for-byte identical with the fallback file
1130 // on the volume AND if the file is not itself the fallback file; returns
1131 // FALSE if the file is not identical to the fallback file OR if the file
1132 // IS the fallback file. Intended for use in excluding the fallback boot
1133 // loader when it's a duplicate of another boot loader.
1134 static BOOLEAN
DuplicatesFallback(IN REFIT_VOLUME
*Volume
, IN CHAR16
*FileName
) {
1135 CHAR8
*FileContents
, *FallbackContents
;
1136 EFI_FILE_HANDLE FileHandle
, FallbackHandle
;
1137 EFI_FILE_INFO
*FileInfo
, *FallbackInfo
;
1138 UINTN FileSize
= 0, FallbackSize
= 0;
1140 BOOLEAN AreIdentical
= FALSE
;
1142 if (!FileExists(Volume
->RootDir
, FileName
) || !FileExists(Volume
->RootDir
, FALLBACK_FULLNAME
))
1145 CleanUpPathNameSlashes(FileName
);
1147 if (StriCmp(FileName
, FALLBACK_FULLNAME
) == 0)
1148 return FALSE
; // identical filenames, so not a duplicate....
1150 Status
= refit_call5_wrapper(Volume
->RootDir
->Open
, Volume
->RootDir
, &FileHandle
, FileName
, EFI_FILE_MODE_READ
, 0);
1151 if (Status
== EFI_SUCCESS
) {
1152 FileInfo
= LibFileInfo(FileHandle
);
1153 FileSize
= FileInfo
->FileSize
;
1158 Status
= refit_call5_wrapper(Volume
->RootDir
->Open
, Volume
->RootDir
, &FallbackHandle
, FALLBACK_FULLNAME
, EFI_FILE_MODE_READ
, 0);
1159 if (Status
== EFI_SUCCESS
) {
1160 FallbackInfo
= LibFileInfo(FallbackHandle
);
1161 FallbackSize
= FallbackInfo
->FileSize
;
1163 refit_call1_wrapper(FileHandle
->Close
, FileHandle
);
1167 if (FallbackSize
!= FileSize
) { // not same size, so can't be identical
1168 AreIdentical
= FALSE
;
1169 } else { // could be identical; do full check....
1170 FileContents
= AllocatePool(FileSize
);
1171 FallbackContents
= AllocatePool(FallbackSize
);
1172 if (FileContents
&& FallbackContents
) {
1173 Status
= refit_call3_wrapper(FileHandle
->Read
, FileHandle
, &FileSize
, FileContents
);
1174 if (Status
== EFI_SUCCESS
) {
1175 Status
= refit_call3_wrapper(FallbackHandle
->Read
, FallbackHandle
, &FallbackSize
, FallbackContents
);
1177 if (Status
== EFI_SUCCESS
) {
1178 AreIdentical
= (CompareMem(FileContents
, FallbackContents
, FileSize
) == 0);
1181 MyFreePool(FileContents
);
1182 MyFreePool(FallbackContents
);
1185 // BUG ALERT: Some systems (e.g., DUET, some Macs with large displays) crash if the
1186 // following two calls are reversed. Go figure....
1187 refit_call1_wrapper(FileHandle
->Close
, FallbackHandle
);
1188 refit_call1_wrapper(FileHandle
->Close
, FileHandle
);
1189 return AreIdentical
;
1190 } // BOOLEAN DuplicatesFallback()
1192 // Returns FALSE if two measures of file size are identical for a single file,
1193 // TRUE if not or if the file can't be opened and the other measure is non-0.
1194 // Despite the function's name, this isn't really a direct test of symbolic
1195 // link status, since EFI doesn't officially support symlinks. It does seem
1196 // to be a reliable indicator, though. (OTOH, some disk errors might cause a
1197 // file to fail to open, which would return a false positive -- but as I use
1198 // this function to exclude symbolic links from the list of boot loaders,
1199 // that would be fine, since such boot loaders wouldn't work.)
1200 static BOOLEAN
IsSymbolicLink(REFIT_VOLUME
*Volume
, CHAR16
*Path
, EFI_FILE_INFO
*DirEntry
) {
1201 EFI_FILE_HANDLE FileHandle
;
1202 EFI_FILE_INFO
*FileInfo
= NULL
;
1204 UINTN FileSize2
= 0;
1207 FileName
= StrDuplicate(Path
);
1208 MergeStrings(&FileName
, DirEntry
->FileName
, L
'\\');
1209 CleanUpPathNameSlashes(FileName
);
1211 Status
= refit_call5_wrapper(Volume
->RootDir
->Open
, Volume
->RootDir
, &FileHandle
, FileName
, EFI_FILE_MODE_READ
, 0);
1212 if (Status
== EFI_SUCCESS
) {
1213 FileInfo
= LibFileInfo(FileHandle
);
1214 if (FileInfo
!= NULL
)
1215 FileSize2
= FileInfo
->FileSize
;
1218 MyFreePool(FileName
);
1219 MyFreePool(FileInfo
);
1221 return (DirEntry
->FileSize
!= FileSize2
);
1222 } // BOOLEAN IsSymbolicLink()
1224 // Returns TRUE if a file with the same name as the original but with
1225 // ".efi.signed" is also present in the same directory. Ubuntu is using
1226 // this filename as a signed version of the original unsigned kernel, and
1227 // there's no point in cluttering the display with two kernels that will
1228 // behave identically on non-SB systems, or when one will fail when SB
1230 static BOOLEAN
HasSignedCounterpart(IN REFIT_VOLUME
*Volume
, IN CHAR16
*Path
, IN CHAR16
*Filename
) {
1231 CHAR16
*NewFile
= NULL
;
1232 BOOLEAN retval
= FALSE
;
1234 MergeStrings(&NewFile
, Path
, 0);
1235 MergeStrings(&NewFile
, Filename
, L
'\\');
1236 MergeStrings(&NewFile
, L
".efi.signed", 0);
1237 if (NewFile
!= NULL
) {
1238 CleanUpPathNameSlashes(NewFile
);
1239 if (FileExists(Volume
->RootDir
, NewFile
))
1241 MyFreePool(NewFile
);
1245 } // BOOLEAN HasSignedCounterpart()
1247 // Scan an individual directory for EFI boot loader files and, if found,
1248 // add them to the list. Exception: Ignores FALLBACK_FULLNAME, which is picked
1249 // up in ScanEfiFiles(). Sorts the entries within the loader directory so that
1250 // the most recent one appears first in the list.
1251 // Returns TRUE if a duplicate for FALLBACK_FILENAME was found, FALSE if not.
1252 static BOOLEAN
ScanLoaderDir(IN REFIT_VOLUME
*Volume
, IN CHAR16
*Path
, IN CHAR16
*Pattern
)
1255 REFIT_DIR_ITER DirIter
;
1256 EFI_FILE_INFO
*DirEntry
;
1257 CHAR16 FileName
[256], *Extension
;
1258 struct LOADER_LIST
*LoaderList
= NULL
, *NewLoader
;
1259 BOOLEAN FoundFallbackDuplicate
= FALSE
;
1261 if ((!SelfDirPath
|| !Path
|| ((StriCmp(Path
, SelfDirPath
) == 0) && (Volume
->DeviceHandle
!= SelfVolume
->DeviceHandle
)) ||
1262 (StriCmp(Path
, SelfDirPath
) != 0)) && (ShouldScan(Volume
, Path
))) {
1263 // look through contents of the directory
1264 DirIterOpen(Volume
->RootDir
, Path
, &DirIter
);
1265 while (DirIterNext(&DirIter
, 2, Pattern
, &DirEntry
)) {
1266 Extension
= FindExtension(DirEntry
->FileName
);
1267 if (DirEntry
->FileName
[0] == '.' ||
1268 StriCmp(Extension
, L
".icns") == 0 ||
1269 StriCmp(Extension
, L
".png") == 0 ||
1270 (StriCmp(DirEntry
->FileName
, FALLBACK_BASENAME
) == 0 && (StriCmp(Path
, L
"EFI\\BOOT") == 0)) ||
1271 StriSubCmp(L
"shell", DirEntry
->FileName
) ||
1272 IsSymbolicLink(Volume
, Path
, DirEntry
) || /* is symbolic link */
1273 HasSignedCounterpart(Volume
, Path
, DirEntry
->FileName
) || /* a file with same name plus ".efi.signed" is present */
1274 FilenameIn(Volume
, Path
, DirEntry
->FileName
, GlobalConfig
.DontScanFiles
))
1275 continue; // skip this
1278 SPrint(FileName
, 255, L
"\\%s\\%s", Path
, DirEntry
->FileName
);
1280 SPrint(FileName
, 255, L
"\\%s", DirEntry
->FileName
);
1281 CleanUpPathNameSlashes(FileName
);
1283 if(!IsValidLoader(Volume
->RootDir
, FileName
))
1286 NewLoader
= AllocateZeroPool(sizeof(struct LOADER_LIST
));
1287 if (NewLoader
!= NULL
) {
1288 NewLoader
->FileName
= StrDuplicate(FileName
);
1289 NewLoader
->TimeStamp
= DirEntry
->ModificationTime
;
1290 LoaderList
= AddLoaderListEntry(LoaderList
, NewLoader
);
1291 if (DuplicatesFallback(Volume
, FileName
))
1292 FoundFallbackDuplicate
= TRUE
;
1294 MyFreePool(Extension
);
1297 NewLoader
= LoaderList
;
1298 while (NewLoader
!= NULL
) {
1299 AddLoaderEntry(NewLoader
->FileName
, NULL
, Volume
);
1300 NewLoader
= NewLoader
->NextEntry
;
1303 CleanUpLoaderList(LoaderList
);
1304 Status
= DirIterClose(&DirIter
);
1305 // NOTE: EFI_INVALID_PARAMETER really is an error that should be reported;
1306 // but I've gotten reports from users who are getting this error occasionally
1307 // and I can't find anything wrong or reproduce the problem, so I'm putting
1308 // it down to buggy EFI implementations and ignoring that particular error....
1309 if ((Status
!= EFI_NOT_FOUND
) && (Status
!= EFI_INVALID_PARAMETER
)) {
1311 SPrint(FileName
, 255, L
"while scanning the %s directory", Path
);
1313 StrCpy(FileName
, L
"while scanning the root directory");
1314 CheckError(Status
, FileName
);
1315 } // if (Status != EFI_NOT_FOUND)
1316 } // if not scanning a blacklisted directory
1318 return FoundFallbackDuplicate
;
1319 } /* static VOID ScanLoaderDir() */
1321 static VOID
ScanEfiFiles(REFIT_VOLUME
*Volume
) {
1323 REFIT_DIR_ITER EfiDirIter
;
1324 EFI_FILE_INFO
*EfiDirEntry
;
1325 CHAR16 FileName
[256], *Directory
= NULL
, *MatchPatterns
, *VolName
= NULL
, *SelfPath
;
1327 BOOLEAN ScanFallbackLoader
= TRUE
;
1328 BOOLEAN FoundBRBackup
= FALSE
;
1330 if ((Volume
->RootDir
!= NULL
) && (Volume
->VolName
!= NULL
) && (Volume
->IsReadable
)) {
1331 MatchPatterns
= StrDuplicate(LOADER_MATCH_PATTERNS
);
1332 if (GlobalConfig
.ScanAllLinux
)
1333 MergeStrings(&MatchPatterns
, LINUX_MATCH_PATTERNS
, L
',');
1335 // check for Mac OS X boot loader
1336 if (ShouldScan(Volume
, L
"System\\Library\\CoreServices")) {
1337 StrCpy(FileName
, MACOSX_LOADER_PATH
);
1338 if (FileExists(Volume
->RootDir
, FileName
) && !FilenameIn(Volume
, Directory
, L
"boot.efi", GlobalConfig
.DontScanFiles
)) {
1339 AddLoaderEntry(FileName
, L
"Mac OS X", Volume
);
1340 if (DuplicatesFallback(Volume
, FileName
))
1341 ScanFallbackLoader
= FALSE
;
1345 StrCpy(FileName
, L
"System\\Library\\CoreServices\\xom.efi");
1346 if (FileExists(Volume
->RootDir
, FileName
) && !FilenameIn(Volume
, Directory
, L
"boot.efi", GlobalConfig
.DontScanFiles
)) {
1347 AddLoaderEntry(FileName
, L
"Windows XP (XoM)", Volume
);
1348 if (DuplicatesFallback(Volume
, FileName
))
1349 ScanFallbackLoader
= FALSE
;
1351 } // if should scan Mac directory
1353 // check for Microsoft boot loader/menu
1354 if (ShouldScan(Volume
, L
"EFI\\Microsoft\\Boot")) {
1355 StrCpy(FileName
, L
"EFI\\Microsoft\\Boot\\bkpbootmgfw.efi");
1356 if (FileExists(Volume
->RootDir
, FileName
) && !FilenameIn(Volume
, Directory
, L
"bkpbootmgfw.efi",
1357 GlobalConfig
.DontScanFiles
)) {
1358 AddLoaderEntry(FileName
, L
"Microsoft EFI boot (Boot Repair backup)", Volume
);
1359 FoundBRBackup
= TRUE
;
1360 if (DuplicatesFallback(Volume
, FileName
))
1361 ScanFallbackLoader
= FALSE
;
1363 StrCpy(FileName
, L
"EFI\\Microsoft\\Boot\\bootmgfw.efi");
1364 if (FileExists(Volume
->RootDir
, FileName
) && !FilenameIn(Volume
, Directory
, L
"bootmgfw.efi", GlobalConfig
.DontScanFiles
)) {
1366 AddLoaderEntry(FileName
, L
"Supposed Microsoft EFI boot (probably GRUB)", Volume
);
1368 AddLoaderEntry(FileName
, L
"Microsoft EFI boot", Volume
);
1369 if (DuplicatesFallback(Volume
, FileName
))
1370 ScanFallbackLoader
= FALSE
;
1374 // scan the root directory for EFI executables
1375 if (ScanLoaderDir(Volume
, L
"\\", MatchPatterns
))
1376 ScanFallbackLoader
= FALSE
;
1378 // scan subdirectories of the EFI directory (as per the standard)
1379 DirIterOpen(Volume
->RootDir
, L
"EFI", &EfiDirIter
);
1380 while (DirIterNext(&EfiDirIter
, 1, NULL
, &EfiDirEntry
)) {
1381 if (StriCmp(EfiDirEntry
->FileName
, L
"tools") == 0 || EfiDirEntry
->FileName
[0] == '.')
1382 continue; // skip this, doesn't contain boot loaders or is scanned later
1383 SPrint(FileName
, 255, L
"EFI\\%s", EfiDirEntry
->FileName
);
1384 if (ScanLoaderDir(Volume
, FileName
, MatchPatterns
))
1385 ScanFallbackLoader
= FALSE
;
1387 Status
= DirIterClose(&EfiDirIter
);
1388 if (Status
!= EFI_NOT_FOUND
)
1389 CheckError(Status
, L
"while scanning the EFI directory");
1391 // Scan user-specified (or additional default) directories....
1393 while ((Directory
= FindCommaDelimited(GlobalConfig
.AlsoScan
, i
++)) != NULL
) {
1394 if (ShouldScan(Volume
, Directory
)) {
1395 SplitVolumeAndFilename(&Directory
, &VolName
);
1396 CleanUpPathNameSlashes(Directory
);
1397 Length
= StrLen(Directory
);
1398 if ((Length
> 0) && ScanLoaderDir(Volume
, Directory
, MatchPatterns
))
1399 ScanFallbackLoader
= FALSE
;
1400 MyFreePool(VolName
);
1401 } // if should scan dir
1402 MyFreePool(Directory
);
1405 // Don't scan the fallback loader if it's on the same volume and a duplicate of rEFInd itself....
1406 SelfPath
= DevicePathToStr(SelfLoadedImage
->FilePath
);
1407 CleanUpPathNameSlashes(SelfPath
);
1408 if ((Volume
->DeviceHandle
== SelfLoadedImage
->DeviceHandle
) && DuplicatesFallback(Volume
, SelfPath
))
1409 ScanFallbackLoader
= FALSE
;
1411 // If not a duplicate & if it exists & if it's not us, create an entry
1412 // for the fallback boot loader
1413 if (ScanFallbackLoader
&& FileExists(Volume
->RootDir
, FALLBACK_FULLNAME
) && ShouldScan(Volume
, L
"EFI\\BOOT"))
1414 AddLoaderEntry(FALLBACK_FULLNAME
, L
"Fallback boot loader", Volume
);
1416 } // static VOID ScanEfiFiles()
1418 // Scan internal disks for valid EFI boot loaders....
1419 static VOID
ScanInternal(VOID
) {
1422 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1423 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_INTERNAL
) {
1424 ScanEfiFiles(Volumes
[VolumeIndex
]);
1427 } // static VOID ScanInternal()
1429 // Scan external disks for valid EFI boot loaders....
1430 static VOID
ScanExternal(VOID
) {
1433 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1434 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_EXTERNAL
) {
1435 ScanEfiFiles(Volumes
[VolumeIndex
]);
1438 } // static VOID ScanExternal()
1440 // Scan internal disks for valid EFI boot loaders....
1441 static VOID
ScanOptical(VOID
) {
1444 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1445 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_OPTICAL
) {
1446 ScanEfiFiles(Volumes
[VolumeIndex
]);
1449 } // static VOID ScanOptical()
1452 // legacy boot functions
1455 static EFI_STATUS
ActivateMbrPartition(IN EFI_BLOCK_IO
*BlockIO
, IN UINTN PartitionIndex
)
1458 UINT8 SectorBuffer
[512];
1459 MBR_PARTITION_INFO
*MbrTable
, *EMbrTable
;
1460 UINT32 ExtBase
, ExtCurrent
, NextExtCurrent
;
1461 UINTN LogicalPartitionIndex
= 4;
1463 BOOLEAN HaveBootCode
;
1466 Status
= refit_call5_wrapper(BlockIO
->ReadBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, 0, 512, SectorBuffer
);
1467 if (EFI_ERROR(Status
))
1469 if (*((UINT16
*)(SectorBuffer
+ 510)) != 0xaa55)
1470 return EFI_NOT_FOUND
; // safety measure #1
1472 // add boot code if necessary
1473 HaveBootCode
= FALSE
;
1474 for (i
= 0; i
< MBR_BOOTCODE_SIZE
; i
++) {
1475 if (SectorBuffer
[i
] != 0) {
1476 HaveBootCode
= TRUE
;
1480 if (!HaveBootCode
) {
1481 // no boot code found in the MBR, add the syslinux MBR code
1482 SetMem(SectorBuffer
, MBR_BOOTCODE_SIZE
, 0);
1483 CopyMem(SectorBuffer
, syslinux_mbr
, SYSLINUX_MBR_SIZE
);
1486 // set the partition active
1487 MbrTable
= (MBR_PARTITION_INFO
*)(SectorBuffer
+ 446);
1489 for (i
= 0; i
< 4; i
++) {
1490 if (MbrTable
[i
].Flags
!= 0x00 && MbrTable
[i
].Flags
!= 0x80)
1491 return EFI_NOT_FOUND
; // safety measure #2
1492 if (i
== PartitionIndex
)
1493 MbrTable
[i
].Flags
= 0x80;
1494 else if (PartitionIndex
>= 4 && IS_EXTENDED_PART_TYPE(MbrTable
[i
].Type
)) {
1495 MbrTable
[i
].Flags
= 0x80;
1496 ExtBase
= MbrTable
[i
].StartLBA
;
1498 MbrTable
[i
].Flags
= 0x00;
1502 Status
= refit_call5_wrapper(BlockIO
->WriteBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, 0, 512, SectorBuffer
);
1503 if (EFI_ERROR(Status
))
1506 if (PartitionIndex
>= 4) {
1507 // we have to activate a logical partition, so walk the EMBR chain
1509 // NOTE: ExtBase was set above while looking at the MBR table
1510 for (ExtCurrent
= ExtBase
; ExtCurrent
; ExtCurrent
= NextExtCurrent
) {
1511 // read current EMBR
1512 Status
= refit_call5_wrapper(BlockIO
->ReadBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, ExtCurrent
, 512, SectorBuffer
);
1513 if (EFI_ERROR(Status
))
1515 if (*((UINT16
*)(SectorBuffer
+ 510)) != 0xaa55)
1516 return EFI_NOT_FOUND
; // safety measure #3
1518 // scan EMBR, set appropriate partition active
1519 EMbrTable
= (MBR_PARTITION_INFO
*)(SectorBuffer
+ 446);
1521 for (i
= 0; i
< 4; i
++) {
1522 if (EMbrTable
[i
].Flags
!= 0x00 && EMbrTable
[i
].Flags
!= 0x80)
1523 return EFI_NOT_FOUND
; // safety measure #4
1524 if (EMbrTable
[i
].StartLBA
== 0 || EMbrTable
[i
].Size
== 0)
1526 if (IS_EXTENDED_PART_TYPE(EMbrTable
[i
].Type
)) {
1527 // link to next EMBR
1528 NextExtCurrent
= ExtBase
+ EMbrTable
[i
].StartLBA
;
1529 EMbrTable
[i
].Flags
= (PartitionIndex
>= LogicalPartitionIndex
) ? 0x80 : 0x00;
1532 // logical partition
1533 EMbrTable
[i
].Flags
= (PartitionIndex
== LogicalPartitionIndex
) ? 0x80 : 0x00;
1534 LogicalPartitionIndex
++;
1538 // write current EMBR
1539 Status
= refit_call5_wrapper(BlockIO
->WriteBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, ExtCurrent
, 512, SectorBuffer
);
1540 if (EFI_ERROR(Status
))
1543 if (PartitionIndex
< LogicalPartitionIndex
)
1544 break; // stop the loop, no need to touch further EMBRs
1550 } /* static EFI_STATUS ActivateMbrPartition() */
1552 // early 2006 Core Duo / Core Solo models
1553 static UINT8 LegacyLoaderDevicePath1Data
[] = {
1554 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1555 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1556 0xFF, 0xFF, 0xF9, 0xFF, 0x00, 0x00, 0x00, 0x00,
1557 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1558 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1559 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1561 // mid-2006 Mac Pro (and probably other Core 2 models)
1562 static UINT8 LegacyLoaderDevicePath2Data
[] = {
1563 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1564 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1565 0xFF, 0xFF, 0xF7, 0xFF, 0x00, 0x00, 0x00, 0x00,
1566 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1567 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1568 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1570 // mid-2007 MBP ("Santa Rosa" based models)
1571 static UINT8 LegacyLoaderDevicePath3Data
[] = {
1572 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1573 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1574 0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
1575 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1576 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1577 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1580 static UINT8 LegacyLoaderDevicePath4Data
[] = {
1581 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1582 0x00, 0x00, 0xC0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1583 0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
1584 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1585 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1586 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1588 // late-2008 MB/MBP (NVidia chipset)
1589 static UINT8 LegacyLoaderDevicePath5Data
[] = {
1590 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1591 0x00, 0x40, 0xCB, 0xFF, 0x00, 0x00, 0x00, 0x00,
1592 0xFF, 0xBF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
1593 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1594 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1595 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1598 static EFI_DEVICE_PATH
*LegacyLoaderList
[] = {
1599 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath1Data
,
1600 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath2Data
,
1601 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath3Data
,
1602 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath4Data
,
1603 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath5Data
,
1607 #define MAX_DISCOVERED_PATHS (16)
1609 static VOID
StartLegacy(IN LEGACY_ENTRY
*Entry
, IN CHAR16
*SelectionName
)
1612 EG_IMAGE
*BootLogoImage
;
1613 UINTN ErrorInStep
= 0;
1614 EFI_DEVICE_PATH
*DiscoveredPathList
[MAX_DISCOVERED_PATHS
];
1616 BeginExternalScreen(TRUE
, L
"Booting Legacy OS (Mac mode)");
1618 BootLogoImage
= LoadOSIcon(Entry
->Volume
->OSIconName
, L
"legacy", TRUE
);
1619 if (BootLogoImage
!= NULL
)
1620 BltImageAlpha(BootLogoImage
,
1621 (UGAWidth
- BootLogoImage
->Width
) >> 1,
1622 (UGAHeight
- BootLogoImage
->Height
) >> 1,
1623 &StdBackgroundPixel
);
1625 if (Entry
->Volume
->IsMbrPartition
) {
1626 ActivateMbrPartition(Entry
->Volume
->WholeDiskBlockIO
, Entry
->Volume
->MbrPartitionIndex
);
1629 ExtractLegacyLoaderPaths(DiscoveredPathList
, MAX_DISCOVERED_PATHS
, LegacyLoaderList
);
1631 StoreLoaderName(SelectionName
);
1632 Status
= StartEFIImageList(DiscoveredPathList
, Entry
->LoadOptions
, TYPE_LEGACY
, L
"legacy loader", 0, &ErrorInStep
, TRUE
);
1633 if (Status
== EFI_NOT_FOUND
) {
1634 if (ErrorInStep
== 1) {
1635 Print(L
"\nPlease make sure that you have the latest firmware update installed.\n");
1636 } else if (ErrorInStep
== 3) {
1637 Print(L
"\nThe firmware refused to boot from the selected volume. Note that external\n"
1638 L
"hard drives are not well-supported by Apple's firmware for legacy OS booting.\n");
1641 FinishExternalScreen();
1642 } /* static VOID StartLegacy() */
1644 // Start a device on a non-Mac using the EFI_LEGACY_BIOS_PROTOCOL
1645 static VOID
StartLegacyUEFI(LEGACY_ENTRY
*Entry
, CHAR16
*SelectionName
)
1647 BeginExternalScreen(TRUE
, L
"Booting Legacy OS (UEFI mode)");
1648 StoreLoaderName(SelectionName
);
1650 BdsLibConnectDevicePath (Entry
->BdsOption
->DevicePath
);
1651 BdsLibDoLegacyBoot(Entry
->BdsOption
);
1653 // If we get here, it means that there was a failure....
1654 Print(L
"Failure booting legacy (BIOS) OS.");
1656 FinishExternalScreen();
1657 } // static VOID StartLegacyUEFI()
1659 static LEGACY_ENTRY
* AddLegacyEntry(IN CHAR16
*LoaderTitle
, IN REFIT_VOLUME
*Volume
)
1661 LEGACY_ENTRY
*Entry
, *SubEntry
;
1662 REFIT_MENU_SCREEN
*SubScreen
;
1663 CHAR16
*VolDesc
, *LegacyTitle
;
1664 CHAR16 ShortcutLetter
= 0;
1666 if (LoaderTitle
== NULL
) {
1667 if (Volume
->OSName
!= NULL
) {
1668 LoaderTitle
= Volume
->OSName
;
1669 if (LoaderTitle
[0] == 'W' || LoaderTitle
[0] == 'L')
1670 ShortcutLetter
= LoaderTitle
[0];
1672 LoaderTitle
= L
"Legacy OS";
1674 if (Volume
->VolName
!= NULL
)
1675 VolDesc
= Volume
->VolName
;
1677 VolDesc
= (Volume
->DiskKind
== DISK_KIND_OPTICAL
) ? L
"CD" : L
"HD";
1679 LegacyTitle
= AllocateZeroPool(256 * sizeof(CHAR16
));
1680 if (LegacyTitle
!= NULL
)
1681 SPrint(LegacyTitle
, 255, L
"Boot %s from %s", LoaderTitle
, VolDesc
);
1682 if (IsInSubstring(LegacyTitle
, GlobalConfig
.DontScanVolumes
)) {
1683 MyFreePool(LegacyTitle
);
1687 // prepare the menu entry
1688 Entry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1689 Entry
->me
.Title
= LegacyTitle
;
1690 Entry
->me
.Tag
= TAG_LEGACY
;
1692 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1693 Entry
->me
.Image
= LoadOSIcon(Volume
->OSIconName
, L
"legacy", FALSE
);
1694 Entry
->me
.BadgeImage
= Volume
->VolBadgeImage
;
1695 Entry
->Volume
= Volume
;
1696 Entry
->LoadOptions
= (Volume
->DiskKind
== DISK_KIND_OPTICAL
) ? L
"CD" :
1697 ((Volume
->DiskKind
== DISK_KIND_EXTERNAL
) ? L
"USB" : L
"HD");
1698 Entry
->Enabled
= TRUE
;
1700 // create the submenu
1701 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
1702 SubScreen
->Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1703 SPrint(SubScreen
->Title
, 255, L
"Boot Options for %s on %s", LoaderTitle
, VolDesc
);
1704 SubScreen
->TitleImage
= Entry
->me
.Image
;
1705 SubScreen
->Hint1
= StrDuplicate(SUBSCREEN_HINT1
);
1706 if (GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_EDITOR
) {
1707 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2_NO_EDITOR
);
1709 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2
);
1713 SubEntry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1714 SubEntry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1715 SPrint(SubEntry
->me
.Title
, 255, L
"Boot %s", LoaderTitle
);
1716 SubEntry
->me
.Tag
= TAG_LEGACY
;
1717 SubEntry
->Volume
= Entry
->Volume
;
1718 SubEntry
->LoadOptions
= Entry
->LoadOptions
;
1719 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
1721 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
1722 Entry
->me
.SubScreen
= SubScreen
;
1723 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1725 } /* static LEGACY_ENTRY * AddLegacyEntry() */
1728 // default volume badge icon based on disk kind
1729 static EG_IMAGE
* GetDiskBadge(IN UINTN DiskType
) {
1730 EG_IMAGE
* Badge
= NULL
;
1734 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_INTERNAL
);
1737 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_EXTERNAL
);
1740 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_OPTICAL
);
1744 } // static EG_IMAGE * GetDiskBadge()
1747 Create a rEFInd boot option from a Legacy BIOS protocol option.
1749 static LEGACY_ENTRY
* AddLegacyEntryUEFI(BDS_COMMON_OPTION
*BdsOption
, IN UINT16 DiskType
)
1751 LEGACY_ENTRY
*Entry
, *SubEntry
;
1752 REFIT_MENU_SCREEN
*SubScreen
;
1753 CHAR16 ShortcutLetter
= 0;
1754 CHAR16
*LegacyDescription
= StrDuplicate(BdsOption
->Description
);
1756 if (IsInSubstring(LegacyDescription
, GlobalConfig
.DontScanVolumes
))
1759 // Remove stray spaces, since many EFIs produce descriptions with lots of
1760 // extra spaces, especially at the end; this throws off centering of the
1761 // description on the screen....
1762 LimitStringLength(LegacyDescription
, 100);
1764 // prepare the menu entry
1765 Entry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1766 Entry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1767 SPrint(Entry
->me
.Title
, 255, L
"Boot legacy target %s", LegacyDescription
);
1768 Entry
->me
.Tag
= TAG_LEGACY_UEFI
;
1770 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1771 Entry
->me
.Image
= LoadOSIcon(L
"legacy", L
"legacy", TRUE
);
1772 Entry
->LoadOptions
= (DiskType
== BBS_CDROM
) ? L
"CD" :
1773 ((DiskType
== BBS_USB
) ? L
"USB" : L
"HD");
1774 Entry
->me
.BadgeImage
= GetDiskBadge(DiskType
);
1775 Entry
->BdsOption
= BdsOption
;
1776 Entry
->Enabled
= TRUE
;
1778 // create the submenu
1779 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
1780 SubScreen
->Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1781 SPrint(SubScreen
->Title
, 255, L
"No boot options for legacy target");
1782 SubScreen
->TitleImage
= Entry
->me
.Image
;
1783 SubScreen
->Hint1
= StrDuplicate(SUBSCREEN_HINT1
);
1784 if (GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_EDITOR
) {
1785 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2_NO_EDITOR
);
1787 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2
);
1791 SubEntry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1792 SubEntry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1793 SPrint(SubEntry
->me
.Title
, 255, L
"Boot %s", LegacyDescription
);
1794 SubEntry
->me
.Tag
= TAG_LEGACY_UEFI
;
1795 Entry
->BdsOption
= BdsOption
;
1796 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
1798 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
1799 Entry
->me
.SubScreen
= SubScreen
;
1800 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1802 } /* static LEGACY_ENTRY * AddLegacyEntryUEFI() */
1805 Scan for legacy BIOS targets on machines that implement EFI_LEGACY_BIOS_PROTOCOL.
1806 In testing, protocol has not been implemented on Macs but has been
1807 implemented on several Dell PCs and an ASUS motherboard.
1808 Restricts output to disks of the specified DiskType.
1810 static VOID
ScanLegacyUEFI(IN UINTN DiskType
)
1813 EFI_LEGACY_BIOS_PROTOCOL
*LegacyBios
;
1814 UINT16
*BootOrder
= NULL
;
1816 CHAR16 BootOption
[10];
1817 UINTN BootOrderSize
= 0;
1819 BDS_COMMON_OPTION
*BdsOption
;
1820 LIST_ENTRY TempList
;
1821 BBS_BBS_DEVICE_PATH
*BbsDevicePath
= NULL
;
1822 BOOLEAN SearchingForUsb
= FALSE
;
1824 InitializeListHead (&TempList
);
1825 ZeroMem (Buffer
, sizeof (Buffer
));
1827 // If LegacyBios protocol is not implemented on this platform, then
1828 //we do not support this type of legacy boot on this machine.
1829 Status
= refit_call3_wrapper(gBS
->LocateProtocol
, &gEfiLegacyBootProtocolGuid
, NULL
, (VOID
**) &LegacyBios
);
1830 if (EFI_ERROR (Status
))
1833 // EFI calls USB drives BBS_HARDDRIVE, but we want to distinguish them,
1834 // so we set DiskType inappropriately elsewhere in the program and
1835 // "translate" it here.
1836 if (DiskType
== BBS_USB
) {
1837 DiskType
= BBS_HARDDISK
;
1838 SearchingForUsb
= TRUE
;
1841 // Grab the boot order
1842 BootOrder
= BdsLibGetVariableAndSize(L
"BootOrder", &gEfiGlobalVariableGuid
, &BootOrderSize
);
1843 if (BootOrder
== NULL
) {
1848 while (Index
< BootOrderSize
/ sizeof (UINT16
))
1850 // Grab each boot option variable from the boot order, and convert
1851 // the variable into a BDS boot option
1852 UnicodeSPrint (BootOption
, sizeof (BootOption
), L
"Boot%04x", BootOrder
[Index
]);
1853 BdsOption
= BdsLibVariableToOption (&TempList
, BootOption
);
1855 if (BdsOption
!= NULL
) {
1856 BbsDevicePath
= (BBS_BBS_DEVICE_PATH
*)BdsOption
->DevicePath
;
1857 // Only add the entry if it is of a requested type (e.g. USB, HD)
1858 // Two checks necessary because some systems return EFI boot loaders
1859 // with a DeviceType value that would inappropriately include them
1860 // as legacy loaders....
1861 if ((BbsDevicePath
->DeviceType
== DiskType
) && (BdsOption
->DevicePath
->Type
== DEVICE_TYPE_BIOS
)) {
1862 // USB flash drives appear as hard disks with certain media flags set.
1863 // Look for this, and if present, pass it on with the (technically
1864 // incorrect, but internally useful) BBS_TYPE_USB flag set.
1865 if (DiskType
== BBS_HARDDISK
) {
1866 if (SearchingForUsb
&& (BbsDevicePath
->StatusFlag
& (BBS_MEDIA_PRESENT
| BBS_MEDIA_MAYBE_PRESENT
))) {
1867 AddLegacyEntryUEFI(BdsOption
, BBS_USB
);
1868 } else if (!SearchingForUsb
&& !(BbsDevicePath
->StatusFlag
& (BBS_MEDIA_PRESENT
| BBS_MEDIA_MAYBE_PRESENT
))) {
1869 AddLegacyEntryUEFI(BdsOption
, DiskType
);
1872 AddLegacyEntryUEFI(BdsOption
, DiskType
);
1875 } // if (BdsOption != NULL)
1878 } /* static VOID ScanLegacyUEFI() */
1880 static VOID
ScanLegacyVolume(REFIT_VOLUME
*Volume
, UINTN VolumeIndex
) {
1882 BOOLEAN ShowVolume
, HideIfOthersFound
;
1885 HideIfOthersFound
= FALSE
;
1886 if (Volume
->IsAppleLegacy
) {
1888 HideIfOthersFound
= TRUE
;
1889 } else if (Volume
->HasBootCode
) {
1891 if (Volume
->BlockIO
== Volume
->WholeDiskBlockIO
&&
1892 Volume
->BlockIOOffset
== 0 &&
1893 Volume
->OSName
== NULL
)
1894 // this is a whole disk (MBR) entry; hide if we have entries for partitions
1895 HideIfOthersFound
= TRUE
;
1897 if (HideIfOthersFound
) {
1898 // check for other bootable entries on the same disk
1899 for (VolumeIndex2
= 0; VolumeIndex2
< VolumesCount
; VolumeIndex2
++) {
1900 if (VolumeIndex2
!= VolumeIndex
&& Volumes
[VolumeIndex2
]->HasBootCode
&&
1901 Volumes
[VolumeIndex2
]->WholeDiskBlockIO
== Volume
->WholeDiskBlockIO
)
1907 AddLegacyEntry(NULL
, Volume
);
1908 } // static VOID ScanLegacyVolume()
1910 // Scan attached optical discs for legacy (BIOS) boot code
1911 // and add anything found to the list....
1912 static VOID
ScanLegacyDisc(VOID
)
1915 REFIT_VOLUME
*Volume
;
1917 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
) {
1918 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1919 Volume
= Volumes
[VolumeIndex
];
1920 if (Volume
->DiskKind
== DISK_KIND_OPTICAL
)
1921 ScanLegacyVolume(Volume
, VolumeIndex
);
1923 } else if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
1924 ScanLegacyUEFI(BBS_CDROM
);
1926 } /* static VOID ScanLegacyDisc() */
1928 // Scan internal hard disks for legacy (BIOS) boot code
1929 // and add anything found to the list....
1930 static VOID
ScanLegacyInternal(VOID
)
1933 REFIT_VOLUME
*Volume
;
1935 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
) {
1936 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1937 Volume
= Volumes
[VolumeIndex
];
1938 if (Volume
->DiskKind
== DISK_KIND_INTERNAL
)
1939 ScanLegacyVolume(Volume
, VolumeIndex
);
1941 } else if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
1942 // TODO: This actually picks up USB flash drives, too; try to find
1943 // a way to differentiate the two....
1944 ScanLegacyUEFI(BBS_HARDDISK
);
1946 } /* static VOID ScanLegacyInternal() */
1948 // Scan external disks for legacy (BIOS) boot code
1949 // and add anything found to the list....
1950 static VOID
ScanLegacyExternal(VOID
)
1953 REFIT_VOLUME
*Volume
;
1955 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
) {
1956 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1957 Volume
= Volumes
[VolumeIndex
];
1958 if (Volume
->DiskKind
== DISK_KIND_EXTERNAL
)
1959 ScanLegacyVolume(Volume
, VolumeIndex
);
1961 } else if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
1962 // TODO: This actually doesn't do anything useful; leaving in hopes of
1963 // fixing it later....
1964 ScanLegacyUEFI(BBS_USB
);
1966 } /* static VOID ScanLegacyExternal() */
1969 // pre-boot tool functions
1972 static VOID
StartTool(IN LOADER_ENTRY
*Entry
)
1974 BeginExternalScreen(Entry
->UseGraphicsMode
, Entry
->me
.Title
+ 6); // assumes "Start <title>" as assigned below
1975 StoreLoaderName(Entry
->me
.Title
);
1976 StartEFIImage(Entry
->DevicePath
, Entry
->LoadOptions
, TYPE_EFI
,
1977 Basename(Entry
->LoaderPath
), Entry
->OSType
, NULL
, TRUE
);
1978 FinishExternalScreen();
1979 } /* static VOID StartTool() */
1981 static LOADER_ENTRY
* AddToolEntry(EFI_HANDLE DeviceHandle
, IN CHAR16
*LoaderPath
, IN CHAR16
*LoaderTitle
, IN EG_IMAGE
*Image
,
1982 IN CHAR16 ShortcutLetter
, IN BOOLEAN UseGraphicsMode
)
1984 LOADER_ENTRY
*Entry
;
1985 CHAR16
*TitleStr
= NULL
;
1987 Entry
= AllocateZeroPool(sizeof(LOADER_ENTRY
));
1989 TitleStr
= PoolPrint(L
"Start %s", LoaderTitle
);
1990 Entry
->me
.Title
= TitleStr
;
1991 Entry
->me
.Tag
= TAG_TOOL
;
1993 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1994 Entry
->me
.Image
= Image
;
1995 Entry
->LoaderPath
= (LoaderPath
) ? StrDuplicate(LoaderPath
) : NULL
;
1996 Entry
->DevicePath
= FileDevicePath(DeviceHandle
, Entry
->LoaderPath
);
1997 Entry
->UseGraphicsMode
= UseGraphicsMode
;
1999 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
2001 } /* static LOADER_ENTRY * AddToolEntry() */
2004 // pre-boot driver functions
2007 static UINTN
ScanDriverDir(IN CHAR16
*Path
)
2010 REFIT_DIR_ITER DirIter
;
2012 EFI_FILE_INFO
*DirEntry
;
2013 CHAR16 FileName
[256];
2015 CleanUpPathNameSlashes(Path
);
2016 // look through contents of the directory
2017 DirIterOpen(SelfRootDir
, Path
, &DirIter
);
2018 while (DirIterNext(&DirIter
, 2, LOADER_MATCH_PATTERNS
, &DirEntry
)) {
2019 if (DirEntry
->FileName
[0] == '.')
2020 continue; // skip this
2022 SPrint(FileName
, 255, L
"%s\\%s", Path
, DirEntry
->FileName
);
2024 Status
= StartEFIImage(FileDevicePath(SelfLoadedImage
->DeviceHandle
, FileName
),
2025 L
"", TYPE_EFI
, DirEntry
->FileName
, 0, NULL
, FALSE
);
2027 Status
= DirIterClose(&DirIter
);
2028 if (Status
!= EFI_NOT_FOUND
) {
2029 SPrint(FileName
, 255, L
"while scanning the %s directory", Path
);
2030 CheckError(Status
, FileName
);
2035 #ifdef __MAKEWITH_GNUEFI
2036 static EFI_STATUS
ConnectAllDriversToAllControllers(VOID
)
2039 UINTN AllHandleCount
;
2040 EFI_HANDLE
*AllHandleBuffer
;
2043 EFI_HANDLE
*HandleBuffer
;
2049 Status
= LibLocateHandle(AllHandles
,
2054 if (EFI_ERROR(Status
))
2057 for (Index
= 0; Index
< AllHandleCount
; Index
++) {
2059 // Scan the handle database
2061 Status
= LibScanHandleDatabase(NULL
,
2063 AllHandleBuffer
[Index
],
2068 if (EFI_ERROR (Status
))
2072 if (HandleType
[Index
] & EFI_HANDLE_TYPE_DRIVER_BINDING_HANDLE
)
2074 if (HandleType
[Index
] & EFI_HANDLE_TYPE_IMAGE_HANDLE
)
2079 for (HandleIndex
= 0; HandleIndex
< HandleCount
; HandleIndex
++) {
2080 if (HandleType
[HandleIndex
] & EFI_HANDLE_TYPE_PARENT_HANDLE
)
2085 if (HandleType
[Index
] & EFI_HANDLE_TYPE_DEVICE_HANDLE
) {
2086 Status
= refit_call4_wrapper(BS
->ConnectController
,
2087 AllHandleBuffer
[Index
],
2095 MyFreePool (HandleBuffer
);
2096 MyFreePool (HandleType
);
2100 MyFreePool (AllHandleBuffer
);
2102 } /* EFI_STATUS ConnectAllDriversToAllControllers() */
2104 static EFI_STATUS
ConnectAllDriversToAllControllers(VOID
) {
2105 BdsLibConnectAllDriversToAllControllers();
2110 // Load all EFI drivers from rEFInd's "drivers" subdirectory and from the
2111 // directories specified by the user in the "scan_driver_dirs" configuration
2113 static VOID
LoadDrivers(VOID
)
2115 CHAR16
*Directory
, *SelfDirectory
;
2116 UINTN i
= 0, Length
, NumFound
= 0;
2118 // load drivers from the subdirectories of rEFInd's home directory specified
2119 // in the DRIVER_DIRS constant.
2120 while ((Directory
= FindCommaDelimited(DRIVER_DIRS
, i
++)) != NULL
) {
2121 SelfDirectory
= SelfDirPath
? StrDuplicate(SelfDirPath
) : NULL
;
2122 CleanUpPathNameSlashes(SelfDirectory
);
2123 MergeStrings(&SelfDirectory
, Directory
, L
'\\');
2124 NumFound
+= ScanDriverDir(SelfDirectory
);
2125 MyFreePool(Directory
);
2126 MyFreePool(SelfDirectory
);
2129 // Scan additional user-specified driver directories....
2131 while ((Directory
= FindCommaDelimited(GlobalConfig
.DriverDirs
, i
++)) != NULL
) {
2132 CleanUpPathNameSlashes(Directory
);
2133 Length
= StrLen(Directory
);
2135 NumFound
+= ScanDriverDir(Directory
);
2137 MyFreePool(Directory
);
2140 // connect all devices
2142 ConnectAllDriversToAllControllers();
2143 } /* static VOID LoadDrivers() */
2145 // Determine what (if any) type of legacy (BIOS) boot support is available
2146 static VOID
FindLegacyBootType(VOID
) {
2148 EFI_LEGACY_BIOS_PROTOCOL
*LegacyBios
;
2150 GlobalConfig
.LegacyType
= LEGACY_TYPE_NONE
;
2152 // UEFI-style legacy BIOS support is available only with some EFI implementations....
2153 Status
= refit_call3_wrapper(gBS
->LocateProtocol
, &gEfiLegacyBootProtocolGuid
, NULL
, (VOID
**) &LegacyBios
);
2154 if (!EFI_ERROR (Status
))
2155 GlobalConfig
.LegacyType
= LEGACY_TYPE_UEFI
;
2157 // Macs have their own system. If the firmware vendor code contains the
2158 // string "Apple", assume it's available. Note that this overrides the
2159 // UEFI type, and might yield false positives if the vendor string
2160 // contains "Apple" as part of something bigger, so this isn't 100%
2162 if (StriSubCmp(L
"Apple", ST
->FirmwareVendor
))
2163 GlobalConfig
.LegacyType
= LEGACY_TYPE_MAC
;
2164 } // static VOID FindLegacyBootType
2166 // Warn the user if legacy OS scans are enabled but the firmware can't support them....
2167 static VOID
WarnIfLegacyProblems(VOID
) {
2168 BOOLEAN found
= FALSE
;
2171 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_NONE
) {
2173 if (GlobalConfig
.ScanFor
[i
] == 'h' || GlobalConfig
.ScanFor
[i
] == 'b' || GlobalConfig
.ScanFor
[i
] == 'c' ||
2174 GlobalConfig
.ScanFor
[i
] == 'H' || GlobalConfig
.ScanFor
[i
] == 'B' || GlobalConfig
.ScanFor
[i
] == 'C')
2177 } while ((i
< NUM_SCAN_OPTIONS
) && (!found
));
2180 Print(L
"NOTE: refind.conf's 'scanfor' line specifies scanning for one or more legacy\n");
2181 Print(L
"(BIOS) boot options; however, this is not possible because your computer lacks\n");
2182 Print(L
"the necessary Compatibility Support Module (CSM) support or that support is\n");
2183 Print(L
"disabled in your firmware.\n");
2186 } // if no legacy support
2187 } // static VOID WarnIfLegacyProblems()
2189 // Locates boot loaders. NOTE: This assumes that GlobalConfig.LegacyType is set correctly.
2190 static VOID
ScanForBootloaders(VOID
) {
2193 BOOLEAN ScanForLegacy
= FALSE
;
2195 // Determine up-front if we'll be scanning for legacy loaders....
2196 for (i
= 0; i
< NUM_SCAN_OPTIONS
; i
++) {
2197 s
= GlobalConfig
.ScanFor
[i
];
2198 if ((s
== 'c') || (s
== 'C') || (s
== 'h') || (s
== 'H') || (s
== 'b') || (s
== 'B'))
2199 ScanForLegacy
= TRUE
;
2202 // If UEFI & scanning for legacy loaders & deep legacy scan, update NVRAM boot manager list
2203 if ((GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) && ScanForLegacy
&& GlobalConfig
.DeepLegacyScan
) {
2204 BdsDeleteAllInvalidLegacyBootOptions();
2205 BdsAddNonExistingLegacyBootOptions();
2208 // scan for loaders and tools, add them to the menu
2209 for (i
= 0; i
< NUM_SCAN_OPTIONS
; i
++) {
2210 switch(GlobalConfig
.ScanFor
[i
]) {
2215 ScanLegacyInternal();
2218 ScanLegacyExternal();
2221 ScanUserConfigured(GlobalConfig
.ConfigFilename
);
2235 // assign shortcut keys
2236 for (i
= 0; i
< MainMenu
.EntryCount
&& MainMenu
.Entries
[i
]->Row
== 0 && i
< 9; i
++)
2237 MainMenu
.Entries
[i
]->ShortcutDigit
= (CHAR16
)('1' + i
);
2239 // wait for user ACK when there were errors
2240 FinishTextScreen(FALSE
);
2241 } // static VOID ScanForBootloaders()
2243 // Locate a single tool from the specified Locations using one of the
2244 // specified Names and add it to the menu.
2245 static VOID
FindTool(CHAR16
*Locations
, CHAR16
*Names
, CHAR16
*Description
, UINTN Icon
) {
2246 UINTN j
= 0, k
, VolumeIndex
;
2247 CHAR16
*DirName
, *FileName
, *PathName
, FullDescription
[256];
2249 while ((DirName
= FindCommaDelimited(Locations
, j
++)) != NULL
) {
2251 while ((FileName
= FindCommaDelimited(Names
, k
++)) != NULL
) {
2252 PathName
= StrDuplicate(DirName
);
2253 MergeStrings(&PathName
, FileName
, (StriCmp(PathName
, L
"\\") == 0) ? 0 : L
'\\');
2254 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
2255 if ((Volumes
[VolumeIndex
]->RootDir
!= NULL
) && (FileExists(Volumes
[VolumeIndex
]->RootDir
, PathName
))) {
2256 SPrint(FullDescription
, 255, L
"%s at %s on %s", Description
, PathName
, Volumes
[VolumeIndex
]->VolName
);
2257 AddToolEntry(Volumes
[VolumeIndex
]->DeviceHandle
, PathName
, FullDescription
, BuiltinIcon(Icon
), 'S', FALSE
);
2260 MyFreePool(PathName
);
2261 MyFreePool(FileName
);
2263 MyFreePool(DirName
);
2264 } // while Locations
2265 } // VOID FindTool()
2267 // Add the second-row tags containing built-in and external tools (EFI shell,
2269 static VOID
ScanForTools(VOID
) {
2270 CHAR16
*FileName
= NULL
, *VolName
= NULL
, *MokLocations
, Description
[256];
2271 REFIT_MENU_ENTRY
*TempMenuEntry
;
2272 UINTN i
, j
, VolumeIndex
;
2276 MokLocations
= StrDuplicate(MOK_LOCATIONS
);
2277 if (MokLocations
!= NULL
)
2278 MergeStrings(&MokLocations
, SelfDirPath
, L
',');
2280 for (i
= 0; i
< NUM_TOOLS
; i
++) {
2281 switch(GlobalConfig
.ShowTools
[i
]) {
2282 // NOTE: Be sure that FileName is NULL at the end of each case.
2284 TempMenuEntry
= CopyMenuEntry(&MenuEntryShutdown
);
2285 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_SHUTDOWN
);
2286 AddMenuEntry(&MainMenu
, TempMenuEntry
);
2290 TempMenuEntry
= CopyMenuEntry(&MenuEntryReset
);
2291 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_RESET
);
2292 AddMenuEntry(&MainMenu
, TempMenuEntry
);
2296 TempMenuEntry
= CopyMenuEntry(&MenuEntryAbout
);
2297 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT
);
2298 AddMenuEntry(&MainMenu
, TempMenuEntry
);
2302 TempMenuEntry
= CopyMenuEntry(&MenuEntryExit
);
2303 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_EXIT
);
2304 AddMenuEntry(&MainMenu
, TempMenuEntry
);
2308 if (EfivarGetRaw(&GlobalGuid
, L
"OsIndicationsSupported", &b
, &j
) == EFI_SUCCESS
) {
2310 if (osind
& EFI_OS_INDICATIONS_BOOT_TO_FW_UI
) {
2311 TempMenuEntry
= CopyMenuEntry(&MenuEntryFirmware
);
2312 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_FIRMWARE
);
2313 AddMenuEntry(&MainMenu
, TempMenuEntry
);
2320 while ((FileName
= FindCommaDelimited(SHELL_NAMES
, j
++)) != NULL
) {
2321 if (FileExists(SelfRootDir
, FileName
)) {
2322 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, L
"EFI Shell", BuiltinIcon(BUILTIN_ICON_TOOL_SHELL
),
2325 MyFreePool(FileName
);
2331 while ((FileName
= FindCommaDelimited(GPTSYNC_NAMES
, j
++)) != NULL
) {
2332 if (FileExists(SelfRootDir
, FileName
)) {
2333 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, L
"Hybrid MBR tool", BuiltinIcon(BUILTIN_ICON_TOOL_PART
),
2336 MyFreePool(FileName
);
2343 while ((FileName
= FindCommaDelimited(GDISK_NAMES
, j
++)) != NULL
) {
2344 if (FileExists(SelfRootDir
, FileName
)) {
2345 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, L
"disk partitioning tool",
2346 BuiltinIcon(BUILTIN_ICON_TOOL_PART
), 'G', FALSE
);
2348 MyFreePool(FileName
);
2353 case TAG_APPLE_RECOVERY
:
2354 FileName
= StrDuplicate(L
"\\com.apple.recovery.boot\\boot.efi");
2355 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
2356 if ((Volumes
[VolumeIndex
]->RootDir
!= NULL
) && (FileExists(Volumes
[VolumeIndex
]->RootDir
, FileName
))) {
2357 SPrint(Description
, 255, L
"Apple Recovery on %s", Volumes
[VolumeIndex
]->VolName
);
2358 AddToolEntry(Volumes
[VolumeIndex
]->DeviceHandle
, FileName
, Description
,
2359 BuiltinIcon(BUILTIN_ICON_TOOL_APPLE_RESCUE
), 'R', TRUE
);
2362 MyFreePool(FileName
);
2366 case TAG_WINDOWS_RECOVERY
:
2368 while ((FileName
= FindCommaDelimited(GlobalConfig
.WindowsRecoveryFiles
, j
++)) != NULL
) {
2369 SplitVolumeAndFilename(&FileName
, &VolName
);
2370 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
2371 if ((Volumes
[VolumeIndex
]->RootDir
!= NULL
) && (FileExists(Volumes
[VolumeIndex
]->RootDir
, FileName
)) &&
2372 ((VolName
== NULL
) || (StriCmp(VolName
, Volumes
[VolumeIndex
]->VolName
) == 0))) {
2373 SPrint(Description
, 255, L
"Microsoft Recovery on %s", Volumes
[VolumeIndex
]->VolName
);
2374 AddToolEntry(Volumes
[VolumeIndex
]->DeviceHandle
, FileName
, Description
,
2375 BuiltinIcon(BUILTIN_ICON_TOOL_WINDOWS_RESCUE
), 'R', TRUE
);
2379 MyFreePool(FileName
);
2381 MyFreePool(VolName
);
2386 FindTool(MokLocations
, MOK_NAMES
, L
"MOK utility", BUILTIN_ICON_TOOL_MOK_TOOL
);
2390 FindTool(MEMTEST_LOCATIONS
, MEMTEST_NAMES
, L
"Memory test utility", BUILTIN_ICON_TOOL_MEMTEST
);
2395 } // static VOID ScanForTools
2397 // Rescan for boot loaders
2398 static VOID
RescanAll(BOOLEAN DisplayMessage
) {
2406 egDisplayMessage(L
"Scanning for new boot loaders; please wait....", &BGColor
);
2407 FreeList((VOID
***) &(MainMenu
.Entries
), &MainMenu
.EntryCount
);
2408 MainMenu
.Entries
= NULL
;
2409 MainMenu
.EntryCount
= 0;
2410 ReadConfig(GlobalConfig
.ConfigFilename
);
2411 ConnectAllDriversToAllControllers();
2413 ScanForBootloaders();
2416 } // VOID RescanAll()
2418 #ifdef __MAKEWITH_TIANO
2420 // Minimal initialization function
2421 static VOID
InitializeLib(IN EFI_HANDLE ImageHandle
, IN EFI_SYSTEM_TABLE
*SystemTable
) {
2423 // gImageHandle = ImageHandle;
2424 gBS
= SystemTable
->BootServices
;
2425 // gRS = SystemTable->RuntimeServices;
2426 gRT
= SystemTable
->RuntimeServices
; // Some BDS functions need gRT to be set
2427 EfiGetSystemConfigurationTable (&gEfiDxeServicesTableGuid
, (VOID
**) &gDS
);
2429 // InitializeConsoleSim();
2434 // Set up our own Secure Boot extensions....
2435 // Returns TRUE on success, FALSE otherwise
2436 static BOOLEAN
SecureBootSetup(VOID
) {
2438 BOOLEAN Success
= FALSE
;
2440 if (secure_mode() && ShimLoaded()) {
2441 Status
= security_policy_install();
2442 if (Status
== EFI_SUCCESS
) {
2445 Print(L
"Failed to install MOK Secure Boot extensions");
2449 } // VOID SecureBootSetup()
2451 // Remove our own Secure Boot extensions....
2452 // Returns TRUE on success, FALSE otherwise
2453 static BOOLEAN
SecureBootUninstall(VOID
) {
2455 BOOLEAN Success
= TRUE
;
2457 if (secure_mode()) {
2458 Status
= security_policy_uninstall();
2459 if (Status
!= EFI_SUCCESS
) {
2461 BeginTextScreen(L
"Secure Boot Policy Failure");
2462 Print(L
"Failed to uninstall MOK Secure Boot extensions; forcing a reboot.");
2464 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);
2468 } // VOID SecureBootUninstall
2470 // Sets the global configuration filename; will be CONFIG_FILE_NAME unless the
2471 // "-c" command-line option is set, in which case that takes precedence.
2472 // If an error is encountered, leaves the value alone (it should be set to
2473 // CONFIG_FILE_NAME when GlobalConfig is initialized).
2474 static VOID
SetConfigFilename(EFI_HANDLE ImageHandle
) {
2475 EFI_LOADED_IMAGE
*Info
;
2476 CHAR16
*Options
, *FileName
;
2480 Status
= refit_call3_wrapper(BS
->HandleProtocol
, ImageHandle
, &LoadedImageProtocol
, (VOID
**) &Info
);
2481 if ((Status
== EFI_SUCCESS
) && (Info
->LoadOptionsSize
> 0)) {
2482 Options
= (CHAR16
*) Info
->LoadOptions
;
2483 Where
= FindSubString(L
" -c ", Options
);
2485 FileName
= StrDuplicate(&Options
[Where
+ 4]);
2486 Where
= FindSubString(L
" ", FileName
);
2488 FileName
[Where
] = L
'\0';
2490 if (FileExists(SelfDir
, FileName
)) {
2491 GlobalConfig
.ConfigFilename
= FileName
;
2493 Print(L
"Specified configuration file (%s) doesn't exist; using\n'refind.conf' default\n", FileName
);
2494 MyFreePool(FileName
);
2498 } // VOID SetConfigFilename()
2505 efi_main (EFI_HANDLE ImageHandle
, EFI_SYSTEM_TABLE
*SystemTable
)
2508 BOOLEAN MainLoopRunning
= TRUE
;
2509 BOOLEAN MokProtocol
;
2510 REFIT_MENU_ENTRY
*ChosenEntry
;
2512 CHAR16
*SelectionName
= NULL
;
2516 InitializeLib(ImageHandle
, SystemTable
);
2517 Status
= InitRefitLib(ImageHandle
);
2518 if (EFI_ERROR(Status
))
2521 // read configuration
2522 CopyMem(GlobalConfig
.ScanFor
, "ieom ", NUM_SCAN_OPTIONS
);
2523 FindLegacyBootType();
2524 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
)
2525 CopyMem(GlobalConfig
.ScanFor
, "ihebocm ", NUM_SCAN_OPTIONS
);
2526 SetConfigFilename(ImageHandle
);
2527 ReadConfig(GlobalConfig
.ConfigFilename
);
2530 WarnIfLegacyProblems();
2531 MainMenu
.TimeoutSeconds
= GlobalConfig
.Timeout
;
2533 // disable EFI watchdog timer
2534 refit_call4_wrapper(BS
->SetWatchdogTimer
, 0x0000, 0x0000, 0x0000, NULL
);
2536 // further bootstrap (now with config available)
2537 MokProtocol
= SecureBootSetup();
2540 ScanForBootloaders();
2544 if (GlobalConfig
.ScanDelay
> 0) {
2549 if (GlobalConfig
.ScanDelay
> 1)
2550 egDisplayMessage(L
"Pausing before disk scan; please wait....", &BGColor
);
2551 for (i
= 0; i
< GlobalConfig
.ScanDelay
; i
++)
2552 refit_call1_wrapper(BS
->Stall
, 1000000);
2553 RescanAll(GlobalConfig
.ScanDelay
> 1);
2556 if (GlobalConfig
.DefaultSelection
)
2557 SelectionName
= StrDuplicate(GlobalConfig
.DefaultSelection
);
2559 while (MainLoopRunning
) {
2560 MenuExit
= RunMainMenu(&MainMenu
, &SelectionName
, &ChosenEntry
);
2562 // The Escape key triggers a re-scan operation....
2563 if (MenuExit
== MENU_EXIT_ESCAPE
) {
2569 switch (ChosenEntry
->Tag
) {
2571 case TAG_REBOOT
: // Reboot
2573 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);
2574 MainLoopRunning
= FALSE
; // just in case we get this far
2577 case TAG_SHUTDOWN
: // Shut Down
2579 refit_call4_wrapper(RT
->ResetSystem
, EfiResetShutdown
, EFI_SUCCESS
, 0, NULL
);
2580 MainLoopRunning
= FALSE
; // just in case we get this far
2583 case TAG_ABOUT
: // About rEFInd
2587 case TAG_LOADER
: // Boot OS via .EFI loader
2588 StartLoader((LOADER_ENTRY
*)ChosenEntry
, SelectionName
);
2591 case TAG_LEGACY
: // Boot legacy OS
2592 StartLegacy((LEGACY_ENTRY
*)ChosenEntry
, SelectionName
);
2595 case TAG_LEGACY_UEFI
: // Boot a legacy OS on a non-Mac
2596 StartLegacyUEFI((LEGACY_ENTRY
*)ChosenEntry
, SelectionName
);
2599 case TAG_TOOL
: // Start a EFI tool
2600 StartTool((LOADER_ENTRY
*)ChosenEntry
);
2603 case TAG_EXIT
: // Terminate rEFInd
2604 if ((MokProtocol
) && !SecureBootUninstall()) {
2605 MainLoopRunning
= FALSE
; // just in case we get this far
2607 BeginTextScreen(L
" ");
2612 case TAG_FIRMWARE
: // Reboot into firmware's user interface
2613 RebootIntoFirmware();
2619 // If we end up here, things have gone wrong. Try to reboot, and if that
2620 // fails, go into an endless loop.
2621 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);