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-2013 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.
52 #include "security_policy.h"
53 #include "../include/Handle.h"
54 #include "../include/refit_call_wrapper.h"
55 #include "driver_support.h"
56 #include "../include/syslinux_mbr.h"
58 #ifdef __MAKEWITH_GNUEFI
59 #define EFI_SECURITY_VIOLATION EFIERR (26)
61 #include "../EfiLib/BdsHelper.h"
62 #endif // __MAKEWITH_GNUEFI
64 #ifndef EFI_OS_INDICATIONS_BOOT_TO_FW_UI
65 #define EFI_OS_INDICATIONS_BOOT_TO_FW_UI 0x0000000000000001ULL
71 #define MACOSX_LOADER_PATH L"System\\Library\\CoreServices\\boot.efi"
72 #define MEMTEST_LOCATIONS L"EFI\\tools,EFI\\tools\\memtest86,EFI\\tools\\memtest,EFI\\memtest86,EFI\\memtest"
74 #define SHELL_NAMES L"\\EFI\\tools\\shell.efi,\\EFI\\tools\\shellx64.efi,\\shell.efi,\\shellx64.efi"
75 #define GPTSYNC_NAMES L"\\EFI\\tools\\gptsync.efi,\\EFI\\tools\\gptsync_x64.efi"
76 #define MEMTEST_NAMES L"memtest86.efi,memtest86_x64.efi,memtest86x64.efi,bootx64.efi"
77 #define DRIVER_DIRS L"drivers,drivers_x64"
78 #define FALLBACK_FULLNAME L"EFI\\BOOT\\bootx64.efi"
79 #define FALLBACK_BASENAME L"bootx64.efi"
80 #define EFI_STUB_ARCH 0x8664
82 #define SHELL_NAMES L"\\EFI\\tools\\shell.efi,\\EFI\\tools\\shellia32.efi,\\shell.efi,\\shellia32.efi"
83 #define GPTSYNC_NAMES L"\\EFI\\tools\\gptsync.efi,\\EFI\\tools\\gptsync_ia32.efi"
84 #define MEMTEST_NAMES L"memtest86.efi,memtest86_ia32.efi,memtest86ia32.efi,bootia32.efi"
85 #define DRIVER_DIRS L"drivers,drivers_ia32"
86 #define FALLBACK_FULLNAME L"EFI\\BOOT\\bootia32.efi"
87 #define FALLBACK_BASENAME L"bootia32.efi"
88 #define EFI_STUB_ARCH 0x014c
90 #define SHELL_NAMES L"\\EFI\\tools\\shell.efi,\\shell.efi"
91 #define GPTSYNC_NAMES L"\\EFI\\tools\\gptsync.efi"
92 #define MEMTEST_NAMES L"memtest86.efi"
93 #define DRIVER_DIRS L"drivers"
94 #define FALLBACK_FULLNAME L"EFI\\BOOT\\boot.efi" /* Not really correct */
95 #define FALLBACK_BASENAME L"boot.efi" /* Not really correct */
97 #define FAT_ARCH 0x0ef1fab9 /* ID for Apple "fat" binary */
99 // Filename patterns that identify EFI boot loaders. Note that a single case (either L"*.efi" or
100 // L"*.EFI") is fine for most systems; but Gigabyte's buggy Hybrid EFI does a case-sensitive
101 // comparison when it should do a case-insensitive comparison, so I'm doubling this up. It does
102 // no harm on other computers, AFAIK. In theory, every case variation should be done for
103 // completeness, but that's ridiculous....
104 #define LOADER_MATCH_PATTERNS L"*.efi,*.EFI"
106 // Patterns that identify Linux kernels. Added to the loader match pattern when the
107 // scan_all_linux_kernels option is set in the configuration file. Causes kernels WITHOUT
108 // a ".efi" extension to be found when scanning for boot loaders.
109 #define LINUX_MATCH_PATTERNS L"vmlinuz*,bzImage*"
111 // Default hint text for program-launch submenus
112 #define SUBSCREEN_HINT1 L"Use arrow keys to move cursor; Enter to boot;"
113 #define SUBSCREEN_HINT2 L"Insert or F2 to edit options; Esc to return to main menu"
114 #define SUBSCREEN_HINT2_NO_EDITOR L"Esc to return to main menu"
118 #define TYPE_LEGACY 2
120 static REFIT_MENU_ENTRY MenuEntryAbout
= { L
"About rEFInd", TAG_ABOUT
, 1, 0, 'A', NULL
, NULL
, NULL
};
121 static REFIT_MENU_ENTRY MenuEntryReset
= { L
"Reboot Computer", TAG_REBOOT
, 1, 0, 'R', NULL
, NULL
, NULL
};
122 static REFIT_MENU_ENTRY MenuEntryShutdown
= { L
"Shut Down Computer", TAG_SHUTDOWN
, 1, 0, 'U', NULL
, NULL
, NULL
};
123 static REFIT_MENU_ENTRY MenuEntryReturn
= { L
"Return to Main Menu", TAG_RETURN
, 1, 0, 0, NULL
, NULL
, NULL
};
124 static REFIT_MENU_ENTRY MenuEntryExit
= { L
"Exit rEFInd", TAG_EXIT
, 1, 0, 0, NULL
, NULL
, NULL
};
125 static REFIT_MENU_ENTRY MenuEntryFirmware
= { L
"Reboot to Computer Setup Utility", TAG_FIRMWARE
, 1, 0, 0, NULL
, NULL
, NULL
};
127 static REFIT_MENU_SCREEN MainMenu
= { L
"Main Menu", NULL
, 0, NULL
, 0, NULL
, 0, L
"Automatic boot",
128 L
"Use arrow keys to move cursor; Enter to boot;",
129 L
"Insert or F2 for more options; Esc to refresh" };
130 static REFIT_MENU_SCREEN AboutMenu
= { L
"About", NULL
, 0, NULL
, 0, NULL
, 0, NULL
, L
"Press Enter to return to main menu", L
"" };
132 REFIT_CONFIG GlobalConfig
= { FALSE
, FALSE
, 0, 0, 0, DONT_CHANGE_TEXT_MODE
, 20, 0, 0, GRAPHICS_FOR_OSX
, LEGACY_TYPE_MAC
, 0, 0,
133 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
134 { TAG_SHELL
, TAG_MEMTEST
, TAG_APPLE_RECOVERY
, TAG_MOK_TOOL
, TAG_ABOUT
, TAG_SHUTDOWN
,
135 TAG_REBOOT
, TAG_FIRMWARE
, 0, 0, 0, 0, 0, 0 }
138 EFI_GUID GlobalGuid
= EFI_GLOBAL_VARIABLE
;
140 // Structure used to hold boot loader filenames and time stamps in
141 // a linked list; used to sort entries within a directory.
145 struct LOADER_LIST
*NextEntry
;
152 static VOID
AboutrEFInd(VOID
)
154 CHAR16
*TempStr
; // Note: Don't deallocate; moved to menu structure
156 if (AboutMenu
.EntryCount
== 0) {
157 AboutMenu
.TitleImage
= BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT
);
158 AddMenuInfoLine(&AboutMenu
, L
"rEFInd Version 0.7.3.6");
159 AddMenuInfoLine(&AboutMenu
, L
"");
160 AddMenuInfoLine(&AboutMenu
, L
"Copyright (c) 2006-2010 Christoph Pfisterer");
161 AddMenuInfoLine(&AboutMenu
, L
"Copyright (c) 2012-2013 Roderick W. Smith");
162 AddMenuInfoLine(&AboutMenu
, L
"Portions Copyright (c) Intel Corporation and others");
163 AddMenuInfoLine(&AboutMenu
, L
"Distributed under the terms of the GNU GPLv3 license");
164 AddMenuInfoLine(&AboutMenu
, L
"");
165 AddMenuInfoLine(&AboutMenu
, L
"Running on:");
166 TempStr
= AllocateZeroPool(256 * sizeof(CHAR16
));
167 SPrint(TempStr
, 255, L
" EFI Revision %d.%02d", ST
->Hdr
.Revision
>> 16, ST
->Hdr
.Revision
& ((1 << 16) - 1));
168 AddMenuInfoLine(&AboutMenu
, TempStr
);
170 AddMenuInfoLine(&AboutMenu
, L
" Platform: x86 (32 bit)");
171 #elif defined(EFIX64)
172 TempStr
= AllocateZeroPool(256 * sizeof(CHAR16
));
173 SPrint(TempStr
, 255, L
" Platform: x86_64 (64 bit); Secure Boot %s", secure_mode() ? L
"active" : L
"inactive");
174 AddMenuInfoLine(&AboutMenu
, TempStr
);
176 AddMenuInfoLine(&AboutMenu
, L
" Platform: unknown");
178 TempStr
= AllocateZeroPool(256 * sizeof(CHAR16
));
179 SPrint(TempStr
, 255, L
" Firmware: %s %d.%02d",
180 ST
->FirmwareVendor
, ST
->FirmwareRevision
>> 16, ST
->FirmwareRevision
& ((1 << 16) - 1));
181 AddMenuInfoLine(&AboutMenu
, TempStr
);
182 TempStr
= AllocateZeroPool(256 * sizeof(CHAR16
));
183 SPrint(TempStr
, 255, L
" Screen Output: %s", egScreenDescription());
184 AddMenuInfoLine(&AboutMenu
, TempStr
);
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
;
272 if (ErrorInStep
!= NULL
)
276 if (LoadOptions
!= NULL
) {
277 if (LoaderType
== TYPE_EFI
) {
278 MergeStrings(&FullLoadOptions
, LoadOptions
, L
' ');
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...
285 MergeStrings(&FullLoadOptions
, LoadOptions
, 0);
287 } else { // LoadOptions == NULL
288 // NOTE: We provide a non-null string when no options are specified for safety;
289 // some systems (at least DUET) can hang when launching some programs (such as
290 // an EFI shell) without this.
291 FullLoadOptions
= StrDuplicate(L
" ");
294 Print(L
"Starting %s\nUsing load options '%s'\n", ImageTitle
, FullLoadOptions
);
296 // load the image into memory (and execute it, in the case of a shim/MOK image).
297 ReturnStatus
= Status
= EFI_NOT_FOUND
; // in case the list is empty
298 for (DevicePathIndex
= 0; DevicePaths
[DevicePathIndex
] != NULL
; DevicePathIndex
++) {
299 FindVolumeAndFilename(DevicePaths
[DevicePathIndex
], &Volume
, &Filename
);
300 // Some EFIs crash if attempting to load driver for invalid architecture, so
301 // protect for this condition; but sometimes Volume comes back NULL, so provide
302 // an exception. (TODO: Handle this special condition better.)
303 if ((LoaderType
== TYPE_LEGACY
) || (Volume
== NULL
) || IsValidLoader(Volume
->RootDir
, Filename
)) {
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
= ((UINT32
)StrLen(FullLoadOptions
) + 1) * sizeof(CHAR16
);
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 MergeStrings(&InitrdName
, Path
, 0);
488 MergeStrings(&InitrdName
, DirEntry
->FileName
, 0);
491 if (InitrdVersion
== NULL
) {
492 MergeStrings(&InitrdName
, Path
, 0);
493 MergeStrings(&InitrdName
, DirEntry
->FileName
, 0);
496 MyFreePool(InitrdVersion
);
498 DirIterClose(&DirIter
);
500 // Note: Don't FreePool(FileName), since Basename returns a pointer WITHIN the string it's passed.
501 MyFreePool(KernelVersion
);
504 } // static CHAR16 * FindInitrd()
506 LOADER_ENTRY
* AddPreparedLoaderEntry(LOADER_ENTRY
*Entry
) {
507 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
510 } // LOADER_ENTRY * AddPreparedLoaderEntry()
512 // Creates a copy of a menu screen.
513 // Returns a pointer to the copy of the menu screen.
514 static REFIT_MENU_SCREEN
* CopyMenuScreen(REFIT_MENU_SCREEN
*Entry
) {
515 REFIT_MENU_SCREEN
*NewEntry
;
518 NewEntry
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
519 if ((Entry
!= NULL
) && (NewEntry
!= NULL
)) {
520 CopyMem(NewEntry
, Entry
, sizeof(REFIT_MENU_SCREEN
));
521 NewEntry
->Title
= (Entry
->Title
) ? StrDuplicate(Entry
->Title
) : NULL
;
522 NewEntry
->TimeoutText
= (Entry
->TimeoutText
) ? StrDuplicate(Entry
->TimeoutText
) : NULL
;
523 if (Entry
->TitleImage
!= NULL
) {
524 NewEntry
->TitleImage
= AllocatePool(sizeof(EG_IMAGE
));
525 if (NewEntry
->TitleImage
!= NULL
)
526 CopyMem(NewEntry
->TitleImage
, Entry
->TitleImage
, sizeof(EG_IMAGE
));
528 NewEntry
->InfoLines
= (CHAR16
**) AllocateZeroPool(Entry
->InfoLineCount
* (sizeof(CHAR16
*)));
529 for (i
= 0; i
< Entry
->InfoLineCount
&& NewEntry
->InfoLines
; i
++) {
530 NewEntry
->InfoLines
[i
] = (Entry
->InfoLines
[i
]) ? StrDuplicate(Entry
->InfoLines
[i
]) : NULL
;
532 NewEntry
->Entries
= (REFIT_MENU_ENTRY
**) AllocateZeroPool(Entry
->EntryCount
* (sizeof (REFIT_MENU_ENTRY
*)));
533 for (i
= 0; i
< Entry
->EntryCount
&& NewEntry
->Entries
; i
++) {
534 AddMenuEntry(NewEntry
, Entry
->Entries
[i
]);
536 NewEntry
->Hint1
= (Entry
->Hint1
) ? StrDuplicate(Entry
->Hint1
) : NULL
;
537 NewEntry
->Hint2
= (Entry
->Hint2
) ? StrDuplicate(Entry
->Hint2
) : NULL
;
540 } // static REFIT_MENU_SCREEN* CopyMenuScreen()
542 // Creates a copy of a menu entry. Intended to enable moving a stack-based
543 // menu entry (such as the ones for the "reboot" and "exit" functions) to
544 // to the heap. This enables easier deletion of the whole set of menu
545 // entries when re-scanning.
546 // Returns a pointer to the copy of the menu entry.
547 static REFIT_MENU_ENTRY
* CopyMenuEntry(REFIT_MENU_ENTRY
*Entry
) {
548 REFIT_MENU_ENTRY
*NewEntry
;
550 NewEntry
= AllocateZeroPool(sizeof(REFIT_MENU_ENTRY
));
551 if ((Entry
!= NULL
) && (NewEntry
!= NULL
)) {
552 CopyMem(NewEntry
, Entry
, sizeof(REFIT_MENU_ENTRY
));
553 NewEntry
->Title
= (Entry
->Title
) ? StrDuplicate(Entry
->Title
) : NULL
;
554 if (Entry
->BadgeImage
!= NULL
) {
555 NewEntry
->BadgeImage
= AllocatePool(sizeof(EG_IMAGE
));
556 if (NewEntry
->BadgeImage
!= NULL
)
557 CopyMem(NewEntry
->BadgeImage
, Entry
->BadgeImage
, sizeof(EG_IMAGE
));
559 if (Entry
->Image
!= NULL
) {
560 NewEntry
->Image
= AllocatePool(sizeof(EG_IMAGE
));
561 if (NewEntry
->Image
!= NULL
)
562 CopyMem(NewEntry
->Image
, Entry
->Image
, sizeof(EG_IMAGE
));
564 if (Entry
->SubScreen
!= NULL
) {
565 NewEntry
->SubScreen
= CopyMenuScreen(Entry
->SubScreen
);
569 } // REFIT_MENU_ENTRY* CopyMenuEntry()
571 // Creates a new LOADER_ENTRY data structure and populates it with
572 // default values from the specified Entry, or NULL values if Entry
573 // is unspecified (NULL).
574 // Returns a pointer to the new data structure, or NULL if it
575 // couldn't be allocated
576 LOADER_ENTRY
*InitializeLoaderEntry(IN LOADER_ENTRY
*Entry
) {
577 LOADER_ENTRY
*NewEntry
= NULL
;
579 NewEntry
= AllocateZeroPool(sizeof(LOADER_ENTRY
));
580 if (NewEntry
!= NULL
) {
581 NewEntry
->me
.Title
= NULL
;
582 NewEntry
->me
.Tag
= TAG_LOADER
;
583 NewEntry
->Enabled
= TRUE
;
584 NewEntry
->UseGraphicsMode
= FALSE
;
585 NewEntry
->OSType
= 0;
587 NewEntry
->LoaderPath
= (Entry
->LoaderPath
) ? StrDuplicate(Entry
->LoaderPath
) : NULL
;
588 NewEntry
->VolName
= (Entry
->VolName
) ? StrDuplicate(Entry
->VolName
) : NULL
;
589 NewEntry
->DevicePath
= Entry
->DevicePath
;
590 NewEntry
->UseGraphicsMode
= Entry
->UseGraphicsMode
;
591 NewEntry
->LoadOptions
= (Entry
->LoadOptions
) ? StrDuplicate(Entry
->LoadOptions
) : NULL
;
592 NewEntry
->InitrdPath
= (Entry
->InitrdPath
) ? StrDuplicate(Entry
->InitrdPath
) : NULL
;
596 } // LOADER_ENTRY *InitializeLoaderEntry()
598 // Adds InitrdPath to Options, but only if Options doesn't already include an
599 // initrd= line. Done to enable overriding the default initrd selection in a
600 // refind_linux.conf file's options list.
601 // Returns a pointer to a new string. The calling function is responsible for
602 // freeing its memory.
603 static CHAR16
*AddInitrdToOptions(CHAR16
*Options
, CHAR16
*InitrdPath
) {
604 CHAR16
*NewOptions
= NULL
;
607 NewOptions
= StrDuplicate(Options
);
608 if ((InitrdPath
!= NULL
) && !StriSubCmp(L
"initrd=", Options
)) {
609 MergeStrings(&NewOptions
, L
"initrd=", L
' ');
610 MergeStrings(&NewOptions
, InitrdPath
, 0);
613 } // CHAR16 *AddInitrdToOptions()
615 // Prepare a REFIT_MENU_SCREEN data structure for a subscreen entry. This sets up
616 // the default entry that launches the boot loader using the same options as the
617 // main Entry does. Subsequent options can be added by the calling function.
618 // If a subscreen already exists in the Entry that's passed to this function,
619 // it's left unchanged and a pointer to it is returned.
620 // Returns a pointer to the new subscreen data structure, or NULL if there
621 // were problems allocating memory.
622 REFIT_MENU_SCREEN
*InitializeSubScreen(IN LOADER_ENTRY
*Entry
) {
623 CHAR16
*FileName
, *MainOptions
= NULL
;
624 REFIT_MENU_SCREEN
*SubScreen
= NULL
;
625 LOADER_ENTRY
*SubEntry
;
627 FileName
= Basename(Entry
->LoaderPath
);
628 if (Entry
->me
.SubScreen
== NULL
) { // No subscreen yet; initialize default entry....
629 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
630 if (SubScreen
!= NULL
) {
631 SubScreen
->Title
= AllocateZeroPool(sizeof(CHAR16
) * 256);
632 SPrint(SubScreen
->Title
, 255, L
"Boot Options for %s on %s",
633 (Entry
->Title
!= NULL
) ? Entry
->Title
: FileName
, Entry
->VolName
);
634 SubScreen
->TitleImage
= Entry
->me
.Image
;
636 SubEntry
= InitializeLoaderEntry(Entry
);
637 if (SubEntry
!= NULL
) {
638 SubEntry
->me
.Title
= StrDuplicate(L
"Boot using default options");
639 MainOptions
= SubEntry
->LoadOptions
;
640 SubEntry
->LoadOptions
= AddInitrdToOptions(MainOptions
, SubEntry
->InitrdPath
);
641 MyFreePool(MainOptions
);
642 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
643 } // if (SubEntry != NULL)
644 SubScreen
->Hint1
= StrDuplicate(SUBSCREEN_HINT1
);
645 if (GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_EDITOR
) {
646 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2_NO_EDITOR
);
648 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2
);
650 } // if (SubScreen != NULL)
651 } else { // existing subscreen; less initialization, and just add new entry later....
652 SubScreen
= Entry
->me
.SubScreen
;
655 } // REFIT_MENU_SCREEN *InitializeSubScreen()
657 VOID
GenerateSubScreen(LOADER_ENTRY
*Entry
, IN REFIT_VOLUME
*Volume
) {
658 REFIT_MENU_SCREEN
*SubScreen
;
659 LOADER_ENTRY
*SubEntry
;
661 CHAR16 DiagsFileName
[256];
666 // create the submenu
667 if (StrLen(Entry
->Title
) == 0) {
668 MyFreePool(Entry
->Title
);
671 SubScreen
= InitializeSubScreen(Entry
);
673 // loader-specific submenu entries
674 if (Entry
->OSType
== 'M') { // entries for Mac OS X
676 SubEntry
= InitializeLoaderEntry(Entry
);
677 if (SubEntry
!= NULL
) {
678 SubEntry
->me
.Title
= L
"Boot Mac OS X with a 64-bit kernel";
679 SubEntry
->LoadOptions
= L
"arch=x86_64";
680 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
681 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
684 SubEntry
= InitializeLoaderEntry(Entry
);
685 if (SubEntry
!= NULL
) {
686 SubEntry
->me
.Title
= L
"Boot Mac OS X with a 32-bit kernel";
687 SubEntry
->LoadOptions
= L
"arch=i386";
688 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
689 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
693 if (!(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_SINGLEUSER
)) {
694 SubEntry
= InitializeLoaderEntry(Entry
);
695 if (SubEntry
!= NULL
) {
696 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode";
697 SubEntry
->UseGraphicsMode
= FALSE
;
698 SubEntry
->LoadOptions
= L
"-v";
699 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
703 SubEntry
= InitializeLoaderEntry(Entry
);
704 if (SubEntry
!= NULL
) {
705 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode (64-bit)";
706 SubEntry
->UseGraphicsMode
= FALSE
;
707 SubEntry
->LoadOptions
= L
"-v arch=x86_64";
708 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
711 SubEntry
= InitializeLoaderEntry(Entry
);
712 if (SubEntry
!= NULL
) {
713 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode (32-bit)";
714 SubEntry
->UseGraphicsMode
= FALSE
;
715 SubEntry
->LoadOptions
= L
"-v arch=i386";
716 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
720 SubEntry
= InitializeLoaderEntry(Entry
);
721 if (SubEntry
!= NULL
) {
722 SubEntry
->me
.Title
= L
"Boot Mac OS X in single user mode";
723 SubEntry
->UseGraphicsMode
= FALSE
;
724 SubEntry
->LoadOptions
= L
"-v -s";
725 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
727 } // single-user mode allowed
729 if (!(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_SAFEMODE
)) {
730 SubEntry
= InitializeLoaderEntry(Entry
);
731 if (SubEntry
!= NULL
) {
732 SubEntry
->me
.Title
= L
"Boot Mac OS X in safe mode";
733 SubEntry
->UseGraphicsMode
= FALSE
;
734 SubEntry
->LoadOptions
= L
"-v -x";
735 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
737 } // safe mode allowed
739 // check for Apple hardware diagnostics
740 StrCpy(DiagsFileName
, L
"System\\Library\\CoreServices\\.diagnostics\\diags.efi");
741 if (FileExists(Volume
->RootDir
, DiagsFileName
) && !(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_HWTEST
)) {
742 SubEntry
= InitializeLoaderEntry(Entry
);
743 if (SubEntry
!= NULL
) {
744 SubEntry
->me
.Title
= L
"Run Apple Hardware Test";
745 MyFreePool(SubEntry
->LoaderPath
);
746 SubEntry
->LoaderPath
= StrDuplicate(DiagsFileName
);
747 SubEntry
->DevicePath
= FileDevicePath(Volume
->DeviceHandle
, SubEntry
->LoaderPath
);
748 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
749 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
751 } // if diagnostics entry found
753 } else if (Entry
->OSType
== 'L') { // entries for Linux kernels with EFI stub loaders
754 File
= ReadLinuxOptionsFile(Entry
->LoaderPath
, Volume
);
756 InitrdName
= FindInitrd(Entry
->LoaderPath
, Volume
);
757 TokenCount
= ReadTokenLine(File
, &TokenList
);
758 // first entry requires special processing, since it was initially set
759 // up with a default title but correct options by InitializeSubScreen(),
761 if ((SubScreen
->Entries
!= NULL
) && (SubScreen
->Entries
[0] != NULL
)) {
762 MyFreePool(SubScreen
->Entries
[0]->Title
);
763 SubScreen
->Entries
[0]->Title
= TokenList
[0] ? StrDuplicate(TokenList
[0]) : StrDuplicate(L
"Boot Linux");
765 FreeTokenLine(&TokenList
, &TokenCount
);
766 while ((TokenCount
= ReadTokenLine(File
, &TokenList
)) > 1) {
767 SubEntry
= InitializeLoaderEntry(Entry
);
768 SubEntry
->me
.Title
= TokenList
[0] ? StrDuplicate(TokenList
[0]) : StrDuplicate(L
"Boot Linux");
769 MyFreePool(SubEntry
->LoadOptions
);
770 SubEntry
->LoadOptions
= AddInitrdToOptions(TokenList
[1], InitrdName
);
771 FreeTokenLine(&TokenList
, &TokenCount
);
772 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_LINUX
;
773 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
775 MyFreePool(InitrdName
);
779 } else if (Entry
->OSType
== 'E') { // entries for ELILO
780 SubEntry
= InitializeLoaderEntry(Entry
);
781 if (SubEntry
!= NULL
) {
782 SubEntry
->me
.Title
= L
"Run ELILO in interactive mode";
783 SubEntry
->LoadOptions
= L
"-p";
784 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
785 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
788 SubEntry
= InitializeLoaderEntry(Entry
);
789 if (SubEntry
!= NULL
) {
790 SubEntry
->me
.Title
= L
"Boot Linux for a 17\" iMac or a 15\" MacBook Pro (*)";
791 SubEntry
->UseGraphicsMode
= TRUE
;
792 SubEntry
->LoadOptions
= L
"-d 0 i17";
793 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
794 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
797 SubEntry
= InitializeLoaderEntry(Entry
);
798 if (SubEntry
!= NULL
) {
799 SubEntry
->me
.Title
= L
"Boot Linux for a 20\" iMac (*)";
800 SubEntry
->UseGraphicsMode
= TRUE
;
801 SubEntry
->LoadOptions
= L
"-d 0 i20";
802 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
803 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
806 SubEntry
= InitializeLoaderEntry(Entry
);
807 if (SubEntry
!= NULL
) {
808 SubEntry
->me
.Title
= L
"Boot Linux for a Mac Mini (*)";
809 SubEntry
->UseGraphicsMode
= TRUE
;
810 SubEntry
->LoadOptions
= L
"-d 0 mini";
811 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
812 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
815 AddMenuInfoLine(SubScreen
, L
"NOTE: This is an example. Entries");
816 AddMenuInfoLine(SubScreen
, L
"marked with (*) may not work.");
818 } else if (Entry
->OSType
== 'X') { // entries for xom.efi
819 // by default, skip the built-in selection and boot from hard disk only
820 Entry
->LoadOptions
= L
"-s -h";
822 SubEntry
= InitializeLoaderEntry(Entry
);
823 if (SubEntry
!= NULL
) {
824 SubEntry
->me
.Title
= L
"Boot Windows from Hard Disk";
825 SubEntry
->LoadOptions
= L
"-s -h";
826 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
827 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
830 SubEntry
= InitializeLoaderEntry(Entry
);
831 if (SubEntry
!= NULL
) {
832 SubEntry
->me
.Title
= L
"Boot Windows from CD-ROM";
833 SubEntry
->LoadOptions
= L
"-s -c";
834 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
835 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
838 SubEntry
= InitializeLoaderEntry(Entry
);
839 if (SubEntry
!= NULL
) {
840 SubEntry
->me
.Title
= L
"Run XOM in text mode";
841 SubEntry
->UseGraphicsMode
= FALSE
;
842 SubEntry
->LoadOptions
= L
"-v";
843 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
844 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
846 } // entries for xom.efi
847 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
848 Entry
->me
.SubScreen
= SubScreen
;
849 } // VOID GenerateSubScreen()
851 // Returns options for a Linux kernel. Reads them from an options file in the
852 // kernel's directory; and if present, adds an initrd= option for an initial
853 // RAM disk file with the same version number as the kernel file.
854 static CHAR16
* GetMainLinuxOptions(IN CHAR16
* LoaderPath
, IN REFIT_VOLUME
*Volume
) {
855 CHAR16
*Options
= NULL
, *InitrdName
, *FullOptions
= NULL
;
857 Options
= GetFirstOptionsFromFile(LoaderPath
, Volume
);
858 InitrdName
= FindInitrd(LoaderPath
, Volume
);
859 FullOptions
= AddInitrdToOptions(Options
, InitrdName
);
862 MyFreePool(InitrdName
);
863 return (FullOptions
);
864 } // static CHAR16 * GetMainLinuxOptions()
866 // Try to guess the name of the Linux distribution & add that name to
868 static VOID
GuessLinuxDistribution(CHAR16
**OSIconName
, REFIT_VOLUME
*Volume
, CHAR16
*LoaderPath
) {
872 UINTN TokenCount
= 0;
874 // If on Linux root fs, /etc/os-release file probably has clues....
875 if (FileExists(Volume
->RootDir
, L
"etc\\os-release") &&
876 (ReadFile(Volume
->RootDir
, L
"etc\\os-release", &File
, &FileSize
) == EFI_SUCCESS
)) {
878 TokenCount
= ReadTokenLine(&File
, &TokenList
);
879 if ((TokenCount
> 1) && ((StriCmp(TokenList
[0], L
"ID") == 0) || (StriCmp(TokenList
[0], L
"NAME") == 0))) {
880 MergeStrings(OSIconName
, TokenList
[1], L
',');
882 FreeTokenLine(&TokenList
, &TokenCount
);
883 } while (TokenCount
> 0);
884 MyFreePool(File
.Buffer
);
887 // Search for clues in the kernel's filename....
888 if (StriSubCmp(L
".fc", LoaderPath
))
889 MergeStrings(OSIconName
, L
"fedora", L
',');
890 if (StriSubCmp(L
".el", LoaderPath
))
891 MergeStrings(OSIconName
, L
"redhat", L
',');
892 } // VOID GuessLinuxDistribution()
894 // Sets a few defaults for a loader entry -- mainly the icon, but also the OS type
895 // code and shortcut letter. For Linux EFI stub loaders, also sets kernel options
896 // that will (with luck) work fairly automatically.
897 VOID
SetLoaderDefaults(LOADER_ENTRY
*Entry
, CHAR16
*LoaderPath
, REFIT_VOLUME
*Volume
) {
898 CHAR16
*FileName
, *PathOnly
, *NoExtension
, *OSIconName
= NULL
, *Temp
, *SubString
;
899 CHAR16 ShortcutLetter
= 0;
902 FileName
= Basename(LoaderPath
);
903 PathOnly
= FindPath(LoaderPath
);
904 NoExtension
= StripEfiExtension(FileName
);
906 // locate a custom icon for the loader
907 // Anything found here takes precedence over the "hints" in the OSIconName variable
908 if (!Entry
->me
.Image
)
909 Entry
->me
.Image
= egLoadIconAnyType(Volume
->RootDir
, PathOnly
, NoExtension
, 128);
910 if (!Entry
->me
.Image
)
911 Entry
->me
.Image
= egCopyImage(Volume
->VolIconImage
);
913 // Begin creating icon "hints" by using last part of directory path leading
915 Temp
= FindLastDirName(LoaderPath
);
916 MergeStrings(&OSIconName
, Temp
, L
',');
919 if (OSIconName
!= NULL
) {
920 ShortcutLetter
= OSIconName
[0];
923 // Add every "word" in the volume label, delimited by spaces, dashes (-), or
924 // underscores (_), to the list of hints to be used in searching for OS
926 if ((Volume
->VolName
) && (StrLen(Volume
->VolName
) > 0)) {
927 Temp
= SubString
= StrDuplicate(Volume
->VolName
);
929 Length
= StrLen(Temp
);
930 for (i
= 0; i
< Length
; i
++) {
931 if ((Temp
[i
] == L
' ') || (Temp
[i
] == L
'_') || (Temp
[i
] == L
'-')) {
933 if (StrLen(SubString
) > 0)
934 MergeStrings(&OSIconName
, SubString
, L
',');
935 SubString
= Temp
+ i
+ 1;
938 MergeStrings(&OSIconName
, SubString
, L
',');
943 // detect specific loaders
944 if (StriSubCmp(L
"bzImage", LoaderPath
) || StriSubCmp(L
"vmlinuz", LoaderPath
)) {
945 GuessLinuxDistribution(&OSIconName
, Volume
, LoaderPath
);
946 MergeStrings(&OSIconName
, L
"linux", L
',');
948 if (ShortcutLetter
== 0)
949 ShortcutLetter
= 'L';
950 Entry
->LoadOptions
= GetMainLinuxOptions(LoaderPath
, Volume
);
951 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_LINUX
;
952 } else if (StriSubCmp(L
"refit", LoaderPath
)) {
953 MergeStrings(&OSIconName
, L
"refit", L
',');
955 ShortcutLetter
= 'R';
956 } else if (StriSubCmp(L
"refind", LoaderPath
)) {
957 MergeStrings(&OSIconName
, L
"refind", L
',');
959 ShortcutLetter
= 'R';
960 } else if (StriCmp(LoaderPath
, MACOSX_LOADER_PATH
) == 0) {
961 if (Volume
->VolIconImage
!= NULL
) { // custom icon file found
962 Entry
->me
.Image
= Volume
->VolIconImage
;
964 MergeStrings(&OSIconName
, L
"mac", L
',');
966 ShortcutLetter
= 'M';
967 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
968 } else if (StriCmp(FileName
, L
"diags.efi") == 0) {
969 MergeStrings(&OSIconName
, L
"hwtest", L
',');
970 } else if (StriCmp(FileName
, L
"e.efi") == 0 || StriCmp(FileName
, L
"elilo.efi") == 0 || StriSubCmp(L
"elilo", FileName
)) {
971 MergeStrings(&OSIconName
, L
"elilo,linux", L
',');
973 if (ShortcutLetter
== 0)
974 ShortcutLetter
= 'L';
975 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
976 } else if (StriSubCmp(L
"grub", FileName
)) {
978 ShortcutLetter
= 'G';
979 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_GRUB
;
980 } else if (StriCmp(FileName
, L
"cdboot.efi") == 0 ||
981 StriCmp(FileName
, L
"bootmgr.efi") == 0 ||
982 StriCmp(FileName
, L
"bootmgfw.efi") == 0 ||
983 StriCmp(FileName
, L
"bkpbootmgfw.efi") == 0) {
984 MergeStrings(&OSIconName
, L
"win", L
',');
986 ShortcutLetter
= 'W';
987 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
988 } else if (StriCmp(FileName
, L
"xom.efi") == 0) {
989 MergeStrings(&OSIconName
, L
"xom,win", L
',');
990 Entry
->UseGraphicsMode
= TRUE
;
992 ShortcutLetter
= 'W';
993 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
996 if ((ShortcutLetter
>= 'a') && (ShortcutLetter
<= 'z'))
997 ShortcutLetter
= ShortcutLetter
- 'a' + 'A'; // convert lowercase to uppercase
998 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
999 if (Entry
->me
.Image
== NULL
)
1000 Entry
->me
.Image
= LoadOSIcon(OSIconName
, L
"unknown", FALSE
);
1001 MyFreePool(PathOnly
);
1002 } // VOID SetLoaderDefaults()
1004 // Add a specified EFI boot loader to the list, using automatic settings
1005 // for icons, options, etc.
1006 LOADER_ENTRY
* AddLoaderEntry(IN CHAR16
*LoaderPath
, IN CHAR16
*LoaderTitle
, IN REFIT_VOLUME
*Volume
) {
1007 LOADER_ENTRY
*Entry
;
1009 CleanUpPathNameSlashes(LoaderPath
);
1010 Entry
= InitializeLoaderEntry(NULL
);
1011 if (Entry
!= NULL
) {
1012 Entry
->Title
= StrDuplicate((LoaderTitle
!= NULL
) ? LoaderTitle
: LoaderPath
);
1013 Entry
->me
.Title
= AllocateZeroPool(sizeof(CHAR16
) * 256);
1014 SPrint(Entry
->me
.Title
, 255, L
"Boot %s from %s ", (LoaderTitle
!= NULL
) ? LoaderTitle
: LoaderPath
, Volume
->VolName
);
1016 Entry
->me
.BadgeImage
= Volume
->VolBadgeImage
;
1017 if ((LoaderPath
!= NULL
) && (LoaderPath
[0] != L
'\\')) {
1018 Entry
->LoaderPath
= StrDuplicate(L
"\\");
1020 Entry
->LoaderPath
= NULL
;
1022 MergeStrings(&(Entry
->LoaderPath
), LoaderPath
, 0);
1023 Entry
->VolName
= Volume
->VolName
;
1024 Entry
->DevicePath
= FileDevicePath(Volume
->DeviceHandle
, Entry
->LoaderPath
);
1025 SetLoaderDefaults(Entry
, LoaderPath
, Volume
);
1026 GenerateSubScreen(Entry
, Volume
);
1027 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1031 } // LOADER_ENTRY * AddLoaderEntry()
1033 // Returns -1 if (Time1 < Time2), +1 if (Time1 > Time2), or 0 if
1034 // (Time1 == Time2). Precision is only to the nearest second; since
1035 // this is used for sorting boot loader entries, differences smaller
1036 // than this are likely to be meaningless (and unlikely!).
1037 INTN
TimeComp(IN EFI_TIME
*Time1
, IN EFI_TIME
*Time2
) {
1038 INT64 Time1InSeconds
, Time2InSeconds
;
1040 // Following values are overestimates; I'm assuming 31 days in every month.
1041 // This is fine for the purpose of this function, which is limited
1042 Time1InSeconds
= Time1
->Second
+ (Time1
->Minute
* 60) + (Time1
->Hour
* 3600) + (Time1
->Day
* 86400) +
1043 (Time1
->Month
* 2678400) + ((Time1
->Year
- 1998) * 32140800);
1044 Time2InSeconds
= Time2
->Second
+ (Time2
->Minute
* 60) + (Time2
->Hour
* 3600) + (Time2
->Day
* 86400) +
1045 (Time2
->Month
* 2678400) + ((Time2
->Year
- 1998) * 32140800);
1046 if (Time1InSeconds
< Time2InSeconds
)
1048 else if (Time1InSeconds
> Time2InSeconds
)
1052 } // INTN TimeComp()
1054 // Adds a loader list element, keeping it sorted by date. Returns the new
1055 // first element (the one with the most recent date).
1056 static struct LOADER_LIST
* AddLoaderListEntry(struct LOADER_LIST
*LoaderList
, struct LOADER_LIST
*NewEntry
) {
1057 struct LOADER_LIST
*LatestEntry
, *CurrentEntry
, *PrevEntry
= NULL
;
1059 LatestEntry
= CurrentEntry
= LoaderList
;
1060 if (LoaderList
== NULL
) {
1061 LatestEntry
= NewEntry
;
1063 while ((CurrentEntry
!= NULL
) && (TimeComp(&(NewEntry
->TimeStamp
), &(CurrentEntry
->TimeStamp
)) < 0)) {
1064 PrevEntry
= CurrentEntry
;
1065 CurrentEntry
= CurrentEntry
->NextEntry
;
1067 NewEntry
->NextEntry
= CurrentEntry
;
1068 if (PrevEntry
== NULL
) {
1069 LatestEntry
= NewEntry
;
1071 PrevEntry
->NextEntry
= NewEntry
;
1074 return (LatestEntry
);
1075 } // static VOID AddLoaderListEntry()
1077 // Delete the LOADER_LIST linked list
1078 static VOID
CleanUpLoaderList(struct LOADER_LIST
*LoaderList
) {
1079 struct LOADER_LIST
*Temp
;
1081 while (LoaderList
!= NULL
) {
1083 LoaderList
= LoaderList
->NextEntry
;
1084 MyFreePool(Temp
->FileName
);
1087 } // static VOID CleanUpLoaderList()
1089 // Returns FALSE if the specified file/volume matches the GlobalConfig.DontScanDirs
1090 // or GlobalConfig.DontScanVolumes specification, or if Path points to a volume
1091 // other than the one specified by Volume, or if the specified path is SelfDir.
1092 // Returns TRUE if none of these conditions is met -- that is, if the path is
1093 // eligible for scanning.
1094 static BOOLEAN
ShouldScan(REFIT_VOLUME
*Volume
, CHAR16
*Path
) {
1095 CHAR16
*VolName
= NULL
, *DontScanDir
, *PathCopy
= NULL
;
1096 UINTN i
= 0, VolNum
;
1097 BOOLEAN ScanIt
= TRUE
;
1099 if (IsIn(Volume
->VolName
, GlobalConfig
.DontScanVolumes
))
1102 if ((StriCmp(Path
, SelfDirPath
) == 0) && (Volume
->DeviceHandle
== SelfVolume
->DeviceHandle
))
1105 // See if Path includes an explicit volume declaration that's NOT Volume....
1106 PathCopy
= StrDuplicate(Path
);
1107 if (SplitVolumeAndFilename(&PathCopy
, &VolName
)) {
1108 if (StriCmp(VolName
, Volume
->VolName
) != 0) {
1109 if ((StrLen(VolName
) > 2) && (VolName
[0] == L
'f') && (VolName
[1] == L
's') && (VolName
[2] >= L
'0') && (VolName
[2] <= L
'9')) {
1110 VolNum
= Atoi(VolName
+ 2);
1111 if (VolNum
!= Volume
->VolNumber
) {
1118 } // if Path includes volume specification
1119 MyFreePool(PathCopy
);
1120 MyFreePool(VolName
);
1123 // See if Volume is in GlobalConfig.DontScanDirs....
1124 while ((DontScanDir
= FindCommaDelimited(GlobalConfig
.DontScanDirs
, i
++)) && ScanIt
) {
1125 SplitVolumeAndFilename(&DontScanDir
, &VolName
);
1126 CleanUpPathNameSlashes(DontScanDir
);
1127 if (VolName
!= NULL
) {
1128 if ((StriCmp(VolName
, Volume
->VolName
) == 0) && (StriCmp(DontScanDir
, Path
) == 0))
1130 if ((StrLen(VolName
) > 2) && (VolName
[0] == L
'f') && (VolName
[1] == L
's') && (VolName
[2] >= L
'0') && (VolName
[2] <= L
'9')) {
1131 VolNum
= Atoi(VolName
+ 2);
1132 if ((VolNum
== Volume
->VolNumber
) && (StriCmp(DontScanDir
, Path
) == 0))
1136 if (StriCmp(DontScanDir
, Path
) == 0)
1139 MyFreePool(DontScanDir
);
1140 MyFreePool(VolName
);
1145 } // BOOLEAN ShouldScan()
1147 // Returns TRUE if the file is byte-for-byte identical with the fallback file
1148 // on the volume AND if the file is not itself the fallback file; returns
1149 // FALSE if the file is not identical to the fallback file OR if the file
1150 // IS the fallback file. Intended for use in excluding the fallback boot
1151 // loader when it's a duplicate of another boot loader.
1152 static BOOLEAN
DuplicatesFallback(IN REFIT_VOLUME
*Volume
, IN CHAR16
*FileName
) {
1153 CHAR8
*FileContents
, *FallbackContents
;
1154 EFI_FILE_HANDLE FileHandle
, FallbackHandle
;
1155 EFI_FILE_INFO
*FileInfo
, *FallbackInfo
;
1156 UINTN FileSize
= 0, FallbackSize
= 0;
1158 BOOLEAN AreIdentical
= FALSE
;
1160 if (!FileExists(Volume
->RootDir
, FileName
) || !FileExists(Volume
->RootDir
, FALLBACK_FULLNAME
))
1163 CleanUpPathNameSlashes(FileName
);
1165 if (StriCmp(FileName
, FALLBACK_FULLNAME
) == 0)
1166 return FALSE
; // identical filenames, so not a duplicate....
1168 Status
= refit_call5_wrapper(Volume
->RootDir
->Open
, Volume
->RootDir
, &FileHandle
, FileName
, EFI_FILE_MODE_READ
, 0);
1169 if (Status
== EFI_SUCCESS
) {
1170 FileInfo
= LibFileInfo(FileHandle
);
1171 FileSize
= FileInfo
->FileSize
;
1176 Status
= refit_call5_wrapper(Volume
->RootDir
->Open
, Volume
->RootDir
, &FallbackHandle
, FALLBACK_FULLNAME
, EFI_FILE_MODE_READ
, 0);
1177 if (Status
== EFI_SUCCESS
) {
1178 FallbackInfo
= LibFileInfo(FallbackHandle
);
1179 FallbackSize
= FallbackInfo
->FileSize
;
1181 refit_call1_wrapper(FileHandle
->Close
, FileHandle
);
1185 if (FallbackSize
!= FileSize
) { // not same size, so can't be identical
1186 AreIdentical
= FALSE
;
1187 } else { // could be identical; do full check....
1188 FileContents
= AllocatePool(FileSize
);
1189 FallbackContents
= AllocatePool(FallbackSize
);
1190 if (FileContents
&& FallbackContents
) {
1191 Status
= refit_call3_wrapper(FileHandle
->Read
, FileHandle
, &FileSize
, FileContents
);
1192 if (Status
== EFI_SUCCESS
) {
1193 Status
= refit_call3_wrapper(FallbackHandle
->Read
, FallbackHandle
, &FallbackSize
, FallbackContents
);
1195 if (Status
== EFI_SUCCESS
) {
1196 AreIdentical
= (CompareMem(FileContents
, FallbackContents
, FileSize
) == 0);
1199 MyFreePool(FileContents
);
1200 MyFreePool(FallbackContents
);
1203 // BUG ALERT: Some systems (e.g., DUET, some Macs with large displays) crash if the
1204 // following two calls are reversed. Go figure....
1205 refit_call1_wrapper(FileHandle
->Close
, FallbackHandle
);
1206 refit_call1_wrapper(FileHandle
->Close
, FileHandle
);
1207 return AreIdentical
;
1208 } // BOOLEAN DuplicatesFallback()
1210 // Returns FALSE if two measures of file size are identical for a single file,
1211 // TRUE if not or if the file can't be opened and the other measure is non-0.
1212 // Despite the function's name, this isn't really a direct test of symbolic
1213 // link status, since EFI doesn't officially support symlinks. It does seem
1214 // to be a reliable indicator, though. (OTOH, some disk errors might cause a
1215 // file to fail to open, which would return a false positive -- but as I use
1216 // this function to exclude symbolic links from the list of boot loaders,
1217 // that would be fine, since such boot loaders wouldn't work.)
1218 static BOOLEAN
IsSymbolicLink(REFIT_VOLUME
*Volume
, CHAR16
*Path
, EFI_FILE_INFO
*DirEntry
) {
1219 EFI_FILE_HANDLE FileHandle
;
1220 EFI_FILE_INFO
*FileInfo
= NULL
;
1222 UINTN FileSize2
= 0;
1225 FileName
= StrDuplicate(Path
);
1226 MergeStrings(&FileName
, DirEntry
->FileName
, L
'\\');
1227 CleanUpPathNameSlashes(FileName
);
1229 Status
= refit_call5_wrapper(Volume
->RootDir
->Open
, Volume
->RootDir
, &FileHandle
, FileName
, EFI_FILE_MODE_READ
, 0);
1230 if (Status
== EFI_SUCCESS
) {
1231 FileInfo
= LibFileInfo(FileHandle
);
1232 if (FileInfo
!= NULL
)
1233 FileSize2
= FileInfo
->FileSize
;
1236 MyFreePool(FileName
);
1237 MyFreePool(FileInfo
);
1239 return (DirEntry
->FileSize
!= FileSize2
);
1240 } // BOOLEAN IsSymbolicLink()
1242 // Scan an individual directory for EFI boot loader files and, if found,
1243 // add them to the list. Exception: Ignores FALLBACK_FULLNAME, which is picked
1244 // up in ScanEfiFiles(). Sorts the entries within the loader directory so that
1245 // the most recent one appears first in the list.
1246 // Returns TRUE if a duplicate for FALLBACK_FILENAME was found, FALSE if not.
1247 static BOOLEAN
ScanLoaderDir(IN REFIT_VOLUME
*Volume
, IN CHAR16
*Path
, IN CHAR16
*Pattern
)
1250 REFIT_DIR_ITER DirIter
;
1251 EFI_FILE_INFO
*DirEntry
;
1252 CHAR16 FileName
[256], *Extension
;
1253 struct LOADER_LIST
*LoaderList
= NULL
, *NewLoader
;
1254 BOOLEAN FoundFallbackDuplicate
= FALSE
;
1256 if ((!SelfDirPath
|| !Path
|| ((StriCmp(Path
, SelfDirPath
) == 0) && (Volume
->DeviceHandle
!= SelfVolume
->DeviceHandle
)) ||
1257 (StriCmp(Path
, SelfDirPath
) != 0)) &&
1258 (ShouldScan(Volume
, Path
))) {
1259 // look through contents of the directory
1260 DirIterOpen(Volume
->RootDir
, Path
, &DirIter
);
1261 while (DirIterNext(&DirIter
, 2, Pattern
, &DirEntry
)) {
1262 Extension
= FindExtension(DirEntry
->FileName
);
1263 if (DirEntry
->FileName
[0] == '.' ||
1264 StriCmp(Extension
, L
".icns") == 0 ||
1265 StriCmp(Extension
, L
".png") == 0 ||
1266 (StriCmp(DirEntry
->FileName
, FALLBACK_BASENAME
) == 0 && (StriCmp(Path
, L
"EFI\\BOOT") == 0)) ||
1267 StriSubCmp(L
"shell", DirEntry
->FileName
) ||
1268 IsSymbolicLink(Volume
, Path
, DirEntry
) || /* is symbolic link */
1269 IsIn(DirEntry
->FileName
, GlobalConfig
.DontScanFiles
))
1270 continue; // skip this
1273 SPrint(FileName
, 255, L
"\\%s\\%s", Path
, DirEntry
->FileName
);
1275 SPrint(FileName
, 255, L
"\\%s", DirEntry
->FileName
);
1276 CleanUpPathNameSlashes(FileName
);
1278 if(!IsValidLoader(Volume
->RootDir
, FileName
))
1281 NewLoader
= AllocateZeroPool(sizeof(struct LOADER_LIST
));
1282 if (NewLoader
!= NULL
) {
1283 NewLoader
->FileName
= StrDuplicate(FileName
);
1284 NewLoader
->TimeStamp
= DirEntry
->ModificationTime
;
1285 LoaderList
= AddLoaderListEntry(LoaderList
, NewLoader
);
1286 if (DuplicatesFallback(Volume
, FileName
))
1287 FoundFallbackDuplicate
= TRUE
;
1289 MyFreePool(Extension
);
1292 NewLoader
= LoaderList
;
1293 while (NewLoader
!= NULL
) {
1294 AddLoaderEntry(NewLoader
->FileName
, NULL
, Volume
);
1295 NewLoader
= NewLoader
->NextEntry
;
1298 CleanUpLoaderList(LoaderList
);
1299 Status
= DirIterClose(&DirIter
);
1300 // NOTE: EFI_INVALID_PARAMETER really is an error that should be reported;
1301 // but I've gotten reports from users who are getting this error occasionally
1302 // and I can't find anything wrong or reproduce the problem, so I'm putting
1303 // it down to buggy EFI implementations and ignoring that particular error....
1304 if ((Status
!= EFI_NOT_FOUND
) && (Status
!= EFI_INVALID_PARAMETER
)) {
1306 SPrint(FileName
, 255, L
"while scanning the %s directory", Path
);
1308 StrCpy(FileName
, L
"while scanning the root directory");
1309 CheckError(Status
, FileName
);
1310 } // if (Status != EFI_NOT_FOUND)
1311 } // if not scanning a blacklisted directory
1313 return FoundFallbackDuplicate
;
1314 } /* static VOID ScanLoaderDir() */
1316 static VOID
ScanEfiFiles(REFIT_VOLUME
*Volume
) {
1318 REFIT_DIR_ITER EfiDirIter
;
1319 EFI_FILE_INFO
*EfiDirEntry
;
1320 CHAR16 FileName
[256], *Directory
, *MatchPatterns
, *VolName
= NULL
, *SelfPath
;
1322 BOOLEAN ScanFallbackLoader
= TRUE
;
1323 BOOLEAN FoundBRBackup
= FALSE
;
1325 MatchPatterns
= StrDuplicate(LOADER_MATCH_PATTERNS
);
1326 if (GlobalConfig
.ScanAllLinux
)
1327 MergeStrings(&MatchPatterns
, LINUX_MATCH_PATTERNS
, L
',');
1329 if ((Volume
->RootDir
!= NULL
) && (Volume
->VolName
!= NULL
)) {
1330 // check for Mac OS X boot loader
1331 if (ShouldScan(Volume
, L
"System\\Library\\CoreServices")) {
1332 StrCpy(FileName
, MACOSX_LOADER_PATH
);
1333 if (FileExists(Volume
->RootDir
, FileName
) && !IsIn(L
"boot.efi", GlobalConfig
.DontScanFiles
)) {
1334 AddLoaderEntry(FileName
, L
"Mac OS X", Volume
);
1335 if (DuplicatesFallback(Volume
, FileName
))
1336 ScanFallbackLoader
= FALSE
;
1340 StrCpy(FileName
, L
"System\\Library\\CoreServices\\xom.efi");
1341 if (FileExists(Volume
->RootDir
, FileName
) && !IsIn(L
"boot.efi", GlobalConfig
.DontScanFiles
)) {
1342 AddLoaderEntry(FileName
, L
"Windows XP (XoM)", Volume
);
1343 if (DuplicatesFallback(Volume
, FileName
))
1344 ScanFallbackLoader
= FALSE
;
1346 } // if should scan Mac directory
1348 // check for Microsoft boot loader/menu
1349 if (ShouldScan(Volume
, L
"EFI\\Microsoft\\Boot")) {
1350 StrCpy(FileName
, L
"EFI\\Microsoft\\Boot\\bkpbootmgfw.efi");
1351 if (FileExists(Volume
->RootDir
, FileName
) && !IsIn(L
"bkpbootmgfw.efi", GlobalConfig
.DontScanFiles
)) {
1352 AddLoaderEntry(FileName
, L
"Microsoft EFI boot (Boot Repair backup)", Volume
);
1353 FoundBRBackup
= TRUE
;
1354 if (DuplicatesFallback(Volume
, FileName
))
1355 ScanFallbackLoader
= FALSE
;
1357 StrCpy(FileName
, L
"EFI\\Microsoft\\Boot\\bootmgfw.efi");
1358 if (FileExists(Volume
->RootDir
, FileName
) && !IsIn(L
"bootmgfw.efi", GlobalConfig
.DontScanFiles
)) {
1360 AddLoaderEntry(FileName
, L
"Supposed Microsoft EFI boot (probably GRUB)", Volume
);
1362 AddLoaderEntry(FileName
, L
"Microsoft EFI boot", Volume
);
1363 if (DuplicatesFallback(Volume
, FileName
))
1364 ScanFallbackLoader
= FALSE
;
1368 // scan the root directory for EFI executables
1369 if (ScanLoaderDir(Volume
, L
"\\", MatchPatterns
))
1370 ScanFallbackLoader
= FALSE
;
1372 // scan subdirectories of the EFI directory (as per the standard)
1373 DirIterOpen(Volume
->RootDir
, L
"EFI", &EfiDirIter
);
1374 while (DirIterNext(&EfiDirIter
, 1, NULL
, &EfiDirEntry
)) {
1375 if (StriCmp(EfiDirEntry
->FileName
, L
"tools") == 0 || (StriCmp(EfiDirEntry
->FileName
, L
"memtest") == 0) ||
1376 (StriCmp(EfiDirEntry
->FileName
, L
"memtest86") == 0) || EfiDirEntry
->FileName
[0] == '.')
1377 continue; // skip this, doesn't contain boot loaders or is scanned later
1378 SPrint(FileName
, 255, L
"EFI\\%s", EfiDirEntry
->FileName
);
1379 if (ScanLoaderDir(Volume
, FileName
, MatchPatterns
))
1380 ScanFallbackLoader
= FALSE
;
1382 Status
= DirIterClose(&EfiDirIter
);
1383 if (Status
!= EFI_NOT_FOUND
)
1384 CheckError(Status
, L
"while scanning the EFI directory");
1386 // Scan user-specified (or additional default) directories....
1388 while ((Directory
= FindCommaDelimited(GlobalConfig
.AlsoScan
, i
++)) != NULL
) {
1389 if (ShouldScan(Volume
, Directory
)) {
1390 SplitVolumeAndFilename(&Directory
, &VolName
);
1391 CleanUpPathNameSlashes(Directory
);
1392 Length
= StrLen(Directory
);
1393 if ((Length
> 0) && ScanLoaderDir(Volume
, Directory
, MatchPatterns
))
1394 ScanFallbackLoader
= FALSE
;
1395 MyFreePool(VolName
);
1396 } // if should scan dir
1397 MyFreePool(Directory
);
1400 // Don't scan the fallback loader if it's on the same volume and a duplicate of rEFInd itself....
1401 SelfPath
= DevicePathToStr(SelfLoadedImage
->FilePath
);
1402 CleanUpPathNameSlashes(SelfPath
);
1403 if ((Volume
->DeviceHandle
== SelfLoadedImage
->DeviceHandle
) && DuplicatesFallback(Volume
, SelfPath
))
1404 ScanFallbackLoader
= FALSE
;
1406 // If not a duplicate & if it exists & if it's not us, create an entry
1407 // for the fallback boot loader
1408 if (ScanFallbackLoader
&& FileExists(Volume
->RootDir
, FALLBACK_FULLNAME
) && ShouldScan(Volume
, L
"EFI\\BOOT"))
1409 AddLoaderEntry(FALLBACK_FULLNAME
, L
"Fallback boot loader", Volume
);
1411 } // static VOID ScanEfiFiles()
1413 // Scan internal disks for valid EFI boot loaders....
1414 static VOID
ScanInternal(VOID
) {
1417 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1418 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_INTERNAL
) {
1419 ScanEfiFiles(Volumes
[VolumeIndex
]);
1422 } // static VOID ScanInternal()
1424 // Scan external disks for valid EFI boot loaders....
1425 static VOID
ScanExternal(VOID
) {
1428 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1429 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_EXTERNAL
) {
1430 ScanEfiFiles(Volumes
[VolumeIndex
]);
1433 } // static VOID ScanExternal()
1435 // Scan internal disks for valid EFI boot loaders....
1436 static VOID
ScanOptical(VOID
) {
1439 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1440 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_OPTICAL
) {
1441 ScanEfiFiles(Volumes
[VolumeIndex
]);
1444 } // static VOID ScanOptical()
1447 // legacy boot functions
1450 static EFI_STATUS
ActivateMbrPartition(IN EFI_BLOCK_IO
*BlockIO
, IN UINTN PartitionIndex
)
1453 UINT8 SectorBuffer
[512];
1454 MBR_PARTITION_INFO
*MbrTable
, *EMbrTable
;
1455 UINT32 ExtBase
, ExtCurrent
, NextExtCurrent
;
1456 UINTN LogicalPartitionIndex
= 4;
1458 BOOLEAN HaveBootCode
;
1461 Status
= refit_call5_wrapper(BlockIO
->ReadBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, 0, 512, SectorBuffer
);
1462 if (EFI_ERROR(Status
))
1464 if (*((UINT16
*)(SectorBuffer
+ 510)) != 0xaa55)
1465 return EFI_NOT_FOUND
; // safety measure #1
1467 // add boot code if necessary
1468 HaveBootCode
= FALSE
;
1469 for (i
= 0; i
< MBR_BOOTCODE_SIZE
; i
++) {
1470 if (SectorBuffer
[i
] != 0) {
1471 HaveBootCode
= TRUE
;
1475 if (!HaveBootCode
) {
1476 // no boot code found in the MBR, add the syslinux MBR code
1477 SetMem(SectorBuffer
, MBR_BOOTCODE_SIZE
, 0);
1478 CopyMem(SectorBuffer
, syslinux_mbr
, SYSLINUX_MBR_SIZE
);
1481 // set the partition active
1482 MbrTable
= (MBR_PARTITION_INFO
*)(SectorBuffer
+ 446);
1484 for (i
= 0; i
< 4; i
++) {
1485 if (MbrTable
[i
].Flags
!= 0x00 && MbrTable
[i
].Flags
!= 0x80)
1486 return EFI_NOT_FOUND
; // safety measure #2
1487 if (i
== PartitionIndex
)
1488 MbrTable
[i
].Flags
= 0x80;
1489 else if (PartitionIndex
>= 4 && IS_EXTENDED_PART_TYPE(MbrTable
[i
].Type
)) {
1490 MbrTable
[i
].Flags
= 0x80;
1491 ExtBase
= MbrTable
[i
].StartLBA
;
1493 MbrTable
[i
].Flags
= 0x00;
1497 Status
= refit_call5_wrapper(BlockIO
->WriteBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, 0, 512, SectorBuffer
);
1498 if (EFI_ERROR(Status
))
1501 if (PartitionIndex
>= 4) {
1502 // we have to activate a logical partition, so walk the EMBR chain
1504 // NOTE: ExtBase was set above while looking at the MBR table
1505 for (ExtCurrent
= ExtBase
; ExtCurrent
; ExtCurrent
= NextExtCurrent
) {
1506 // read current EMBR
1507 Status
= refit_call5_wrapper(BlockIO
->ReadBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, ExtCurrent
, 512, SectorBuffer
);
1508 if (EFI_ERROR(Status
))
1510 if (*((UINT16
*)(SectorBuffer
+ 510)) != 0xaa55)
1511 return EFI_NOT_FOUND
; // safety measure #3
1513 // scan EMBR, set appropriate partition active
1514 EMbrTable
= (MBR_PARTITION_INFO
*)(SectorBuffer
+ 446);
1516 for (i
= 0; i
< 4; i
++) {
1517 if (EMbrTable
[i
].Flags
!= 0x00 && EMbrTable
[i
].Flags
!= 0x80)
1518 return EFI_NOT_FOUND
; // safety measure #4
1519 if (EMbrTable
[i
].StartLBA
== 0 || EMbrTable
[i
].Size
== 0)
1521 if (IS_EXTENDED_PART_TYPE(EMbrTable
[i
].Type
)) {
1522 // link to next EMBR
1523 NextExtCurrent
= ExtBase
+ EMbrTable
[i
].StartLBA
;
1524 EMbrTable
[i
].Flags
= (PartitionIndex
>= LogicalPartitionIndex
) ? 0x80 : 0x00;
1527 // logical partition
1528 EMbrTable
[i
].Flags
= (PartitionIndex
== LogicalPartitionIndex
) ? 0x80 : 0x00;
1529 LogicalPartitionIndex
++;
1533 // write current EMBR
1534 Status
= refit_call5_wrapper(BlockIO
->WriteBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, ExtCurrent
, 512, SectorBuffer
);
1535 if (EFI_ERROR(Status
))
1538 if (PartitionIndex
< LogicalPartitionIndex
)
1539 break; // stop the loop, no need to touch further EMBRs
1545 } /* static EFI_STATUS ActivateMbrPartition() */
1547 // early 2006 Core Duo / Core Solo models
1548 static UINT8 LegacyLoaderDevicePath1Data
[] = {
1549 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1550 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1551 0xFF, 0xFF, 0xF9, 0xFF, 0x00, 0x00, 0x00, 0x00,
1552 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1553 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1554 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1556 // mid-2006 Mac Pro (and probably other Core 2 models)
1557 static UINT8 LegacyLoaderDevicePath2Data
[] = {
1558 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1559 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1560 0xFF, 0xFF, 0xF7, 0xFF, 0x00, 0x00, 0x00, 0x00,
1561 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1562 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1563 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1565 // mid-2007 MBP ("Santa Rosa" based models)
1566 static UINT8 LegacyLoaderDevicePath3Data
[] = {
1567 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1568 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1569 0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
1570 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1571 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1572 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1575 static UINT8 LegacyLoaderDevicePath4Data
[] = {
1576 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1577 0x00, 0x00, 0xC0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1578 0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
1579 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1580 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1581 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1583 // late-2008 MB/MBP (NVidia chipset)
1584 static UINT8 LegacyLoaderDevicePath5Data
[] = {
1585 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1586 0x00, 0x40, 0xCB, 0xFF, 0x00, 0x00, 0x00, 0x00,
1587 0xFF, 0xBF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
1588 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1589 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1590 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1593 static EFI_DEVICE_PATH
*LegacyLoaderList
[] = {
1594 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath1Data
,
1595 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath2Data
,
1596 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath3Data
,
1597 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath4Data
,
1598 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath5Data
,
1602 #define MAX_DISCOVERED_PATHS (16)
1604 static VOID
StartLegacy(IN LEGACY_ENTRY
*Entry
)
1607 EG_IMAGE
*BootLogoImage
;
1608 UINTN ErrorInStep
= 0;
1609 EFI_DEVICE_PATH
*DiscoveredPathList
[MAX_DISCOVERED_PATHS
];
1611 BeginExternalScreen(TRUE
, L
"Booting Legacy OS (Mac mode)");
1613 BootLogoImage
= LoadOSIcon(Entry
->Volume
->OSIconName
, L
"legacy", TRUE
);
1614 if (BootLogoImage
!= NULL
)
1615 BltImageAlpha(BootLogoImage
,
1616 (UGAWidth
- BootLogoImage
->Width
) >> 1,
1617 (UGAHeight
- BootLogoImage
->Height
) >> 1,
1618 &StdBackgroundPixel
);
1620 if (Entry
->Volume
->IsMbrPartition
) {
1621 ActivateMbrPartition(Entry
->Volume
->WholeDiskBlockIO
, Entry
->Volume
->MbrPartitionIndex
);
1624 ExtractLegacyLoaderPaths(DiscoveredPathList
, MAX_DISCOVERED_PATHS
, LegacyLoaderList
);
1626 Status
= StartEFIImageList(DiscoveredPathList
, Entry
->LoadOptions
, TYPE_LEGACY
, L
"legacy loader", 0, &ErrorInStep
, TRUE
);
1627 if (Status
== EFI_NOT_FOUND
) {
1628 if (ErrorInStep
== 1) {
1629 Print(L
"\nPlease make sure that you have the latest firmware update installed.\n");
1630 } else if (ErrorInStep
== 3) {
1631 Print(L
"\nThe firmware refused to boot from the selected volume. Note that external\n"
1632 L
"hard drives are not well-supported by Apple's firmware for legacy OS booting.\n");
1635 FinishExternalScreen();
1636 } /* static VOID StartLegacy() */
1638 // Start a device on a non-Mac using the EFI_LEGACY_BIOS_PROTOCOL
1639 #ifdef __MAKEWITH_TIANO
1640 static VOID
StartLegacyUEFI(IN LEGACY_ENTRY
*Entry
)
1642 BeginExternalScreen(TRUE
, L
"Booting Legacy OS (UEFI mode)");
1644 BdsLibConnectDevicePath (Entry
->BdsOption
->DevicePath
);
1645 BdsLibDoLegacyBoot(Entry
->BdsOption
);
1647 // If we get here, it means that there was a failure....
1648 Print(L
"Failure booting legacy (BIOS) OS.");
1650 FinishExternalScreen();
1651 } // static VOID StartLegacyUEFI()
1652 #endif // __MAKEWITH_TIANO
1654 static LEGACY_ENTRY
* AddLegacyEntry(IN CHAR16
*LoaderTitle
, IN REFIT_VOLUME
*Volume
)
1656 LEGACY_ENTRY
*Entry
, *SubEntry
;
1657 REFIT_MENU_SCREEN
*SubScreen
;
1659 CHAR16 ShortcutLetter
= 0;
1661 if (LoaderTitle
== NULL
) {
1662 if (Volume
->OSName
!= NULL
) {
1663 LoaderTitle
= Volume
->OSName
;
1664 if (LoaderTitle
[0] == 'W' || LoaderTitle
[0] == 'L')
1665 ShortcutLetter
= LoaderTitle
[0];
1667 LoaderTitle
= L
"Legacy OS";
1669 if (Volume
->VolName
!= NULL
)
1670 VolDesc
= Volume
->VolName
;
1672 VolDesc
= (Volume
->DiskKind
== DISK_KIND_OPTICAL
) ? L
"CD" : L
"HD";
1674 // prepare the menu entry
1675 Entry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1676 Entry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1677 SPrint(Entry
->me
.Title
, 255, L
"Boot %s from %s", LoaderTitle
, VolDesc
);
1678 Entry
->me
.Tag
= TAG_LEGACY
;
1680 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1681 Entry
->me
.Image
= LoadOSIcon(Volume
->OSIconName
, L
"legacy", FALSE
);
1682 Entry
->me
.BadgeImage
= Volume
->VolBadgeImage
;
1683 Entry
->Volume
= Volume
;
1684 Entry
->LoadOptions
= (Volume
->DiskKind
== DISK_KIND_OPTICAL
) ? L
"CD" :
1685 ((Volume
->DiskKind
== DISK_KIND_EXTERNAL
) ? L
"USB" : L
"HD");
1686 Entry
->Enabled
= TRUE
;
1688 // create the submenu
1689 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
1690 SubScreen
->Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1691 SPrint(SubScreen
->Title
, 255, L
"Boot Options for %s on %s", LoaderTitle
, VolDesc
);
1692 SubScreen
->TitleImage
= Entry
->me
.Image
;
1693 SubScreen
->Hint1
= StrDuplicate(SUBSCREEN_HINT1
);
1694 if (GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_EDITOR
) {
1695 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2_NO_EDITOR
);
1697 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2
);
1701 SubEntry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1702 SubEntry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1703 SPrint(SubEntry
->me
.Title
, 255, L
"Boot %s", LoaderTitle
);
1704 SubEntry
->me
.Tag
= TAG_LEGACY
;
1705 SubEntry
->Volume
= Entry
->Volume
;
1706 SubEntry
->LoadOptions
= Entry
->LoadOptions
;
1707 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
1709 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
1710 Entry
->me
.SubScreen
= SubScreen
;
1711 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1713 } /* static LEGACY_ENTRY * AddLegacyEntry() */
1716 #ifdef __MAKEWITH_GNUEFI
1717 static VOID
ScanLegacyUEFI(IN UINTN DiskType
){}
1719 // default volume badge icon based on disk kind
1720 static EG_IMAGE
* GetDiskBadge(IN UINTN DiskType
) {
1721 EG_IMAGE
* Badge
= NULL
;
1725 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_INTERNAL
);
1728 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_EXTERNAL
);
1731 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_OPTICAL
);
1735 } // static EG_IMAGE * GetDiskBadge()
1738 Create a rEFInd boot option from a Legacy BIOS protocol option.
1740 static LEGACY_ENTRY
* AddLegacyEntryUEFI(BDS_COMMON_OPTION
*BdsOption
, IN UINT16 DiskType
)
1742 LEGACY_ENTRY
*Entry
, *SubEntry
;
1743 REFIT_MENU_SCREEN
*SubScreen
;
1744 CHAR16 ShortcutLetter
= 0;
1745 CHAR16
*LegacyDescription
= BdsOption
->Description
;
1747 // prepare the menu entry
1748 Entry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1749 Entry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1750 SPrint(Entry
->me
.Title
, 255, L
"Boot legacy target %s", LegacyDescription
);
1751 Entry
->me
.Tag
= TAG_LEGACY_UEFI
;
1753 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1754 Entry
->me
.Image
= LoadOSIcon(L
"legacy", L
"legacy", TRUE
);
1755 Entry
->LoadOptions
= (DiskType
== BBS_CDROM
) ? L
"CD" :
1756 ((DiskType
== BBS_USB
) ? L
"USB" : L
"HD");
1757 Entry
->me
.BadgeImage
= GetDiskBadge(DiskType
);
1758 Entry
->BdsOption
= BdsOption
;
1759 Entry
->Enabled
= TRUE
;
1761 // create the submenu
1762 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
1763 SubScreen
->Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1764 SPrint(SubScreen
->Title
, 255, L
"No boot options for legacy target");
1765 SubScreen
->TitleImage
= Entry
->me
.Image
;
1766 SubScreen
->Hint1
= StrDuplicate(SUBSCREEN_HINT1
);
1767 if (GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_EDITOR
) {
1768 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2_NO_EDITOR
);
1770 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2
);
1774 SubEntry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1775 SubEntry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1776 SPrint(SubEntry
->me
.Title
, 255, L
"Boot %s", LegacyDescription
);
1777 SubEntry
->me
.Tag
= TAG_LEGACY_UEFI
;
1778 Entry
->BdsOption
= BdsOption
;
1779 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
1781 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
1782 Entry
->me
.SubScreen
= SubScreen
;
1783 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1785 } /* static LEGACY_ENTRY * AddLegacyEntryUEFI() */
1788 Scan for legacy BIOS targets on machines that implement EFI_LEGACY_BIOS_PROTOCOL.
1789 In testing, protocol has not been implemented on Macs but has been
1790 implemented on several Dell PCs and an ASUS motherboard.
1791 Restricts output to disks of the specified DiskType.
1793 static VOID
ScanLegacyUEFI(IN UINTN DiskType
)
1796 EFI_LEGACY_BIOS_PROTOCOL
*LegacyBios
;
1797 UINT16
*BootOrder
= NULL
;
1799 CHAR16 BootOption
[10];
1800 UINTN BootOrderSize
= 0;
1802 BDS_COMMON_OPTION
*BdsOption
;
1803 LIST_ENTRY TempList
;
1804 BBS_BBS_DEVICE_PATH
* BbsDevicePath
= NULL
;
1806 InitializeListHead (&TempList
);
1807 ZeroMem (Buffer
, sizeof (Buffer
));
1809 // If LegacyBios protocol is not implemented on this platform, then
1810 //we do not support this type of legacy boot on this machine.
1811 Status
= gBS
->LocateProtocol(&gEfiLegacyBootProtocolGuid
, NULL
, (VOID
**) &LegacyBios
);
1812 if (EFI_ERROR (Status
))
1815 // Grab the boot order
1816 BootOrder
= BdsLibGetVariableAndSize(L
"BootOrder", &gEfiGlobalVariableGuid
, &BootOrderSize
);
1817 if (BootOrder
== NULL
) {
1822 while (Index
< BootOrderSize
/ sizeof (UINT16
))
1824 // Grab each boot option variable from the boot order, and convert
1825 // the variable into a BDS boot option
1826 UnicodeSPrint (BootOption
, sizeof (BootOption
), L
"Boot%04x", BootOrder
[Index
]);
1827 BdsOption
= BdsLibVariableToOption (&TempList
, BootOption
);
1829 if (BdsOption
!= NULL
) {
1830 BbsDevicePath
= (BBS_BBS_DEVICE_PATH
*)BdsOption
->DevicePath
;
1832 // Only add the entry if it is of a requested type (e.g. USB, HD)
1834 // Two checks necessary because some systems return EFI boot loaders
1835 // with a DeviceType value that would inappropriately include them
1836 // as legacy loaders....
1837 if ((BbsDevicePath
->DeviceType
== DiskType
) && (BdsOption
->DevicePath
->Type
== DEVICE_TYPE_BIOS
)) {
1838 AddLegacyEntryUEFI(BdsOption
, BbsDevicePath
->DeviceType
);
1843 } /* static VOID ScanLegacyUEFI() */
1844 #endif // __MAKEWITH_GNUEFI
1846 static VOID
ScanLegacyVolume(REFIT_VOLUME
*Volume
, UINTN VolumeIndex
) {
1848 BOOLEAN ShowVolume
, HideIfOthersFound
;
1851 HideIfOthersFound
= FALSE
;
1852 if (Volume
->IsAppleLegacy
) {
1854 HideIfOthersFound
= TRUE
;
1855 } else if (Volume
->HasBootCode
) {
1857 if (Volume
->BlockIO
== Volume
->WholeDiskBlockIO
&&
1858 Volume
->BlockIOOffset
== 0 &&
1859 Volume
->OSName
== NULL
)
1860 // this is a whole disk (MBR) entry; hide if we have entries for partitions
1861 HideIfOthersFound
= TRUE
;
1863 if (HideIfOthersFound
) {
1864 // check for other bootable entries on the same disk
1865 for (VolumeIndex2
= 0; VolumeIndex2
< VolumesCount
; VolumeIndex2
++) {
1866 if (VolumeIndex2
!= VolumeIndex
&& Volumes
[VolumeIndex2
]->HasBootCode
&&
1867 Volumes
[VolumeIndex2
]->WholeDiskBlockIO
== Volume
->WholeDiskBlockIO
)
1873 AddLegacyEntry(NULL
, Volume
);
1874 } // static VOID ScanLegacyVolume()
1876 // Scan attached optical discs for legacy (BIOS) boot code
1877 // and add anything found to the list....
1878 static VOID
ScanLegacyDisc(VOID
)
1881 REFIT_VOLUME
*Volume
;
1883 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
) {
1884 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1885 Volume
= Volumes
[VolumeIndex
];
1886 if (Volume
->DiskKind
== DISK_KIND_OPTICAL
)
1887 ScanLegacyVolume(Volume
, VolumeIndex
);
1889 } else if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
1890 ScanLegacyUEFI(BBS_CDROM
);
1892 } /* static VOID ScanLegacyDisc() */
1894 // Scan internal hard disks for legacy (BIOS) boot code
1895 // and add anything found to the list....
1896 static VOID
ScanLegacyInternal(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_INTERNAL
)
1905 ScanLegacyVolume(Volume
, VolumeIndex
);
1907 } else if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
1908 ScanLegacyUEFI(BBS_HARDDISK
);
1910 } /* static VOID ScanLegacyInternal() */
1912 // Scan external disks for legacy (BIOS) boot code
1913 // and add anything found to the list....
1914 static VOID
ScanLegacyExternal(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_EXTERNAL
)
1923 ScanLegacyVolume(Volume
, VolumeIndex
);
1925 } else if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
1926 ScanLegacyUEFI(BBS_USB
);
1928 } /* static VOID ScanLegacyExternal() */
1931 // pre-boot tool functions
1934 static VOID
StartTool(IN LOADER_ENTRY
*Entry
)
1936 BeginExternalScreen(Entry
->UseGraphicsMode
, Entry
->me
.Title
+ 6); // assumes "Start <title>" as assigned below
1937 StartEFIImage(Entry
->DevicePath
, Entry
->LoadOptions
, TYPE_EFI
,
1938 Basename(Entry
->LoaderPath
), Entry
->OSType
, NULL
, TRUE
);
1939 FinishExternalScreen();
1940 } /* static VOID StartTool() */
1942 static LOADER_ENTRY
* AddToolEntry(EFI_HANDLE DeviceHandle
, IN CHAR16
*LoaderPath
, IN CHAR16
*LoaderTitle
, IN EG_IMAGE
*Image
,
1943 IN CHAR16 ShortcutLetter
, IN BOOLEAN UseGraphicsMode
)
1945 LOADER_ENTRY
*Entry
;
1946 CHAR16
*TitleStr
= NULL
;
1948 Entry
= AllocateZeroPool(sizeof(LOADER_ENTRY
));
1950 MergeStrings(&TitleStr
, L
"Start ", 0);
1951 MergeStrings(&TitleStr
, LoaderTitle
, 0);
1952 Entry
->me
.Title
= TitleStr
;
1953 Entry
->me
.Tag
= TAG_TOOL
;
1955 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1956 Entry
->me
.Image
= Image
;
1957 Entry
->LoaderPath
= (LoaderPath
) ? StrDuplicate(LoaderPath
) : NULL
;
1958 Entry
->DevicePath
= FileDevicePath(DeviceHandle
, Entry
->LoaderPath
);
1959 Entry
->UseGraphicsMode
= UseGraphicsMode
;
1961 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1963 } /* static LOADER_ENTRY * AddToolEntry() */
1966 // pre-boot driver functions
1969 static UINTN
ScanDriverDir(IN CHAR16
*Path
)
1972 REFIT_DIR_ITER DirIter
;
1974 EFI_FILE_INFO
*DirEntry
;
1975 CHAR16 FileName
[256];
1977 CleanUpPathNameSlashes(Path
);
1978 // look through contents of the directory
1979 DirIterOpen(SelfRootDir
, Path
, &DirIter
);
1980 while (DirIterNext(&DirIter
, 2, LOADER_MATCH_PATTERNS
, &DirEntry
)) {
1981 if (DirEntry
->FileName
[0] == '.')
1982 continue; // skip this
1984 SPrint(FileName
, 255, L
"%s\\%s", Path
, DirEntry
->FileName
);
1986 Status
= StartEFIImage(FileDevicePath(SelfLoadedImage
->DeviceHandle
, FileName
),
1987 L
"", TYPE_EFI
, DirEntry
->FileName
, 0, NULL
, FALSE
);
1989 Status
= DirIterClose(&DirIter
);
1990 if (Status
!= EFI_NOT_FOUND
) {
1991 SPrint(FileName
, 255, L
"while scanning the %s directory", Path
);
1992 CheckError(Status
, FileName
);
1997 #ifdef __MAKEWITH_GNUEFI
1998 static EFI_STATUS
ConnectAllDriversToAllControllers(VOID
)
2001 UINTN AllHandleCount
;
2002 EFI_HANDLE
*AllHandleBuffer
;
2005 EFI_HANDLE
*HandleBuffer
;
2011 Status
= LibLocateHandle(AllHandles
,
2016 if (EFI_ERROR(Status
))
2019 for (Index
= 0; Index
< AllHandleCount
; Index
++) {
2021 // Scan the handle database
2023 Status
= LibScanHandleDatabase(NULL
,
2025 AllHandleBuffer
[Index
],
2030 if (EFI_ERROR (Status
))
2034 if (HandleType
[Index
] & EFI_HANDLE_TYPE_DRIVER_BINDING_HANDLE
)
2036 if (HandleType
[Index
] & EFI_HANDLE_TYPE_IMAGE_HANDLE
)
2041 for (HandleIndex
= 0; HandleIndex
< HandleCount
; HandleIndex
++) {
2042 if (HandleType
[HandleIndex
] & EFI_HANDLE_TYPE_PARENT_HANDLE
)
2047 if (HandleType
[Index
] & EFI_HANDLE_TYPE_DEVICE_HANDLE
) {
2048 Status
= refit_call4_wrapper(BS
->ConnectController
,
2049 AllHandleBuffer
[Index
],
2057 MyFreePool (HandleBuffer
);
2058 MyFreePool (HandleType
);
2062 MyFreePool (AllHandleBuffer
);
2064 } /* EFI_STATUS ConnectAllDriversToAllControllers() */
2066 static EFI_STATUS
ConnectAllDriversToAllControllers(VOID
) {
2067 BdsLibConnectAllDriversToAllControllers();
2072 // Load all EFI drivers from rEFInd's "drivers" subdirectory and from the
2073 // directories specified by the user in the "scan_driver_dirs" configuration
2075 static VOID
LoadDrivers(VOID
)
2077 CHAR16
*Directory
, *SelfDirectory
;
2078 UINTN i
= 0, Length
, NumFound
= 0;
2080 // load drivers from the subdirectories of rEFInd's home directory specified
2081 // in the DRIVER_DIRS constant.
2082 while ((Directory
= FindCommaDelimited(DRIVER_DIRS
, i
++)) != NULL
) {
2083 SelfDirectory
= SelfDirPath
? StrDuplicate(SelfDirPath
) : NULL
;
2084 CleanUpPathNameSlashes(SelfDirectory
);
2085 MergeStrings(&SelfDirectory
, Directory
, L
'\\');
2086 NumFound
+= ScanDriverDir(SelfDirectory
);
2087 MyFreePool(Directory
);
2088 MyFreePool(SelfDirectory
);
2091 // Scan additional user-specified driver directories....
2093 while ((Directory
= FindCommaDelimited(GlobalConfig
.DriverDirs
, i
++)) != NULL
) {
2094 CleanUpPathNameSlashes(Directory
);
2095 Length
= StrLen(Directory
);
2097 NumFound
+= ScanDriverDir(Directory
);
2099 MyFreePool(Directory
);
2102 // connect all devices
2104 ConnectAllDriversToAllControllers();
2105 } /* static VOID LoadDrivers() */
2107 // Determine what (if any) type of legacy (BIOS) boot support is available
2108 static VOID
FindLegacyBootType(VOID
) {
2109 #ifdef __MAKEWITH_TIANO
2111 EFI_LEGACY_BIOS_PROTOCOL
*LegacyBios
;
2114 GlobalConfig
.LegacyType
= LEGACY_TYPE_NONE
;
2116 // UEFI-style legacy BIOS support is available only with the TianoCore EDK2
2117 // build environment, and then only with some EFI implementations....
2118 #ifdef __MAKEWITH_TIANO
2119 Status
= gBS
->LocateProtocol (&gEfiLegacyBootProtocolGuid
, NULL
, (VOID
**) &LegacyBios
);
2120 if (!EFI_ERROR (Status
))
2121 GlobalConfig
.LegacyType
= LEGACY_TYPE_UEFI
;
2124 // Macs have their own system. If the firmware vendor code contains the
2125 // string "Apple", assume it's available. Note that this overrides the
2126 // UEFI type, and might yield false positives if the vendor string
2127 // contains "Apple" as part of something bigger, so this isn't 100%
2129 if (StriSubCmp(L
"Apple", ST
->FirmwareVendor
))
2130 GlobalConfig
.LegacyType
= LEGACY_TYPE_MAC
;
2131 } // static VOID FindLegacyBootType
2133 // Warn the user if legacy OS scans are enabled but the firmware or this
2134 // application can't support them....
2135 static VOID
WarnIfLegacyProblems() {
2136 BOOLEAN found
= FALSE
;
2139 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_NONE
) {
2141 if (GlobalConfig
.ScanFor
[i
] == 'h' || GlobalConfig
.ScanFor
[i
] == 'b' || GlobalConfig
.ScanFor
[i
] == 'c')
2144 } while ((i
< NUM_SCAN_OPTIONS
) && (!found
));
2146 Print(L
"NOTE: refind.conf's 'scanfor' line specifies scanning for one or more legacy\n");
2147 Print(L
"(BIOS) boot options; however, this is not possible because ");
2148 #ifdef __MAKEWITH_TIANO
2149 Print(L
"your computer lacks\n");
2150 Print(L
"the necessary Compatibility Support Module (CSM) support.\n");
2152 Print(L
"this program was\n");
2153 Print(L
"compiled without the necessary support. Please visit\n");
2154 Print(L
"http://www.rodsbooks.com/refind/getting.html and download and install a rEFInd\n");
2155 Print(L
"binary built with the TianoCore EDK2 to enable legacy boot support.\n");
2159 } // if no legacy support
2160 } // static VOID WarnIfLegacyProblems()
2162 // Locates boot loaders. NOTE: This assumes that GlobalConfig.LegacyType is set correctly.
2163 static VOID
ScanForBootloaders(VOID
) {
2168 // scan for loaders and tools, add them to the menu
2169 for (i
= 0; i
< NUM_SCAN_OPTIONS
; i
++) {
2170 switch(GlobalConfig
.ScanFor
[i
]) {
2175 ScanLegacyInternal();
2178 ScanLegacyExternal();
2181 ScanUserConfigured(CONFIG_FILE_NAME
);
2195 // assign shortcut keys
2196 for (i
= 0; i
< MainMenu
.EntryCount
&& MainMenu
.Entries
[i
]->Row
== 0 && i
< 9; i
++)
2197 MainMenu
.Entries
[i
]->ShortcutDigit
= (CHAR16
)('1' + i
);
2199 // wait for user ACK when there were errors
2200 FinishTextScreen(FALSE
);
2201 } // static VOID ScanForBootloaders()
2203 // Add the second-row tags containing built-in and external tools (EFI shell,
2205 static VOID
ScanForTools(VOID
) {
2206 CHAR16
*FileName
= NULL
, *MokLocations
, *MokName
, *MemtestName
, *PathName
, Description
[256];
2207 REFIT_MENU_ENTRY
*TempMenuEntry
;
2208 UINTN i
, j
, k
, VolumeIndex
;
2212 MokLocations
= StrDuplicate(MOK_LOCATIONS
);
2213 if (MokLocations
!= NULL
)
2214 MergeStrings(&MokLocations
, SelfDirPath
, L
',');
2216 for (i
= 0; i
< NUM_TOOLS
; i
++) {
2217 switch(GlobalConfig
.ShowTools
[i
]) {
2218 // NOTE: Be sure that FileName is NULL at the end of each case.
2220 TempMenuEntry
= CopyMenuEntry(&MenuEntryShutdown
);
2221 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_SHUTDOWN
);
2222 AddMenuEntry(&MainMenu
, TempMenuEntry
);
2225 TempMenuEntry
= CopyMenuEntry(&MenuEntryReset
);
2226 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_RESET
);
2227 AddMenuEntry(&MainMenu
, TempMenuEntry
);
2230 TempMenuEntry
= CopyMenuEntry(&MenuEntryAbout
);
2231 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT
);
2232 AddMenuEntry(&MainMenu
, TempMenuEntry
);
2235 TempMenuEntry
= CopyMenuEntry(&MenuEntryExit
);
2236 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_EXIT
);
2237 AddMenuEntry(&MainMenu
, TempMenuEntry
);
2240 if (EfivarGetRaw(&GlobalGuid
, L
"OsIndicationsSupported", &b
, &j
) == EFI_SUCCESS
) {
2242 if (osind
& EFI_OS_INDICATIONS_BOOT_TO_FW_UI
) {
2243 TempMenuEntry
= CopyMenuEntry(&MenuEntryFirmware
);
2244 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_FIRMWARE
);
2245 AddMenuEntry(&MainMenu
, TempMenuEntry
);
2251 while ((FileName
= FindCommaDelimited(SHELL_NAMES
, j
++)) != NULL
) {
2252 if (FileExists(SelfRootDir
, FileName
)) {
2253 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, L
"EFI Shell", BuiltinIcon(BUILTIN_ICON_TOOL_SHELL
),
2256 MyFreePool(FileName
);
2261 while ((FileName
= FindCommaDelimited(GPTSYNC_NAMES
, j
++)) != NULL
) {
2262 if (FileExists(SelfRootDir
, FileName
)) {
2263 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, L
"Hybrid MBR tool", BuiltinIcon(BUILTIN_ICON_TOOL_PART
),
2266 MyFreePool(FileName
);
2271 case TAG_APPLE_RECOVERY
:
2272 FileName
= StrDuplicate(L
"\\com.apple.recovery.boot\\boot.efi");
2273 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
2274 if ((Volumes
[VolumeIndex
]->RootDir
!= NULL
) && (FileExists(Volumes
[VolumeIndex
]->RootDir
, FileName
))) {
2275 SPrint(Description
, 255, L
"Apple Recovery on %s", Volumes
[VolumeIndex
]->VolName
);
2276 AddToolEntry(Volumes
[VolumeIndex
]->DeviceHandle
, FileName
, Description
,
2277 BuiltinIcon(BUILTIN_ICON_TOOL_APPLE_RESCUE
), 'R', TRUE
);
2280 MyFreePool(FileName
);
2286 while ((FileName
= FindCommaDelimited(MokLocations
, j
++)) != NULL
) {
2288 while ((MokName
= FindCommaDelimited(MOK_NAMES
, k
++)) != NULL
) {
2289 PathName
= StrDuplicate(FileName
);
2290 MergeStrings(&PathName
, MokName
, (StriCmp(PathName
, L
"\\") == 0) ? 0 : L
'\\');
2291 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
2292 if ((Volumes
[VolumeIndex
]->RootDir
!= NULL
) && (FileExists(Volumes
[VolumeIndex
]->RootDir
, PathName
))) {
2293 SPrint(Description
, 255, L
"MOK utility at %s on %s", PathName
, Volumes
[VolumeIndex
]->VolName
);
2294 AddToolEntry(Volumes
[VolumeIndex
]->DeviceHandle
, PathName
, Description
,
2295 BuiltinIcon(BUILTIN_ICON_TOOL_MOK_TOOL
), 'S', FALSE
);
2298 MyFreePool(PathName
);
2299 MyFreePool(MokName
);
2300 } // while MOK_NAMES
2301 MyFreePool(FileName
);
2302 } // while MokLocations
2307 while ((FileName
= FindCommaDelimited(MEMTEST_LOCATIONS
, j
++)) != NULL
) {
2309 while ((MemtestName
= FindCommaDelimited(MEMTEST_NAMES
, k
++)) != NULL
) {
2310 PathName
= StrDuplicate(FileName
);
2311 MergeStrings(&PathName
, MemtestName
, (StriCmp(PathName
, L
"\\") == 0) ? 0 : L
'\\');
2312 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
2313 if ((Volumes
[VolumeIndex
]->RootDir
!= NULL
) && (FileExists(Volumes
[VolumeIndex
]->RootDir
, PathName
))) {
2314 SPrint(Description
, 255, L
"Memory test utility at %s on %s", PathName
, Volumes
[VolumeIndex
]->VolName
);
2315 AddToolEntry(Volumes
[VolumeIndex
]->DeviceHandle
, PathName
, Description
,
2316 BuiltinIcon(BUILTIN_ICON_FUNC_FIRMWARE
), 'S', FALSE
);
2319 MyFreePool(PathName
);
2320 MyFreePool(MemtestName
);
2321 } // while MEMTEST_NAMES
2322 MyFreePool(FileName
);
2323 } // while MEMTEST_LOCATIONS
2328 } // static VOID ScanForTools
2330 // Rescan for boot loaders
2331 VOID
RescanAll(VOID
) {
2338 egDisplayMessage(L
"Scanning for new boot loaders; please wait....", &BGColor
);
2339 FreeList((VOID
***) &(MainMenu
.Entries
), &MainMenu
.EntryCount
);
2340 MainMenu
.Entries
= NULL
;
2341 MainMenu
.EntryCount
= 0;
2342 ReadConfig(CONFIG_FILE_NAME
);
2343 ConnectAllDriversToAllControllers();
2345 ScanForBootloaders();
2348 } // VOID RescanAll()
2350 #ifdef __MAKEWITH_TIANO
2352 // Minimal initialization function
2353 static VOID
InitializeLib(IN EFI_HANDLE ImageHandle
, IN EFI_SYSTEM_TABLE
*SystemTable
) {
2355 // gImageHandle = ImageHandle;
2356 gBS
= SystemTable
->BootServices
;
2357 // gRS = SystemTable->RuntimeServices;
2358 gRT
= SystemTable
->RuntimeServices
; // Some BDS functions need gRT to be set
2359 EfiGetSystemConfigurationTable (&gEfiDxeServicesTableGuid
, (VOID
**) &gDS
);
2361 InitializeConsoleSim();
2366 // Set up our own Secure Boot extensions....
2367 // Returns TRUE on success, FALSE otherwise
2368 static BOOLEAN
SecureBootSetup(VOID
) {
2370 BOOLEAN Success
= FALSE
;
2372 if (secure_mode() && ShimLoaded()) {
2373 Status
= security_policy_install();
2374 if (Status
== EFI_SUCCESS
) {
2377 Print(L
"Failed to install MOK Secure Boot extensions");
2381 } // VOID SecureBootSetup()
2383 // Remove our own Secure Boot extensions....
2384 // Returns TRUE on success, FALSE otherwise
2385 static BOOLEAN
SecureBootUninstall(VOID
) {
2387 BOOLEAN Success
= TRUE
;
2389 if (secure_mode()) {
2390 Status
= security_policy_uninstall();
2391 if (Status
!= EFI_SUCCESS
) {
2393 BeginTextScreen(L
"Secure Boot Policy Failure");
2394 Print(L
"Failed to uninstall MOK Secure Boot extensions; forcing a reboot.");
2396 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);
2400 } // VOID SecureBootUninstall
2407 efi_main (EFI_HANDLE ImageHandle
, EFI_SYSTEM_TABLE
*SystemTable
)
2410 BOOLEAN MainLoopRunning
= TRUE
;
2411 BOOLEAN MokProtocol
;
2412 REFIT_MENU_ENTRY
*ChosenEntry
;
2414 CHAR16
*Selection
= NULL
;
2418 InitializeLib(ImageHandle
, SystemTable
);
2419 Status
= InitRefitLib(ImageHandle
);
2420 if (EFI_ERROR(Status
))
2423 // read configuration
2424 CopyMem(GlobalConfig
.ScanFor
, "ieom ", NUM_SCAN_OPTIONS
);
2425 FindLegacyBootType();
2426 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
)
2427 CopyMem(GlobalConfig
.ScanFor
, "ihebocm ", NUM_SCAN_OPTIONS
);
2428 ReadConfig(CONFIG_FILE_NAME
);
2432 WarnIfLegacyProblems();
2433 MainMenu
.TimeoutSeconds
= GlobalConfig
.Timeout
;
2435 // disable EFI watchdog timer
2436 refit_call4_wrapper(BS
->SetWatchdogTimer
, 0x0000, 0x0000, 0x0000, NULL
);
2438 // further bootstrap (now with config available)
2439 MokProtocol
= SecureBootSetup();
2441 ScanForBootloaders();
2445 if (GlobalConfig
.ScanDelay
> 0) {
2450 egDisplayMessage(L
"Pausing before disk scan; please wait....", &BGColor
);
2451 for (i
= 0; i
< GlobalConfig
.ScanDelay
; i
++)
2452 refit_call1_wrapper(BS
->Stall
, 1000000);
2456 if (GlobalConfig
.DefaultSelection
)
2457 Selection
= StrDuplicate(GlobalConfig
.DefaultSelection
);
2459 while (MainLoopRunning
) {
2460 MenuExit
= RunMainMenu(&MainMenu
, Selection
, &ChosenEntry
);
2462 // The Escape key triggers a re-scan operation....
2463 if (MenuExit
== MENU_EXIT_ESCAPE
) {
2468 switch (ChosenEntry
->Tag
) {
2470 case TAG_REBOOT
: // Reboot
2472 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);
2473 MainLoopRunning
= FALSE
; // just in case we get this far
2476 case TAG_SHUTDOWN
: // Shut Down
2478 refit_call4_wrapper(RT
->ResetSystem
, EfiResetShutdown
, EFI_SUCCESS
, 0, NULL
);
2479 MainLoopRunning
= FALSE
; // just in case we get this far
2482 case TAG_ABOUT
: // About rEFInd
2486 case TAG_LOADER
: // Boot OS via .EFI loader
2487 StartLoader((LOADER_ENTRY
*)ChosenEntry
);
2490 case TAG_LEGACY
: // Boot legacy OS
2491 StartLegacy((LEGACY_ENTRY
*)ChosenEntry
);
2494 #ifdef __MAKEWITH_TIANO
2495 case TAG_LEGACY_UEFI
: // Boot a legacy OS on a non-Mac
2496 StartLegacyUEFI((LEGACY_ENTRY
*)ChosenEntry
);
2500 case TAG_TOOL
: // Start a EFI tool
2501 StartTool((LOADER_ENTRY
*)ChosenEntry
);
2504 case TAG_EXIT
: // Terminate rEFInd
2505 if ((MokProtocol
) && !SecureBootUninstall()) {
2506 MainLoopRunning
= FALSE
; // just in case we get this far
2508 BeginTextScreen(L
" ");
2513 case TAG_FIRMWARE
: // Reboot into firmware's user interface
2514 RebootIntoFirmware();
2518 MyFreePool(Selection
);
2519 Selection
= (ChosenEntry
->Title
) ? StrDuplicate(ChosenEntry
->Title
) : NULL
;
2522 // If we end up here, things have gone wrong. Try to reboot, and if that
2523 // fails, go into an endless loop.
2524 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);