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)
64 #include "../EfiLib/BdsHelper.h"
65 #include "../EfiLib/legacy.h"
66 #endif // __MAKEWITH_GNUEFI
68 #ifndef EFI_OS_INDICATIONS_BOOT_TO_FW_UI
69 #define EFI_OS_INDICATIONS_BOOT_TO_FW_UI 0x0000000000000001ULL
75 #define MACOSX_LOADER_PATH L"System\\Library\\CoreServices\\boot.efi"
77 #define SHELL_NAMES L"\\EFI\\tools\\shell.efi,\\EFI\\tools\\shellx64.efi,\\shell.efi,\\shellx64.efi"
78 #define GPTSYNC_NAMES L"\\EFI\\tools\\gptsync.efi,\\EFI\\tools\\gptsync_x64.efi"
79 #define GDISK_NAMES L"\\EFI\\tools\\gdisk.efi,\\EFI\\tools\\gdisk_x64.efi"
80 #define MEMTEST_NAMES L"memtest86.efi,memtest86_x64.efi,memtest86x64.efi,bootx64.efi"
81 #define DRIVER_DIRS L"drivers,drivers_x64"
82 #define FALLBACK_FULLNAME L"EFI\\BOOT\\bootx64.efi"
83 #define FALLBACK_BASENAME L"bootx64.efi"
84 #define EFI_STUB_ARCH 0x8664
86 #define SHELL_NAMES L"\\EFI\\tools\\shell.efi,\\EFI\\tools\\shellia32.efi,\\shell.efi,\\shellia32.efi"
87 #define GPTSYNC_NAMES L"\\EFI\\tools\\gptsync.efi,\\EFI\\tools\\gptsync_ia32.efi"
88 #define GDISK_NAMES L"\\EFI\\tools\\gdisk.efi,\\EFI\\tools\\gdisk_ia32.efi"
89 #define MEMTEST_NAMES L"memtest86.efi,memtest86_ia32.efi,memtest86ia32.efi,bootia32.efi"
90 #define DRIVER_DIRS L"drivers,drivers_ia32"
91 #define FALLBACK_FULLNAME L"EFI\\BOOT\\bootia32.efi"
92 #define FALLBACK_BASENAME L"bootia32.efi"
93 #define EFI_STUB_ARCH 0x014c
95 #define SHELL_NAMES L"\\EFI\\tools\\shell.efi,\\shell.efi"
96 #define GPTSYNC_NAMES L"\\EFI\\tools\\gptsync.efi"
97 #define GDISK_NAMES L"\\EFI\\tools\\gdisk.efi"
98 #define MEMTEST_NAMES L"memtest86.efi"
99 #define DRIVER_DIRS L"drivers"
100 #define FALLBACK_FULLNAME L"EFI\\BOOT\\boot.efi" /* Not really correct */
101 #define FALLBACK_BASENAME L"boot.efi" /* Not really correct */
103 #define FAT_ARCH 0x0ef1fab9 /* ID for Apple "fat" binary */
105 // Filename patterns that identify EFI boot loaders. Note that a single case (either L"*.efi" or
106 // L"*.EFI") is fine for most systems; but Gigabyte's buggy Hybrid EFI does a case-sensitive
107 // comparison when it should do a case-insensitive comparison, so I'm doubling this up. It does
108 // no harm on other computers, AFAIK. In theory, every case variation should be done for
109 // completeness, but that's ridiculous....
110 #define LOADER_MATCH_PATTERNS L"*.efi,*.EFI"
112 // Patterns that identify Linux kernels. Added to the loader match pattern when the
113 // scan_all_linux_kernels option is set in the configuration file. Causes kernels WITHOUT
114 // a ".efi" extension to be found when scanning for boot loaders.
115 #define LINUX_MATCH_PATTERNS L"vmlinuz*,bzImage*"
117 // Default hint text for program-launch submenus
118 #define SUBSCREEN_HINT1 L"Use arrow keys to move cursor; Enter to boot;"
119 #define SUBSCREEN_HINT2 L"Insert or F2 to edit options; Esc to return to main menu"
120 #define SUBSCREEN_HINT2_NO_EDITOR L"Esc to return to main menu"
124 #define TYPE_LEGACY 2
126 static REFIT_MENU_ENTRY MenuEntryAbout
= { L
"About rEFInd", TAG_ABOUT
, 1, 0, 'A', NULL
, NULL
, NULL
};
127 static REFIT_MENU_ENTRY MenuEntryReset
= { L
"Reboot Computer", TAG_REBOOT
, 1, 0, 'R', NULL
, NULL
, NULL
};
128 static REFIT_MENU_ENTRY MenuEntryShutdown
= { L
"Shut Down Computer", TAG_SHUTDOWN
, 1, 0, 'U', NULL
, NULL
, NULL
};
129 static REFIT_MENU_ENTRY MenuEntryReturn
= { L
"Return to Main Menu", TAG_RETURN
, 1, 0, 0, NULL
, NULL
, NULL
};
130 static REFIT_MENU_ENTRY MenuEntryExit
= { L
"Exit rEFInd", TAG_EXIT
, 1, 0, 0, NULL
, NULL
, NULL
};
131 static REFIT_MENU_ENTRY MenuEntryFirmware
= { L
"Reboot to Computer Setup Utility", TAG_FIRMWARE
, 1, 0, 0, NULL
, NULL
, NULL
};
133 static REFIT_MENU_SCREEN MainMenu
= { L
"Main Menu", NULL
, 0, NULL
, 0, NULL
, 0, L
"Automatic boot",
134 L
"Use arrow keys to move cursor; Enter to boot;",
135 L
"Insert or F2 for more options; Esc to refresh" };
136 static REFIT_MENU_SCREEN AboutMenu
= { L
"About", NULL
, 0, NULL
, 0, NULL
, 0, NULL
, L
"Press Enter to return to main menu", L
"" };
138 REFIT_CONFIG GlobalConfig
= { FALSE
, FALSE
, 0, 0, 0, DONT_CHANGE_TEXT_MODE
, 20, 0, 0, GRAPHICS_FOR_OSX
, LEGACY_TYPE_MAC
, 0, 0,
139 { DEFAULT_BIG_ICON_SIZE
/ 4, DEFAULT_SMALL_ICON_SIZE
, DEFAULT_BIG_ICON_SIZE
}, BANNER_NOSCALE
,
140 NULL
, NULL
, CONFIG_FILE_NAME
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
141 { TAG_SHELL
, TAG_MEMTEST
, TAG_GDISK
, TAG_APPLE_RECOVERY
, TAG_WINDOWS_RECOVERY
, TAG_MOK_TOOL
,
142 TAG_ABOUT
, TAG_SHUTDOWN
, TAG_REBOOT
, TAG_FIRMWARE
, 0, 0, 0, 0, 0, 0 }
145 EFI_GUID GlobalGuid
= EFI_GLOBAL_VARIABLE
;
147 GPT_DATA
*gPartitions
= NULL
;
149 // Structure used to hold boot loader filenames and time stamps in
150 // a linked list; used to sort entries within a directory.
154 struct LOADER_LIST
*NextEntry
;
161 static VOID
AboutrEFInd(VOID
)
163 if (AboutMenu
.EntryCount
== 0) {
164 AboutMenu
.TitleImage
= BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT
);
165 AddMenuInfoLine(&AboutMenu
, L
"rEFInd Version 0.7.9.4");
166 AddMenuInfoLine(&AboutMenu
, L
"");
167 AddMenuInfoLine(&AboutMenu
, L
"Copyright (c) 2006-2010 Christoph Pfisterer");
168 AddMenuInfoLine(&AboutMenu
, L
"Copyright (c) 2012-2014 Roderick W. Smith");
169 AddMenuInfoLine(&AboutMenu
, L
"Portions Copyright (c) Intel Corporation and others");
170 AddMenuInfoLine(&AboutMenu
, L
"Distributed under the terms of the GNU GPLv3 license");
171 AddMenuInfoLine(&AboutMenu
, L
"");
172 AddMenuInfoLine(&AboutMenu
, L
"Running on:");
173 AddMenuInfoLine(&AboutMenu
, PoolPrint(L
" EFI Revision %d.%02d", ST
->Hdr
.Revision
>> 16, ST
->Hdr
.Revision
& ((1 << 16) - 1)));
175 AddMenuInfoLine(&AboutMenu
, L
" Platform: x86 (32 bit)");
176 #elif defined(EFIX64)
177 AddMenuInfoLine(&AboutMenu
, PoolPrint(L
" Platform: x86_64 (64 bit); Secure Boot %s",
178 secure_mode() ? L
"active" : L
"inactive"));
180 AddMenuInfoLine(&AboutMenu
, L
" Platform: unknown");
182 AddMenuInfoLine(&AboutMenu
, PoolPrint(L
" Firmware: %s %d.%02d", ST
->FirmwareVendor
, ST
->FirmwareRevision
>> 16,
183 ST
->FirmwareRevision
& ((1 << 16) - 1)));
184 AddMenuInfoLine(&AboutMenu
, PoolPrint(L
" Screen Output: %s", egScreenDescription()));
185 AddMenuInfoLine(&AboutMenu
, L
"");
186 #if defined(__MAKEWITH_GNUEFI)
187 AddMenuInfoLine(&AboutMenu
, L
"Built with GNU-EFI");
189 AddMenuInfoLine(&AboutMenu
, L
"Built with TianoCore EDK2");
191 AddMenuInfoLine(&AboutMenu
, L
"");
192 AddMenuInfoLine(&AboutMenu
, L
"For more information, see the rEFInd Web site:");
193 AddMenuInfoLine(&AboutMenu
, L
"http://www.rodsbooks.com/refind/");
194 AddMenuEntry(&AboutMenu
, &MenuEntryReturn
);
197 RunMenu(&AboutMenu
, NULL
);
198 } /* VOID AboutrEFInd() */
200 static VOID
WarnSecureBootError(CHAR16
*Name
, BOOLEAN Verbose
) {
202 Name
= L
"the loader";
204 refit_call2_wrapper(ST
->ConOut
->SetAttribute
, ST
->ConOut
, ATTR_ERROR
);
205 Print(L
"Secure Boot validation failure loading %s!\n", Name
);
206 refit_call2_wrapper(ST
->ConOut
->SetAttribute
, ST
->ConOut
, ATTR_BASIC
);
207 if (Verbose
&& secure_mode()) {
208 Print(L
"\nThis computer is configured with Secure Boot active, but\n%s has failed validation.\n", Name
);
209 Print(L
"\nYou can:\n * Launch another boot loader\n");
210 Print(L
" * Disable Secure Boot in your firmware\n");
211 Print(L
" * Sign %s with a machine owner key (MOK)\n", Name
);
212 Print(L
" * Use a MOK utility (often present on the second row) to add a MOK with which\n");
213 Print(L
" %s has already been signed.\n", Name
);
214 Print(L
" * Use a MOK utility to register %s (\"enroll its hash\") without\n", Name
);
215 Print(L
" signing it.\n");
216 Print(L
"\nSee http://www.rodsbooks.com/refind/secureboot.html for more information\n");
219 } // VOID WarnSecureBootError()
221 // Returns TRUE if this file is a valid EFI loader file, and is proper ARCH
222 static BOOLEAN
IsValidLoader(EFI_FILE
*RootDir
, CHAR16
*FileName
) {
223 BOOLEAN IsValid
= TRUE
;
224 #if defined (EFIX64) | defined (EFI32)
226 EFI_FILE_HANDLE FileHandle
;
228 UINTN Size
= sizeof(Header
);
230 if ((RootDir
== NULL
) || (FileName
== NULL
)) {
231 // Assume valid here, because Macs produce NULL RootDir (& maybe FileName)
232 // when launching from a Firewire drive. This should be handled better, but
233 // fix would have to be in StartEFIImageList() and/or in FindVolumeAndFilename().
237 Status
= refit_call5_wrapper(RootDir
->Open
, RootDir
, &FileHandle
, FileName
, EFI_FILE_MODE_READ
, 0);
238 if (EFI_ERROR(Status
))
241 Status
= refit_call3_wrapper(FileHandle
->Read
, FileHandle
, &Size
, Header
);
242 refit_call1_wrapper(FileHandle
->Close
, FileHandle
);
244 IsValid
= !EFI_ERROR(Status
) &&
245 Size
== sizeof(Header
) &&
246 ((Header
[0] == 'M' && Header
[1] == 'Z' &&
247 (Size
= *(UINT32
*)&Header
[0x3c]) < 0x180 &&
248 Header
[Size
] == 'P' && Header
[Size
+1] == 'E' &&
249 Header
[Size
+2] == 0 && Header
[Size
+3] == 0 &&
250 *(UINT16
*)&Header
[Size
+4] == EFI_STUB_ARCH
) ||
251 (*(UINT32
*)&Header
== FAT_ARCH
));
254 } // BOOLEAN IsValidLoader()
256 // Launch an EFI binary.
257 static EFI_STATUS
StartEFIImageList(IN EFI_DEVICE_PATH
**DevicePaths
,
258 IN CHAR16
*LoadOptions
, IN UINTN LoaderType
,
259 IN CHAR16
*ImageTitle
, IN CHAR8 OSType
,
260 OUT UINTN
*ErrorInStep
,
263 EFI_STATUS Status
, ReturnStatus
;
264 EFI_HANDLE ChildImageHandle
;
265 EFI_LOADED_IMAGE
*ChildLoadedImage
= NULL
;
266 REFIT_VOLUME
*Volume
= NULL
;
267 UINTN DevicePathIndex
;
268 CHAR16 ErrorInfo
[256];
269 CHAR16
*FullLoadOptions
= NULL
;
270 CHAR16
*Filename
= NULL
;
273 if (ErrorInStep
!= NULL
)
277 if (LoadOptions
!= NULL
) {
278 FullLoadOptions
= StrDuplicate(LoadOptions
);
279 if ((LoaderType
== TYPE_EFI
) && (OSType
== 'M')) {
280 MergeStrings(&FullLoadOptions
, L
" ", 0);
281 // NOTE: That last space is also added by the EFI shell and seems to be significant
282 // when passing options to Apple's boot.efi...
284 } // if (LoadOptions != NULL)
286 Print(L
"Starting %s\nUsing load options '%s'\n", ImageTitle
, FullLoadOptions
? FullLoadOptions
: L
"");
288 // load the image into memory (and execute it, in the case of a shim/MOK image).
289 ReturnStatus
= Status
= EFI_NOT_FOUND
; // in case the list is empty
290 for (DevicePathIndex
= 0; DevicePaths
[DevicePathIndex
] != NULL
; DevicePathIndex
++) {
291 FindVolumeAndFilename(DevicePaths
[DevicePathIndex
], &Volume
, &Filename
);
292 // Some EFIs crash if attempting to load driver for invalid architecture, so
293 // protect for this condition; but sometimes Volume comes back NULL, so provide
294 // an exception. (TODO: Handle this special condition better.)
295 if ((LoaderType
== TYPE_LEGACY
) || (Volume
== NULL
) || IsValidLoader(Volume
->RootDir
, Filename
)) {
296 if (Filename
&& (LoaderType
!= TYPE_LEGACY
)) {
297 Temp
= PoolPrint(L
"\\%s %s", Filename
, FullLoadOptions
? FullLoadOptions
: L
"");
299 MyFreePool(FullLoadOptions
);
300 FullLoadOptions
= Temp
;
304 // NOTE: Below commented-out line could be more efficient if file were read ahead of
305 // time and passed as a pre-loaded image to LoadImage(), but it doesn't work on my
306 // 32-bit Mac Mini or my 64-bit Intel box when launching a Linux kernel; the
307 // kernel returns a "Failed to handle fs_proto" error message.
308 // TODO: Track down the cause of this error and fix it, if possible.
309 // ReturnStatus = Status = refit_call6_wrapper(BS->LoadImage, FALSE, SelfImageHandle, DevicePaths[DevicePathIndex],
310 // ImageData, ImageSize, &ChildImageHandle);
311 ReturnStatus
= Status
= refit_call6_wrapper(BS
->LoadImage
, FALSE
, SelfImageHandle
, DevicePaths
[DevicePathIndex
],
312 NULL
, 0, &ChildImageHandle
);
314 Print(L
"Invalid loader file!\n");
315 ReturnStatus
= EFI_LOAD_ERROR
;
317 if (ReturnStatus
!= EFI_NOT_FOUND
) {
321 if ((Status
== EFI_ACCESS_DENIED
) || (Status
== EFI_SECURITY_VIOLATION
)) {
322 WarnSecureBootError(ImageTitle
, Verbose
);
325 SPrint(ErrorInfo
, 255, L
"while loading %s", ImageTitle
);
326 if (CheckError(Status
, ErrorInfo
)) {
327 if (ErrorInStep
!= NULL
)
332 ReturnStatus
= Status
= refit_call3_wrapper(BS
->HandleProtocol
, ChildImageHandle
, &LoadedImageProtocol
,
333 (VOID
**) &ChildLoadedImage
);
334 if (CheckError(Status
, L
"while getting a LoadedImageProtocol handle")) {
335 if (ErrorInStep
!= NULL
)
339 ChildLoadedImage
->LoadOptions
= (VOID
*)FullLoadOptions
;
340 ChildLoadedImage
->LoadOptionsSize
= FullLoadOptions
? ((UINT32
)StrLen(FullLoadOptions
) + 1) * sizeof(CHAR16
) : 0;
341 // turn control over to the image
342 // TODO: (optionally) re-enable the EFI watchdog timer!
344 // close open file handles
346 ReturnStatus
= Status
= refit_call3_wrapper(BS
->StartImage
, ChildImageHandle
, NULL
, NULL
);
348 // control returns here when the child image calls Exit()
349 SPrint(ErrorInfo
, 255, L
"returned from %s", ImageTitle
);
350 if (CheckError(Status
, ErrorInfo
)) {
351 if (ErrorInStep
!= NULL
)
355 // re-open file handles
359 // unload the image, we don't care if it works or not...
360 Status
= refit_call1_wrapper(BS
->UnloadImage
, ChildImageHandle
);
363 MyFreePool(FullLoadOptions
);
365 } /* static EFI_STATUS StartEFIImageList() */
367 static EFI_STATUS
StartEFIImage(IN EFI_DEVICE_PATH
*DevicePath
,
368 IN CHAR16
*LoadOptions
, IN UINTN LoaderType
,
369 IN CHAR16
*ImageTitle
, IN CHAR8 OSType
,
370 OUT UINTN
*ErrorInStep
,
373 EFI_DEVICE_PATH
*DevicePaths
[2];
375 DevicePaths
[0] = DevicePath
;
376 DevicePaths
[1] = NULL
;
377 return StartEFIImageList(DevicePaths
, LoadOptions
, LoaderType
, ImageTitle
, OSType
, ErrorInStep
, Verbose
);
378 } /* static EFI_STATUS StartEFIImage() */
380 // From gummiboot: Retrieve a raw EFI variable.
381 // Returns EFI status
382 static EFI_STATUS
EfivarGetRaw(EFI_GUID
*vendor
, CHAR16
*name
, CHAR8
**buffer
, UINTN
*size
) {
387 l
= sizeof(CHAR16
*) * EFI_MAXIMUM_VARIABLE_SIZE
;
388 buf
= AllocatePool(l
);
390 return EFI_OUT_OF_RESOURCES
;
392 err
= refit_call5_wrapper(RT
->GetVariable
, name
, vendor
, NULL
, &l
, buf
);
393 if (EFI_ERROR(err
) == EFI_SUCCESS
) {
400 } // EFI_STATUS EfivarGetRaw()
402 // From gummiboot: Set an EFI variable
403 static EFI_STATUS
EfivarSetRaw(EFI_GUID
*vendor
, CHAR16
*name
, CHAR8
*buf
, UINTN size
, BOOLEAN persistent
) {
406 flags
= EFI_VARIABLE_BOOTSERVICE_ACCESS
|EFI_VARIABLE_RUNTIME_ACCESS
;
408 flags
|= EFI_VARIABLE_NON_VOLATILE
;
410 return refit_call5_wrapper(RT
->SetVariable
, name
, vendor
, flags
, size
, buf
);
411 } // EFI_STATUS EfivarSetRaw()
413 // From gummiboot: Reboot the computer into its built-in user interface
414 static EFI_STATUS
RebootIntoFirmware(VOID
) {
420 osind
= EFI_OS_INDICATIONS_BOOT_TO_FW_UI
;
422 err
= EfivarGetRaw(&GlobalGuid
, L
"OsIndications", &b
, &size
);
423 if (err
== EFI_SUCCESS
)
427 err
= EfivarSetRaw(&GlobalGuid
, L
"OsIndications", (CHAR8
*)&osind
, sizeof(UINT64
), TRUE
);
428 if (err
!= EFI_SUCCESS
)
431 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);
432 Print(L
"Error calling ResetSystem: %r", err
);
439 // EFI OS loader functions
442 static VOID
StartLoader(LOADER_ENTRY
*Entry
)
444 UINTN ErrorInStep
= 0;
446 BeginExternalScreen(Entry
->UseGraphicsMode
, L
"Booting OS");
447 StartEFIImage(Entry
->DevicePath
, Entry
->LoadOptions
, TYPE_EFI
,
448 Basename(Entry
->LoaderPath
), Entry
->OSType
, &ErrorInStep
, !Entry
->UseGraphicsMode
);
449 FinishExternalScreen();
452 // Locate an initrd or initramfs file that matches the kernel specified by LoaderPath.
453 // The matching file has a name that begins with "init" and includes the same version
454 // number string as is found in LoaderPath -- but not a longer version number string.
455 // For instance, if LoaderPath is \EFI\kernels\bzImage-3.3.0.efi, and if \EFI\kernels
456 // has a file called initramfs-3.3.0.img, this function will return the string
457 // '\EFI\kernels\initramfs-3.3.0.img'. If the directory ALSO contains the file
458 // initramfs-3.3.0-rc7.img or initramfs-13.3.0.img, those files will NOT match;
459 // however, initmine-3.3.0.img might match. (FindInitrd() returns the first match it
460 // finds). Thus, care should be taken to avoid placing duplicate matching files in
461 // the kernel's directory.
462 // If no matching init file can be found, returns NULL.
463 static CHAR16
* FindInitrd(IN CHAR16
*LoaderPath
, IN REFIT_VOLUME
*Volume
) {
464 CHAR16
*InitrdName
= NULL
, *FileName
, *KernelVersion
, *InitrdVersion
, *Path
;
465 REFIT_DIR_ITER DirIter
;
466 EFI_FILE_INFO
*DirEntry
;
468 FileName
= Basename(LoaderPath
);
469 KernelVersion
= FindNumbers(FileName
);
470 Path
= FindPath(LoaderPath
);
472 // Add trailing backslash for root directory; necessary on some systems, but must
473 // NOT be added to all directories, since on other systems, a trailing backslash on
474 // anything but the root directory causes them to flake out!
475 if (StrLen(Path
) == 0) {
476 MergeStrings(&Path
, L
"\\", 0);
478 DirIterOpen(Volume
->RootDir
, Path
, &DirIter
);
479 // Now add a trailing backslash if it was NOT added earlier, for consistency in
480 // building the InitrdName later....
481 if ((StrLen(Path
) > 0) && (Path
[StrLen(Path
) - 1] != L
'\\'))
482 MergeStrings(&Path
, L
"\\", 0);
483 while ((DirIterNext(&DirIter
, 2, L
"init*", &DirEntry
)) && (InitrdName
== NULL
)) {
484 InitrdVersion
= FindNumbers(DirEntry
->FileName
);
485 if (KernelVersion
!= NULL
) {
486 if (StriCmp(InitrdVersion
, KernelVersion
) == 0) {
487 InitrdName
= PoolPrint(L
"%s%s", Path
, DirEntry
->FileName
);
490 if (InitrdVersion
== NULL
) {
491 InitrdName
= PoolPrint(L
"%s%s", Path
, DirEntry
->FileName
);
494 MyFreePool(InitrdVersion
);
496 DirIterClose(&DirIter
);
498 // Note: Don't FreePool(FileName), since Basename returns a pointer WITHIN the string it's passed.
499 MyFreePool(KernelVersion
);
502 } // static CHAR16 * FindInitrd()
504 LOADER_ENTRY
* AddPreparedLoaderEntry(LOADER_ENTRY
*Entry
) {
505 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
508 } // LOADER_ENTRY * AddPreparedLoaderEntry()
510 // Creates a copy of a menu screen.
511 // Returns a pointer to the copy of the menu screen.
512 static REFIT_MENU_SCREEN
* CopyMenuScreen(REFIT_MENU_SCREEN
*Entry
) {
513 REFIT_MENU_SCREEN
*NewEntry
;
516 NewEntry
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
517 if ((Entry
!= NULL
) && (NewEntry
!= NULL
)) {
518 CopyMem(NewEntry
, Entry
, sizeof(REFIT_MENU_SCREEN
));
519 NewEntry
->Title
= (Entry
->Title
) ? StrDuplicate(Entry
->Title
) : NULL
;
520 NewEntry
->TimeoutText
= (Entry
->TimeoutText
) ? StrDuplicate(Entry
->TimeoutText
) : NULL
;
521 if (Entry
->TitleImage
!= NULL
) {
522 NewEntry
->TitleImage
= AllocatePool(sizeof(EG_IMAGE
));
523 if (NewEntry
->TitleImage
!= NULL
)
524 CopyMem(NewEntry
->TitleImage
, Entry
->TitleImage
, sizeof(EG_IMAGE
));
526 NewEntry
->InfoLines
= (CHAR16
**) AllocateZeroPool(Entry
->InfoLineCount
* (sizeof(CHAR16
*)));
527 for (i
= 0; i
< Entry
->InfoLineCount
&& NewEntry
->InfoLines
; i
++) {
528 NewEntry
->InfoLines
[i
] = (Entry
->InfoLines
[i
]) ? StrDuplicate(Entry
->InfoLines
[i
]) : NULL
;
530 NewEntry
->Entries
= (REFIT_MENU_ENTRY
**) AllocateZeroPool(Entry
->EntryCount
* (sizeof (REFIT_MENU_ENTRY
*)));
531 for (i
= 0; i
< Entry
->EntryCount
&& NewEntry
->Entries
; i
++) {
532 AddMenuEntry(NewEntry
, Entry
->Entries
[i
]);
534 NewEntry
->Hint1
= (Entry
->Hint1
) ? StrDuplicate(Entry
->Hint1
) : NULL
;
535 NewEntry
->Hint2
= (Entry
->Hint2
) ? StrDuplicate(Entry
->Hint2
) : NULL
;
538 } // static REFIT_MENU_SCREEN* CopyMenuScreen()
540 // Creates a copy of a menu entry. Intended to enable moving a stack-based
541 // menu entry (such as the ones for the "reboot" and "exit" functions) to
542 // to the heap. This enables easier deletion of the whole set of menu
543 // entries when re-scanning.
544 // Returns a pointer to the copy of the menu entry.
545 static REFIT_MENU_ENTRY
* CopyMenuEntry(REFIT_MENU_ENTRY
*Entry
) {
546 REFIT_MENU_ENTRY
*NewEntry
;
548 NewEntry
= AllocateZeroPool(sizeof(REFIT_MENU_ENTRY
));
549 if ((Entry
!= NULL
) && (NewEntry
!= NULL
)) {
550 CopyMem(NewEntry
, Entry
, sizeof(REFIT_MENU_ENTRY
));
551 NewEntry
->Title
= (Entry
->Title
) ? StrDuplicate(Entry
->Title
) : NULL
;
552 if (Entry
->BadgeImage
!= NULL
) {
553 NewEntry
->BadgeImage
= AllocatePool(sizeof(EG_IMAGE
));
554 if (NewEntry
->BadgeImage
!= NULL
)
555 CopyMem(NewEntry
->BadgeImage
, Entry
->BadgeImage
, sizeof(EG_IMAGE
));
557 if (Entry
->Image
!= NULL
) {
558 NewEntry
->Image
= AllocatePool(sizeof(EG_IMAGE
));
559 if (NewEntry
->Image
!= NULL
)
560 CopyMem(NewEntry
->Image
, Entry
->Image
, sizeof(EG_IMAGE
));
562 if (Entry
->SubScreen
!= NULL
) {
563 NewEntry
->SubScreen
= CopyMenuScreen(Entry
->SubScreen
);
567 } // REFIT_MENU_ENTRY* CopyMenuEntry()
569 // Creates a new LOADER_ENTRY data structure and populates it with
570 // default values from the specified Entry, or NULL values if Entry
571 // is unspecified (NULL).
572 // Returns a pointer to the new data structure, or NULL if it
573 // couldn't be allocated
574 LOADER_ENTRY
*InitializeLoaderEntry(IN LOADER_ENTRY
*Entry
) {
575 LOADER_ENTRY
*NewEntry
= NULL
;
577 NewEntry
= AllocateZeroPool(sizeof(LOADER_ENTRY
));
578 if (NewEntry
!= NULL
) {
579 NewEntry
->me
.Title
= NULL
;
580 NewEntry
->me
.Tag
= TAG_LOADER
;
581 NewEntry
->Enabled
= TRUE
;
582 NewEntry
->UseGraphicsMode
= FALSE
;
583 NewEntry
->OSType
= 0;
585 NewEntry
->LoaderPath
= (Entry
->LoaderPath
) ? StrDuplicate(Entry
->LoaderPath
) : NULL
;
586 NewEntry
->VolName
= (Entry
->VolName
) ? StrDuplicate(Entry
->VolName
) : NULL
;
587 NewEntry
->DevicePath
= Entry
->DevicePath
;
588 NewEntry
->UseGraphicsMode
= Entry
->UseGraphicsMode
;
589 NewEntry
->LoadOptions
= (Entry
->LoadOptions
) ? StrDuplicate(Entry
->LoadOptions
) : NULL
;
590 NewEntry
->InitrdPath
= (Entry
->InitrdPath
) ? StrDuplicate(Entry
->InitrdPath
) : NULL
;
594 } // LOADER_ENTRY *InitializeLoaderEntry()
596 // Adds InitrdPath to Options, but only if Options doesn't already include an
597 // initrd= line. Done to enable overriding the default initrd selection in a
598 // refind_linux.conf file's options list.
599 // Returns a pointer to a new string. The calling function is responsible for
600 // freeing its memory.
601 static CHAR16
*AddInitrdToOptions(CHAR16
*Options
, CHAR16
*InitrdPath
) {
602 CHAR16
*NewOptions
= NULL
;
605 NewOptions
= StrDuplicate(Options
);
606 if ((InitrdPath
!= NULL
) && !StriSubCmp(L
"initrd=", Options
)) {
607 MergeStrings(&NewOptions
, L
"initrd=", L
' ');
608 MergeStrings(&NewOptions
, InitrdPath
, 0);
611 } // CHAR16 *AddInitrdToOptions()
613 // Prepare a REFIT_MENU_SCREEN data structure for a subscreen entry. This sets up
614 // the default entry that launches the boot loader using the same options as the
615 // main Entry does. Subsequent options can be added by the calling function.
616 // If a subscreen already exists in the Entry that's passed to this function,
617 // it's left unchanged and a pointer to it is returned.
618 // Returns a pointer to the new subscreen data structure, or NULL if there
619 // were problems allocating memory.
620 REFIT_MENU_SCREEN
*InitializeSubScreen(IN LOADER_ENTRY
*Entry
) {
621 CHAR16
*FileName
, *MainOptions
= NULL
;
622 REFIT_MENU_SCREEN
*SubScreen
= NULL
;
623 LOADER_ENTRY
*SubEntry
;
625 FileName
= Basename(Entry
->LoaderPath
);
626 if (Entry
->me
.SubScreen
== NULL
) { // No subscreen yet; initialize default entry....
627 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
628 if (SubScreen
!= NULL
) {
629 SubScreen
->Title
= AllocateZeroPool(sizeof(CHAR16
) * 256);
630 SPrint(SubScreen
->Title
, 255, L
"Boot Options for %s on %s",
631 (Entry
->Title
!= NULL
) ? Entry
->Title
: FileName
, Entry
->VolName
);
632 SubScreen
->TitleImage
= Entry
->me
.Image
;
634 SubEntry
= InitializeLoaderEntry(Entry
);
635 if (SubEntry
!= NULL
) {
636 SubEntry
->me
.Title
= StrDuplicate(L
"Boot using default options");
637 MainOptions
= SubEntry
->LoadOptions
;
638 SubEntry
->LoadOptions
= AddInitrdToOptions(MainOptions
, SubEntry
->InitrdPath
);
639 MyFreePool(MainOptions
);
640 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
641 } // if (SubEntry != NULL)
642 SubScreen
->Hint1
= StrDuplicate(SUBSCREEN_HINT1
);
643 if (GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_EDITOR
) {
644 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2_NO_EDITOR
);
646 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2
);
648 } // if (SubScreen != NULL)
649 } else { // existing subscreen; less initialization, and just add new entry later....
650 SubScreen
= Entry
->me
.SubScreen
;
653 } // REFIT_MENU_SCREEN *InitializeSubScreen()
655 VOID
GenerateSubScreen(LOADER_ENTRY
*Entry
, IN REFIT_VOLUME
*Volume
) {
656 REFIT_MENU_SCREEN
*SubScreen
;
657 LOADER_ENTRY
*SubEntry
;
659 CHAR16 DiagsFileName
[256];
664 // create the submenu
665 if (StrLen(Entry
->Title
) == 0) {
666 MyFreePool(Entry
->Title
);
669 SubScreen
= InitializeSubScreen(Entry
);
671 // loader-specific submenu entries
672 if (Entry
->OSType
== 'M') { // entries for Mac OS X
674 SubEntry
= InitializeLoaderEntry(Entry
);
675 if (SubEntry
!= NULL
) {
676 SubEntry
->me
.Title
= L
"Boot Mac OS X with a 64-bit kernel";
677 SubEntry
->LoadOptions
= L
"arch=x86_64";
678 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
679 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
682 SubEntry
= InitializeLoaderEntry(Entry
);
683 if (SubEntry
!= NULL
) {
684 SubEntry
->me
.Title
= L
"Boot Mac OS X with a 32-bit kernel";
685 SubEntry
->LoadOptions
= L
"arch=i386";
686 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
687 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
691 if (!(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_SINGLEUSER
)) {
692 SubEntry
= InitializeLoaderEntry(Entry
);
693 if (SubEntry
!= NULL
) {
694 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode";
695 SubEntry
->UseGraphicsMode
= FALSE
;
696 SubEntry
->LoadOptions
= L
"-v";
697 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
701 SubEntry
= InitializeLoaderEntry(Entry
);
702 if (SubEntry
!= NULL
) {
703 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode (64-bit)";
704 SubEntry
->UseGraphicsMode
= FALSE
;
705 SubEntry
->LoadOptions
= L
"-v arch=x86_64";
706 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
709 SubEntry
= InitializeLoaderEntry(Entry
);
710 if (SubEntry
!= NULL
) {
711 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode (32-bit)";
712 SubEntry
->UseGraphicsMode
= FALSE
;
713 SubEntry
->LoadOptions
= L
"-v arch=i386";
714 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
718 SubEntry
= InitializeLoaderEntry(Entry
);
719 if (SubEntry
!= NULL
) {
720 SubEntry
->me
.Title
= L
"Boot Mac OS X in single user mode";
721 SubEntry
->UseGraphicsMode
= FALSE
;
722 SubEntry
->LoadOptions
= L
"-v -s";
723 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
725 } // single-user mode allowed
727 if (!(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_SAFEMODE
)) {
728 SubEntry
= InitializeLoaderEntry(Entry
);
729 if (SubEntry
!= NULL
) {
730 SubEntry
->me
.Title
= L
"Boot Mac OS X in safe mode";
731 SubEntry
->UseGraphicsMode
= FALSE
;
732 SubEntry
->LoadOptions
= L
"-v -x";
733 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
735 } // safe mode allowed
737 // check for Apple hardware diagnostics
738 StrCpy(DiagsFileName
, L
"System\\Library\\CoreServices\\.diagnostics\\diags.efi");
739 if (FileExists(Volume
->RootDir
, DiagsFileName
) && !(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_HWTEST
)) {
740 SubEntry
= InitializeLoaderEntry(Entry
);
741 if (SubEntry
!= NULL
) {
742 SubEntry
->me
.Title
= L
"Run Apple Hardware Test";
743 MyFreePool(SubEntry
->LoaderPath
);
744 SubEntry
->LoaderPath
= StrDuplicate(DiagsFileName
);
745 SubEntry
->DevicePath
= FileDevicePath(Volume
->DeviceHandle
, SubEntry
->LoaderPath
);
746 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
747 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
749 } // if diagnostics entry found
751 } else if (Entry
->OSType
== 'L') { // entries for Linux kernels with EFI stub loaders
752 File
= ReadLinuxOptionsFile(Entry
->LoaderPath
, Volume
);
754 InitrdName
= FindInitrd(Entry
->LoaderPath
, Volume
);
755 TokenCount
= ReadTokenLine(File
, &TokenList
);
756 // first entry requires special processing, since it was initially set
757 // up with a default title but correct options by InitializeSubScreen(),
759 if ((SubScreen
->Entries
!= NULL
) && (SubScreen
->Entries
[0] != NULL
)) {
760 MyFreePool(SubScreen
->Entries
[0]->Title
);
761 SubScreen
->Entries
[0]->Title
= TokenList
[0] ? StrDuplicate(TokenList
[0]) : StrDuplicate(L
"Boot Linux");
763 FreeTokenLine(&TokenList
, &TokenCount
);
764 while ((TokenCount
= ReadTokenLine(File
, &TokenList
)) > 1) {
765 SubEntry
= InitializeLoaderEntry(Entry
);
766 SubEntry
->me
.Title
= TokenList
[0] ? StrDuplicate(TokenList
[0]) : StrDuplicate(L
"Boot Linux");
767 MyFreePool(SubEntry
->LoadOptions
);
768 SubEntry
->LoadOptions
= AddInitrdToOptions(TokenList
[1], InitrdName
);
769 FreeTokenLine(&TokenList
, &TokenCount
);
770 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_LINUX
;
771 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
773 MyFreePool(InitrdName
);
777 } else if (Entry
->OSType
== 'E') { // entries for ELILO
778 SubEntry
= InitializeLoaderEntry(Entry
);
779 if (SubEntry
!= NULL
) {
780 SubEntry
->me
.Title
= L
"Run ELILO in interactive mode";
781 SubEntry
->LoadOptions
= L
"-p";
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 17\" iMac or a 15\" MacBook Pro (*)";
789 SubEntry
->UseGraphicsMode
= TRUE
;
790 SubEntry
->LoadOptions
= L
"-d 0 i17";
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 20\" iMac (*)";
798 SubEntry
->UseGraphicsMode
= TRUE
;
799 SubEntry
->LoadOptions
= L
"-d 0 i20";
800 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
801 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
804 SubEntry
= InitializeLoaderEntry(Entry
);
805 if (SubEntry
!= NULL
) {
806 SubEntry
->me
.Title
= L
"Boot Linux for a Mac Mini (*)";
807 SubEntry
->UseGraphicsMode
= TRUE
;
808 SubEntry
->LoadOptions
= L
"-d 0 mini";
809 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
810 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
813 AddMenuInfoLine(SubScreen
, L
"NOTE: This is an example. Entries");
814 AddMenuInfoLine(SubScreen
, L
"marked with (*) may not work.");
816 } else if (Entry
->OSType
== 'X') { // entries for xom.efi
817 // by default, skip the built-in selection and boot from hard disk only
818 Entry
->LoadOptions
= L
"-s -h";
820 SubEntry
= InitializeLoaderEntry(Entry
);
821 if (SubEntry
!= NULL
) {
822 SubEntry
->me
.Title
= L
"Boot Windows from Hard Disk";
823 SubEntry
->LoadOptions
= L
"-s -h";
824 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
825 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
828 SubEntry
= InitializeLoaderEntry(Entry
);
829 if (SubEntry
!= NULL
) {
830 SubEntry
->me
.Title
= L
"Boot Windows from CD-ROM";
831 SubEntry
->LoadOptions
= L
"-s -c";
832 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
833 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
836 SubEntry
= InitializeLoaderEntry(Entry
);
837 if (SubEntry
!= NULL
) {
838 SubEntry
->me
.Title
= L
"Run XOM in text mode";
839 SubEntry
->UseGraphicsMode
= FALSE
;
840 SubEntry
->LoadOptions
= L
"-v";
841 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
842 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
844 } // entries for xom.efi
845 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
846 Entry
->me
.SubScreen
= SubScreen
;
847 } // VOID GenerateSubScreen()
849 // Returns options for a Linux kernel. Reads them from an options file in the
850 // kernel's directory; and if present, adds an initrd= option for an initial
851 // RAM disk file with the same version number as the kernel file.
852 static CHAR16
* GetMainLinuxOptions(IN CHAR16
* LoaderPath
, IN REFIT_VOLUME
*Volume
) {
853 CHAR16
*Options
= NULL
, *InitrdName
, *FullOptions
= NULL
;
855 Options
= GetFirstOptionsFromFile(LoaderPath
, Volume
);
856 InitrdName
= FindInitrd(LoaderPath
, Volume
);
857 FullOptions
= AddInitrdToOptions(Options
, InitrdName
);
860 MyFreePool(InitrdName
);
861 return (FullOptions
);
862 } // static CHAR16 * GetMainLinuxOptions()
864 // Try to guess the name of the Linux distribution & add that name to
866 static VOID
GuessLinuxDistribution(CHAR16
**OSIconName
, REFIT_VOLUME
*Volume
, CHAR16
*LoaderPath
) {
870 UINTN TokenCount
= 0;
872 // If on Linux root fs, /etc/os-release file probably has clues....
873 if (FileExists(Volume
->RootDir
, L
"etc\\os-release") &&
874 (ReadFile(Volume
->RootDir
, L
"etc\\os-release", &File
, &FileSize
) == EFI_SUCCESS
)) {
876 TokenCount
= ReadTokenLine(&File
, &TokenList
);
877 if ((TokenCount
> 1) && ((StriCmp(TokenList
[0], L
"ID") == 0) || (StriCmp(TokenList
[0], L
"NAME") == 0))) {
878 MergeStrings(OSIconName
, TokenList
[1], L
',');
880 FreeTokenLine(&TokenList
, &TokenCount
);
881 } while (TokenCount
> 0);
882 MyFreePool(File
.Buffer
);
885 // Search for clues in the kernel's filename....
886 if (StriSubCmp(L
".fc", LoaderPath
))
887 MergeStrings(OSIconName
, L
"fedora", L
',');
888 if (StriSubCmp(L
".el", LoaderPath
))
889 MergeStrings(OSIconName
, L
"redhat", L
',');
890 } // VOID GuessLinuxDistribution()
892 // Sets a few defaults for a loader entry -- mainly the icon, but also the OS type
893 // code and shortcut letter. For Linux EFI stub loaders, also sets kernel options
894 // that will (with luck) work fairly automatically.
895 VOID
SetLoaderDefaults(LOADER_ENTRY
*Entry
, CHAR16
*LoaderPath
, REFIT_VOLUME
*Volume
) {
896 CHAR16
*FileName
, *PathOnly
, *NoExtension
, *OSIconName
= NULL
, *Temp
, *SubString
;
897 CHAR16 ShortcutLetter
= 0;
900 FileName
= Basename(LoaderPath
);
901 PathOnly
= FindPath(LoaderPath
);
902 NoExtension
= StripEfiExtension(FileName
);
904 // locate a custom icon for the loader
905 // Anything found here takes precedence over the "hints" in the OSIconName variable
906 if (!Entry
->me
.Image
)
907 Entry
->me
.Image
= egLoadIconAnyType(Volume
->RootDir
, PathOnly
, NoExtension
, GlobalConfig
.IconSizes
[ICON_SIZE_BIG
]);
908 if (!Entry
->me
.Image
)
909 Entry
->me
.Image
= egCopyImage(Volume
->VolIconImage
);
911 // Begin creating icon "hints" by using last part of directory path leading
913 Temp
= FindLastDirName(LoaderPath
);
914 MergeStrings(&OSIconName
, Temp
, L
',');
917 if (OSIconName
!= NULL
) {
918 ShortcutLetter
= OSIconName
[0];
921 // Add every "word" in the volume label, delimited by spaces, dashes (-), or
922 // underscores (_), to the list of hints to be used in searching for OS
924 if ((Volume
->VolName
) && (StrLen(Volume
->VolName
) > 0)) {
925 Temp
= SubString
= StrDuplicate(Volume
->VolName
);
927 Length
= StrLen(Temp
);
928 for (i
= 0; i
< Length
; i
++) {
929 if ((Temp
[i
] == L
' ') || (Temp
[i
] == L
'_') || (Temp
[i
] == L
'-')) {
931 if (StrLen(SubString
) > 0)
932 MergeStrings(&OSIconName
, SubString
, L
',');
933 SubString
= Temp
+ i
+ 1;
936 MergeStrings(&OSIconName
, SubString
, L
',');
941 // detect specific loaders
942 if (StriSubCmp(L
"bzImage", FileName
) || StriSubCmp(L
"vmlinuz", FileName
)) {
943 GuessLinuxDistribution(&OSIconName
, Volume
, LoaderPath
);
944 MergeStrings(&OSIconName
, L
"linux", L
',');
946 if (ShortcutLetter
== 0)
947 ShortcutLetter
= 'L';
948 Entry
->LoadOptions
= GetMainLinuxOptions(LoaderPath
, Volume
);
949 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_LINUX
;
950 } else if (StriSubCmp(L
"refit", LoaderPath
)) {
951 MergeStrings(&OSIconName
, L
"refit", L
',');
953 ShortcutLetter
= 'R';
954 } else if (StriSubCmp(L
"refind", LoaderPath
)) {
955 MergeStrings(&OSIconName
, L
"refind", L
',');
957 ShortcutLetter
= 'R';
958 } else if (StriCmp(LoaderPath
, MACOSX_LOADER_PATH
) == 0) {
959 if (Volume
->VolIconImage
!= NULL
) { // custom icon file found
960 Entry
->me
.Image
= Volume
->VolIconImage
;
962 MergeStrings(&OSIconName
, L
"mac", L
',');
964 ShortcutLetter
= 'M';
965 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
966 } else if (StriCmp(FileName
, L
"diags.efi") == 0) {
967 MergeStrings(&OSIconName
, L
"hwtest", L
',');
968 } else if (StriCmp(FileName
, L
"e.efi") == 0 || StriCmp(FileName
, L
"elilo.efi") == 0 || StriSubCmp(L
"elilo", FileName
)) {
969 MergeStrings(&OSIconName
, L
"elilo,linux", L
',');
971 if (ShortcutLetter
== 0)
972 ShortcutLetter
= 'L';
973 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
974 } else if (StriSubCmp(L
"grub", FileName
)) {
976 ShortcutLetter
= 'G';
977 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_GRUB
;
978 } else if (StriCmp(FileName
, L
"cdboot.efi") == 0 ||
979 StriCmp(FileName
, L
"bootmgr.efi") == 0 ||
980 StriCmp(FileName
, L
"bootmgfw.efi") == 0 ||
981 StriCmp(FileName
, L
"bkpbootmgfw.efi") == 0) {
982 MergeStrings(&OSIconName
, L
"win", L
',');
984 ShortcutLetter
= 'W';
985 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
986 } else if (StriCmp(FileName
, L
"xom.efi") == 0) {
987 MergeStrings(&OSIconName
, L
"xom,win", L
',');
988 Entry
->UseGraphicsMode
= TRUE
;
990 ShortcutLetter
= 'W';
991 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
994 if ((ShortcutLetter
>= 'a') && (ShortcutLetter
<= 'z'))
995 ShortcutLetter
= ShortcutLetter
- 'a' + 'A'; // convert lowercase to uppercase
996 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
997 if (Entry
->me
.Image
== NULL
)
998 Entry
->me
.Image
= LoadOSIcon(OSIconName
, L
"unknown", FALSE
);
999 MyFreePool(PathOnly
);
1000 } // VOID SetLoaderDefaults()
1002 // Add a specified EFI boot loader to the list, using automatic settings
1003 // for icons, options, etc.
1004 LOADER_ENTRY
* AddLoaderEntry(IN CHAR16
*LoaderPath
, IN CHAR16
*LoaderTitle
, IN REFIT_VOLUME
*Volume
) {
1005 LOADER_ENTRY
*Entry
;
1007 CleanUpPathNameSlashes(LoaderPath
);
1008 Entry
= InitializeLoaderEntry(NULL
);
1009 if (Entry
!= NULL
) {
1010 Entry
->Title
= StrDuplicate((LoaderTitle
!= NULL
) ? LoaderTitle
: LoaderPath
);
1011 Entry
->me
.Title
= AllocateZeroPool(sizeof(CHAR16
) * 256);
1012 SPrint(Entry
->me
.Title
, 255, L
"Boot %s from %s ", (LoaderTitle
!= NULL
) ? LoaderTitle
: LoaderPath
, Volume
->VolName
);
1014 Entry
->me
.BadgeImage
= Volume
->VolBadgeImage
;
1015 if ((LoaderPath
!= NULL
) && (LoaderPath
[0] != L
'\\')) {
1016 Entry
->LoaderPath
= StrDuplicate(L
"\\");
1018 Entry
->LoaderPath
= NULL
;
1020 MergeStrings(&(Entry
->LoaderPath
), LoaderPath
, 0);
1021 Entry
->VolName
= Volume
->VolName
;
1022 Entry
->DevicePath
= FileDevicePath(Volume
->DeviceHandle
, Entry
->LoaderPath
);
1023 SetLoaderDefaults(Entry
, LoaderPath
, Volume
);
1024 GenerateSubScreen(Entry
, Volume
);
1025 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1029 } // LOADER_ENTRY * AddLoaderEntry()
1031 // Returns -1 if (Time1 < Time2), +1 if (Time1 > Time2), or 0 if
1032 // (Time1 == Time2). Precision is only to the nearest second; since
1033 // this is used for sorting boot loader entries, differences smaller
1034 // than this are likely to be meaningless (and unlikely!).
1035 INTN
TimeComp(IN EFI_TIME
*Time1
, IN EFI_TIME
*Time2
) {
1036 INT64 Time1InSeconds
, Time2InSeconds
;
1038 // Following values are overestimates; I'm assuming 31 days in every month.
1039 // This is fine for the purpose of this function, which is limited
1040 Time1InSeconds
= Time1
->Second
+ (Time1
->Minute
* 60) + (Time1
->Hour
* 3600) + (Time1
->Day
* 86400) +
1041 (Time1
->Month
* 2678400) + ((Time1
->Year
- 1998) * 32140800);
1042 Time2InSeconds
= Time2
->Second
+ (Time2
->Minute
* 60) + (Time2
->Hour
* 3600) + (Time2
->Day
* 86400) +
1043 (Time2
->Month
* 2678400) + ((Time2
->Year
- 1998) * 32140800);
1044 if (Time1InSeconds
< Time2InSeconds
)
1046 else if (Time1InSeconds
> Time2InSeconds
)
1050 } // INTN TimeComp()
1052 // Adds a loader list element, keeping it sorted by date. Returns the new
1053 // first element (the one with the most recent date).
1054 static struct LOADER_LIST
* AddLoaderListEntry(struct LOADER_LIST
*LoaderList
, struct LOADER_LIST
*NewEntry
) {
1055 struct LOADER_LIST
*LatestEntry
, *CurrentEntry
, *PrevEntry
= NULL
;
1057 LatestEntry
= CurrentEntry
= LoaderList
;
1058 if (LoaderList
== NULL
) {
1059 LatestEntry
= NewEntry
;
1061 while ((CurrentEntry
!= NULL
) && (TimeComp(&(NewEntry
->TimeStamp
), &(CurrentEntry
->TimeStamp
)) < 0)) {
1062 PrevEntry
= CurrentEntry
;
1063 CurrentEntry
= CurrentEntry
->NextEntry
;
1065 NewEntry
->NextEntry
= CurrentEntry
;
1066 if (PrevEntry
== NULL
) {
1067 LatestEntry
= NewEntry
;
1069 PrevEntry
->NextEntry
= NewEntry
;
1072 return (LatestEntry
);
1073 } // static VOID AddLoaderListEntry()
1075 // Delete the LOADER_LIST linked list
1076 static VOID
CleanUpLoaderList(struct LOADER_LIST
*LoaderList
) {
1077 struct LOADER_LIST
*Temp
;
1079 while (LoaderList
!= NULL
) {
1081 LoaderList
= LoaderList
->NextEntry
;
1082 MyFreePool(Temp
->FileName
);
1085 } // static VOID CleanUpLoaderList()
1087 // Returns FALSE if the specified file/volume matches the GlobalConfig.DontScanDirs
1088 // or GlobalConfig.DontScanVolumes specification, or if Path points to a volume
1089 // other than the one specified by Volume, or if the specified path is SelfDir.
1090 // Returns TRUE if none of these conditions is met -- that is, if the path is
1091 // eligible for scanning.
1092 static BOOLEAN
ShouldScan(REFIT_VOLUME
*Volume
, CHAR16
*Path
) {
1093 CHAR16
*VolName
= NULL
, *DontScanDir
, *PathCopy
= NULL
;
1095 BOOLEAN ScanIt
= TRUE
;
1097 if (IsIn(Volume
->VolName
, GlobalConfig
.DontScanVolumes
))
1100 if ((StriCmp(Path
, SelfDirPath
) == 0) && (Volume
->DeviceHandle
== SelfVolume
->DeviceHandle
))
1103 // See if Path includes an explicit volume declaration that's NOT Volume....
1104 PathCopy
= StrDuplicate(Path
);
1105 if (SplitVolumeAndFilename(&PathCopy
, &VolName
)) {
1106 VolumeNumberToName(Volume
, &VolName
);
1107 if (VolName
&& StriCmp(VolName
, Volume
->VolName
) != 0) {
1110 } // if Path includes volume specification
1111 MyFreePool(PathCopy
);
1112 MyFreePool(VolName
);
1115 // See if Volume is in GlobalConfig.DontScanDirs....
1116 while (ScanIt
&& (DontScanDir
= FindCommaDelimited(GlobalConfig
.DontScanDirs
, i
++))) {
1117 SplitVolumeAndFilename(&DontScanDir
, &VolName
);
1118 CleanUpPathNameSlashes(DontScanDir
);
1119 VolumeNumberToName(Volume
, &VolName
);
1120 if (VolName
!= NULL
) {
1121 if ((StriCmp(VolName
, Volume
->VolName
) == 0) && (StriCmp(DontScanDir
, Path
) == 0))
1124 if (StriCmp(DontScanDir
, Path
) == 0)
1127 MyFreePool(DontScanDir
);
1128 MyFreePool(VolName
);
1134 } // BOOLEAN ShouldScan()
1136 // Returns TRUE if the file is byte-for-byte identical with the fallback file
1137 // on the volume AND if the file is not itself the fallback file; returns
1138 // FALSE if the file is not identical to the fallback file OR if the file
1139 // IS the fallback file. Intended for use in excluding the fallback boot
1140 // loader when it's a duplicate of another boot loader.
1141 static BOOLEAN
DuplicatesFallback(IN REFIT_VOLUME
*Volume
, IN CHAR16
*FileName
) {
1142 CHAR8
*FileContents
, *FallbackContents
;
1143 EFI_FILE_HANDLE FileHandle
, FallbackHandle
;
1144 EFI_FILE_INFO
*FileInfo
, *FallbackInfo
;
1145 UINTN FileSize
= 0, FallbackSize
= 0;
1147 BOOLEAN AreIdentical
= FALSE
;
1149 if (!FileExists(Volume
->RootDir
, FileName
) || !FileExists(Volume
->RootDir
, FALLBACK_FULLNAME
))
1152 CleanUpPathNameSlashes(FileName
);
1154 if (StriCmp(FileName
, FALLBACK_FULLNAME
) == 0)
1155 return FALSE
; // identical filenames, so not a duplicate....
1157 Status
= refit_call5_wrapper(Volume
->RootDir
->Open
, Volume
->RootDir
, &FileHandle
, FileName
, EFI_FILE_MODE_READ
, 0);
1158 if (Status
== EFI_SUCCESS
) {
1159 FileInfo
= LibFileInfo(FileHandle
);
1160 FileSize
= FileInfo
->FileSize
;
1165 Status
= refit_call5_wrapper(Volume
->RootDir
->Open
, Volume
->RootDir
, &FallbackHandle
, FALLBACK_FULLNAME
, EFI_FILE_MODE_READ
, 0);
1166 if (Status
== EFI_SUCCESS
) {
1167 FallbackInfo
= LibFileInfo(FallbackHandle
);
1168 FallbackSize
= FallbackInfo
->FileSize
;
1170 refit_call1_wrapper(FileHandle
->Close
, FileHandle
);
1174 if (FallbackSize
!= FileSize
) { // not same size, so can't be identical
1175 AreIdentical
= FALSE
;
1176 } else { // could be identical; do full check....
1177 FileContents
= AllocatePool(FileSize
);
1178 FallbackContents
= AllocatePool(FallbackSize
);
1179 if (FileContents
&& FallbackContents
) {
1180 Status
= refit_call3_wrapper(FileHandle
->Read
, FileHandle
, &FileSize
, FileContents
);
1181 if (Status
== EFI_SUCCESS
) {
1182 Status
= refit_call3_wrapper(FallbackHandle
->Read
, FallbackHandle
, &FallbackSize
, FallbackContents
);
1184 if (Status
== EFI_SUCCESS
) {
1185 AreIdentical
= (CompareMem(FileContents
, FallbackContents
, FileSize
) == 0);
1188 MyFreePool(FileContents
);
1189 MyFreePool(FallbackContents
);
1192 // BUG ALERT: Some systems (e.g., DUET, some Macs with large displays) crash if the
1193 // following two calls are reversed. Go figure....
1194 refit_call1_wrapper(FileHandle
->Close
, FallbackHandle
);
1195 refit_call1_wrapper(FileHandle
->Close
, FileHandle
);
1196 return AreIdentical
;
1197 } // BOOLEAN DuplicatesFallback()
1199 // Returns FALSE if two measures of file size are identical for a single file,
1200 // TRUE if not or if the file can't be opened and the other measure is non-0.
1201 // Despite the function's name, this isn't really a direct test of symbolic
1202 // link status, since EFI doesn't officially support symlinks. It does seem
1203 // to be a reliable indicator, though. (OTOH, some disk errors might cause a
1204 // file to fail to open, which would return a false positive -- but as I use
1205 // this function to exclude symbolic links from the list of boot loaders,
1206 // that would be fine, since such boot loaders wouldn't work.)
1207 static BOOLEAN
IsSymbolicLink(REFIT_VOLUME
*Volume
, CHAR16
*Path
, EFI_FILE_INFO
*DirEntry
) {
1208 EFI_FILE_HANDLE FileHandle
;
1209 EFI_FILE_INFO
*FileInfo
= NULL
;
1211 UINTN FileSize2
= 0;
1214 FileName
= StrDuplicate(Path
);
1215 MergeStrings(&FileName
, DirEntry
->FileName
, L
'\\');
1216 CleanUpPathNameSlashes(FileName
);
1218 Status
= refit_call5_wrapper(Volume
->RootDir
->Open
, Volume
->RootDir
, &FileHandle
, FileName
, EFI_FILE_MODE_READ
, 0);
1219 if (Status
== EFI_SUCCESS
) {
1220 FileInfo
= LibFileInfo(FileHandle
);
1221 if (FileInfo
!= NULL
)
1222 FileSize2
= FileInfo
->FileSize
;
1225 MyFreePool(FileName
);
1226 MyFreePool(FileInfo
);
1228 return (DirEntry
->FileSize
!= FileSize2
);
1229 } // BOOLEAN IsSymbolicLink()
1231 // Returns TRUE if a file with the same name as the original but with
1232 // ".efi.signed" is also present in the same directory. Ubuntu is using
1233 // this filename as a signed version of the original unsigned kernel, and
1234 // there's no point in cluttering the display with two kernels that will
1235 // behave identically on non-SB systems, or when one will fail when SB
1237 static BOOLEAN
HasSignedCounterpart(IN REFIT_VOLUME
*Volume
, IN CHAR16
*Path
, IN CHAR16
*Filename
) {
1238 CHAR16
*NewFile
= NULL
;
1239 BOOLEAN retval
= FALSE
;
1241 MergeStrings(&NewFile
, Path
, 0);
1242 MergeStrings(&NewFile
, Filename
, L
'\\');
1243 MergeStrings(&NewFile
, L
".efi.signed", 0);
1244 if (FileExists(Volume
->RootDir
, NewFile
))
1246 MyFreePool(NewFile
);
1249 } // BOOLEAN HasSignedCounterpart()
1251 // Scan an individual directory for EFI boot loader files and, if found,
1252 // add them to the list. Exception: Ignores FALLBACK_FULLNAME, which is picked
1253 // up in ScanEfiFiles(). Sorts the entries within the loader directory so that
1254 // the most recent one appears first in the list.
1255 // Returns TRUE if a duplicate for FALLBACK_FILENAME was found, FALSE if not.
1256 static BOOLEAN
ScanLoaderDir(IN REFIT_VOLUME
*Volume
, IN CHAR16
*Path
, IN CHAR16
*Pattern
)
1259 REFIT_DIR_ITER DirIter
;
1260 EFI_FILE_INFO
*DirEntry
;
1261 CHAR16 FileName
[256], *Extension
;
1262 struct LOADER_LIST
*LoaderList
= NULL
, *NewLoader
;
1263 BOOLEAN FoundFallbackDuplicate
= FALSE
;
1265 if ((!SelfDirPath
|| !Path
|| ((StriCmp(Path
, SelfDirPath
) == 0) && (Volume
->DeviceHandle
!= SelfVolume
->DeviceHandle
)) ||
1266 (StriCmp(Path
, SelfDirPath
) != 0)) &&
1267 (ShouldScan(Volume
, Path
))) {
1268 // look through contents of the directory
1269 DirIterOpen(Volume
->RootDir
, Path
, &DirIter
);
1270 while (DirIterNext(&DirIter
, 2, Pattern
, &DirEntry
)) {
1271 Extension
= FindExtension(DirEntry
->FileName
);
1272 if (DirEntry
->FileName
[0] == '.' ||
1273 StriCmp(Extension
, L
".icns") == 0 ||
1274 StriCmp(Extension
, L
".png") == 0 ||
1275 (StriCmp(DirEntry
->FileName
, FALLBACK_BASENAME
) == 0 && (StriCmp(Path
, L
"EFI\\BOOT") == 0)) ||
1276 StriSubCmp(L
"shell", DirEntry
->FileName
) ||
1277 IsSymbolicLink(Volume
, Path
, DirEntry
) || /* is symbolic link */
1278 HasSignedCounterpart(Volume
, Path
, DirEntry
->FileName
) || /* a file with same name plus ".efi.signed" is present */
1279 FilenameIn(Volume
, Path
, DirEntry
->FileName
, GlobalConfig
.DontScanFiles
))
1280 continue; // skip this
1283 SPrint(FileName
, 255, L
"\\%s\\%s", Path
, DirEntry
->FileName
);
1285 SPrint(FileName
, 255, L
"\\%s", DirEntry
->FileName
);
1286 CleanUpPathNameSlashes(FileName
);
1288 if(!IsValidLoader(Volume
->RootDir
, FileName
))
1291 NewLoader
= AllocateZeroPool(sizeof(struct LOADER_LIST
));
1292 if (NewLoader
!= NULL
) {
1293 NewLoader
->FileName
= StrDuplicate(FileName
);
1294 NewLoader
->TimeStamp
= DirEntry
->ModificationTime
;
1295 LoaderList
= AddLoaderListEntry(LoaderList
, NewLoader
);
1296 if (DuplicatesFallback(Volume
, FileName
))
1297 FoundFallbackDuplicate
= TRUE
;
1299 MyFreePool(Extension
);
1302 NewLoader
= LoaderList
;
1303 while (NewLoader
!= NULL
) {
1304 AddLoaderEntry(NewLoader
->FileName
, NULL
, Volume
);
1305 NewLoader
= NewLoader
->NextEntry
;
1308 CleanUpLoaderList(LoaderList
);
1309 Status
= DirIterClose(&DirIter
);
1310 // NOTE: EFI_INVALID_PARAMETER really is an error that should be reported;
1311 // but I've gotten reports from users who are getting this error occasionally
1312 // and I can't find anything wrong or reproduce the problem, so I'm putting
1313 // it down to buggy EFI implementations and ignoring that particular error....
1314 if ((Status
!= EFI_NOT_FOUND
) && (Status
!= EFI_INVALID_PARAMETER
)) {
1316 SPrint(FileName
, 255, L
"while scanning the %s directory", Path
);
1318 StrCpy(FileName
, L
"while scanning the root directory");
1319 CheckError(Status
, FileName
);
1320 } // if (Status != EFI_NOT_FOUND)
1321 } // if not scanning a blacklisted directory
1323 return FoundFallbackDuplicate
;
1324 } /* static VOID ScanLoaderDir() */
1326 static VOID
ScanEfiFiles(REFIT_VOLUME
*Volume
) {
1328 REFIT_DIR_ITER EfiDirIter
;
1329 EFI_FILE_INFO
*EfiDirEntry
;
1330 CHAR16 FileName
[256], *Directory
= NULL
, *MatchPatterns
, *VolName
= NULL
, *SelfPath
;
1332 BOOLEAN ScanFallbackLoader
= TRUE
;
1333 BOOLEAN FoundBRBackup
= FALSE
;
1335 if ((Volume
->RootDir
!= NULL
) && (Volume
->VolName
!= NULL
) && (Volume
->IsReadable
)) {
1336 MatchPatterns
= StrDuplicate(LOADER_MATCH_PATTERNS
);
1337 if (GlobalConfig
.ScanAllLinux
)
1338 MergeStrings(&MatchPatterns
, LINUX_MATCH_PATTERNS
, L
',');
1340 // check for Mac OS X boot loader
1341 if (ShouldScan(Volume
, L
"System\\Library\\CoreServices")) {
1342 StrCpy(FileName
, MACOSX_LOADER_PATH
);
1343 if (FileExists(Volume
->RootDir
, FileName
) && !FilenameIn(Volume
, Directory
, L
"boot.efi", GlobalConfig
.DontScanFiles
)) {
1344 AddLoaderEntry(FileName
, L
"Mac OS X", Volume
);
1345 if (DuplicatesFallback(Volume
, FileName
))
1346 ScanFallbackLoader
= FALSE
;
1350 StrCpy(FileName
, L
"System\\Library\\CoreServices\\xom.efi");
1351 if (FileExists(Volume
->RootDir
, FileName
) && !FilenameIn(Volume
, Directory
, L
"boot.efi", GlobalConfig
.DontScanFiles
)) {
1352 AddLoaderEntry(FileName
, L
"Windows XP (XoM)", Volume
);
1353 if (DuplicatesFallback(Volume
, FileName
))
1354 ScanFallbackLoader
= FALSE
;
1356 } // if should scan Mac directory
1358 // check for Microsoft boot loader/menu
1359 if (ShouldScan(Volume
, L
"EFI\\Microsoft\\Boot")) {
1360 StrCpy(FileName
, L
"EFI\\Microsoft\\Boot\\bkpbootmgfw.efi");
1361 if (FileExists(Volume
->RootDir
, FileName
) && !FilenameIn(Volume
, Directory
, L
"bkpbootmgfw.efi",
1362 GlobalConfig
.DontScanFiles
)) {
1363 AddLoaderEntry(FileName
, L
"Microsoft EFI boot (Boot Repair backup)", Volume
);
1364 FoundBRBackup
= TRUE
;
1365 if (DuplicatesFallback(Volume
, FileName
))
1366 ScanFallbackLoader
= FALSE
;
1368 StrCpy(FileName
, L
"EFI\\Microsoft\\Boot\\bootmgfw.efi");
1369 if (FileExists(Volume
->RootDir
, FileName
) && !FilenameIn(Volume
, Directory
, L
"bootmgfw.efi", GlobalConfig
.DontScanFiles
)) {
1371 AddLoaderEntry(FileName
, L
"Supposed Microsoft EFI boot (probably GRUB)", Volume
);
1373 AddLoaderEntry(FileName
, L
"Microsoft EFI boot", Volume
);
1374 if (DuplicatesFallback(Volume
, FileName
))
1375 ScanFallbackLoader
= FALSE
;
1379 // scan the root directory for EFI executables
1380 if (ScanLoaderDir(Volume
, L
"\\", MatchPatterns
))
1381 ScanFallbackLoader
= FALSE
;
1383 // scan subdirectories of the EFI directory (as per the standard)
1384 DirIterOpen(Volume
->RootDir
, L
"EFI", &EfiDirIter
);
1385 while (DirIterNext(&EfiDirIter
, 1, NULL
, &EfiDirEntry
)) {
1386 if (StriCmp(EfiDirEntry
->FileName
, L
"tools") == 0 || EfiDirEntry
->FileName
[0] == '.')
1387 continue; // skip this, doesn't contain boot loaders or is scanned later
1388 SPrint(FileName
, 255, L
"EFI\\%s", EfiDirEntry
->FileName
);
1389 if (ScanLoaderDir(Volume
, FileName
, MatchPatterns
))
1390 ScanFallbackLoader
= FALSE
;
1392 Status
= DirIterClose(&EfiDirIter
);
1393 if (Status
!= EFI_NOT_FOUND
)
1394 CheckError(Status
, L
"while scanning the EFI directory");
1396 // Scan user-specified (or additional default) directories....
1398 while ((Directory
= FindCommaDelimited(GlobalConfig
.AlsoScan
, i
++)) != NULL
) {
1399 if (ShouldScan(Volume
, Directory
)) {
1400 SplitVolumeAndFilename(&Directory
, &VolName
);
1401 CleanUpPathNameSlashes(Directory
);
1402 Length
= StrLen(Directory
);
1403 if ((Length
> 0) && ScanLoaderDir(Volume
, Directory
, MatchPatterns
))
1404 ScanFallbackLoader
= FALSE
;
1405 MyFreePool(VolName
);
1406 } // if should scan dir
1407 MyFreePool(Directory
);
1410 // Don't scan the fallback loader if it's on the same volume and a duplicate of rEFInd itself....
1411 SelfPath
= DevicePathToStr(SelfLoadedImage
->FilePath
);
1412 CleanUpPathNameSlashes(SelfPath
);
1413 if ((Volume
->DeviceHandle
== SelfLoadedImage
->DeviceHandle
) && DuplicatesFallback(Volume
, SelfPath
))
1414 ScanFallbackLoader
= FALSE
;
1416 // If not a duplicate & if it exists & if it's not us, create an entry
1417 // for the fallback boot loader
1418 if (ScanFallbackLoader
&& FileExists(Volume
->RootDir
, FALLBACK_FULLNAME
) && ShouldScan(Volume
, L
"EFI\\BOOT"))
1419 AddLoaderEntry(FALLBACK_FULLNAME
, L
"Fallback boot loader", Volume
);
1421 } // static VOID ScanEfiFiles()
1423 // Scan internal disks for valid EFI boot loaders....
1424 static VOID
ScanInternal(VOID
) {
1427 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1428 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_INTERNAL
) {
1429 ScanEfiFiles(Volumes
[VolumeIndex
]);
1432 } // static VOID ScanInternal()
1434 // Scan external disks for valid EFI boot loaders....
1435 static VOID
ScanExternal(VOID
) {
1438 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1439 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_EXTERNAL
) {
1440 ScanEfiFiles(Volumes
[VolumeIndex
]);
1443 } // static VOID ScanExternal()
1445 // Scan internal disks for valid EFI boot loaders....
1446 static VOID
ScanOptical(VOID
) {
1449 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1450 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_OPTICAL
) {
1451 ScanEfiFiles(Volumes
[VolumeIndex
]);
1454 } // static VOID ScanOptical()
1457 // legacy boot functions
1460 static EFI_STATUS
ActivateMbrPartition(IN EFI_BLOCK_IO
*BlockIO
, IN UINTN PartitionIndex
)
1463 UINT8 SectorBuffer
[512];
1464 MBR_PARTITION_INFO
*MbrTable
, *EMbrTable
;
1465 UINT32 ExtBase
, ExtCurrent
, NextExtCurrent
;
1466 UINTN LogicalPartitionIndex
= 4;
1468 BOOLEAN HaveBootCode
;
1471 Status
= refit_call5_wrapper(BlockIO
->ReadBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, 0, 512, SectorBuffer
);
1472 if (EFI_ERROR(Status
))
1474 if (*((UINT16
*)(SectorBuffer
+ 510)) != 0xaa55)
1475 return EFI_NOT_FOUND
; // safety measure #1
1477 // add boot code if necessary
1478 HaveBootCode
= FALSE
;
1479 for (i
= 0; i
< MBR_BOOTCODE_SIZE
; i
++) {
1480 if (SectorBuffer
[i
] != 0) {
1481 HaveBootCode
= TRUE
;
1485 if (!HaveBootCode
) {
1486 // no boot code found in the MBR, add the syslinux MBR code
1487 SetMem(SectorBuffer
, MBR_BOOTCODE_SIZE
, 0);
1488 CopyMem(SectorBuffer
, syslinux_mbr
, SYSLINUX_MBR_SIZE
);
1491 // set the partition active
1492 MbrTable
= (MBR_PARTITION_INFO
*)(SectorBuffer
+ 446);
1494 for (i
= 0; i
< 4; i
++) {
1495 if (MbrTable
[i
].Flags
!= 0x00 && MbrTable
[i
].Flags
!= 0x80)
1496 return EFI_NOT_FOUND
; // safety measure #2
1497 if (i
== PartitionIndex
)
1498 MbrTable
[i
].Flags
= 0x80;
1499 else if (PartitionIndex
>= 4 && IS_EXTENDED_PART_TYPE(MbrTable
[i
].Type
)) {
1500 MbrTable
[i
].Flags
= 0x80;
1501 ExtBase
= MbrTable
[i
].StartLBA
;
1503 MbrTable
[i
].Flags
= 0x00;
1507 Status
= refit_call5_wrapper(BlockIO
->WriteBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, 0, 512, SectorBuffer
);
1508 if (EFI_ERROR(Status
))
1511 if (PartitionIndex
>= 4) {
1512 // we have to activate a logical partition, so walk the EMBR chain
1514 // NOTE: ExtBase was set above while looking at the MBR table
1515 for (ExtCurrent
= ExtBase
; ExtCurrent
; ExtCurrent
= NextExtCurrent
) {
1516 // read current EMBR
1517 Status
= refit_call5_wrapper(BlockIO
->ReadBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, ExtCurrent
, 512, SectorBuffer
);
1518 if (EFI_ERROR(Status
))
1520 if (*((UINT16
*)(SectorBuffer
+ 510)) != 0xaa55)
1521 return EFI_NOT_FOUND
; // safety measure #3
1523 // scan EMBR, set appropriate partition active
1524 EMbrTable
= (MBR_PARTITION_INFO
*)(SectorBuffer
+ 446);
1526 for (i
= 0; i
< 4; i
++) {
1527 if (EMbrTable
[i
].Flags
!= 0x00 && EMbrTable
[i
].Flags
!= 0x80)
1528 return EFI_NOT_FOUND
; // safety measure #4
1529 if (EMbrTable
[i
].StartLBA
== 0 || EMbrTable
[i
].Size
== 0)
1531 if (IS_EXTENDED_PART_TYPE(EMbrTable
[i
].Type
)) {
1532 // link to next EMBR
1533 NextExtCurrent
= ExtBase
+ EMbrTable
[i
].StartLBA
;
1534 EMbrTable
[i
].Flags
= (PartitionIndex
>= LogicalPartitionIndex
) ? 0x80 : 0x00;
1537 // logical partition
1538 EMbrTable
[i
].Flags
= (PartitionIndex
== LogicalPartitionIndex
) ? 0x80 : 0x00;
1539 LogicalPartitionIndex
++;
1543 // write current EMBR
1544 Status
= refit_call5_wrapper(BlockIO
->WriteBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, ExtCurrent
, 512, SectorBuffer
);
1545 if (EFI_ERROR(Status
))
1548 if (PartitionIndex
< LogicalPartitionIndex
)
1549 break; // stop the loop, no need to touch further EMBRs
1555 } /* static EFI_STATUS ActivateMbrPartition() */
1557 // early 2006 Core Duo / Core Solo models
1558 static UINT8 LegacyLoaderDevicePath1Data
[] = {
1559 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1560 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1561 0xFF, 0xFF, 0xF9, 0xFF, 0x00, 0x00, 0x00, 0x00,
1562 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1563 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1564 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1566 // mid-2006 Mac Pro (and probably other Core 2 models)
1567 static UINT8 LegacyLoaderDevicePath2Data
[] = {
1568 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1569 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1570 0xFF, 0xFF, 0xF7, 0xFF, 0x00, 0x00, 0x00, 0x00,
1571 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1572 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1573 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1575 // mid-2007 MBP ("Santa Rosa" based models)
1576 static UINT8 LegacyLoaderDevicePath3Data
[] = {
1577 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1578 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1579 0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
1580 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1581 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1582 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1585 static UINT8 LegacyLoaderDevicePath4Data
[] = {
1586 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1587 0x00, 0x00, 0xC0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1588 0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
1589 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1590 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1591 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1593 // late-2008 MB/MBP (NVidia chipset)
1594 static UINT8 LegacyLoaderDevicePath5Data
[] = {
1595 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1596 0x00, 0x40, 0xCB, 0xFF, 0x00, 0x00, 0x00, 0x00,
1597 0xFF, 0xBF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
1598 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1599 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1600 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1603 static EFI_DEVICE_PATH
*LegacyLoaderList
[] = {
1604 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath1Data
,
1605 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath2Data
,
1606 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath3Data
,
1607 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath4Data
,
1608 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath5Data
,
1612 #define MAX_DISCOVERED_PATHS (16)
1614 static VOID
StartLegacy(IN LEGACY_ENTRY
*Entry
)
1617 EG_IMAGE
*BootLogoImage
;
1618 UINTN ErrorInStep
= 0;
1619 EFI_DEVICE_PATH
*DiscoveredPathList
[MAX_DISCOVERED_PATHS
];
1621 BeginExternalScreen(TRUE
, L
"Booting Legacy OS (Mac mode)");
1623 BootLogoImage
= LoadOSIcon(Entry
->Volume
->OSIconName
, L
"legacy", TRUE
);
1624 if (BootLogoImage
!= NULL
)
1625 BltImageAlpha(BootLogoImage
,
1626 (UGAWidth
- BootLogoImage
->Width
) >> 1,
1627 (UGAHeight
- BootLogoImage
->Height
) >> 1,
1628 &StdBackgroundPixel
);
1630 if (Entry
->Volume
->IsMbrPartition
) {
1631 ActivateMbrPartition(Entry
->Volume
->WholeDiskBlockIO
, Entry
->Volume
->MbrPartitionIndex
);
1634 ExtractLegacyLoaderPaths(DiscoveredPathList
, MAX_DISCOVERED_PATHS
, LegacyLoaderList
);
1636 Status
= StartEFIImageList(DiscoveredPathList
, Entry
->LoadOptions
, TYPE_LEGACY
, L
"legacy loader", 0, &ErrorInStep
, TRUE
);
1637 if (Status
== EFI_NOT_FOUND
) {
1638 if (ErrorInStep
== 1) {
1639 Print(L
"\nPlease make sure that you have the latest firmware update installed.\n");
1640 } else if (ErrorInStep
== 3) {
1641 Print(L
"\nThe firmware refused to boot from the selected volume. Note that external\n"
1642 L
"hard drives are not well-supported by Apple's firmware for legacy OS booting.\n");
1645 FinishExternalScreen();
1646 } /* static VOID StartLegacy() */
1648 // Start a device on a non-Mac using the EFI_LEGACY_BIOS_PROTOCOL
1649 #ifdef __MAKEWITH_TIANO
1650 static VOID
StartLegacyUEFI(LEGACY_ENTRY
*Entry
)
1652 // UINTN ExitDataSize = 0;
1653 // CHAR16 *ExitData = NULL;
1654 // EFI_STATUS Status;
1656 BeginExternalScreen(TRUE
, L
"Booting Legacy OS (UEFI mode)");
1657 // Print(L"Launching from '%s'\n", DevicePathToStr(Entry->BdsOption->DevicePath));
1660 // Status = BdsLibBootViaBootOption(Entry->BdsOption, Entry->BdsOption->DevicePath, &ExitDataSize, &ExitData);
1661 // Print(L"BdsLibBootViaBootOption() returned %d\n", Status);
1662 BdsLibConnectDevicePath (Entry
->BdsOption
->DevicePath
);
1663 BdsLibDoLegacyBoot(Entry
->BdsOption
);
1665 // If we get here, it means that there was a failure....
1666 Print(L
"Failure booting legacy (BIOS) OS.");
1668 FinishExternalScreen();
1669 } // static VOID StartLegacyUEFI()
1670 #endif // __MAKEWITH_TIANO
1672 static LEGACY_ENTRY
* AddLegacyEntry(IN CHAR16
*LoaderTitle
, IN REFIT_VOLUME
*Volume
)
1674 LEGACY_ENTRY
*Entry
, *SubEntry
;
1675 REFIT_MENU_SCREEN
*SubScreen
;
1677 CHAR16 ShortcutLetter
= 0;
1679 if (LoaderTitle
== NULL
) {
1680 if (Volume
->OSName
!= NULL
) {
1681 LoaderTitle
= Volume
->OSName
;
1682 if (LoaderTitle
[0] == 'W' || LoaderTitle
[0] == 'L')
1683 ShortcutLetter
= LoaderTitle
[0];
1685 LoaderTitle
= L
"Legacy OS";
1687 if (Volume
->VolName
!= NULL
)
1688 VolDesc
= Volume
->VolName
;
1690 VolDesc
= (Volume
->DiskKind
== DISK_KIND_OPTICAL
) ? L
"CD" : L
"HD";
1692 // prepare the menu entry
1693 Entry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1694 Entry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1695 SPrint(Entry
->me
.Title
, 255, L
"Boot %s from %s", LoaderTitle
, VolDesc
);
1696 Entry
->me
.Tag
= TAG_LEGACY
;
1698 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1699 Entry
->me
.Image
= LoadOSIcon(Volume
->OSIconName
, L
"legacy", FALSE
);
1700 Entry
->me
.BadgeImage
= Volume
->VolBadgeImage
;
1701 Entry
->Volume
= Volume
;
1702 Entry
->LoadOptions
= (Volume
->DiskKind
== DISK_KIND_OPTICAL
) ? L
"CD" :
1703 ((Volume
->DiskKind
== DISK_KIND_EXTERNAL
) ? L
"USB" : L
"HD");
1704 Entry
->Enabled
= TRUE
;
1706 // create the submenu
1707 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
1708 SubScreen
->Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1709 SPrint(SubScreen
->Title
, 255, L
"Boot Options for %s on %s", LoaderTitle
, VolDesc
);
1710 SubScreen
->TitleImage
= Entry
->me
.Image
;
1711 SubScreen
->Hint1
= StrDuplicate(SUBSCREEN_HINT1
);
1712 if (GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_EDITOR
) {
1713 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2_NO_EDITOR
);
1715 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2
);
1719 SubEntry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1720 SubEntry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1721 SPrint(SubEntry
->me
.Title
, 255, L
"Boot %s", LoaderTitle
);
1722 SubEntry
->me
.Tag
= TAG_LEGACY
;
1723 SubEntry
->Volume
= Entry
->Volume
;
1724 SubEntry
->LoadOptions
= Entry
->LoadOptions
;
1725 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
1727 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
1728 Entry
->me
.SubScreen
= SubScreen
;
1729 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1731 } /* static LEGACY_ENTRY * AddLegacyEntry() */
1734 #ifdef __MAKEWITH_GNUEFI
1735 static VOID
ScanLegacyUEFI(IN UINTN DiskType
){}
1737 // default volume badge icon based on disk kind
1738 static EG_IMAGE
* GetDiskBadge(IN UINTN DiskType
) {
1739 EG_IMAGE
* Badge
= NULL
;
1743 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_INTERNAL
);
1746 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_EXTERNAL
);
1749 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_OPTICAL
);
1753 } // static EG_IMAGE * GetDiskBadge()
1756 Create a rEFInd boot option from a Legacy BIOS protocol option.
1758 static LEGACY_ENTRY
* AddLegacyEntryUEFI(BDS_COMMON_OPTION
*BdsOption
, IN UINT16 DiskType
)
1760 LEGACY_ENTRY
*Entry
, *SubEntry
;
1761 REFIT_MENU_SCREEN
*SubScreen
;
1762 CHAR16 ShortcutLetter
= 0;
1763 CHAR16
*LegacyDescription
= BdsOption
->Description
;
1765 // prepare the menu entry
1766 Entry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1767 Entry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1768 SPrint(Entry
->me
.Title
, 255, L
"Boot legacy target %s", LegacyDescription
);
1769 Entry
->me
.Tag
= TAG_LEGACY_UEFI
;
1771 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1772 Entry
->me
.Image
= LoadOSIcon(L
"legacy", L
"legacy", TRUE
);
1773 Entry
->LoadOptions
= (DiskType
== BBS_CDROM
) ? L
"CD" :
1774 ((DiskType
== BBS_USB
) ? L
"USB" : L
"HD");
1775 Entry
->me
.BadgeImage
= GetDiskBadge(DiskType
);
1776 Entry
->BdsOption
= BdsOption
;
1777 Entry
->Enabled
= TRUE
;
1779 // create the submenu
1780 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
1781 SubScreen
->Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1782 SPrint(SubScreen
->Title
, 255, L
"No boot options for legacy target");
1783 SubScreen
->TitleImage
= Entry
->me
.Image
;
1784 SubScreen
->Hint1
= StrDuplicate(SUBSCREEN_HINT1
);
1785 if (GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_EDITOR
) {
1786 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2_NO_EDITOR
);
1788 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2
);
1792 SubEntry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1793 SubEntry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1794 SPrint(SubEntry
->me
.Title
, 255, L
"Boot %s", LegacyDescription
);
1795 SubEntry
->me
.Tag
= TAG_LEGACY_UEFI
;
1796 Entry
->BdsOption
= BdsOption
;
1797 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
1799 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
1800 Entry
->me
.SubScreen
= SubScreen
;
1801 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1803 } /* static LEGACY_ENTRY * AddLegacyEntryUEFI() */
1806 Scan for legacy BIOS targets on machines that implement EFI_LEGACY_BIOS_PROTOCOL.
1807 In testing, protocol has not been implemented on Macs but has been
1808 implemented on several Dell PCs and an ASUS motherboard.
1809 Restricts output to disks of the specified DiskType.
1811 static VOID
ScanLegacyUEFI(IN UINTN DiskType
)
1814 EFI_LEGACY_BIOS_PROTOCOL
*LegacyBios
;
1815 UINT16
*BootOrder
= NULL
;
1817 CHAR16 BootOption
[10];
1818 UINTN BootOrderSize
= 0;
1820 BDS_COMMON_OPTION
*BdsOption
;
1821 LIST_ENTRY TempList
;
1822 BBS_BBS_DEVICE_PATH
* BbsDevicePath
= NULL
;
1824 InitializeListHead (&TempList
);
1825 ZeroMem (Buffer
, sizeof (Buffer
));
1827 // If LegacyBios protocol is not implemented on this platform, then
1828 //we do not support this type of legacy boot on this machine.
1829 Status
= gBS
->LocateProtocol(&gEfiLegacyBootProtocolGuid
, NULL
, (VOID
**) &LegacyBios
);
1830 if (EFI_ERROR (Status
))
1833 // Grab the boot order
1834 BootOrder
= BdsLibGetVariableAndSize(L
"BootOrder", &gEfiGlobalVariableGuid
, &BootOrderSize
);
1835 if (BootOrder
== NULL
) {
1840 while (Index
< BootOrderSize
/ sizeof (UINT16
))
1842 // Grab each boot option variable from the boot order, and convert
1843 // the variable into a BDS boot option
1844 UnicodeSPrint (BootOption
, sizeof (BootOption
), L
"Boot%04x", BootOrder
[Index
]);
1845 BdsOption
= BdsLibVariableToOption (&TempList
, BootOption
);
1847 if (BdsOption
!= NULL
) {
1848 BbsDevicePath
= (BBS_BBS_DEVICE_PATH
*)BdsOption
->DevicePath
;
1850 // Only add the entry if it is of a requested type (e.g. USB, HD)
1852 // Two checks necessary because some systems return EFI boot loaders
1853 // with a DeviceType value that would inappropriately include them
1854 // as legacy loaders....
1855 if ((BbsDevicePath
->DeviceType
== DiskType
) && (BdsOption
->DevicePath
->Type
== DEVICE_TYPE_BIOS
)) {
1856 AddLegacyEntryUEFI(BdsOption
, BbsDevicePath
->DeviceType
);
1861 } /* static VOID ScanLegacyUEFI() */
1862 #endif // __MAKEWITH_GNUEFI
1864 static VOID
ScanLegacyVolume(REFIT_VOLUME
*Volume
, UINTN VolumeIndex
) {
1866 BOOLEAN ShowVolume
, HideIfOthersFound
;
1869 HideIfOthersFound
= FALSE
;
1870 if (Volume
->IsAppleLegacy
) {
1872 HideIfOthersFound
= TRUE
;
1873 } else if (Volume
->HasBootCode
) {
1875 if (Volume
->BlockIO
== Volume
->WholeDiskBlockIO
&&
1876 Volume
->BlockIOOffset
== 0 &&
1877 Volume
->OSName
== NULL
)
1878 // this is a whole disk (MBR) entry; hide if we have entries for partitions
1879 HideIfOthersFound
= TRUE
;
1881 if (HideIfOthersFound
) {
1882 // check for other bootable entries on the same disk
1883 for (VolumeIndex2
= 0; VolumeIndex2
< VolumesCount
; VolumeIndex2
++) {
1884 if (VolumeIndex2
!= VolumeIndex
&& Volumes
[VolumeIndex2
]->HasBootCode
&&
1885 Volumes
[VolumeIndex2
]->WholeDiskBlockIO
== Volume
->WholeDiskBlockIO
)
1891 AddLegacyEntry(NULL
, Volume
);
1892 } // static VOID ScanLegacyVolume()
1894 // Scan attached optical discs for legacy (BIOS) boot code
1895 // and add anything found to the list....
1896 static VOID
ScanLegacyDisc(VOID
)
1899 REFIT_VOLUME
*Volume
;
1901 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
) {
1902 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1903 Volume
= Volumes
[VolumeIndex
];
1904 if (Volume
->DiskKind
== DISK_KIND_OPTICAL
)
1905 ScanLegacyVolume(Volume
, VolumeIndex
);
1907 } else if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
1908 ScanLegacyUEFI(BBS_CDROM
);
1910 } /* static VOID ScanLegacyDisc() */
1912 // Scan internal hard disks for legacy (BIOS) boot code
1913 // and add anything found to the list....
1914 static VOID
ScanLegacyInternal(VOID
)
1917 REFIT_VOLUME
*Volume
;
1919 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
) {
1920 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1921 Volume
= Volumes
[VolumeIndex
];
1922 if (Volume
->DiskKind
== DISK_KIND_INTERNAL
)
1923 ScanLegacyVolume(Volume
, VolumeIndex
);
1925 } else if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
1926 ScanLegacyUEFI(BBS_HARDDISK
);
1928 } /* static VOID ScanLegacyInternal() */
1930 // Scan external disks for legacy (BIOS) boot code
1931 // and add anything found to the list....
1932 static VOID
ScanLegacyExternal(VOID
)
1935 REFIT_VOLUME
*Volume
;
1937 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
) {
1938 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1939 Volume
= Volumes
[VolumeIndex
];
1940 if (Volume
->DiskKind
== DISK_KIND_EXTERNAL
)
1941 ScanLegacyVolume(Volume
, VolumeIndex
);
1943 } else if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
1944 ScanLegacyUEFI(BBS_USB
);
1946 } /* static VOID ScanLegacyExternal() */
1949 // pre-boot tool functions
1952 static VOID
StartTool(IN LOADER_ENTRY
*Entry
)
1954 BeginExternalScreen(Entry
->UseGraphicsMode
, Entry
->me
.Title
+ 6); // assumes "Start <title>" as assigned below
1955 StartEFIImage(Entry
->DevicePath
, Entry
->LoadOptions
, TYPE_EFI
,
1956 Basename(Entry
->LoaderPath
), Entry
->OSType
, NULL
, TRUE
);
1957 FinishExternalScreen();
1958 } /* static VOID StartTool() */
1960 static LOADER_ENTRY
* AddToolEntry(EFI_HANDLE DeviceHandle
, IN CHAR16
*LoaderPath
, IN CHAR16
*LoaderTitle
, IN EG_IMAGE
*Image
,
1961 IN CHAR16 ShortcutLetter
, IN BOOLEAN UseGraphicsMode
)
1963 LOADER_ENTRY
*Entry
;
1964 CHAR16
*TitleStr
= NULL
;
1966 Entry
= AllocateZeroPool(sizeof(LOADER_ENTRY
));
1968 TitleStr
= PoolPrint(L
"Start %s", LoaderTitle
);
1969 Entry
->me
.Title
= TitleStr
;
1970 Entry
->me
.Tag
= TAG_TOOL
;
1972 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1973 Entry
->me
.Image
= Image
;
1974 Entry
->LoaderPath
= (LoaderPath
) ? StrDuplicate(LoaderPath
) : NULL
;
1975 Entry
->DevicePath
= FileDevicePath(DeviceHandle
, Entry
->LoaderPath
);
1976 Entry
->UseGraphicsMode
= UseGraphicsMode
;
1978 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1980 } /* static LOADER_ENTRY * AddToolEntry() */
1983 // pre-boot driver functions
1986 static UINTN
ScanDriverDir(IN CHAR16
*Path
)
1989 REFIT_DIR_ITER DirIter
;
1991 EFI_FILE_INFO
*DirEntry
;
1992 CHAR16 FileName
[256];
1994 CleanUpPathNameSlashes(Path
);
1995 // look through contents of the directory
1996 DirIterOpen(SelfRootDir
, Path
, &DirIter
);
1997 while (DirIterNext(&DirIter
, 2, LOADER_MATCH_PATTERNS
, &DirEntry
)) {
1998 if (DirEntry
->FileName
[0] == '.')
1999 continue; // skip this
2001 SPrint(FileName
, 255, L
"%s\\%s", Path
, DirEntry
->FileName
);
2003 Status
= StartEFIImage(FileDevicePath(SelfLoadedImage
->DeviceHandle
, FileName
),
2004 L
"", TYPE_EFI
, DirEntry
->FileName
, 0, NULL
, FALSE
);
2006 Status
= DirIterClose(&DirIter
);
2007 if (Status
!= EFI_NOT_FOUND
) {
2008 SPrint(FileName
, 255, L
"while scanning the %s directory", Path
);
2009 CheckError(Status
, FileName
);
2014 #ifdef __MAKEWITH_GNUEFI
2015 static EFI_STATUS
ConnectAllDriversToAllControllers(VOID
)
2018 UINTN AllHandleCount
;
2019 EFI_HANDLE
*AllHandleBuffer
;
2022 EFI_HANDLE
*HandleBuffer
;
2028 Status
= LibLocateHandle(AllHandles
,
2033 if (EFI_ERROR(Status
))
2036 for (Index
= 0; Index
< AllHandleCount
; Index
++) {
2038 // Scan the handle database
2040 Status
= LibScanHandleDatabase(NULL
,
2042 AllHandleBuffer
[Index
],
2047 if (EFI_ERROR (Status
))
2051 if (HandleType
[Index
] & EFI_HANDLE_TYPE_DRIVER_BINDING_HANDLE
)
2053 if (HandleType
[Index
] & EFI_HANDLE_TYPE_IMAGE_HANDLE
)
2058 for (HandleIndex
= 0; HandleIndex
< HandleCount
; HandleIndex
++) {
2059 if (HandleType
[HandleIndex
] & EFI_HANDLE_TYPE_PARENT_HANDLE
)
2064 if (HandleType
[Index
] & EFI_HANDLE_TYPE_DEVICE_HANDLE
) {
2065 Status
= refit_call4_wrapper(BS
->ConnectController
,
2066 AllHandleBuffer
[Index
],
2074 MyFreePool (HandleBuffer
);
2075 MyFreePool (HandleType
);
2079 MyFreePool (AllHandleBuffer
);
2081 } /* EFI_STATUS ConnectAllDriversToAllControllers() */
2083 static EFI_STATUS
ConnectAllDriversToAllControllers(VOID
) {
2084 BdsLibConnectAllDriversToAllControllers();
2089 // Load all EFI drivers from rEFInd's "drivers" subdirectory and from the
2090 // directories specified by the user in the "scan_driver_dirs" configuration
2092 static VOID
LoadDrivers(VOID
)
2094 CHAR16
*Directory
, *SelfDirectory
;
2095 UINTN i
= 0, Length
, NumFound
= 0;
2097 // load drivers from the subdirectories of rEFInd's home directory specified
2098 // in the DRIVER_DIRS constant.
2099 while ((Directory
= FindCommaDelimited(DRIVER_DIRS
, i
++)) != NULL
) {
2100 SelfDirectory
= SelfDirPath
? StrDuplicate(SelfDirPath
) : NULL
;
2101 CleanUpPathNameSlashes(SelfDirectory
);
2102 MergeStrings(&SelfDirectory
, Directory
, L
'\\');
2103 NumFound
+= ScanDriverDir(SelfDirectory
);
2104 MyFreePool(Directory
);
2105 MyFreePool(SelfDirectory
);
2108 // Scan additional user-specified driver directories....
2110 while ((Directory
= FindCommaDelimited(GlobalConfig
.DriverDirs
, i
++)) != NULL
) {
2111 CleanUpPathNameSlashes(Directory
);
2112 Length
= StrLen(Directory
);
2114 NumFound
+= ScanDriverDir(Directory
);
2116 MyFreePool(Directory
);
2119 // connect all devices
2121 ConnectAllDriversToAllControllers();
2122 } /* static VOID LoadDrivers() */
2124 // Determine what (if any) type of legacy (BIOS) boot support is available
2125 static VOID
FindLegacyBootType(VOID
) {
2126 #ifdef __MAKEWITH_TIANO
2128 EFI_LEGACY_BIOS_PROTOCOL
*LegacyBios
;
2131 GlobalConfig
.LegacyType
= LEGACY_TYPE_NONE
;
2133 // UEFI-style legacy BIOS support is available only with the TianoCore EDK2
2134 // build environment, and then only with some EFI implementations....
2135 #ifdef __MAKEWITH_TIANO
2136 Status
= gBS
->LocateProtocol (&gEfiLegacyBootProtocolGuid
, NULL
, (VOID
**) &LegacyBios
);
2137 if (!EFI_ERROR (Status
))
2138 GlobalConfig
.LegacyType
= LEGACY_TYPE_UEFI
;
2141 // Macs have their own system. If the firmware vendor code contains the
2142 // string "Apple", assume it's available. Note that this overrides the
2143 // UEFI type, and might yield false positives if the vendor string
2144 // contains "Apple" as part of something bigger, so this isn't 100%
2146 if (StriSubCmp(L
"Apple", ST
->FirmwareVendor
))
2147 GlobalConfig
.LegacyType
= LEGACY_TYPE_MAC
;
2148 } // static VOID FindLegacyBootType
2150 // Warn the user if legacy OS scans are enabled but the firmware or this
2151 // application can't support them....
2152 static VOID
WarnIfLegacyProblems() {
2153 BOOLEAN found
= FALSE
;
2156 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_NONE
) {
2158 if (GlobalConfig
.ScanFor
[i
] == 'h' || GlobalConfig
.ScanFor
[i
] == 'b' || GlobalConfig
.ScanFor
[i
] == 'c')
2161 } while ((i
< NUM_SCAN_OPTIONS
) && (!found
));
2163 Print(L
"NOTE: refind.conf's 'scanfor' line specifies scanning for one or more legacy\n");
2164 Print(L
"(BIOS) boot options; however, this is not possible because ");
2165 #ifdef __MAKEWITH_TIANO
2166 Print(L
"your computer lacks\n");
2167 Print(L
"the necessary Compatibility Support Module (CSM) support.\n");
2169 Print(L
"this program was\n");
2170 Print(L
"compiled without the necessary support. Please visit\n");
2171 Print(L
"http://www.rodsbooks.com/refind/getting.html and download and install a rEFInd\n");
2172 Print(L
"binary built with the TianoCore EDK2 to enable legacy boot support.\n");
2176 } // if no legacy support
2177 } // static VOID WarnIfLegacyProblems()
2179 // Locates boot loaders. NOTE: This assumes that GlobalConfig.LegacyType is set correctly.
2180 static VOID
ScanForBootloaders(VOID
) {
2183 // if (GlobalConfig.LegacyType == LEGACY_TYPE_UEFI) {
2184 // Print(L"About to call BdsDeleteAllInvalidLegacyBootOptions()\n");
2185 // BdsDeleteAllInvalidLegacyBootOptions();
2186 // Print(L"About to call BdsAddNonExistingLegacyBootOptions()\n");
2187 // BdsAddNonExistingLegacyBootOptions();
2188 // Print(L"About to call BdsUpdateLegacyDevOrder()\n");
2189 // // BdsUpdateLegacyDevOrder(); // EXTREME CAUTION: HOSED ONE FIRMWARE!
2190 // Print(L"Done with legacy boot updates!\n");
2196 // scan for loaders and tools, add them to the menu
2197 for (i
= 0; i
< NUM_SCAN_OPTIONS
; i
++) {
2198 switch(GlobalConfig
.ScanFor
[i
]) {
2203 ScanLegacyInternal();
2206 ScanLegacyExternal();
2209 ScanUserConfigured(GlobalConfig
.ConfigFilename
);
2223 // assign shortcut keys
2224 for (i
= 0; i
< MainMenu
.EntryCount
&& MainMenu
.Entries
[i
]->Row
== 0 && i
< 9; i
++)
2225 MainMenu
.Entries
[i
]->ShortcutDigit
= (CHAR16
)('1' + i
);
2227 // wait for user ACK when there were errors
2228 FinishTextScreen(FALSE
);
2229 } // static VOID ScanForBootloaders()
2231 // Locate a single tool from the specified Locations using one of the
2232 // specified Names and add it to the menu.
2233 static VOID
FindTool(CHAR16
*Locations
, CHAR16
*Names
, CHAR16
*Description
, UINTN Icon
) {
2234 UINTN j
= 0, k
, VolumeIndex
;
2235 CHAR16
*DirName
, *FileName
, *PathName
, FullDescription
[256];
2237 while ((DirName
= FindCommaDelimited(Locations
, j
++)) != NULL
) {
2239 while ((FileName
= FindCommaDelimited(Names
, k
++)) != NULL
) {
2240 PathName
= StrDuplicate(DirName
);
2241 MergeStrings(&PathName
, FileName
, (StriCmp(PathName
, L
"\\") == 0) ? 0 : L
'\\');
2242 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
2243 if ((Volumes
[VolumeIndex
]->RootDir
!= NULL
) && (FileExists(Volumes
[VolumeIndex
]->RootDir
, PathName
))) {
2244 SPrint(FullDescription
, 255, L
"%s at %s on %s", Description
, PathName
, Volumes
[VolumeIndex
]->VolName
);
2245 AddToolEntry(Volumes
[VolumeIndex
]->DeviceHandle
, PathName
, FullDescription
, BuiltinIcon(Icon
), 'S', FALSE
);
2248 MyFreePool(PathName
);
2249 MyFreePool(FileName
);
2251 MyFreePool(DirName
);
2252 } // while Locations
2253 } // VOID FindTool()
2255 // Add the second-row tags containing built-in and external tools (EFI shell,
2257 static VOID
ScanForTools(VOID
) {
2258 CHAR16
*FileName
= NULL
, *VolName
= NULL
, *MokLocations
, Description
[256];
2259 REFIT_MENU_ENTRY
*TempMenuEntry
;
2260 UINTN i
, j
, VolumeIndex
;
2264 MokLocations
= StrDuplicate(MOK_LOCATIONS
);
2265 if (MokLocations
!= NULL
)
2266 MergeStrings(&MokLocations
, SelfDirPath
, L
',');
2268 for (i
= 0; i
< NUM_TOOLS
; i
++) {
2269 switch(GlobalConfig
.ShowTools
[i
]) {
2270 // NOTE: Be sure that FileName is NULL at the end of each case.
2272 TempMenuEntry
= CopyMenuEntry(&MenuEntryShutdown
);
2273 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_SHUTDOWN
);
2274 AddMenuEntry(&MainMenu
, TempMenuEntry
);
2278 TempMenuEntry
= CopyMenuEntry(&MenuEntryReset
);
2279 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_RESET
);
2280 AddMenuEntry(&MainMenu
, TempMenuEntry
);
2284 TempMenuEntry
= CopyMenuEntry(&MenuEntryAbout
);
2285 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT
);
2286 AddMenuEntry(&MainMenu
, TempMenuEntry
);
2290 TempMenuEntry
= CopyMenuEntry(&MenuEntryExit
);
2291 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_EXIT
);
2292 AddMenuEntry(&MainMenu
, TempMenuEntry
);
2296 if (EfivarGetRaw(&GlobalGuid
, L
"OsIndicationsSupported", &b
, &j
) == EFI_SUCCESS
) {
2298 if (osind
& EFI_OS_INDICATIONS_BOOT_TO_FW_UI
) {
2299 TempMenuEntry
= CopyMenuEntry(&MenuEntryFirmware
);
2300 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_FIRMWARE
);
2301 AddMenuEntry(&MainMenu
, TempMenuEntry
);
2308 while ((FileName
= FindCommaDelimited(SHELL_NAMES
, j
++)) != NULL
) {
2309 if (FileExists(SelfRootDir
, FileName
)) {
2310 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, L
"EFI Shell", BuiltinIcon(BUILTIN_ICON_TOOL_SHELL
),
2313 MyFreePool(FileName
);
2319 while ((FileName
= FindCommaDelimited(GPTSYNC_NAMES
, j
++)) != NULL
) {
2320 if (FileExists(SelfRootDir
, FileName
)) {
2321 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, L
"Hybrid MBR tool", BuiltinIcon(BUILTIN_ICON_TOOL_PART
),
2324 MyFreePool(FileName
);
2331 while ((FileName
= FindCommaDelimited(GDISK_NAMES
, j
++)) != NULL
) {
2332 if (FileExists(SelfRootDir
, FileName
)) {
2333 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, L
"disk partitioning tool",
2334 BuiltinIcon(BUILTIN_ICON_TOOL_PART
), 'G', FALSE
);
2336 MyFreePool(FileName
);
2341 case TAG_APPLE_RECOVERY
:
2342 FileName
= StrDuplicate(L
"\\com.apple.recovery.boot\\boot.efi");
2343 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
2344 if ((Volumes
[VolumeIndex
]->RootDir
!= NULL
) && (FileExists(Volumes
[VolumeIndex
]->RootDir
, FileName
))) {
2345 SPrint(Description
, 255, L
"Apple Recovery on %s", Volumes
[VolumeIndex
]->VolName
);
2346 AddToolEntry(Volumes
[VolumeIndex
]->DeviceHandle
, FileName
, Description
,
2347 BuiltinIcon(BUILTIN_ICON_TOOL_APPLE_RESCUE
), 'R', TRUE
);
2350 MyFreePool(FileName
);
2354 case TAG_WINDOWS_RECOVERY
:
2356 while ((FileName
= FindCommaDelimited(GlobalConfig
.WindowsRecoveryFiles
, j
++)) != NULL
) {
2357 SplitVolumeAndFilename(&FileName
, &VolName
);
2358 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
2359 if ((Volumes
[VolumeIndex
]->RootDir
!= NULL
) && (FileExists(Volumes
[VolumeIndex
]->RootDir
, FileName
)) &&
2360 ((VolName
== NULL
) || (StriCmp(VolName
, Volumes
[VolumeIndex
]->VolName
) == 0))) {
2361 SPrint(Description
, 255, L
"Microsoft Recovery on %s", Volumes
[VolumeIndex
]->VolName
);
2362 AddToolEntry(Volumes
[VolumeIndex
]->DeviceHandle
, FileName
, Description
,
2363 BuiltinIcon(BUILTIN_ICON_TOOL_WINDOWS_RESCUE
), 'R', TRUE
);
2367 MyFreePool(FileName
);
2369 MyFreePool(VolName
);
2374 FindTool(MokLocations
, MOK_NAMES
, L
"MOK utility", BUILTIN_ICON_TOOL_MOK_TOOL
);
2378 FindTool(MEMTEST_LOCATIONS
, MEMTEST_NAMES
, L
"Memory test utility", BUILTIN_ICON_TOOL_MEMTEST
);
2383 } // static VOID ScanForTools
2385 // Rescan for boot loaders
2386 VOID
RescanAll(VOID
) {
2393 egDisplayMessage(L
"Scanning for new boot loaders; please wait....", &BGColor
);
2394 FreeList((VOID
***) &(MainMenu
.Entries
), &MainMenu
.EntryCount
);
2395 MainMenu
.Entries
= NULL
;
2396 MainMenu
.EntryCount
= 0;
2397 ReadConfig(GlobalConfig
.ConfigFilename
);
2398 ConnectAllDriversToAllControllers();
2400 ScanForBootloaders();
2403 } // VOID RescanAll()
2405 #ifdef __MAKEWITH_TIANO
2407 // Minimal initialization function
2408 static VOID
InitializeLib(IN EFI_HANDLE ImageHandle
, IN EFI_SYSTEM_TABLE
*SystemTable
) {
2410 // gImageHandle = ImageHandle;
2411 gBS
= SystemTable
->BootServices
;
2412 // gRS = SystemTable->RuntimeServices;
2413 gRT
= SystemTable
->RuntimeServices
; // Some BDS functions need gRT to be set
2414 EfiGetSystemConfigurationTable (&gEfiDxeServicesTableGuid
, (VOID
**) &gDS
);
2416 InitializeConsoleSim();
2421 // Set up our own Secure Boot extensions....
2422 // Returns TRUE on success, FALSE otherwise
2423 static BOOLEAN
SecureBootSetup(VOID
) {
2425 BOOLEAN Success
= FALSE
;
2427 if (secure_mode() && ShimLoaded()) {
2428 Status
= security_policy_install();
2429 if (Status
== EFI_SUCCESS
) {
2432 Print(L
"Failed to install MOK Secure Boot extensions");
2436 } // VOID SecureBootSetup()
2438 // Remove our own Secure Boot extensions....
2439 // Returns TRUE on success, FALSE otherwise
2440 static BOOLEAN
SecureBootUninstall(VOID
) {
2442 BOOLEAN Success
= TRUE
;
2444 if (secure_mode()) {
2445 Status
= security_policy_uninstall();
2446 if (Status
!= EFI_SUCCESS
) {
2448 BeginTextScreen(L
"Secure Boot Policy Failure");
2449 Print(L
"Failed to uninstall MOK Secure Boot extensions; forcing a reboot.");
2451 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);
2455 } // VOID SecureBootUninstall
2457 // Sets the global configuration filename; will be CONFIG_FILE_NAME unless the
2458 // "-c" command-line option is set, in which case that takes precedence.
2459 // If an error is encountered, leaves the value alone (it should be set to
2460 // CONFIG_FILE_NAME when GlobalConfig is initialized).
2461 static VOID
SetConfigFilename(EFI_HANDLE ImageHandle
) {
2462 EFI_LOADED_IMAGE
*Info
;
2463 CHAR16
*Options
, *FileName
;
2467 Status
= refit_call3_wrapper(BS
->HandleProtocol
, ImageHandle
, &LoadedImageProtocol
, (VOID
**) &Info
);
2468 if ((Status
== EFI_SUCCESS
) && (Info
->LoadOptionsSize
> 0)) {
2469 Options
= (CHAR16
*) Info
->LoadOptions
;
2470 Where
= FindSubString(L
" -c ", Options
);
2472 FileName
= StrDuplicate(&Options
[Where
+ 4]);
2473 Where
= FindSubString(L
" ", FileName
);
2475 FileName
[Where
] = L
'\0';
2477 if (FileExists(SelfDir
, FileName
)) {
2478 GlobalConfig
.ConfigFilename
= FileName
;
2480 Print(L
"Specified configuration file (%s) doesn't exist; using\n'refind.conf' default\n", FileName
);
2481 MyFreePool(FileName
);
2485 } // VOID SetConfigFilename()
2492 efi_main (EFI_HANDLE ImageHandle
, EFI_SYSTEM_TABLE
*SystemTable
)
2495 BOOLEAN MainLoopRunning
= TRUE
;
2496 BOOLEAN MokProtocol
;
2497 REFIT_MENU_ENTRY
*ChosenEntry
;
2499 CHAR16
*Selection
= NULL
;
2503 InitializeLib(ImageHandle
, SystemTable
);
2504 Status
= InitRefitLib(ImageHandle
);
2505 if (EFI_ERROR(Status
))
2508 // read configuration
2509 CopyMem(GlobalConfig
.ScanFor
, "ieom ", NUM_SCAN_OPTIONS
);
2510 FindLegacyBootType();
2511 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
)
2512 CopyMem(GlobalConfig
.ScanFor
, "ihebocm ", NUM_SCAN_OPTIONS
);
2513 SetConfigFilename(ImageHandle
);
2514 ReadConfig(GlobalConfig
.ConfigFilename
);
2518 WarnIfLegacyProblems();
2519 MainMenu
.TimeoutSeconds
= GlobalConfig
.Timeout
;
2521 // disable EFI watchdog timer
2522 refit_call4_wrapper(BS
->SetWatchdogTimer
, 0x0000, 0x0000, 0x0000, NULL
);
2524 // further bootstrap (now with config available)
2525 MokProtocol
= SecureBootSetup();
2528 ScanForBootloaders();
2532 if (GlobalConfig
.ScanDelay
> 0) {
2537 egDisplayMessage(L
"Pausing before disk scan; please wait....", &BGColor
);
2538 for (i
= 0; i
< GlobalConfig
.ScanDelay
; i
++)
2539 refit_call1_wrapper(BS
->Stall
, 1000000);
2543 if (GlobalConfig
.DefaultSelection
)
2544 Selection
= StrDuplicate(GlobalConfig
.DefaultSelection
);
2546 while (MainLoopRunning
) {
2547 MenuExit
= RunMainMenu(&MainMenu
, Selection
, &ChosenEntry
);
2549 // The Escape key triggers a re-scan operation....
2550 if (MenuExit
== MENU_EXIT_ESCAPE
) {
2556 switch (ChosenEntry
->Tag
) {
2558 case TAG_REBOOT
: // Reboot
2560 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);
2561 MainLoopRunning
= FALSE
; // just in case we get this far
2564 case TAG_SHUTDOWN
: // Shut Down
2566 refit_call4_wrapper(RT
->ResetSystem
, EfiResetShutdown
, EFI_SUCCESS
, 0, NULL
);
2567 MainLoopRunning
= FALSE
; // just in case we get this far
2570 case TAG_ABOUT
: // About rEFInd
2574 case TAG_LOADER
: // Boot OS via .EFI loader
2575 StartLoader((LOADER_ENTRY
*)ChosenEntry
);
2578 case TAG_LEGACY
: // Boot legacy OS
2579 StartLegacy((LEGACY_ENTRY
*)ChosenEntry
);
2582 #ifdef __MAKEWITH_TIANO
2583 case TAG_LEGACY_UEFI
: // Boot a legacy OS on a non-Mac
2584 StartLegacyUEFI((LEGACY_ENTRY
*)ChosenEntry
);
2588 case TAG_TOOL
: // Start a EFI tool
2589 StartTool((LOADER_ENTRY
*)ChosenEntry
);
2592 case TAG_EXIT
: // Terminate rEFInd
2593 if ((MokProtocol
) && !SecureBootUninstall()) {
2594 MainLoopRunning
= FALSE
; // just in case we get this far
2596 BeginTextScreen(L
" ");
2601 case TAG_FIRMWARE
: // Reboot into firmware's user interface
2602 RebootIntoFirmware();
2606 MyFreePool(Selection
);
2607 Selection
= (ChosenEntry
->Title
) ? StrDuplicate(ChosenEntry
->Title
) : NULL
;
2610 // If we end up here, things have gone wrong. Try to reboot, and if that
2611 // fails, go into an endless loop.
2612 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);