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.1.3");
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 static VOID
StoreLoaderName(IN CHAR16
*Name
) {
416 CHAR16
*OldName
= NULL
;
420 Status
= EfivarGetRaw(&RefindGuid
, L
"PreviousBoot", (CHAR8
**) &OldName
, &Length
);
421 if ((Status
!= EFI_SUCCESS
) || (StrCmp(OldName
, Name
) != 0)) {
422 EfivarSetRaw(&RefindGuid
, L
"PreviousBoot", (CHAR8
*) Name
, StrLen(Name
) * 2 + 2, TRUE
);
426 } // VOID StorePreviousLoader()
429 // EFI OS loader functions
432 static VOID
StartLoader(LOADER_ENTRY
*Entry
)
434 UINTN ErrorInStep
= 0;
436 BeginExternalScreen(Entry
->UseGraphicsMode
, L
"Booting OS");
437 StoreLoaderName(Entry
->me
.Title
);
438 StartEFIImage(Entry
->DevicePath
, Entry
->LoadOptions
, TYPE_EFI
,
439 Basename(Entry
->LoaderPath
), Entry
->OSType
, &ErrorInStep
, !Entry
->UseGraphicsMode
);
440 FinishExternalScreen();
443 // Locate an initrd or initramfs file that matches the kernel specified by LoaderPath.
444 // The matching file has a name that begins with "init" and includes the same version
445 // number string as is found in LoaderPath -- but not a longer version number string.
446 // For instance, if LoaderPath is \EFI\kernels\bzImage-3.3.0.efi, and if \EFI\kernels
447 // has a file called initramfs-3.3.0.img, this function will return the string
448 // '\EFI\kernels\initramfs-3.3.0.img'. If the directory ALSO contains the file
449 // initramfs-3.3.0-rc7.img or initramfs-13.3.0.img, those files will NOT match;
450 // however, initmine-3.3.0.img might match. (FindInitrd() returns the first match it
451 // finds). Thus, care should be taken to avoid placing duplicate matching files in
452 // the kernel's directory.
453 // If no matching init file can be found, returns NULL.
454 static CHAR16
* FindInitrd(IN CHAR16
*LoaderPath
, IN REFIT_VOLUME
*Volume
) {
455 CHAR16
*InitrdName
= NULL
, *FileName
, *KernelVersion
, *InitrdVersion
, *Path
;
456 REFIT_DIR_ITER DirIter
;
457 EFI_FILE_INFO
*DirEntry
;
459 FileName
= Basename(LoaderPath
);
460 KernelVersion
= FindNumbers(FileName
);
461 Path
= FindPath(LoaderPath
);
463 // Add trailing backslash for root directory; necessary on some systems, but must
464 // NOT be added to all directories, since on other systems, a trailing backslash on
465 // anything but the root directory causes them to flake out!
466 if (StrLen(Path
) == 0) {
467 MergeStrings(&Path
, L
"\\", 0);
469 DirIterOpen(Volume
->RootDir
, Path
, &DirIter
);
470 // Now add a trailing backslash if it was NOT added earlier, for consistency in
471 // building the InitrdName later....
472 if ((StrLen(Path
) > 0) && (Path
[StrLen(Path
) - 1] != L
'\\'))
473 MergeStrings(&Path
, L
"\\", 0);
474 while ((DirIterNext(&DirIter
, 2, L
"init*", &DirEntry
)) && (InitrdName
== NULL
)) {
475 InitrdVersion
= FindNumbers(DirEntry
->FileName
);
476 if (KernelVersion
!= NULL
) {
477 if (StriCmp(InitrdVersion
, KernelVersion
) == 0) {
478 InitrdName
= PoolPrint(L
"%s%s", Path
, DirEntry
->FileName
);
481 if (InitrdVersion
== NULL
) {
482 InitrdName
= PoolPrint(L
"%s%s", Path
, DirEntry
->FileName
);
485 MyFreePool(InitrdVersion
);
487 DirIterClose(&DirIter
);
489 // Note: Don't FreePool(FileName), since Basename returns a pointer WITHIN the string it's passed.
490 MyFreePool(KernelVersion
);
493 } // static CHAR16 * FindInitrd()
495 LOADER_ENTRY
* AddPreparedLoaderEntry(LOADER_ENTRY
*Entry
) {
496 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
499 } // LOADER_ENTRY * AddPreparedLoaderEntry()
501 // Creates a copy of a menu screen.
502 // Returns a pointer to the copy of the menu screen.
503 static REFIT_MENU_SCREEN
* CopyMenuScreen(REFIT_MENU_SCREEN
*Entry
) {
504 REFIT_MENU_SCREEN
*NewEntry
;
507 NewEntry
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
508 if ((Entry
!= NULL
) && (NewEntry
!= NULL
)) {
509 CopyMem(NewEntry
, Entry
, sizeof(REFIT_MENU_SCREEN
));
510 NewEntry
->Title
= (Entry
->Title
) ? StrDuplicate(Entry
->Title
) : NULL
;
511 NewEntry
->TimeoutText
= (Entry
->TimeoutText
) ? StrDuplicate(Entry
->TimeoutText
) : NULL
;
512 if (Entry
->TitleImage
!= NULL
) {
513 NewEntry
->TitleImage
= AllocatePool(sizeof(EG_IMAGE
));
514 if (NewEntry
->TitleImage
!= NULL
)
515 CopyMem(NewEntry
->TitleImage
, Entry
->TitleImage
, sizeof(EG_IMAGE
));
517 NewEntry
->InfoLines
= (CHAR16
**) AllocateZeroPool(Entry
->InfoLineCount
* (sizeof(CHAR16
*)));
518 for (i
= 0; i
< Entry
->InfoLineCount
&& NewEntry
->InfoLines
; i
++) {
519 NewEntry
->InfoLines
[i
] = (Entry
->InfoLines
[i
]) ? StrDuplicate(Entry
->InfoLines
[i
]) : NULL
;
521 NewEntry
->Entries
= (REFIT_MENU_ENTRY
**) AllocateZeroPool(Entry
->EntryCount
* (sizeof (REFIT_MENU_ENTRY
*)));
522 for (i
= 0; i
< Entry
->EntryCount
&& NewEntry
->Entries
; i
++) {
523 AddMenuEntry(NewEntry
, Entry
->Entries
[i
]);
525 NewEntry
->Hint1
= (Entry
->Hint1
) ? StrDuplicate(Entry
->Hint1
) : NULL
;
526 NewEntry
->Hint2
= (Entry
->Hint2
) ? StrDuplicate(Entry
->Hint2
) : NULL
;
529 } // static REFIT_MENU_SCREEN* CopyMenuScreen()
531 // Creates a copy of a menu entry. Intended to enable moving a stack-based
532 // menu entry (such as the ones for the "reboot" and "exit" functions) to
533 // to the heap. This enables easier deletion of the whole set of menu
534 // entries when re-scanning.
535 // Returns a pointer to the copy of the menu entry.
536 static REFIT_MENU_ENTRY
* CopyMenuEntry(REFIT_MENU_ENTRY
*Entry
) {
537 REFIT_MENU_ENTRY
*NewEntry
;
539 NewEntry
= AllocateZeroPool(sizeof(REFIT_MENU_ENTRY
));
540 if ((Entry
!= NULL
) && (NewEntry
!= NULL
)) {
541 CopyMem(NewEntry
, Entry
, sizeof(REFIT_MENU_ENTRY
));
542 NewEntry
->Title
= (Entry
->Title
) ? StrDuplicate(Entry
->Title
) : NULL
;
543 if (Entry
->BadgeImage
!= NULL
) {
544 NewEntry
->BadgeImage
= AllocatePool(sizeof(EG_IMAGE
));
545 if (NewEntry
->BadgeImage
!= NULL
)
546 CopyMem(NewEntry
->BadgeImage
, Entry
->BadgeImage
, sizeof(EG_IMAGE
));
548 if (Entry
->Image
!= NULL
) {
549 NewEntry
->Image
= AllocatePool(sizeof(EG_IMAGE
));
550 if (NewEntry
->Image
!= NULL
)
551 CopyMem(NewEntry
->Image
, Entry
->Image
, sizeof(EG_IMAGE
));
553 if (Entry
->SubScreen
!= NULL
) {
554 NewEntry
->SubScreen
= CopyMenuScreen(Entry
->SubScreen
);
558 } // REFIT_MENU_ENTRY* CopyMenuEntry()
560 // Creates a new LOADER_ENTRY data structure and populates it with
561 // default values from the specified Entry, or NULL values if Entry
562 // is unspecified (NULL).
563 // Returns a pointer to the new data structure, or NULL if it
564 // couldn't be allocated
565 LOADER_ENTRY
*InitializeLoaderEntry(IN LOADER_ENTRY
*Entry
) {
566 LOADER_ENTRY
*NewEntry
= NULL
;
568 NewEntry
= AllocateZeroPool(sizeof(LOADER_ENTRY
));
569 if (NewEntry
!= NULL
) {
570 NewEntry
->me
.Title
= NULL
;
571 NewEntry
->me
.Tag
= TAG_LOADER
;
572 NewEntry
->Enabled
= TRUE
;
573 NewEntry
->UseGraphicsMode
= FALSE
;
574 NewEntry
->OSType
= 0;
576 NewEntry
->LoaderPath
= (Entry
->LoaderPath
) ? StrDuplicate(Entry
->LoaderPath
) : NULL
;
577 NewEntry
->VolName
= (Entry
->VolName
) ? StrDuplicate(Entry
->VolName
) : NULL
;
578 NewEntry
->DevicePath
= Entry
->DevicePath
;
579 NewEntry
->UseGraphicsMode
= Entry
->UseGraphicsMode
;
580 NewEntry
->LoadOptions
= (Entry
->LoadOptions
) ? StrDuplicate(Entry
->LoadOptions
) : NULL
;
581 NewEntry
->InitrdPath
= (Entry
->InitrdPath
) ? StrDuplicate(Entry
->InitrdPath
) : NULL
;
585 } // LOADER_ENTRY *InitializeLoaderEntry()
587 // Adds InitrdPath to Options, but only if Options doesn't already include an
588 // initrd= line. Done to enable overriding the default initrd selection in a
589 // refind_linux.conf file's options list.
590 // Returns a pointer to a new string. The calling function is responsible for
591 // freeing its memory.
592 static CHAR16
*AddInitrdToOptions(CHAR16
*Options
, CHAR16
*InitrdPath
) {
593 CHAR16
*NewOptions
= NULL
;
596 NewOptions
= StrDuplicate(Options
);
597 if ((InitrdPath
!= NULL
) && !StriSubCmp(L
"initrd=", Options
)) {
598 MergeStrings(&NewOptions
, L
"initrd=", L
' ');
599 MergeStrings(&NewOptions
, InitrdPath
, 0);
602 } // CHAR16 *AddInitrdToOptions()
604 // Prepare a REFIT_MENU_SCREEN data structure for a subscreen entry. This sets up
605 // the default entry that launches the boot loader using the same options as the
606 // main Entry does. Subsequent options can be added by the calling function.
607 // If a subscreen already exists in the Entry that's passed to this function,
608 // it's left unchanged and a pointer to it is returned.
609 // Returns a pointer to the new subscreen data structure, or NULL if there
610 // were problems allocating memory.
611 REFIT_MENU_SCREEN
*InitializeSubScreen(IN LOADER_ENTRY
*Entry
) {
612 CHAR16
*FileName
, *MainOptions
= NULL
;
613 REFIT_MENU_SCREEN
*SubScreen
= NULL
;
614 LOADER_ENTRY
*SubEntry
;
616 FileName
= Basename(Entry
->LoaderPath
);
617 if (Entry
->me
.SubScreen
== NULL
) { // No subscreen yet; initialize default entry....
618 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
619 if (SubScreen
!= NULL
) {
620 SubScreen
->Title
= AllocateZeroPool(sizeof(CHAR16
) * 256);
621 SPrint(SubScreen
->Title
, 255, L
"Boot Options for %s on %s",
622 (Entry
->Title
!= NULL
) ? Entry
->Title
: FileName
, Entry
->VolName
);
623 SubScreen
->TitleImage
= Entry
->me
.Image
;
625 SubEntry
= InitializeLoaderEntry(Entry
);
626 if (SubEntry
!= NULL
) {
627 SubEntry
->me
.Title
= StrDuplicate(L
"Boot using default options");
628 MainOptions
= SubEntry
->LoadOptions
;
629 SubEntry
->LoadOptions
= AddInitrdToOptions(MainOptions
, SubEntry
->InitrdPath
);
630 MyFreePool(MainOptions
);
631 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
632 } // if (SubEntry != NULL)
633 SubScreen
->Hint1
= StrDuplicate(SUBSCREEN_HINT1
);
634 if (GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_EDITOR
) {
635 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2_NO_EDITOR
);
637 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2
);
639 } // if (SubScreen != NULL)
640 } else { // existing subscreen; less initialization, and just add new entry later....
641 SubScreen
= Entry
->me
.SubScreen
;
644 } // REFIT_MENU_SCREEN *InitializeSubScreen()
646 VOID
GenerateSubScreen(LOADER_ENTRY
*Entry
, IN REFIT_VOLUME
*Volume
) {
647 REFIT_MENU_SCREEN
*SubScreen
;
648 LOADER_ENTRY
*SubEntry
;
650 CHAR16 DiagsFileName
[256];
655 // create the submenu
656 if (StrLen(Entry
->Title
) == 0) {
657 MyFreePool(Entry
->Title
);
660 SubScreen
= InitializeSubScreen(Entry
);
662 // loader-specific submenu entries
663 if (Entry
->OSType
== 'M') { // entries for Mac OS X
665 SubEntry
= InitializeLoaderEntry(Entry
);
666 if (SubEntry
!= NULL
) {
667 SubEntry
->me
.Title
= L
"Boot Mac OS X with a 64-bit kernel";
668 SubEntry
->LoadOptions
= L
"arch=x86_64";
669 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
670 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
673 SubEntry
= InitializeLoaderEntry(Entry
);
674 if (SubEntry
!= NULL
) {
675 SubEntry
->me
.Title
= L
"Boot Mac OS X with a 32-bit kernel";
676 SubEntry
->LoadOptions
= L
"arch=i386";
677 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
678 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
682 if (!(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_SINGLEUSER
)) {
683 SubEntry
= InitializeLoaderEntry(Entry
);
684 if (SubEntry
!= NULL
) {
685 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode";
686 SubEntry
->UseGraphicsMode
= FALSE
;
687 SubEntry
->LoadOptions
= L
"-v";
688 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
692 SubEntry
= InitializeLoaderEntry(Entry
);
693 if (SubEntry
!= NULL
) {
694 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode (64-bit)";
695 SubEntry
->UseGraphicsMode
= FALSE
;
696 SubEntry
->LoadOptions
= L
"-v arch=x86_64";
697 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
700 SubEntry
= InitializeLoaderEntry(Entry
);
701 if (SubEntry
!= NULL
) {
702 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode (32-bit)";
703 SubEntry
->UseGraphicsMode
= FALSE
;
704 SubEntry
->LoadOptions
= L
"-v arch=i386";
705 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
709 SubEntry
= InitializeLoaderEntry(Entry
);
710 if (SubEntry
!= NULL
) {
711 SubEntry
->me
.Title
= L
"Boot Mac OS X in single user mode";
712 SubEntry
->UseGraphicsMode
= FALSE
;
713 SubEntry
->LoadOptions
= L
"-v -s";
714 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
716 } // single-user mode allowed
718 if (!(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_SAFEMODE
)) {
719 SubEntry
= InitializeLoaderEntry(Entry
);
720 if (SubEntry
!= NULL
) {
721 SubEntry
->me
.Title
= L
"Boot Mac OS X in safe mode";
722 SubEntry
->UseGraphicsMode
= FALSE
;
723 SubEntry
->LoadOptions
= L
"-v -x";
724 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
726 } // safe mode allowed
728 // check for Apple hardware diagnostics
729 StrCpy(DiagsFileName
, L
"System\\Library\\CoreServices\\.diagnostics\\diags.efi");
730 if (FileExists(Volume
->RootDir
, DiagsFileName
) && !(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_HWTEST
)) {
731 SubEntry
= InitializeLoaderEntry(Entry
);
732 if (SubEntry
!= NULL
) {
733 SubEntry
->me
.Title
= L
"Run Apple Hardware Test";
734 MyFreePool(SubEntry
->LoaderPath
);
735 SubEntry
->LoaderPath
= StrDuplicate(DiagsFileName
);
736 SubEntry
->DevicePath
= FileDevicePath(Volume
->DeviceHandle
, SubEntry
->LoaderPath
);
737 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
738 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
740 } // if diagnostics entry found
742 } else if (Entry
->OSType
== 'L') { // entries for Linux kernels with EFI stub loaders
743 File
= ReadLinuxOptionsFile(Entry
->LoaderPath
, Volume
);
745 InitrdName
= FindInitrd(Entry
->LoaderPath
, Volume
);
746 TokenCount
= ReadTokenLine(File
, &TokenList
);
747 // first entry requires special processing, since it was initially set
748 // up with a default title but correct options by InitializeSubScreen(),
750 if ((SubScreen
->Entries
!= NULL
) && (SubScreen
->Entries
[0] != NULL
)) {
751 MyFreePool(SubScreen
->Entries
[0]->Title
);
752 SubScreen
->Entries
[0]->Title
= TokenList
[0] ? StrDuplicate(TokenList
[0]) : StrDuplicate(L
"Boot Linux");
754 FreeTokenLine(&TokenList
, &TokenCount
);
755 while ((TokenCount
= ReadTokenLine(File
, &TokenList
)) > 1) {
756 SubEntry
= InitializeLoaderEntry(Entry
);
757 SubEntry
->me
.Title
= TokenList
[0] ? StrDuplicate(TokenList
[0]) : StrDuplicate(L
"Boot Linux");
758 MyFreePool(SubEntry
->LoadOptions
);
759 SubEntry
->LoadOptions
= AddInitrdToOptions(TokenList
[1], InitrdName
);
760 FreeTokenLine(&TokenList
, &TokenCount
);
761 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_LINUX
;
762 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
764 MyFreePool(InitrdName
);
768 } else if (Entry
->OSType
== 'E') { // entries for ELILO
769 SubEntry
= InitializeLoaderEntry(Entry
);
770 if (SubEntry
!= NULL
) {
771 SubEntry
->me
.Title
= L
"Run ELILO in interactive mode";
772 SubEntry
->LoadOptions
= L
"-p";
773 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
774 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
777 SubEntry
= InitializeLoaderEntry(Entry
);
778 if (SubEntry
!= NULL
) {
779 SubEntry
->me
.Title
= L
"Boot Linux for a 17\" iMac or a 15\" MacBook Pro (*)";
780 SubEntry
->UseGraphicsMode
= TRUE
;
781 SubEntry
->LoadOptions
= L
"-d 0 i17";
782 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
783 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
786 SubEntry
= InitializeLoaderEntry(Entry
);
787 if (SubEntry
!= NULL
) {
788 SubEntry
->me
.Title
= L
"Boot Linux for a 20\" iMac (*)";
789 SubEntry
->UseGraphicsMode
= TRUE
;
790 SubEntry
->LoadOptions
= L
"-d 0 i20";
791 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
792 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
795 SubEntry
= InitializeLoaderEntry(Entry
);
796 if (SubEntry
!= NULL
) {
797 SubEntry
->me
.Title
= L
"Boot Linux for a Mac Mini (*)";
798 SubEntry
->UseGraphicsMode
= TRUE
;
799 SubEntry
->LoadOptions
= L
"-d 0 mini";
800 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
801 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
804 AddMenuInfoLine(SubScreen
, L
"NOTE: This is an example. Entries");
805 AddMenuInfoLine(SubScreen
, L
"marked with (*) may not work.");
807 } else if (Entry
->OSType
== 'X') { // entries for xom.efi
808 // by default, skip the built-in selection and boot from hard disk only
809 Entry
->LoadOptions
= L
"-s -h";
811 SubEntry
= InitializeLoaderEntry(Entry
);
812 if (SubEntry
!= NULL
) {
813 SubEntry
->me
.Title
= L
"Boot Windows from Hard Disk";
814 SubEntry
->LoadOptions
= L
"-s -h";
815 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
816 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
819 SubEntry
= InitializeLoaderEntry(Entry
);
820 if (SubEntry
!= NULL
) {
821 SubEntry
->me
.Title
= L
"Boot Windows from CD-ROM";
822 SubEntry
->LoadOptions
= L
"-s -c";
823 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
824 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
827 SubEntry
= InitializeLoaderEntry(Entry
);
828 if (SubEntry
!= NULL
) {
829 SubEntry
->me
.Title
= L
"Run XOM in text mode";
830 SubEntry
->UseGraphicsMode
= FALSE
;
831 SubEntry
->LoadOptions
= L
"-v";
832 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
833 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
835 } // entries for xom.efi
836 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
837 Entry
->me
.SubScreen
= SubScreen
;
838 } // VOID GenerateSubScreen()
840 // Returns options for a Linux kernel. Reads them from an options file in the
841 // kernel's directory; and if present, adds an initrd= option for an initial
842 // RAM disk file with the same version number as the kernel file.
843 static CHAR16
* GetMainLinuxOptions(IN CHAR16
* LoaderPath
, IN REFIT_VOLUME
*Volume
) {
844 CHAR16
*Options
= NULL
, *InitrdName
, *FullOptions
= NULL
;
846 Options
= GetFirstOptionsFromFile(LoaderPath
, Volume
);
847 InitrdName
= FindInitrd(LoaderPath
, Volume
);
848 FullOptions
= AddInitrdToOptions(Options
, InitrdName
);
851 MyFreePool(InitrdName
);
852 return (FullOptions
);
853 } // static CHAR16 * GetMainLinuxOptions()
855 // Try to guess the name of the Linux distribution & add that name to
857 static VOID
GuessLinuxDistribution(CHAR16
**OSIconName
, REFIT_VOLUME
*Volume
, CHAR16
*LoaderPath
) {
861 UINTN TokenCount
= 0;
863 // If on Linux root fs, /etc/os-release file probably has clues....
864 if (FileExists(Volume
->RootDir
, L
"etc\\os-release") &&
865 (ReadFile(Volume
->RootDir
, L
"etc\\os-release", &File
, &FileSize
) == EFI_SUCCESS
)) {
867 TokenCount
= ReadTokenLine(&File
, &TokenList
);
868 if ((TokenCount
> 1) && ((StriCmp(TokenList
[0], L
"ID") == 0) || (StriCmp(TokenList
[0], L
"NAME") == 0))) {
869 MergeStrings(OSIconName
, TokenList
[1], L
',');
871 FreeTokenLine(&TokenList
, &TokenCount
);
872 } while (TokenCount
> 0);
873 MyFreePool(File
.Buffer
);
876 // Search for clues in the kernel's filename....
877 if (StriSubCmp(L
".fc", LoaderPath
))
878 MergeStrings(OSIconName
, L
"fedora", L
',');
879 if (StriSubCmp(L
".el", LoaderPath
))
880 MergeStrings(OSIconName
, L
"redhat", L
',');
881 } // VOID GuessLinuxDistribution()
883 // Sets a few defaults for a loader entry -- mainly the icon, but also the OS type
884 // code and shortcut letter. For Linux EFI stub loaders, also sets kernel options
885 // that will (with luck) work fairly automatically.
886 VOID
SetLoaderDefaults(LOADER_ENTRY
*Entry
, CHAR16
*LoaderPath
, REFIT_VOLUME
*Volume
) {
887 CHAR16
*FileName
, *PathOnly
, *NoExtension
, *OSIconName
= NULL
, *Temp
, *SubString
;
888 CHAR16 ShortcutLetter
= 0;
891 FileName
= Basename(LoaderPath
);
892 PathOnly
= FindPath(LoaderPath
);
893 NoExtension
= StripEfiExtension(FileName
);
895 // locate a custom icon for the loader
896 // Anything found here takes precedence over the "hints" in the OSIconName variable
897 if (!Entry
->me
.Image
) {
898 Entry
->me
.Image
= egLoadIconAnyType(Volume
->RootDir
, PathOnly
, NoExtension
, GlobalConfig
.IconSizes
[ICON_SIZE_BIG
]);
900 if (!Entry
->me
.Image
) {
901 Entry
->me
.Image
= egCopyImage(Volume
->VolIconImage
);
904 // Begin creating icon "hints" by using last part of directory path leading
906 Temp
= FindLastDirName(LoaderPath
);
907 MergeStrings(&OSIconName
, Temp
, L
',');
910 if (OSIconName
!= NULL
) {
911 ShortcutLetter
= OSIconName
[0];
914 // Add every "word" in the volume label, delimited by spaces, dashes (-), or
915 // underscores (_), to the list of hints to be used in searching for OS
917 if ((Volume
->VolName
) && (StrLen(Volume
->VolName
) > 0)) {
918 Temp
= SubString
= StrDuplicate(Volume
->VolName
);
920 Length
= StrLen(Temp
);
921 for (i
= 0; i
< Length
; i
++) {
922 if ((Temp
[i
] == L
' ') || (Temp
[i
] == L
'_') || (Temp
[i
] == L
'-')) {
924 if (StrLen(SubString
) > 0)
925 MergeStrings(&OSIconName
, SubString
, L
',');
926 SubString
= Temp
+ i
+ 1;
929 MergeStrings(&OSIconName
, SubString
, L
',');
934 // detect specific loaders
935 if (StriSubCmp(L
"bzImage", FileName
) || StriSubCmp(L
"vmlinuz", FileName
)) {
936 GuessLinuxDistribution(&OSIconName
, Volume
, LoaderPath
);
937 MergeStrings(&OSIconName
, L
"linux", L
',');
939 if (ShortcutLetter
== 0)
940 ShortcutLetter
= 'L';
941 Entry
->LoadOptions
= GetMainLinuxOptions(LoaderPath
, Volume
);
942 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_LINUX
;
943 } else if (StriSubCmp(L
"refit", LoaderPath
)) {
944 MergeStrings(&OSIconName
, L
"refit", L
',');
946 ShortcutLetter
= 'R';
947 } else if (StriSubCmp(L
"refind", LoaderPath
)) {
948 MergeStrings(&OSIconName
, L
"refind", L
',');
950 ShortcutLetter
= 'R';
951 } else if (StriCmp(LoaderPath
, MACOSX_LOADER_PATH
) == 0) {
952 MergeStrings(&OSIconName
, L
"mac", L
',');
954 ShortcutLetter
= 'M';
955 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
956 } else if (StriCmp(FileName
, L
"diags.efi") == 0) {
957 MergeStrings(&OSIconName
, L
"hwtest", L
',');
958 } else if (StriCmp(FileName
, L
"e.efi") == 0 || StriCmp(FileName
, L
"elilo.efi") == 0 || StriSubCmp(L
"elilo", FileName
)) {
959 MergeStrings(&OSIconName
, L
"elilo,linux", L
',');
961 if (ShortcutLetter
== 0)
962 ShortcutLetter
= 'L';
963 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
964 } else if (StriSubCmp(L
"grub", FileName
)) {
966 ShortcutLetter
= 'G';
967 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_GRUB
;
968 } else if (StriCmp(FileName
, L
"cdboot.efi") == 0 ||
969 StriCmp(FileName
, L
"bootmgr.efi") == 0 ||
970 StriCmp(FileName
, L
"bootmgfw.efi") == 0 ||
971 StriCmp(FileName
, L
"bkpbootmgfw.efi") == 0) {
972 MergeStrings(&OSIconName
, L
"win", L
',');
974 ShortcutLetter
= 'W';
975 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
976 } else if (StriCmp(FileName
, L
"xom.efi") == 0) {
977 MergeStrings(&OSIconName
, L
"xom,win", L
',');
978 Entry
->UseGraphicsMode
= TRUE
;
980 ShortcutLetter
= 'W';
981 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
984 if ((ShortcutLetter
>= 'a') && (ShortcutLetter
<= 'z'))
985 ShortcutLetter
= ShortcutLetter
- 'a' + 'A'; // convert lowercase to uppercase
986 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
987 if (Entry
->me
.Image
== NULL
)
988 Entry
->me
.Image
= LoadOSIcon(OSIconName
, L
"unknown", FALSE
);
989 MyFreePool(PathOnly
);
990 } // VOID SetLoaderDefaults()
992 // Add a specified EFI boot loader to the list, using automatic settings
993 // for icons, options, etc.
994 LOADER_ENTRY
* AddLoaderEntry(IN CHAR16
*LoaderPath
, IN CHAR16
*LoaderTitle
, IN REFIT_VOLUME
*Volume
) {
997 CleanUpPathNameSlashes(LoaderPath
);
998 Entry
= InitializeLoaderEntry(NULL
);
1000 Entry
->Title
= StrDuplicate((LoaderTitle
!= NULL
) ? LoaderTitle
: LoaderPath
);
1001 Entry
->me
.Title
= AllocateZeroPool(sizeof(CHAR16
) * 256);
1002 // Extra space at end of Entry->me.Title enables searching on Volume->VolName even if another volume
1003 // name is identical except for something added to the end (e.g., VolB1 vs. VolB12).
1004 SPrint(Entry
->me
.Title
, 255, L
"Boot %s from %s ", (LoaderTitle
!= NULL
) ? LoaderTitle
: LoaderPath
, Volume
->VolName
);
1006 Entry
->me
.BadgeImage
= Volume
->VolBadgeImage
;
1007 if ((LoaderPath
!= NULL
) && (LoaderPath
[0] != L
'\\')) {
1008 Entry
->LoaderPath
= StrDuplicate(L
"\\");
1010 Entry
->LoaderPath
= NULL
;
1012 MergeStrings(&(Entry
->LoaderPath
), LoaderPath
, 0);
1013 Entry
->VolName
= Volume
->VolName
;
1014 Entry
->DevicePath
= FileDevicePath(Volume
->DeviceHandle
, Entry
->LoaderPath
);
1015 SetLoaderDefaults(Entry
, LoaderPath
, Volume
);
1016 GenerateSubScreen(Entry
, Volume
);
1017 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1021 } // LOADER_ENTRY * AddLoaderEntry()
1023 // Returns -1 if (Time1 < Time2), +1 if (Time1 > Time2), or 0 if
1024 // (Time1 == Time2). Precision is only to the nearest second; since
1025 // this is used for sorting boot loader entries, differences smaller
1026 // than this are likely to be meaningless (and unlikely!).
1027 INTN
TimeComp(IN EFI_TIME
*Time1
, IN EFI_TIME
*Time2
) {
1028 INT64 Time1InSeconds
, Time2InSeconds
;
1030 // Following values are overestimates; I'm assuming 31 days in every month.
1031 // This is fine for the purpose of this function, which is limited
1032 Time1InSeconds
= Time1
->Second
+ (Time1
->Minute
* 60) + (Time1
->Hour
* 3600) + (Time1
->Day
* 86400) +
1033 (Time1
->Month
* 2678400) + ((Time1
->Year
- 1998) * 32140800);
1034 Time2InSeconds
= Time2
->Second
+ (Time2
->Minute
* 60) + (Time2
->Hour
* 3600) + (Time2
->Day
* 86400) +
1035 (Time2
->Month
* 2678400) + ((Time2
->Year
- 1998) * 32140800);
1036 if (Time1InSeconds
< Time2InSeconds
)
1038 else if (Time1InSeconds
> Time2InSeconds
)
1042 } // INTN TimeComp()
1044 // Adds a loader list element, keeping it sorted by date. Returns the new
1045 // first element (the one with the most recent date).
1046 static struct LOADER_LIST
* AddLoaderListEntry(struct LOADER_LIST
*LoaderList
, struct LOADER_LIST
*NewEntry
) {
1047 struct LOADER_LIST
*LatestEntry
, *CurrentEntry
, *PrevEntry
= NULL
;
1049 LatestEntry
= CurrentEntry
= LoaderList
;
1050 if (LoaderList
== NULL
) {
1051 LatestEntry
= NewEntry
;
1053 while ((CurrentEntry
!= NULL
) && (TimeComp(&(NewEntry
->TimeStamp
), &(CurrentEntry
->TimeStamp
)) < 0)) {
1054 PrevEntry
= CurrentEntry
;
1055 CurrentEntry
= CurrentEntry
->NextEntry
;
1057 NewEntry
->NextEntry
= CurrentEntry
;
1058 if (PrevEntry
== NULL
) {
1059 LatestEntry
= NewEntry
;
1061 PrevEntry
->NextEntry
= NewEntry
;
1064 return (LatestEntry
);
1065 } // static VOID AddLoaderListEntry()
1067 // Delete the LOADER_LIST linked list
1068 static VOID
CleanUpLoaderList(struct LOADER_LIST
*LoaderList
) {
1069 struct LOADER_LIST
*Temp
;
1071 while (LoaderList
!= NULL
) {
1073 LoaderList
= LoaderList
->NextEntry
;
1074 MyFreePool(Temp
->FileName
);
1077 } // static VOID CleanUpLoaderList()
1079 // Returns FALSE if the specified file/volume matches the GlobalConfig.DontScanDirs
1080 // or GlobalConfig.DontScanVolumes specification, or if Path points to a volume
1081 // other than the one specified by Volume, or if the specified path is SelfDir.
1082 // Returns TRUE if none of these conditions is met -- that is, if the path is
1083 // eligible for scanning.
1084 static BOOLEAN
ShouldScan(REFIT_VOLUME
*Volume
, CHAR16
*Path
) {
1085 CHAR16
*VolName
= NULL
, *DontScanDir
, *PathCopy
= NULL
;
1087 BOOLEAN ScanIt
= TRUE
;
1089 if ((IsIn(Volume
->VolName
, GlobalConfig
.DontScanVolumes
)) || (IsIn(Volume
->PartName
, GlobalConfig
.DontScanVolumes
)))
1092 if ((StriCmp(Path
, SelfDirPath
) == 0) && (Volume
->DeviceHandle
== SelfVolume
->DeviceHandle
))
1095 // See if Path includes an explicit volume declaration that's NOT Volume....
1096 PathCopy
= StrDuplicate(Path
);
1097 if (SplitVolumeAndFilename(&PathCopy
, &VolName
)) {
1098 VolumeNumberToName(Volume
, &VolName
);
1099 if (VolName
&& StriCmp(VolName
, Volume
->VolName
) != 0) {
1102 } // if Path includes volume specification
1103 MyFreePool(PathCopy
);
1104 MyFreePool(VolName
);
1107 // See if Volume is in GlobalConfig.DontScanDirs....
1108 while (ScanIt
&& (DontScanDir
= FindCommaDelimited(GlobalConfig
.DontScanDirs
, i
++))) {
1109 SplitVolumeAndFilename(&DontScanDir
, &VolName
);
1110 CleanUpPathNameSlashes(DontScanDir
);
1111 VolumeNumberToName(Volume
, &VolName
);
1112 if (VolName
!= NULL
) {
1113 if ((StriCmp(VolName
, Volume
->VolName
) == 0) && (StriCmp(DontScanDir
, Path
) == 0))
1116 if (StriCmp(DontScanDir
, Path
) == 0)
1119 MyFreePool(DontScanDir
);
1120 MyFreePool(VolName
);
1126 } // BOOLEAN ShouldScan()
1128 // Returns TRUE if the file is byte-for-byte identical with the fallback file
1129 // on the volume AND if the file is not itself the fallback file; returns
1130 // FALSE if the file is not identical to the fallback file OR if the file
1131 // IS the fallback file. Intended for use in excluding the fallback boot
1132 // loader when it's a duplicate of another boot loader.
1133 static BOOLEAN
DuplicatesFallback(IN REFIT_VOLUME
*Volume
, IN CHAR16
*FileName
) {
1134 CHAR8
*FileContents
, *FallbackContents
;
1135 EFI_FILE_HANDLE FileHandle
, FallbackHandle
;
1136 EFI_FILE_INFO
*FileInfo
, *FallbackInfo
;
1137 UINTN FileSize
= 0, FallbackSize
= 0;
1139 BOOLEAN AreIdentical
= FALSE
;
1141 if (!FileExists(Volume
->RootDir
, FileName
) || !FileExists(Volume
->RootDir
, FALLBACK_FULLNAME
))
1144 CleanUpPathNameSlashes(FileName
);
1146 if (StriCmp(FileName
, FALLBACK_FULLNAME
) == 0)
1147 return FALSE
; // identical filenames, so not a duplicate....
1149 Status
= refit_call5_wrapper(Volume
->RootDir
->Open
, Volume
->RootDir
, &FileHandle
, FileName
, EFI_FILE_MODE_READ
, 0);
1150 if (Status
== EFI_SUCCESS
) {
1151 FileInfo
= LibFileInfo(FileHandle
);
1152 FileSize
= FileInfo
->FileSize
;
1157 Status
= refit_call5_wrapper(Volume
->RootDir
->Open
, Volume
->RootDir
, &FallbackHandle
, FALLBACK_FULLNAME
, EFI_FILE_MODE_READ
, 0);
1158 if (Status
== EFI_SUCCESS
) {
1159 FallbackInfo
= LibFileInfo(FallbackHandle
);
1160 FallbackSize
= FallbackInfo
->FileSize
;
1162 refit_call1_wrapper(FileHandle
->Close
, FileHandle
);
1166 if (FallbackSize
!= FileSize
) { // not same size, so can't be identical
1167 AreIdentical
= FALSE
;
1168 } else { // could be identical; do full check....
1169 FileContents
= AllocatePool(FileSize
);
1170 FallbackContents
= AllocatePool(FallbackSize
);
1171 if (FileContents
&& FallbackContents
) {
1172 Status
= refit_call3_wrapper(FileHandle
->Read
, FileHandle
, &FileSize
, FileContents
);
1173 if (Status
== EFI_SUCCESS
) {
1174 Status
= refit_call3_wrapper(FallbackHandle
->Read
, FallbackHandle
, &FallbackSize
, FallbackContents
);
1176 if (Status
== EFI_SUCCESS
) {
1177 AreIdentical
= (CompareMem(FileContents
, FallbackContents
, FileSize
) == 0);
1180 MyFreePool(FileContents
);
1181 MyFreePool(FallbackContents
);
1184 // BUG ALERT: Some systems (e.g., DUET, some Macs with large displays) crash if the
1185 // following two calls are reversed. Go figure....
1186 refit_call1_wrapper(FileHandle
->Close
, FallbackHandle
);
1187 refit_call1_wrapper(FileHandle
->Close
, FileHandle
);
1188 return AreIdentical
;
1189 } // BOOLEAN DuplicatesFallback()
1191 // Returns FALSE if two measures of file size are identical for a single file,
1192 // TRUE if not or if the file can't be opened and the other measure is non-0.
1193 // Despite the function's name, this isn't really a direct test of symbolic
1194 // link status, since EFI doesn't officially support symlinks. It does seem
1195 // to be a reliable indicator, though. (OTOH, some disk errors might cause a
1196 // file to fail to open, which would return a false positive -- but as I use
1197 // this function to exclude symbolic links from the list of boot loaders,
1198 // that would be fine, since such boot loaders wouldn't work.)
1199 static BOOLEAN
IsSymbolicLink(REFIT_VOLUME
*Volume
, CHAR16
*Path
, EFI_FILE_INFO
*DirEntry
) {
1200 EFI_FILE_HANDLE FileHandle
;
1201 EFI_FILE_INFO
*FileInfo
= NULL
;
1203 UINTN FileSize2
= 0;
1206 FileName
= StrDuplicate(Path
);
1207 MergeStrings(&FileName
, DirEntry
->FileName
, L
'\\');
1208 CleanUpPathNameSlashes(FileName
);
1210 Status
= refit_call5_wrapper(Volume
->RootDir
->Open
, Volume
->RootDir
, &FileHandle
, FileName
, EFI_FILE_MODE_READ
, 0);
1211 if (Status
== EFI_SUCCESS
) {
1212 FileInfo
= LibFileInfo(FileHandle
);
1213 if (FileInfo
!= NULL
)
1214 FileSize2
= FileInfo
->FileSize
;
1217 MyFreePool(FileName
);
1218 MyFreePool(FileInfo
);
1220 return (DirEntry
->FileSize
!= FileSize2
);
1221 } // BOOLEAN IsSymbolicLink()
1223 // Returns TRUE if a file with the same name as the original but with
1224 // ".efi.signed" is also present in the same directory. Ubuntu is using
1225 // this filename as a signed version of the original unsigned kernel, and
1226 // there's no point in cluttering the display with two kernels that will
1227 // behave identically on non-SB systems, or when one will fail when SB
1229 static BOOLEAN
HasSignedCounterpart(IN REFIT_VOLUME
*Volume
, IN CHAR16
*Path
, IN CHAR16
*Filename
) {
1230 CHAR16
*NewFile
= NULL
;
1231 BOOLEAN retval
= FALSE
;
1233 MergeStrings(&NewFile
, Path
, 0);
1234 MergeStrings(&NewFile
, Filename
, L
'\\');
1235 MergeStrings(&NewFile
, L
".efi.signed", 0);
1236 if (NewFile
!= NULL
) {
1237 CleanUpPathNameSlashes(NewFile
);
1238 if (FileExists(Volume
->RootDir
, NewFile
))
1240 MyFreePool(NewFile
);
1244 } // BOOLEAN HasSignedCounterpart()
1246 // Scan an individual directory for EFI boot loader files and, if found,
1247 // add them to the list. Exception: Ignores FALLBACK_FULLNAME, which is picked
1248 // up in ScanEfiFiles(). Sorts the entries within the loader directory so that
1249 // the most recent one appears first in the list.
1250 // Returns TRUE if a duplicate for FALLBACK_FILENAME was found, FALSE if not.
1251 static BOOLEAN
ScanLoaderDir(IN REFIT_VOLUME
*Volume
, IN CHAR16
*Path
, IN CHAR16
*Pattern
)
1254 REFIT_DIR_ITER DirIter
;
1255 EFI_FILE_INFO
*DirEntry
;
1256 CHAR16 FileName
[256], *Extension
;
1257 struct LOADER_LIST
*LoaderList
= NULL
, *NewLoader
;
1258 BOOLEAN FoundFallbackDuplicate
= FALSE
;
1260 if ((!SelfDirPath
|| !Path
|| ((StriCmp(Path
, SelfDirPath
) == 0) && (Volume
->DeviceHandle
!= SelfVolume
->DeviceHandle
)) ||
1261 (StriCmp(Path
, SelfDirPath
) != 0)) && (ShouldScan(Volume
, Path
))) {
1262 // look through contents of the directory
1263 DirIterOpen(Volume
->RootDir
, Path
, &DirIter
);
1264 while (DirIterNext(&DirIter
, 2, Pattern
, &DirEntry
)) {
1265 Extension
= FindExtension(DirEntry
->FileName
);
1266 if (DirEntry
->FileName
[0] == '.' ||
1267 StriCmp(Extension
, L
".icns") == 0 ||
1268 StriCmp(Extension
, L
".png") == 0 ||
1269 (StriCmp(DirEntry
->FileName
, FALLBACK_BASENAME
) == 0 && (StriCmp(Path
, L
"EFI\\BOOT") == 0)) ||
1270 StriSubCmp(L
"shell", DirEntry
->FileName
) ||
1271 IsSymbolicLink(Volume
, Path
, DirEntry
) || /* is symbolic link */
1272 HasSignedCounterpart(Volume
, Path
, DirEntry
->FileName
) || /* a file with same name plus ".efi.signed" is present */
1273 FilenameIn(Volume
, Path
, DirEntry
->FileName
, GlobalConfig
.DontScanFiles
))
1274 continue; // skip this
1277 SPrint(FileName
, 255, L
"\\%s\\%s", Path
, DirEntry
->FileName
);
1279 SPrint(FileName
, 255, L
"\\%s", DirEntry
->FileName
);
1280 CleanUpPathNameSlashes(FileName
);
1282 if(!IsValidLoader(Volume
->RootDir
, FileName
))
1285 NewLoader
= AllocateZeroPool(sizeof(struct LOADER_LIST
));
1286 if (NewLoader
!= NULL
) {
1287 NewLoader
->FileName
= StrDuplicate(FileName
);
1288 NewLoader
->TimeStamp
= DirEntry
->ModificationTime
;
1289 LoaderList
= AddLoaderListEntry(LoaderList
, NewLoader
);
1290 if (DuplicatesFallback(Volume
, FileName
))
1291 FoundFallbackDuplicate
= TRUE
;
1293 MyFreePool(Extension
);
1296 NewLoader
= LoaderList
;
1297 while (NewLoader
!= NULL
) {
1298 AddLoaderEntry(NewLoader
->FileName
, NULL
, Volume
);
1299 NewLoader
= NewLoader
->NextEntry
;
1302 CleanUpLoaderList(LoaderList
);
1303 Status
= DirIterClose(&DirIter
);
1304 // NOTE: EFI_INVALID_PARAMETER really is an error that should be reported;
1305 // but I've gotten reports from users who are getting this error occasionally
1306 // and I can't find anything wrong or reproduce the problem, so I'm putting
1307 // it down to buggy EFI implementations and ignoring that particular error....
1308 if ((Status
!= EFI_NOT_FOUND
) && (Status
!= EFI_INVALID_PARAMETER
)) {
1310 SPrint(FileName
, 255, L
"while scanning the %s directory", Path
);
1312 StrCpy(FileName
, L
"while scanning the root directory");
1313 CheckError(Status
, FileName
);
1314 } // if (Status != EFI_NOT_FOUND)
1315 } // if not scanning a blacklisted directory
1317 return FoundFallbackDuplicate
;
1318 } /* static VOID ScanLoaderDir() */
1320 static VOID
ScanEfiFiles(REFIT_VOLUME
*Volume
) {
1322 REFIT_DIR_ITER EfiDirIter
;
1323 EFI_FILE_INFO
*EfiDirEntry
;
1324 CHAR16 FileName
[256], *Directory
= NULL
, *MatchPatterns
, *VolName
= NULL
, *SelfPath
;
1326 BOOLEAN ScanFallbackLoader
= TRUE
;
1327 BOOLEAN FoundBRBackup
= FALSE
;
1329 if ((Volume
->RootDir
!= NULL
) && (Volume
->VolName
!= NULL
) && (Volume
->IsReadable
)) {
1330 MatchPatterns
= StrDuplicate(LOADER_MATCH_PATTERNS
);
1331 if (GlobalConfig
.ScanAllLinux
)
1332 MergeStrings(&MatchPatterns
, LINUX_MATCH_PATTERNS
, L
',');
1334 // check for Mac OS X boot loader
1335 if (ShouldScan(Volume
, L
"System\\Library\\CoreServices")) {
1336 StrCpy(FileName
, MACOSX_LOADER_PATH
);
1337 if (FileExists(Volume
->RootDir
, FileName
) && !FilenameIn(Volume
, Directory
, L
"boot.efi", GlobalConfig
.DontScanFiles
)) {
1338 AddLoaderEntry(FileName
, L
"Mac OS X", Volume
);
1339 if (DuplicatesFallback(Volume
, FileName
))
1340 ScanFallbackLoader
= FALSE
;
1344 StrCpy(FileName
, L
"System\\Library\\CoreServices\\xom.efi");
1345 if (FileExists(Volume
->RootDir
, FileName
) && !FilenameIn(Volume
, Directory
, L
"boot.efi", GlobalConfig
.DontScanFiles
)) {
1346 AddLoaderEntry(FileName
, L
"Windows XP (XoM)", Volume
);
1347 if (DuplicatesFallback(Volume
, FileName
))
1348 ScanFallbackLoader
= FALSE
;
1350 } // if should scan Mac directory
1352 // check for Microsoft boot loader/menu
1353 if (ShouldScan(Volume
, L
"EFI\\Microsoft\\Boot")) {
1354 StrCpy(FileName
, L
"EFI\\Microsoft\\Boot\\bkpbootmgfw.efi");
1355 if (FileExists(Volume
->RootDir
, FileName
) && !FilenameIn(Volume
, Directory
, L
"bkpbootmgfw.efi",
1356 GlobalConfig
.DontScanFiles
)) {
1357 AddLoaderEntry(FileName
, L
"Microsoft EFI boot (Boot Repair backup)", Volume
);
1358 FoundBRBackup
= TRUE
;
1359 if (DuplicatesFallback(Volume
, FileName
))
1360 ScanFallbackLoader
= FALSE
;
1362 StrCpy(FileName
, L
"EFI\\Microsoft\\Boot\\bootmgfw.efi");
1363 if (FileExists(Volume
->RootDir
, FileName
) && !FilenameIn(Volume
, Directory
, L
"bootmgfw.efi", GlobalConfig
.DontScanFiles
)) {
1365 AddLoaderEntry(FileName
, L
"Supposed Microsoft EFI boot (probably GRUB)", Volume
);
1367 AddLoaderEntry(FileName
, L
"Microsoft EFI boot", Volume
);
1368 if (DuplicatesFallback(Volume
, FileName
))
1369 ScanFallbackLoader
= FALSE
;
1373 // scan the root directory for EFI executables
1374 if (ScanLoaderDir(Volume
, L
"\\", MatchPatterns
))
1375 ScanFallbackLoader
= FALSE
;
1377 // scan subdirectories of the EFI directory (as per the standard)
1378 DirIterOpen(Volume
->RootDir
, L
"EFI", &EfiDirIter
);
1379 while (DirIterNext(&EfiDirIter
, 1, NULL
, &EfiDirEntry
)) {
1380 if (StriCmp(EfiDirEntry
->FileName
, L
"tools") == 0 || EfiDirEntry
->FileName
[0] == '.')
1381 continue; // skip this, doesn't contain boot loaders or is scanned later
1382 SPrint(FileName
, 255, L
"EFI\\%s", EfiDirEntry
->FileName
);
1383 if (ScanLoaderDir(Volume
, FileName
, MatchPatterns
))
1384 ScanFallbackLoader
= FALSE
;
1386 Status
= DirIterClose(&EfiDirIter
);
1387 if (Status
!= EFI_NOT_FOUND
)
1388 CheckError(Status
, L
"while scanning the EFI directory");
1390 // Scan user-specified (or additional default) directories....
1392 while ((Directory
= FindCommaDelimited(GlobalConfig
.AlsoScan
, i
++)) != NULL
) {
1393 if (ShouldScan(Volume
, Directory
)) {
1394 SplitVolumeAndFilename(&Directory
, &VolName
);
1395 CleanUpPathNameSlashes(Directory
);
1396 Length
= StrLen(Directory
);
1397 if ((Length
> 0) && ScanLoaderDir(Volume
, Directory
, MatchPatterns
))
1398 ScanFallbackLoader
= FALSE
;
1399 MyFreePool(VolName
);
1400 } // if should scan dir
1401 MyFreePool(Directory
);
1404 // Don't scan the fallback loader if it's on the same volume and a duplicate of rEFInd itself....
1405 SelfPath
= DevicePathToStr(SelfLoadedImage
->FilePath
);
1406 CleanUpPathNameSlashes(SelfPath
);
1407 if ((Volume
->DeviceHandle
== SelfLoadedImage
->DeviceHandle
) && DuplicatesFallback(Volume
, SelfPath
))
1408 ScanFallbackLoader
= FALSE
;
1410 // If not a duplicate & if it exists & if it's not us, create an entry
1411 // for the fallback boot loader
1412 if (ScanFallbackLoader
&& FileExists(Volume
->RootDir
, FALLBACK_FULLNAME
) && ShouldScan(Volume
, L
"EFI\\BOOT"))
1413 AddLoaderEntry(FALLBACK_FULLNAME
, L
"Fallback boot loader", Volume
);
1415 } // static VOID ScanEfiFiles()
1417 // Scan internal disks for valid EFI boot loaders....
1418 static VOID
ScanInternal(VOID
) {
1421 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1422 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_INTERNAL
) {
1423 ScanEfiFiles(Volumes
[VolumeIndex
]);
1426 } // static VOID ScanInternal()
1428 // Scan external disks for valid EFI boot loaders....
1429 static VOID
ScanExternal(VOID
) {
1432 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1433 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_EXTERNAL
) {
1434 ScanEfiFiles(Volumes
[VolumeIndex
]);
1437 } // static VOID ScanExternal()
1439 // Scan internal disks for valid EFI boot loaders....
1440 static VOID
ScanOptical(VOID
) {
1443 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1444 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_OPTICAL
) {
1445 ScanEfiFiles(Volumes
[VolumeIndex
]);
1448 } // static VOID ScanOptical()
1451 // legacy boot functions
1454 static EFI_STATUS
ActivateMbrPartition(IN EFI_BLOCK_IO
*BlockIO
, IN UINTN PartitionIndex
)
1457 UINT8 SectorBuffer
[512];
1458 MBR_PARTITION_INFO
*MbrTable
, *EMbrTable
;
1459 UINT32 ExtBase
, ExtCurrent
, NextExtCurrent
;
1460 UINTN LogicalPartitionIndex
= 4;
1462 BOOLEAN HaveBootCode
;
1465 Status
= refit_call5_wrapper(BlockIO
->ReadBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, 0, 512, SectorBuffer
);
1466 if (EFI_ERROR(Status
))
1468 if (*((UINT16
*)(SectorBuffer
+ 510)) != 0xaa55)
1469 return EFI_NOT_FOUND
; // safety measure #1
1471 // add boot code if necessary
1472 HaveBootCode
= FALSE
;
1473 for (i
= 0; i
< MBR_BOOTCODE_SIZE
; i
++) {
1474 if (SectorBuffer
[i
] != 0) {
1475 HaveBootCode
= TRUE
;
1479 if (!HaveBootCode
) {
1480 // no boot code found in the MBR, add the syslinux MBR code
1481 SetMem(SectorBuffer
, MBR_BOOTCODE_SIZE
, 0);
1482 CopyMem(SectorBuffer
, syslinux_mbr
, SYSLINUX_MBR_SIZE
);
1485 // set the partition active
1486 MbrTable
= (MBR_PARTITION_INFO
*)(SectorBuffer
+ 446);
1488 for (i
= 0; i
< 4; i
++) {
1489 if (MbrTable
[i
].Flags
!= 0x00 && MbrTable
[i
].Flags
!= 0x80)
1490 return EFI_NOT_FOUND
; // safety measure #2
1491 if (i
== PartitionIndex
)
1492 MbrTable
[i
].Flags
= 0x80;
1493 else if (PartitionIndex
>= 4 && IS_EXTENDED_PART_TYPE(MbrTable
[i
].Type
)) {
1494 MbrTable
[i
].Flags
= 0x80;
1495 ExtBase
= MbrTable
[i
].StartLBA
;
1497 MbrTable
[i
].Flags
= 0x00;
1501 Status
= refit_call5_wrapper(BlockIO
->WriteBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, 0, 512, SectorBuffer
);
1502 if (EFI_ERROR(Status
))
1505 if (PartitionIndex
>= 4) {
1506 // we have to activate a logical partition, so walk the EMBR chain
1508 // NOTE: ExtBase was set above while looking at the MBR table
1509 for (ExtCurrent
= ExtBase
; ExtCurrent
; ExtCurrent
= NextExtCurrent
) {
1510 // read current EMBR
1511 Status
= refit_call5_wrapper(BlockIO
->ReadBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, ExtCurrent
, 512, SectorBuffer
);
1512 if (EFI_ERROR(Status
))
1514 if (*((UINT16
*)(SectorBuffer
+ 510)) != 0xaa55)
1515 return EFI_NOT_FOUND
; // safety measure #3
1517 // scan EMBR, set appropriate partition active
1518 EMbrTable
= (MBR_PARTITION_INFO
*)(SectorBuffer
+ 446);
1520 for (i
= 0; i
< 4; i
++) {
1521 if (EMbrTable
[i
].Flags
!= 0x00 && EMbrTable
[i
].Flags
!= 0x80)
1522 return EFI_NOT_FOUND
; // safety measure #4
1523 if (EMbrTable
[i
].StartLBA
== 0 || EMbrTable
[i
].Size
== 0)
1525 if (IS_EXTENDED_PART_TYPE(EMbrTable
[i
].Type
)) {
1526 // link to next EMBR
1527 NextExtCurrent
= ExtBase
+ EMbrTable
[i
].StartLBA
;
1528 EMbrTable
[i
].Flags
= (PartitionIndex
>= LogicalPartitionIndex
) ? 0x80 : 0x00;
1531 // logical partition
1532 EMbrTable
[i
].Flags
= (PartitionIndex
== LogicalPartitionIndex
) ? 0x80 : 0x00;
1533 LogicalPartitionIndex
++;
1537 // write current EMBR
1538 Status
= refit_call5_wrapper(BlockIO
->WriteBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, ExtCurrent
, 512, SectorBuffer
);
1539 if (EFI_ERROR(Status
))
1542 if (PartitionIndex
< LogicalPartitionIndex
)
1543 break; // stop the loop, no need to touch further EMBRs
1549 } /* static EFI_STATUS ActivateMbrPartition() */
1551 // early 2006 Core Duo / Core Solo models
1552 static UINT8 LegacyLoaderDevicePath1Data
[] = {
1553 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1554 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1555 0xFF, 0xFF, 0xF9, 0xFF, 0x00, 0x00, 0x00, 0x00,
1556 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1557 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1558 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1560 // mid-2006 Mac Pro (and probably other Core 2 models)
1561 static UINT8 LegacyLoaderDevicePath2Data
[] = {
1562 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1563 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1564 0xFF, 0xFF, 0xF7, 0xFF, 0x00, 0x00, 0x00, 0x00,
1565 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1566 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1567 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1569 // mid-2007 MBP ("Santa Rosa" based models)
1570 static UINT8 LegacyLoaderDevicePath3Data
[] = {
1571 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1572 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1573 0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
1574 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1575 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1576 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1579 static UINT8 LegacyLoaderDevicePath4Data
[] = {
1580 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1581 0x00, 0x00, 0xC0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1582 0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
1583 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1584 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1585 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1587 // late-2008 MB/MBP (NVidia chipset)
1588 static UINT8 LegacyLoaderDevicePath5Data
[] = {
1589 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1590 0x00, 0x40, 0xCB, 0xFF, 0x00, 0x00, 0x00, 0x00,
1591 0xFF, 0xBF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
1592 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1593 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1594 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1597 static EFI_DEVICE_PATH
*LegacyLoaderList
[] = {
1598 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath1Data
,
1599 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath2Data
,
1600 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath3Data
,
1601 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath4Data
,
1602 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath5Data
,
1606 #define MAX_DISCOVERED_PATHS (16)
1608 static VOID
StartLegacy(IN LEGACY_ENTRY
*Entry
)
1611 EG_IMAGE
*BootLogoImage
;
1612 UINTN ErrorInStep
= 0;
1613 EFI_DEVICE_PATH
*DiscoveredPathList
[MAX_DISCOVERED_PATHS
];
1615 BeginExternalScreen(TRUE
, L
"Booting Legacy OS (Mac mode)");
1617 BootLogoImage
= LoadOSIcon(Entry
->Volume
->OSIconName
, L
"legacy", TRUE
);
1618 if (BootLogoImage
!= NULL
)
1619 BltImageAlpha(BootLogoImage
,
1620 (UGAWidth
- BootLogoImage
->Width
) >> 1,
1621 (UGAHeight
- BootLogoImage
->Height
) >> 1,
1622 &StdBackgroundPixel
);
1624 if (Entry
->Volume
->IsMbrPartition
) {
1625 ActivateMbrPartition(Entry
->Volume
->WholeDiskBlockIO
, Entry
->Volume
->MbrPartitionIndex
);
1628 ExtractLegacyLoaderPaths(DiscoveredPathList
, MAX_DISCOVERED_PATHS
, LegacyLoaderList
);
1630 StoreLoaderName(Entry
->me
.Title
);
1631 Status
= StartEFIImageList(DiscoveredPathList
, Entry
->LoadOptions
, TYPE_LEGACY
, L
"legacy loader", 0, &ErrorInStep
, TRUE
);
1632 if (Status
== EFI_NOT_FOUND
) {
1633 if (ErrorInStep
== 1) {
1634 Print(L
"\nPlease make sure that you have the latest firmware update installed.\n");
1635 } else if (ErrorInStep
== 3) {
1636 Print(L
"\nThe firmware refused to boot from the selected volume. Note that external\n"
1637 L
"hard drives are not well-supported by Apple's firmware for legacy OS booting.\n");
1640 FinishExternalScreen();
1641 } /* static VOID StartLegacy() */
1643 // Start a device on a non-Mac using the EFI_LEGACY_BIOS_PROTOCOL
1644 static VOID
StartLegacyUEFI(LEGACY_ENTRY
*Entry
)
1646 BeginExternalScreen(TRUE
, L
"Booting Legacy OS (UEFI mode)");
1647 StoreLoaderName(Entry
->me
.Title
);
1649 BdsLibConnectDevicePath (Entry
->BdsOption
->DevicePath
);
1650 BdsLibDoLegacyBoot(Entry
->BdsOption
);
1652 // If we get here, it means that there was a failure....
1653 Print(L
"Failure booting legacy (BIOS) OS.");
1655 FinishExternalScreen();
1656 } // static VOID StartLegacyUEFI()
1658 static LEGACY_ENTRY
* AddLegacyEntry(IN CHAR16
*LoaderTitle
, IN REFIT_VOLUME
*Volume
)
1660 LEGACY_ENTRY
*Entry
, *SubEntry
;
1661 REFIT_MENU_SCREEN
*SubScreen
;
1662 CHAR16
*VolDesc
, *LegacyTitle
;
1663 CHAR16 ShortcutLetter
= 0;
1665 if (LoaderTitle
== NULL
) {
1666 if (Volume
->OSName
!= NULL
) {
1667 LoaderTitle
= Volume
->OSName
;
1668 if (LoaderTitle
[0] == 'W' || LoaderTitle
[0] == 'L')
1669 ShortcutLetter
= LoaderTitle
[0];
1671 LoaderTitle
= L
"Legacy OS";
1673 if (Volume
->VolName
!= NULL
)
1674 VolDesc
= Volume
->VolName
;
1676 VolDesc
= (Volume
->DiskKind
== DISK_KIND_OPTICAL
) ? L
"CD" : L
"HD";
1678 LegacyTitle
= AllocateZeroPool(256 * sizeof(CHAR16
));
1679 if (LegacyTitle
!= NULL
)
1680 SPrint(LegacyTitle
, 255, L
"Boot %s from %s", LoaderTitle
, VolDesc
);
1681 if (IsInSubstring(LegacyTitle
, GlobalConfig
.DontScanVolumes
)) {
1682 MyFreePool(LegacyTitle
);
1686 // prepare the menu entry
1687 Entry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1688 Entry
->me
.Title
= LegacyTitle
;
1689 Entry
->me
.Tag
= TAG_LEGACY
;
1691 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1692 Entry
->me
.Image
= LoadOSIcon(Volume
->OSIconName
, L
"legacy", FALSE
);
1693 Entry
->me
.BadgeImage
= Volume
->VolBadgeImage
;
1694 Entry
->Volume
= Volume
;
1695 Entry
->LoadOptions
= (Volume
->DiskKind
== DISK_KIND_OPTICAL
) ? L
"CD" :
1696 ((Volume
->DiskKind
== DISK_KIND_EXTERNAL
) ? L
"USB" : L
"HD");
1697 Entry
->Enabled
= TRUE
;
1699 // create the submenu
1700 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
1701 SubScreen
->Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1702 SPrint(SubScreen
->Title
, 255, L
"Boot Options for %s on %s", LoaderTitle
, VolDesc
);
1703 SubScreen
->TitleImage
= Entry
->me
.Image
;
1704 SubScreen
->Hint1
= StrDuplicate(SUBSCREEN_HINT1
);
1705 if (GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_EDITOR
) {
1706 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2_NO_EDITOR
);
1708 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2
);
1712 SubEntry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1713 SubEntry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1714 SPrint(SubEntry
->me
.Title
, 255, L
"Boot %s", LoaderTitle
);
1715 SubEntry
->me
.Tag
= TAG_LEGACY
;
1716 SubEntry
->Volume
= Entry
->Volume
;
1717 SubEntry
->LoadOptions
= Entry
->LoadOptions
;
1718 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
1720 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
1721 Entry
->me
.SubScreen
= SubScreen
;
1722 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1724 } /* static LEGACY_ENTRY * AddLegacyEntry() */
1727 // default volume badge icon based on disk kind
1728 static EG_IMAGE
* GetDiskBadge(IN UINTN DiskType
) {
1729 EG_IMAGE
* Badge
= NULL
;
1733 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_INTERNAL
);
1736 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_EXTERNAL
);
1739 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_OPTICAL
);
1743 } // static EG_IMAGE * GetDiskBadge()
1746 Create a rEFInd boot option from a Legacy BIOS protocol option.
1748 static LEGACY_ENTRY
* AddLegacyEntryUEFI(BDS_COMMON_OPTION
*BdsOption
, IN UINT16 DiskType
)
1750 LEGACY_ENTRY
*Entry
, *SubEntry
;
1751 REFIT_MENU_SCREEN
*SubScreen
;
1752 CHAR16 ShortcutLetter
= 0;
1753 CHAR16
*LegacyDescription
= StrDuplicate(BdsOption
->Description
);
1755 if (IsInSubstring(LegacyDescription
, GlobalConfig
.DontScanVolumes
))
1758 // Remove stray spaces, since many EFIs produce descriptions with lots of
1759 // extra spaces, especially at the end; this throws off centering of the
1760 // description on the screen....
1761 LimitStringLength(LegacyDescription
, 100);
1763 // prepare the menu entry
1764 Entry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1765 Entry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1766 SPrint(Entry
->me
.Title
, 255, L
"Boot legacy target %s", LegacyDescription
);
1767 Entry
->me
.Tag
= TAG_LEGACY_UEFI
;
1769 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1770 Entry
->me
.Image
= LoadOSIcon(L
"legacy", L
"legacy", TRUE
);
1771 Entry
->LoadOptions
= (DiskType
== BBS_CDROM
) ? L
"CD" :
1772 ((DiskType
== BBS_USB
) ? L
"USB" : L
"HD");
1773 Entry
->me
.BadgeImage
= GetDiskBadge(DiskType
);
1774 Entry
->BdsOption
= BdsOption
;
1775 Entry
->Enabled
= TRUE
;
1777 // create the submenu
1778 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
1779 SubScreen
->Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1780 SPrint(SubScreen
->Title
, 255, L
"No boot options for legacy target");
1781 SubScreen
->TitleImage
= Entry
->me
.Image
;
1782 SubScreen
->Hint1
= StrDuplicate(SUBSCREEN_HINT1
);
1783 if (GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_EDITOR
) {
1784 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2_NO_EDITOR
);
1786 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2
);
1790 SubEntry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1791 SubEntry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1792 SPrint(SubEntry
->me
.Title
, 255, L
"Boot %s", LegacyDescription
);
1793 SubEntry
->me
.Tag
= TAG_LEGACY_UEFI
;
1794 Entry
->BdsOption
= BdsOption
;
1795 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
1797 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
1798 Entry
->me
.SubScreen
= SubScreen
;
1799 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1801 } /* static LEGACY_ENTRY * AddLegacyEntryUEFI() */
1804 Scan for legacy BIOS targets on machines that implement EFI_LEGACY_BIOS_PROTOCOL.
1805 In testing, protocol has not been implemented on Macs but has been
1806 implemented on several Dell PCs and an ASUS motherboard.
1807 Restricts output to disks of the specified DiskType.
1809 static VOID
ScanLegacyUEFI(IN UINTN DiskType
)
1812 EFI_LEGACY_BIOS_PROTOCOL
*LegacyBios
;
1813 UINT16
*BootOrder
= NULL
;
1815 CHAR16 BootOption
[10];
1816 UINTN BootOrderSize
= 0;
1818 BDS_COMMON_OPTION
*BdsOption
;
1819 LIST_ENTRY TempList
;
1820 BBS_BBS_DEVICE_PATH
*BbsDevicePath
= NULL
;
1821 BOOLEAN SearchingForUsb
= FALSE
;
1823 InitializeListHead (&TempList
);
1824 ZeroMem (Buffer
, sizeof (Buffer
));
1826 // If LegacyBios protocol is not implemented on this platform, then
1827 //we do not support this type of legacy boot on this machine.
1828 Status
= refit_call3_wrapper(gBS
->LocateProtocol
, &gEfiLegacyBootProtocolGuid
, NULL
, (VOID
**) &LegacyBios
);
1829 if (EFI_ERROR (Status
))
1832 // EFI calls USB drives BBS_HARDDRIVE, but we want to distinguish them,
1833 // so we set DiskType inappropriately elsewhere in the program and
1834 // "translate" it here.
1835 if (DiskType
== BBS_USB
) {
1836 DiskType
= BBS_HARDDISK
;
1837 SearchingForUsb
= TRUE
;
1840 // Grab the boot order
1841 BootOrder
= BdsLibGetVariableAndSize(L
"BootOrder", &gEfiGlobalVariableGuid
, &BootOrderSize
);
1842 if (BootOrder
== NULL
) {
1847 while (Index
< BootOrderSize
/ sizeof (UINT16
))
1849 // Grab each boot option variable from the boot order, and convert
1850 // the variable into a BDS boot option
1851 UnicodeSPrint (BootOption
, sizeof (BootOption
), L
"Boot%04x", BootOrder
[Index
]);
1852 BdsOption
= BdsLibVariableToOption (&TempList
, BootOption
);
1854 if (BdsOption
!= NULL
) {
1855 BbsDevicePath
= (BBS_BBS_DEVICE_PATH
*)BdsOption
->DevicePath
;
1856 // Only add the entry if it is of a requested type (e.g. USB, HD)
1857 // Two checks necessary because some systems return EFI boot loaders
1858 // with a DeviceType value that would inappropriately include them
1859 // as legacy loaders....
1860 if ((BbsDevicePath
->DeviceType
== DiskType
) && (BdsOption
->DevicePath
->Type
== DEVICE_TYPE_BIOS
)) {
1861 // USB flash drives appear as hard disks with certain media flags set.
1862 // Look for this, and if present, pass it on with the (technically
1863 // incorrect, but internally useful) BBS_TYPE_USB flag set.
1864 if (DiskType
== BBS_HARDDISK
) {
1865 if (SearchingForUsb
&& (BbsDevicePath
->StatusFlag
& (BBS_MEDIA_PRESENT
| BBS_MEDIA_MAYBE_PRESENT
))) {
1866 AddLegacyEntryUEFI(BdsOption
, BBS_USB
);
1867 } else if (!SearchingForUsb
&& !(BbsDevicePath
->StatusFlag
& (BBS_MEDIA_PRESENT
| BBS_MEDIA_MAYBE_PRESENT
))) {
1868 AddLegacyEntryUEFI(BdsOption
, DiskType
);
1871 AddLegacyEntryUEFI(BdsOption
, DiskType
);
1874 } // if (BdsOption != NULL)
1877 } /* static VOID ScanLegacyUEFI() */
1879 static VOID
ScanLegacyVolume(REFIT_VOLUME
*Volume
, UINTN VolumeIndex
) {
1881 BOOLEAN ShowVolume
, HideIfOthersFound
;
1884 HideIfOthersFound
= FALSE
;
1885 if (Volume
->IsAppleLegacy
) {
1887 HideIfOthersFound
= TRUE
;
1888 } else if (Volume
->HasBootCode
) {
1890 if (Volume
->BlockIO
== Volume
->WholeDiskBlockIO
&&
1891 Volume
->BlockIOOffset
== 0 &&
1892 Volume
->OSName
== NULL
)
1893 // this is a whole disk (MBR) entry; hide if we have entries for partitions
1894 HideIfOthersFound
= TRUE
;
1896 if (HideIfOthersFound
) {
1897 // check for other bootable entries on the same disk
1898 for (VolumeIndex2
= 0; VolumeIndex2
< VolumesCount
; VolumeIndex2
++) {
1899 if (VolumeIndex2
!= VolumeIndex
&& Volumes
[VolumeIndex2
]->HasBootCode
&&
1900 Volumes
[VolumeIndex2
]->WholeDiskBlockIO
== Volume
->WholeDiskBlockIO
)
1906 AddLegacyEntry(NULL
, Volume
);
1907 } // static VOID ScanLegacyVolume()
1909 // Scan attached optical discs for legacy (BIOS) boot code
1910 // and add anything found to the list....
1911 static VOID
ScanLegacyDisc(VOID
)
1914 REFIT_VOLUME
*Volume
;
1916 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
) {
1917 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1918 Volume
= Volumes
[VolumeIndex
];
1919 if (Volume
->DiskKind
== DISK_KIND_OPTICAL
)
1920 ScanLegacyVolume(Volume
, VolumeIndex
);
1922 } else if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
1923 ScanLegacyUEFI(BBS_CDROM
);
1925 } /* static VOID ScanLegacyDisc() */
1927 // Scan internal hard disks for legacy (BIOS) boot code
1928 // and add anything found to the list....
1929 static VOID
ScanLegacyInternal(VOID
)
1932 REFIT_VOLUME
*Volume
;
1934 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
) {
1935 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1936 Volume
= Volumes
[VolumeIndex
];
1937 if (Volume
->DiskKind
== DISK_KIND_INTERNAL
)
1938 ScanLegacyVolume(Volume
, VolumeIndex
);
1940 } else if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
1941 // TODO: This actually picks up USB flash drives, too; try to find
1942 // a way to differentiate the two....
1943 ScanLegacyUEFI(BBS_HARDDISK
);
1945 } /* static VOID ScanLegacyInternal() */
1947 // Scan external disks for legacy (BIOS) boot code
1948 // and add anything found to the list....
1949 static VOID
ScanLegacyExternal(VOID
)
1952 REFIT_VOLUME
*Volume
;
1954 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
) {
1955 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1956 Volume
= Volumes
[VolumeIndex
];
1957 if (Volume
->DiskKind
== DISK_KIND_EXTERNAL
)
1958 ScanLegacyVolume(Volume
, VolumeIndex
);
1960 } else if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
1961 // TODO: This actually doesn't do anything useful; leaving in hopes of
1962 // fixing it later....
1963 ScanLegacyUEFI(BBS_USB
);
1965 } /* static VOID ScanLegacyExternal() */
1968 // pre-boot tool functions
1971 static VOID
StartTool(IN LOADER_ENTRY
*Entry
)
1973 BeginExternalScreen(Entry
->UseGraphicsMode
, Entry
->me
.Title
+ 6); // assumes "Start <title>" as assigned below
1974 StoreLoaderName(Entry
->me
.Title
);
1975 StartEFIImage(Entry
->DevicePath
, Entry
->LoadOptions
, TYPE_EFI
,
1976 Basename(Entry
->LoaderPath
), Entry
->OSType
, NULL
, TRUE
);
1977 FinishExternalScreen();
1978 } /* static VOID StartTool() */
1980 static LOADER_ENTRY
* AddToolEntry(EFI_HANDLE DeviceHandle
, IN CHAR16
*LoaderPath
, IN CHAR16
*LoaderTitle
, IN EG_IMAGE
*Image
,
1981 IN CHAR16 ShortcutLetter
, IN BOOLEAN UseGraphicsMode
)
1983 LOADER_ENTRY
*Entry
;
1984 CHAR16
*TitleStr
= NULL
;
1986 Entry
= AllocateZeroPool(sizeof(LOADER_ENTRY
));
1988 TitleStr
= PoolPrint(L
"Start %s", LoaderTitle
);
1989 Entry
->me
.Title
= TitleStr
;
1990 Entry
->me
.Tag
= TAG_TOOL
;
1992 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1993 Entry
->me
.Image
= Image
;
1994 Entry
->LoaderPath
= (LoaderPath
) ? StrDuplicate(LoaderPath
) : NULL
;
1995 Entry
->DevicePath
= FileDevicePath(DeviceHandle
, Entry
->LoaderPath
);
1996 Entry
->UseGraphicsMode
= UseGraphicsMode
;
1998 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
2000 } /* static LOADER_ENTRY * AddToolEntry() */
2003 // pre-boot driver functions
2006 static UINTN
ScanDriverDir(IN CHAR16
*Path
)
2009 REFIT_DIR_ITER DirIter
;
2011 EFI_FILE_INFO
*DirEntry
;
2012 CHAR16 FileName
[256];
2014 CleanUpPathNameSlashes(Path
);
2015 // look through contents of the directory
2016 DirIterOpen(SelfRootDir
, Path
, &DirIter
);
2017 while (DirIterNext(&DirIter
, 2, LOADER_MATCH_PATTERNS
, &DirEntry
)) {
2018 if (DirEntry
->FileName
[0] == '.')
2019 continue; // skip this
2021 SPrint(FileName
, 255, L
"%s\\%s", Path
, DirEntry
->FileName
);
2023 Status
= StartEFIImage(FileDevicePath(SelfLoadedImage
->DeviceHandle
, FileName
),
2024 L
"", TYPE_EFI
, DirEntry
->FileName
, 0, NULL
, FALSE
);
2026 Status
= DirIterClose(&DirIter
);
2027 if (Status
!= EFI_NOT_FOUND
) {
2028 SPrint(FileName
, 255, L
"while scanning the %s directory", Path
);
2029 CheckError(Status
, FileName
);
2034 #ifdef __MAKEWITH_GNUEFI
2035 static EFI_STATUS
ConnectAllDriversToAllControllers(VOID
)
2038 UINTN AllHandleCount
;
2039 EFI_HANDLE
*AllHandleBuffer
;
2042 EFI_HANDLE
*HandleBuffer
;
2048 Status
= LibLocateHandle(AllHandles
,
2053 if (EFI_ERROR(Status
))
2056 for (Index
= 0; Index
< AllHandleCount
; Index
++) {
2058 // Scan the handle database
2060 Status
= LibScanHandleDatabase(NULL
,
2062 AllHandleBuffer
[Index
],
2067 if (EFI_ERROR (Status
))
2071 if (HandleType
[Index
] & EFI_HANDLE_TYPE_DRIVER_BINDING_HANDLE
)
2073 if (HandleType
[Index
] & EFI_HANDLE_TYPE_IMAGE_HANDLE
)
2078 for (HandleIndex
= 0; HandleIndex
< HandleCount
; HandleIndex
++) {
2079 if (HandleType
[HandleIndex
] & EFI_HANDLE_TYPE_PARENT_HANDLE
)
2084 if (HandleType
[Index
] & EFI_HANDLE_TYPE_DEVICE_HANDLE
) {
2085 Status
= refit_call4_wrapper(BS
->ConnectController
,
2086 AllHandleBuffer
[Index
],
2094 MyFreePool (HandleBuffer
);
2095 MyFreePool (HandleType
);
2099 MyFreePool (AllHandleBuffer
);
2101 } /* EFI_STATUS ConnectAllDriversToAllControllers() */
2103 static EFI_STATUS
ConnectAllDriversToAllControllers(VOID
) {
2104 BdsLibConnectAllDriversToAllControllers();
2109 // Load all EFI drivers from rEFInd's "drivers" subdirectory and from the
2110 // directories specified by the user in the "scan_driver_dirs" configuration
2112 static VOID
LoadDrivers(VOID
)
2114 CHAR16
*Directory
, *SelfDirectory
;
2115 UINTN i
= 0, Length
, NumFound
= 0;
2117 // load drivers from the subdirectories of rEFInd's home directory specified
2118 // in the DRIVER_DIRS constant.
2119 while ((Directory
= FindCommaDelimited(DRIVER_DIRS
, i
++)) != NULL
) {
2120 SelfDirectory
= SelfDirPath
? StrDuplicate(SelfDirPath
) : NULL
;
2121 CleanUpPathNameSlashes(SelfDirectory
);
2122 MergeStrings(&SelfDirectory
, Directory
, L
'\\');
2123 NumFound
+= ScanDriverDir(SelfDirectory
);
2124 MyFreePool(Directory
);
2125 MyFreePool(SelfDirectory
);
2128 // Scan additional user-specified driver directories....
2130 while ((Directory
= FindCommaDelimited(GlobalConfig
.DriverDirs
, i
++)) != NULL
) {
2131 CleanUpPathNameSlashes(Directory
);
2132 Length
= StrLen(Directory
);
2134 NumFound
+= ScanDriverDir(Directory
);
2136 MyFreePool(Directory
);
2139 // connect all devices
2141 ConnectAllDriversToAllControllers();
2142 } /* static VOID LoadDrivers() */
2144 // Determine what (if any) type of legacy (BIOS) boot support is available
2145 static VOID
FindLegacyBootType(VOID
) {
2147 EFI_LEGACY_BIOS_PROTOCOL
*LegacyBios
;
2149 GlobalConfig
.LegacyType
= LEGACY_TYPE_NONE
;
2151 // UEFI-style legacy BIOS support is available only with some EFI implementations....
2152 Status
= refit_call3_wrapper(gBS
->LocateProtocol
, &gEfiLegacyBootProtocolGuid
, NULL
, (VOID
**) &LegacyBios
);
2153 if (!EFI_ERROR (Status
))
2154 GlobalConfig
.LegacyType
= LEGACY_TYPE_UEFI
;
2156 // Macs have their own system. If the firmware vendor code contains the
2157 // string "Apple", assume it's available. Note that this overrides the
2158 // UEFI type, and might yield false positives if the vendor string
2159 // contains "Apple" as part of something bigger, so this isn't 100%
2161 if (StriSubCmp(L
"Apple", ST
->FirmwareVendor
))
2162 GlobalConfig
.LegacyType
= LEGACY_TYPE_MAC
;
2163 } // static VOID FindLegacyBootType
2165 // Warn the user if legacy OS scans are enabled but the firmware can't support them....
2166 static VOID
WarnIfLegacyProblems(VOID
) {
2167 BOOLEAN found
= FALSE
;
2170 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_NONE
) {
2172 if (GlobalConfig
.ScanFor
[i
] == 'h' || GlobalConfig
.ScanFor
[i
] == 'b' || GlobalConfig
.ScanFor
[i
] == 'c' ||
2173 GlobalConfig
.ScanFor
[i
] == 'H' || GlobalConfig
.ScanFor
[i
] == 'B' || GlobalConfig
.ScanFor
[i
] == 'C')
2176 } while ((i
< NUM_SCAN_OPTIONS
) && (!found
));
2179 Print(L
"NOTE: refind.conf's 'scanfor' line specifies scanning for one or more legacy\n");
2180 Print(L
"(BIOS) boot options; however, this is not possible because your computer lacks\n");
2181 Print(L
"the necessary Compatibility Support Module (CSM) support or that support is\n");
2182 Print(L
"disabled in your firmware.\n");
2185 } // if no legacy support
2186 } // static VOID WarnIfLegacyProblems()
2188 // Locates boot loaders. NOTE: This assumes that GlobalConfig.LegacyType is set correctly.
2189 static VOID
ScanForBootloaders(VOID
) {
2192 BOOLEAN ScanForLegacy
= FALSE
;
2194 // Determine up-front if we'll be scanning for legacy loaders....
2195 for (i
= 0; i
< NUM_SCAN_OPTIONS
; i
++) {
2196 s
= GlobalConfig
.ScanFor
[i
];
2197 if ((s
== 'c') || (s
== 'C') || (s
== 'h') || (s
== 'H') || (s
== 'b') || (s
== 'B'))
2198 ScanForLegacy
= TRUE
;
2201 // If UEFI & scanning for legacy loaders & deep legacy scan, update NVRAM boot manager list
2202 if ((GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) && ScanForLegacy
&& GlobalConfig
.DeepLegacyScan
) {
2203 BdsDeleteAllInvalidLegacyBootOptions();
2204 BdsAddNonExistingLegacyBootOptions();
2207 // scan for loaders and tools, add them to the menu
2208 for (i
= 0; i
< NUM_SCAN_OPTIONS
; i
++) {
2209 switch(GlobalConfig
.ScanFor
[i
]) {
2214 ScanLegacyInternal();
2217 ScanLegacyExternal();
2220 ScanUserConfigured(GlobalConfig
.ConfigFilename
);
2234 // assign shortcut keys
2235 for (i
= 0; i
< MainMenu
.EntryCount
&& MainMenu
.Entries
[i
]->Row
== 0 && i
< 9; i
++)
2236 MainMenu
.Entries
[i
]->ShortcutDigit
= (CHAR16
)('1' + i
);
2238 // wait for user ACK when there were errors
2239 FinishTextScreen(FALSE
);
2240 } // static VOID ScanForBootloaders()
2242 // Locate a single tool from the specified Locations using one of the
2243 // specified Names and add it to the menu.
2244 static VOID
FindTool(CHAR16
*Locations
, CHAR16
*Names
, CHAR16
*Description
, UINTN Icon
) {
2245 UINTN j
= 0, k
, VolumeIndex
;
2246 CHAR16
*DirName
, *FileName
, *PathName
, FullDescription
[256];
2248 while ((DirName
= FindCommaDelimited(Locations
, j
++)) != NULL
) {
2250 while ((FileName
= FindCommaDelimited(Names
, k
++)) != NULL
) {
2251 PathName
= StrDuplicate(DirName
);
2252 MergeStrings(&PathName
, FileName
, (StriCmp(PathName
, L
"\\") == 0) ? 0 : L
'\\');
2253 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
2254 if ((Volumes
[VolumeIndex
]->RootDir
!= NULL
) && (FileExists(Volumes
[VolumeIndex
]->RootDir
, PathName
))) {
2255 SPrint(FullDescription
, 255, L
"%s at %s on %s", Description
, PathName
, Volumes
[VolumeIndex
]->VolName
);
2256 AddToolEntry(Volumes
[VolumeIndex
]->DeviceHandle
, PathName
, FullDescription
, BuiltinIcon(Icon
), 'S', FALSE
);
2259 MyFreePool(PathName
);
2260 MyFreePool(FileName
);
2262 MyFreePool(DirName
);
2263 } // while Locations
2264 } // VOID FindTool()
2266 // Add the second-row tags containing built-in and external tools (EFI shell,
2268 static VOID
ScanForTools(VOID
) {
2269 CHAR16
*FileName
= NULL
, *VolName
= NULL
, *MokLocations
, Description
[256];
2270 REFIT_MENU_ENTRY
*TempMenuEntry
;
2271 UINTN i
, j
, VolumeIndex
;
2275 MokLocations
= StrDuplicate(MOK_LOCATIONS
);
2276 if (MokLocations
!= NULL
)
2277 MergeStrings(&MokLocations
, SelfDirPath
, L
',');
2279 for (i
= 0; i
< NUM_TOOLS
; i
++) {
2280 switch(GlobalConfig
.ShowTools
[i
]) {
2281 // NOTE: Be sure that FileName is NULL at the end of each case.
2283 TempMenuEntry
= CopyMenuEntry(&MenuEntryShutdown
);
2284 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_SHUTDOWN
);
2285 AddMenuEntry(&MainMenu
, TempMenuEntry
);
2289 TempMenuEntry
= CopyMenuEntry(&MenuEntryReset
);
2290 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_RESET
);
2291 AddMenuEntry(&MainMenu
, TempMenuEntry
);
2295 TempMenuEntry
= CopyMenuEntry(&MenuEntryAbout
);
2296 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT
);
2297 AddMenuEntry(&MainMenu
, TempMenuEntry
);
2301 TempMenuEntry
= CopyMenuEntry(&MenuEntryExit
);
2302 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_EXIT
);
2303 AddMenuEntry(&MainMenu
, TempMenuEntry
);
2307 if (EfivarGetRaw(&GlobalGuid
, L
"OsIndicationsSupported", &b
, &j
) == EFI_SUCCESS
) {
2309 if (osind
& EFI_OS_INDICATIONS_BOOT_TO_FW_UI
) {
2310 TempMenuEntry
= CopyMenuEntry(&MenuEntryFirmware
);
2311 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_FIRMWARE
);
2312 AddMenuEntry(&MainMenu
, TempMenuEntry
);
2319 while ((FileName
= FindCommaDelimited(SHELL_NAMES
, j
++)) != NULL
) {
2320 if (FileExists(SelfRootDir
, FileName
)) {
2321 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, L
"EFI Shell", BuiltinIcon(BUILTIN_ICON_TOOL_SHELL
),
2324 MyFreePool(FileName
);
2330 while ((FileName
= FindCommaDelimited(GPTSYNC_NAMES
, j
++)) != NULL
) {
2331 if (FileExists(SelfRootDir
, FileName
)) {
2332 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, L
"Hybrid MBR tool", BuiltinIcon(BUILTIN_ICON_TOOL_PART
),
2335 MyFreePool(FileName
);
2342 while ((FileName
= FindCommaDelimited(GDISK_NAMES
, j
++)) != NULL
) {
2343 if (FileExists(SelfRootDir
, FileName
)) {
2344 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, L
"disk partitioning tool",
2345 BuiltinIcon(BUILTIN_ICON_TOOL_PART
), 'G', FALSE
);
2347 MyFreePool(FileName
);
2352 case TAG_APPLE_RECOVERY
:
2353 FileName
= StrDuplicate(L
"\\com.apple.recovery.boot\\boot.efi");
2354 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
2355 if ((Volumes
[VolumeIndex
]->RootDir
!= NULL
) && (FileExists(Volumes
[VolumeIndex
]->RootDir
, FileName
))) {
2356 SPrint(Description
, 255, L
"Apple Recovery on %s", Volumes
[VolumeIndex
]->VolName
);
2357 AddToolEntry(Volumes
[VolumeIndex
]->DeviceHandle
, FileName
, Description
,
2358 BuiltinIcon(BUILTIN_ICON_TOOL_APPLE_RESCUE
), 'R', TRUE
);
2361 MyFreePool(FileName
);
2365 case TAG_WINDOWS_RECOVERY
:
2367 while ((FileName
= FindCommaDelimited(GlobalConfig
.WindowsRecoveryFiles
, j
++)) != NULL
) {
2368 SplitVolumeAndFilename(&FileName
, &VolName
);
2369 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
2370 if ((Volumes
[VolumeIndex
]->RootDir
!= NULL
) && (FileExists(Volumes
[VolumeIndex
]->RootDir
, FileName
)) &&
2371 ((VolName
== NULL
) || (StriCmp(VolName
, Volumes
[VolumeIndex
]->VolName
) == 0))) {
2372 SPrint(Description
, 255, L
"Microsoft Recovery on %s", Volumes
[VolumeIndex
]->VolName
);
2373 AddToolEntry(Volumes
[VolumeIndex
]->DeviceHandle
, FileName
, Description
,
2374 BuiltinIcon(BUILTIN_ICON_TOOL_WINDOWS_RESCUE
), 'R', TRUE
);
2378 MyFreePool(FileName
);
2380 MyFreePool(VolName
);
2385 FindTool(MokLocations
, MOK_NAMES
, L
"MOK utility", BUILTIN_ICON_TOOL_MOK_TOOL
);
2389 FindTool(MEMTEST_LOCATIONS
, MEMTEST_NAMES
, L
"Memory test utility", BUILTIN_ICON_TOOL_MEMTEST
);
2394 } // static VOID ScanForTools
2396 // Rescan for boot loaders
2397 static VOID
RescanAll(BOOLEAN DisplayMessage
) {
2405 egDisplayMessage(L
"Scanning for new boot loaders; please wait....", &BGColor
);
2406 FreeList((VOID
***) &(MainMenu
.Entries
), &MainMenu
.EntryCount
);
2407 MainMenu
.Entries
= NULL
;
2408 MainMenu
.EntryCount
= 0;
2409 ReadConfig(GlobalConfig
.ConfigFilename
);
2410 ConnectAllDriversToAllControllers();
2412 ScanForBootloaders();
2415 } // VOID RescanAll()
2417 #ifdef __MAKEWITH_TIANO
2419 // Minimal initialization function
2420 static VOID
InitializeLib(IN EFI_HANDLE ImageHandle
, IN EFI_SYSTEM_TABLE
*SystemTable
) {
2422 // gImageHandle = ImageHandle;
2423 gBS
= SystemTable
->BootServices
;
2424 // gRS = SystemTable->RuntimeServices;
2425 gRT
= SystemTable
->RuntimeServices
; // Some BDS functions need gRT to be set
2426 EfiGetSystemConfigurationTable (&gEfiDxeServicesTableGuid
, (VOID
**) &gDS
);
2428 // InitializeConsoleSim();
2433 // Set up our own Secure Boot extensions....
2434 // Returns TRUE on success, FALSE otherwise
2435 static BOOLEAN
SecureBootSetup(VOID
) {
2437 BOOLEAN Success
= FALSE
;
2439 if (secure_mode() && ShimLoaded()) {
2440 Status
= security_policy_install();
2441 if (Status
== EFI_SUCCESS
) {
2444 Print(L
"Failed to install MOK Secure Boot extensions");
2448 } // VOID SecureBootSetup()
2450 // Remove our own Secure Boot extensions....
2451 // Returns TRUE on success, FALSE otherwise
2452 static BOOLEAN
SecureBootUninstall(VOID
) {
2454 BOOLEAN Success
= TRUE
;
2456 if (secure_mode()) {
2457 Status
= security_policy_uninstall();
2458 if (Status
!= EFI_SUCCESS
) {
2460 BeginTextScreen(L
"Secure Boot Policy Failure");
2461 Print(L
"Failed to uninstall MOK Secure Boot extensions; forcing a reboot.");
2463 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);
2467 } // VOID SecureBootUninstall
2469 // Sets the global configuration filename; will be CONFIG_FILE_NAME unless the
2470 // "-c" command-line option is set, in which case that takes precedence.
2471 // If an error is encountered, leaves the value alone (it should be set to
2472 // CONFIG_FILE_NAME when GlobalConfig is initialized).
2473 static VOID
SetConfigFilename(EFI_HANDLE ImageHandle
) {
2474 EFI_LOADED_IMAGE
*Info
;
2475 CHAR16
*Options
, *FileName
;
2479 Status
= refit_call3_wrapper(BS
->HandleProtocol
, ImageHandle
, &LoadedImageProtocol
, (VOID
**) &Info
);
2480 if ((Status
== EFI_SUCCESS
) && (Info
->LoadOptionsSize
> 0)) {
2481 Options
= (CHAR16
*) Info
->LoadOptions
;
2482 Where
= FindSubString(L
" -c ", Options
);
2484 FileName
= StrDuplicate(&Options
[Where
+ 4]);
2485 Where
= FindSubString(L
" ", FileName
);
2487 FileName
[Where
] = L
'\0';
2489 if (FileExists(SelfDir
, FileName
)) {
2490 GlobalConfig
.ConfigFilename
= FileName
;
2492 Print(L
"Specified configuration file (%s) doesn't exist; using\n'refind.conf' default\n", FileName
);
2493 MyFreePool(FileName
);
2497 } // VOID SetConfigFilename()
2504 efi_main (EFI_HANDLE ImageHandle
, EFI_SYSTEM_TABLE
*SystemTable
)
2507 BOOLEAN MainLoopRunning
= TRUE
;
2508 BOOLEAN MokProtocol
;
2509 REFIT_MENU_ENTRY
*ChosenEntry
;
2511 CHAR16
*Selection
= NULL
;
2515 InitializeLib(ImageHandle
, SystemTable
);
2516 Status
= InitRefitLib(ImageHandle
);
2517 if (EFI_ERROR(Status
))
2520 // read configuration
2521 CopyMem(GlobalConfig
.ScanFor
, "ieom ", NUM_SCAN_OPTIONS
);
2522 FindLegacyBootType();
2523 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
)
2524 CopyMem(GlobalConfig
.ScanFor
, "ihebocm ", NUM_SCAN_OPTIONS
);
2525 SetConfigFilename(ImageHandle
);
2526 ReadConfig(GlobalConfig
.ConfigFilename
);
2529 WarnIfLegacyProblems();
2530 MainMenu
.TimeoutSeconds
= GlobalConfig
.Timeout
;
2532 // disable EFI watchdog timer
2533 refit_call4_wrapper(BS
->SetWatchdogTimer
, 0x0000, 0x0000, 0x0000, NULL
);
2535 // further bootstrap (now with config available)
2536 MokProtocol
= SecureBootSetup();
2539 ScanForBootloaders();
2543 if (GlobalConfig
.ScanDelay
> 0) {
2548 if (GlobalConfig
.ScanDelay
> 1)
2549 egDisplayMessage(L
"Pausing before disk scan; please wait....", &BGColor
);
2550 for (i
= 0; i
< GlobalConfig
.ScanDelay
; i
++)
2551 refit_call1_wrapper(BS
->Stall
, 1000000);
2552 RescanAll(GlobalConfig
.ScanDelay
> 1);
2555 if (GlobalConfig
.DefaultSelection
)
2556 Selection
= StrDuplicate(GlobalConfig
.DefaultSelection
);
2558 while (MainLoopRunning
) {
2559 MenuExit
= RunMainMenu(&MainMenu
, Selection
, &ChosenEntry
);
2561 // The Escape key triggers a re-scan operation....
2562 if (MenuExit
== MENU_EXIT_ESCAPE
) {
2568 switch (ChosenEntry
->Tag
) {
2570 case TAG_REBOOT
: // Reboot
2572 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);
2573 MainLoopRunning
= FALSE
; // just in case we get this far
2576 case TAG_SHUTDOWN
: // Shut Down
2578 refit_call4_wrapper(RT
->ResetSystem
, EfiResetShutdown
, EFI_SUCCESS
, 0, NULL
);
2579 MainLoopRunning
= FALSE
; // just in case we get this far
2582 case TAG_ABOUT
: // About rEFInd
2586 case TAG_LOADER
: // Boot OS via .EFI loader
2587 StartLoader((LOADER_ENTRY
*)ChosenEntry
);
2590 case TAG_LEGACY
: // Boot legacy OS
2591 StartLegacy((LEGACY_ENTRY
*)ChosenEntry
);
2594 case TAG_LEGACY_UEFI
: // Boot a legacy OS on a non-Mac
2595 StartLegacyUEFI((LEGACY_ENTRY
*)ChosenEntry
);
2598 case TAG_TOOL
: // Start a EFI tool
2599 StartTool((LOADER_ENTRY
*)ChosenEntry
);
2602 case TAG_EXIT
: // Terminate rEFInd
2603 if ((MokProtocol
) && !SecureBootUninstall()) {
2604 MainLoopRunning
= FALSE
; // just in case we get this far
2606 BeginTextScreen(L
" ");
2611 case TAG_FIRMWARE
: // Reboot into firmware's user interface
2612 RebootIntoFirmware();
2616 MyFreePool(Selection
);
2617 Selection
= (ChosenEntry
->Title
) ? StrDuplicate(ChosenEntry
->Title
) : NULL
;
2620 // If we end up here, things have gone wrong. Try to reboot, and if that
2621 // fails, go into an endless loop.
2622 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);