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 #include "../EfiLib/legacy.h"
63 #endif // __MAKEWITH_GNUEFI
65 #ifndef EFI_OS_INDICATIONS_BOOT_TO_FW_UI
66 #define EFI_OS_INDICATIONS_BOOT_TO_FW_UI 0x0000000000000001ULL
72 #define MACOSX_LOADER_PATH L"System\\Library\\CoreServices\\boot.efi"
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 if (AboutMenu
.EntryCount
== 0) {
155 AboutMenu
.TitleImage
= BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT
);
156 AddMenuInfoLine(&AboutMenu
, L
"rEFInd Version 0.7.4.3");
157 AddMenuInfoLine(&AboutMenu
, L
"");
158 AddMenuInfoLine(&AboutMenu
, L
"Copyright (c) 2006-2010 Christoph Pfisterer");
159 AddMenuInfoLine(&AboutMenu
, L
"Copyright (c) 2012-2013 Roderick W. Smith");
160 AddMenuInfoLine(&AboutMenu
, L
"Portions Copyright (c) Intel Corporation and others");
161 AddMenuInfoLine(&AboutMenu
, L
"Distributed under the terms of the GNU GPLv3 license");
162 AddMenuInfoLine(&AboutMenu
, L
"");
163 AddMenuInfoLine(&AboutMenu
, L
"Running on:");
164 AddMenuInfoLine(&AboutMenu
, PoolPrint(L
" EFI Revision %d.%02d", ST
->Hdr
.Revision
>> 16, ST
->Hdr
.Revision
& ((1 << 16) - 1)));
166 AddMenuInfoLine(&AboutMenu
, L
" Platform: x86 (32 bit)");
167 #elif defined(EFIX64)
168 AddMenuInfoLine(&AboutMenu
, PoolPrint(L
" Platform: x86_64 (64 bit); Secure Boot %s",
169 secure_mode() ? L
"active" : L
"inactive"));
171 AddMenuInfoLine(&AboutMenu
, L
" Platform: unknown");
173 AddMenuInfoLine(&AboutMenu
, PoolPrint(L
" Firmware: %s %d.%02d", ST
->FirmwareVendor
, ST
->FirmwareRevision
>> 16,
174 ST
->FirmwareRevision
& ((1 << 16) - 1)));
175 AddMenuInfoLine(&AboutMenu
, PoolPrint(L
" Screen Output: %s", egScreenDescription()));
176 AddMenuInfoLine(&AboutMenu
, L
"");
177 #if defined(__MAKEWITH_GNUEFI)
178 AddMenuInfoLine(&AboutMenu
, L
"Built with GNU-EFI");
180 AddMenuInfoLine(&AboutMenu
, L
"Built with TianoCore EDK2");
182 AddMenuInfoLine(&AboutMenu
, L
"");
183 AddMenuInfoLine(&AboutMenu
, L
"For more information, see the rEFInd Web site:");
184 AddMenuInfoLine(&AboutMenu
, L
"http://www.rodsbooks.com/refind/");
185 AddMenuEntry(&AboutMenu
, &MenuEntryReturn
);
188 RunMenu(&AboutMenu
, NULL
);
189 } /* VOID AboutrEFInd() */
191 static VOID
WarnSecureBootError(CHAR16
*Name
, BOOLEAN Verbose
) {
193 Name
= L
"the loader";
195 refit_call2_wrapper(ST
->ConOut
->SetAttribute
, ST
->ConOut
, ATTR_ERROR
);
196 Print(L
"Secure Boot validation failure loading %s!\n", Name
);
197 refit_call2_wrapper(ST
->ConOut
->SetAttribute
, ST
->ConOut
, ATTR_BASIC
);
198 if (Verbose
&& secure_mode()) {
199 Print(L
"\nThis computer is configured with Secure Boot active, but\n%s has failed validation.\n", Name
);
200 Print(L
"\nYou can:\n * Launch another boot loader\n");
201 Print(L
" * Disable Secure Boot in your firmware\n");
202 Print(L
" * Sign %s with a machine owner key (MOK)\n", Name
);
203 Print(L
" * Use a MOK utility (often present on the second row) to add a MOK with which\n");
204 Print(L
" %s has already been signed.\n", Name
);
205 Print(L
" * Use a MOK utility to register %s (\"enroll its hash\") without\n", Name
);
206 Print(L
" signing it.\n");
207 Print(L
"\nSee http://www.rodsbooks.com/refind/secureboot.html for more information\n");
210 } // VOID WarnSecureBootError()
212 // Returns TRUE if this file is a valid EFI loader file, and is proper ARCH
213 static BOOLEAN
IsValidLoader(EFI_FILE
*RootDir
, CHAR16
*FileName
) {
214 BOOLEAN IsValid
= TRUE
;
215 #if defined (EFIX64) | defined (EFI32)
217 EFI_FILE_HANDLE FileHandle
;
219 UINTN Size
= sizeof(Header
);
221 if ((RootDir
== NULL
) || (FileName
== NULL
)) {
222 // Assume valid here, because Macs produce NULL RootDir (& maybe FileName)
223 // when launching from a Firewire drive. This should be handled better, but
224 // fix would have to be in StartEFIImageList() and/or in FindVolumeAndFilename().
228 Status
= refit_call5_wrapper(RootDir
->Open
, RootDir
, &FileHandle
, FileName
, EFI_FILE_MODE_READ
, 0);
229 if (EFI_ERROR(Status
))
232 Status
= refit_call3_wrapper(FileHandle
->Read
, FileHandle
, &Size
, Header
);
233 refit_call1_wrapper(FileHandle
->Close
, FileHandle
);
235 IsValid
= !EFI_ERROR(Status
) &&
236 Size
== sizeof(Header
) &&
237 ((Header
[0] == 'M' && Header
[1] == 'Z' &&
238 (Size
= *(UINT32
*)&Header
[0x3c]) < 0x180 &&
239 Header
[Size
] == 'P' && Header
[Size
+1] == 'E' &&
240 Header
[Size
+2] == 0 && Header
[Size
+3] == 0 &&
241 *(UINT16
*)&Header
[Size
+4] == EFI_STUB_ARCH
) ||
242 (*(UINT32
*)&Header
== FAT_ARCH
));
245 } // BOOLEAN IsValidLoader()
247 // Launch an EFI binary.
248 static EFI_STATUS
StartEFIImageList(IN EFI_DEVICE_PATH
**DevicePaths
,
249 IN CHAR16
*LoadOptions
, IN UINTN LoaderType
,
250 IN CHAR16
*ImageTitle
, IN CHAR8 OSType
,
251 OUT UINTN
*ErrorInStep
,
254 EFI_STATUS Status
, ReturnStatus
;
255 EFI_HANDLE ChildImageHandle
;
256 EFI_LOADED_IMAGE
*ChildLoadedImage
= NULL
;
257 REFIT_VOLUME
*Volume
= NULL
;
258 UINTN DevicePathIndex
;
259 CHAR16 ErrorInfo
[256];
260 CHAR16
*FullLoadOptions
= NULL
;
261 CHAR16
*Filename
= NULL
;
264 if (ErrorInStep
!= NULL
)
268 if (LoadOptions
!= NULL
) {
269 FullLoadOptions
= StrDuplicate(LoadOptions
);
270 if ((LoaderType
== TYPE_EFI
) && (OSType
== 'M')) {
271 MergeStrings(&FullLoadOptions
, L
" ", 0);
272 // NOTE: That last space is also added by the EFI shell and seems to be significant
273 // when passing options to Apple's boot.efi...
275 } // if (LoadOptions != NULL)
277 Print(L
"Starting %s\nUsing load options '%s'\n", ImageTitle
, FullLoadOptions
? FullLoadOptions
: L
"");
279 // load the image into memory (and execute it, in the case of a shim/MOK image).
280 ReturnStatus
= Status
= EFI_NOT_FOUND
; // in case the list is empty
281 for (DevicePathIndex
= 0; DevicePaths
[DevicePathIndex
] != NULL
; DevicePathIndex
++) {
282 FindVolumeAndFilename(DevicePaths
[DevicePathIndex
], &Volume
, &Filename
);
283 // Some EFIs crash if attempting to load driver for invalid architecture, so
284 // protect for this condition; but sometimes Volume comes back NULL, so provide
285 // an exception. (TODO: Handle this special condition better.)
286 if ((LoaderType
== TYPE_LEGACY
) || (Volume
== NULL
) || IsValidLoader(Volume
->RootDir
, Filename
)) {
287 if (Filename
&& (LoaderType
!= TYPE_LEGACY
)) {
288 Temp
= PoolPrint(L
"\\%s %s", Filename
, FullLoadOptions
? FullLoadOptions
: L
"");
290 MyFreePool(FullLoadOptions
);
291 FullLoadOptions
= Temp
;
295 // NOTE: Below commented-out line could be more efficient if file were read ahead of
296 // time and passed as a pre-loaded image to LoadImage(), but it doesn't work on my
297 // 32-bit Mac Mini or my 64-bit Intel box when launching a Linux kernel; the
298 // kernel returns a "Failed to handle fs_proto" error message.
299 // TODO: Track down the cause of this error and fix it, if possible.
300 // ReturnStatus = Status = refit_call6_wrapper(BS->LoadImage, FALSE, SelfImageHandle, DevicePaths[DevicePathIndex],
301 // ImageData, ImageSize, &ChildImageHandle);
302 ReturnStatus
= Status
= refit_call6_wrapper(BS
->LoadImage
, FALSE
, SelfImageHandle
, DevicePaths
[DevicePathIndex
],
303 NULL
, 0, &ChildImageHandle
);
305 Print(L
"Invalid loader file!\n");
306 ReturnStatus
= EFI_LOAD_ERROR
;
308 if (ReturnStatus
!= EFI_NOT_FOUND
) {
312 if ((Status
== EFI_ACCESS_DENIED
) || (Status
== EFI_SECURITY_VIOLATION
)) {
313 WarnSecureBootError(ImageTitle
, Verbose
);
316 SPrint(ErrorInfo
, 255, L
"while loading %s", ImageTitle
);
317 if (CheckError(Status
, ErrorInfo
)) {
318 if (ErrorInStep
!= NULL
)
323 ReturnStatus
= Status
= refit_call3_wrapper(BS
->HandleProtocol
, ChildImageHandle
, &LoadedImageProtocol
,
324 (VOID
**) &ChildLoadedImage
);
325 if (CheckError(Status
, L
"while getting a LoadedImageProtocol handle")) {
326 if (ErrorInStep
!= NULL
)
330 ChildLoadedImage
->LoadOptions
= (VOID
*)FullLoadOptions
;
331 ChildLoadedImage
->LoadOptionsSize
= FullLoadOptions
? ((UINT32
)StrLen(FullLoadOptions
) + 1) * sizeof(CHAR16
) : 0;
332 // turn control over to the image
333 // TODO: (optionally) re-enable the EFI watchdog timer!
335 // close open file handles
337 ReturnStatus
= Status
= refit_call3_wrapper(BS
->StartImage
, ChildImageHandle
, NULL
, NULL
);
339 // control returns here when the child image calls Exit()
340 SPrint(ErrorInfo
, 255, L
"returned from %s", ImageTitle
);
341 if (CheckError(Status
, ErrorInfo
)) {
342 if (ErrorInStep
!= NULL
)
346 // re-open file handles
350 // unload the image, we don't care if it works or not...
351 Status
= refit_call1_wrapper(BS
->UnloadImage
, ChildImageHandle
);
354 MyFreePool(FullLoadOptions
);
356 } /* static EFI_STATUS StartEFIImageList() */
358 static EFI_STATUS
StartEFIImage(IN EFI_DEVICE_PATH
*DevicePath
,
359 IN CHAR16
*LoadOptions
, IN UINTN LoaderType
,
360 IN CHAR16
*ImageTitle
, IN CHAR8 OSType
,
361 OUT UINTN
*ErrorInStep
,
364 EFI_DEVICE_PATH
*DevicePaths
[2];
366 DevicePaths
[0] = DevicePath
;
367 DevicePaths
[1] = NULL
;
368 return StartEFIImageList(DevicePaths
, LoadOptions
, LoaderType
, ImageTitle
, OSType
, ErrorInStep
, Verbose
);
369 } /* static EFI_STATUS StartEFIImage() */
371 // From gummiboot: Retrieve a raw EFI variable.
372 // Returns EFI status
373 static EFI_STATUS
EfivarGetRaw(EFI_GUID
*vendor
, CHAR16
*name
, CHAR8
**buffer
, UINTN
*size
) {
378 l
= sizeof(CHAR16
*) * EFI_MAXIMUM_VARIABLE_SIZE
;
379 buf
= AllocatePool(l
);
381 return EFI_OUT_OF_RESOURCES
;
383 err
= refit_call5_wrapper(RT
->GetVariable
, name
, vendor
, NULL
, &l
, buf
);
384 if (EFI_ERROR(err
) == EFI_SUCCESS
) {
391 } // EFI_STATUS EfivarGetRaw()
393 // From gummiboot: Set an EFI variable
394 static EFI_STATUS
EfivarSetRaw(EFI_GUID
*vendor
, CHAR16
*name
, CHAR8
*buf
, UINTN size
, BOOLEAN persistent
) {
397 flags
= EFI_VARIABLE_BOOTSERVICE_ACCESS
|EFI_VARIABLE_RUNTIME_ACCESS
;
399 flags
|= EFI_VARIABLE_NON_VOLATILE
;
401 return refit_call5_wrapper(RT
->SetVariable
, name
, vendor
, flags
, size
, buf
);
402 } // EFI_STATUS EfivarSetRaw()
404 // From gummiboot: Reboot the computer into its built-in user interface
405 static EFI_STATUS
RebootIntoFirmware(VOID
) {
411 osind
= EFI_OS_INDICATIONS_BOOT_TO_FW_UI
;
413 err
= EfivarGetRaw(&GlobalGuid
, L
"OsIndications", &b
, &size
);
414 if (err
== EFI_SUCCESS
)
418 err
= EfivarSetRaw(&GlobalGuid
, L
"OsIndications", (CHAR8
*)&osind
, sizeof(UINT64
), TRUE
);
419 if (err
!= EFI_SUCCESS
)
422 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);
423 Print(L
"Error calling ResetSystem: %r", err
);
430 // EFI OS loader functions
433 static VOID
StartLoader(LOADER_ENTRY
*Entry
)
435 UINTN ErrorInStep
= 0;
437 BeginExternalScreen(Entry
->UseGraphicsMode
, L
"Booting OS");
438 StartEFIImage(Entry
->DevicePath
, Entry
->LoadOptions
, TYPE_EFI
,
439 Basename(Entry
->LoaderPath
), Entry
->OSType
, &ErrorInStep
, !Entry
->UseGraphicsMode
);
440 FinishExternalScreen();
443 // Locate an initrd or initramfs file that matches the kernel specified by LoaderPath.
444 // The matching file has a name that begins with "init" and includes the same version
445 // number string as is found in LoaderPath -- but not a longer version number string.
446 // For instance, if LoaderPath is \EFI\kernels\bzImage-3.3.0.efi, and if \EFI\kernels
447 // has a file called initramfs-3.3.0.img, this function will return the string
448 // '\EFI\kernels\initramfs-3.3.0.img'. If the directory ALSO contains the file
449 // initramfs-3.3.0-rc7.img or initramfs-13.3.0.img, those files will NOT match;
450 // however, initmine-3.3.0.img might match. (FindInitrd() returns the first match it
451 // finds). Thus, care should be taken to avoid placing duplicate matching files in
452 // the kernel's directory.
453 // If no matching init file can be found, returns NULL.
454 static CHAR16
* FindInitrd(IN CHAR16
*LoaderPath
, IN REFIT_VOLUME
*Volume
) {
455 CHAR16
*InitrdName
= NULL
, *FileName
, *KernelVersion
, *InitrdVersion
, *Path
;
456 REFIT_DIR_ITER DirIter
;
457 EFI_FILE_INFO
*DirEntry
;
459 FileName
= Basename(LoaderPath
);
460 KernelVersion
= FindNumbers(FileName
);
461 Path
= FindPath(LoaderPath
);
463 // Add trailing backslash for root directory; necessary on some systems, but must
464 // NOT be added to all directories, since on other systems, a trailing backslash on
465 // anything but the root directory causes them to flake out!
466 if (StrLen(Path
) == 0) {
467 MergeStrings(&Path
, L
"\\", 0);
469 DirIterOpen(Volume
->RootDir
, Path
, &DirIter
);
470 // Now add a trailing backslash if it was NOT added earlier, for consistency in
471 // building the InitrdName later....
472 if ((StrLen(Path
) > 0) && (Path
[StrLen(Path
) - 1] != L
'\\'))
473 MergeStrings(&Path
, L
"\\", 0);
474 while ((DirIterNext(&DirIter
, 2, L
"init*", &DirEntry
)) && (InitrdName
== NULL
)) {
475 InitrdVersion
= FindNumbers(DirEntry
->FileName
);
476 if (KernelVersion
!= NULL
) {
477 if (StriCmp(InitrdVersion
, KernelVersion
) == 0) {
478 InitrdName
= PoolPrint(L
"%s%s", Path
, DirEntry
->FileName
);
481 if (InitrdVersion
== NULL
) {
482 InitrdName
= PoolPrint(L
"%s%s", Path
, DirEntry
->FileName
);
485 MyFreePool(InitrdVersion
);
487 DirIterClose(&DirIter
);
489 // Note: Don't FreePool(FileName), since Basename returns a pointer WITHIN the string it's passed.
490 MyFreePool(KernelVersion
);
493 } // static CHAR16 * FindInitrd()
495 LOADER_ENTRY
* AddPreparedLoaderEntry(LOADER_ENTRY
*Entry
) {
496 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
499 } // LOADER_ENTRY * AddPreparedLoaderEntry()
501 // Creates a copy of a menu screen.
502 // Returns a pointer to the copy of the menu screen.
503 static REFIT_MENU_SCREEN
* CopyMenuScreen(REFIT_MENU_SCREEN
*Entry
) {
504 REFIT_MENU_SCREEN
*NewEntry
;
507 NewEntry
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
508 if ((Entry
!= NULL
) && (NewEntry
!= NULL
)) {
509 CopyMem(NewEntry
, Entry
, sizeof(REFIT_MENU_SCREEN
));
510 NewEntry
->Title
= (Entry
->Title
) ? StrDuplicate(Entry
->Title
) : NULL
;
511 NewEntry
->TimeoutText
= (Entry
->TimeoutText
) ? StrDuplicate(Entry
->TimeoutText
) : NULL
;
512 if (Entry
->TitleImage
!= NULL
) {
513 NewEntry
->TitleImage
= AllocatePool(sizeof(EG_IMAGE
));
514 if (NewEntry
->TitleImage
!= NULL
)
515 CopyMem(NewEntry
->TitleImage
, Entry
->TitleImage
, sizeof(EG_IMAGE
));
517 NewEntry
->InfoLines
= (CHAR16
**) AllocateZeroPool(Entry
->InfoLineCount
* (sizeof(CHAR16
*)));
518 for (i
= 0; i
< Entry
->InfoLineCount
&& NewEntry
->InfoLines
; i
++) {
519 NewEntry
->InfoLines
[i
] = (Entry
->InfoLines
[i
]) ? StrDuplicate(Entry
->InfoLines
[i
]) : NULL
;
521 NewEntry
->Entries
= (REFIT_MENU_ENTRY
**) AllocateZeroPool(Entry
->EntryCount
* (sizeof (REFIT_MENU_ENTRY
*)));
522 for (i
= 0; i
< Entry
->EntryCount
&& NewEntry
->Entries
; i
++) {
523 AddMenuEntry(NewEntry
, Entry
->Entries
[i
]);
525 NewEntry
->Hint1
= (Entry
->Hint1
) ? StrDuplicate(Entry
->Hint1
) : NULL
;
526 NewEntry
->Hint2
= (Entry
->Hint2
) ? StrDuplicate(Entry
->Hint2
) : NULL
;
529 } // static REFIT_MENU_SCREEN* CopyMenuScreen()
531 // Creates a copy of a menu entry. Intended to enable moving a stack-based
532 // menu entry (such as the ones for the "reboot" and "exit" functions) to
533 // to the heap. This enables easier deletion of the whole set of menu
534 // entries when re-scanning.
535 // Returns a pointer to the copy of the menu entry.
536 static REFIT_MENU_ENTRY
* CopyMenuEntry(REFIT_MENU_ENTRY
*Entry
) {
537 REFIT_MENU_ENTRY
*NewEntry
;
539 NewEntry
= AllocateZeroPool(sizeof(REFIT_MENU_ENTRY
));
540 if ((Entry
!= NULL
) && (NewEntry
!= NULL
)) {
541 CopyMem(NewEntry
, Entry
, sizeof(REFIT_MENU_ENTRY
));
542 NewEntry
->Title
= (Entry
->Title
) ? StrDuplicate(Entry
->Title
) : NULL
;
543 if (Entry
->BadgeImage
!= NULL
) {
544 NewEntry
->BadgeImage
= AllocatePool(sizeof(EG_IMAGE
));
545 if (NewEntry
->BadgeImage
!= NULL
)
546 CopyMem(NewEntry
->BadgeImage
, Entry
->BadgeImage
, sizeof(EG_IMAGE
));
548 if (Entry
->Image
!= NULL
) {
549 NewEntry
->Image
= AllocatePool(sizeof(EG_IMAGE
));
550 if (NewEntry
->Image
!= NULL
)
551 CopyMem(NewEntry
->Image
, Entry
->Image
, sizeof(EG_IMAGE
));
553 if (Entry
->SubScreen
!= NULL
) {
554 NewEntry
->SubScreen
= CopyMenuScreen(Entry
->SubScreen
);
558 } // REFIT_MENU_ENTRY* CopyMenuEntry()
560 // Creates a new LOADER_ENTRY data structure and populates it with
561 // default values from the specified Entry, or NULL values if Entry
562 // is unspecified (NULL).
563 // Returns a pointer to the new data structure, or NULL if it
564 // couldn't be allocated
565 LOADER_ENTRY
*InitializeLoaderEntry(IN LOADER_ENTRY
*Entry
) {
566 LOADER_ENTRY
*NewEntry
= NULL
;
568 NewEntry
= AllocateZeroPool(sizeof(LOADER_ENTRY
));
569 if (NewEntry
!= NULL
) {
570 NewEntry
->me
.Title
= NULL
;
571 NewEntry
->me
.Tag
= TAG_LOADER
;
572 NewEntry
->Enabled
= TRUE
;
573 NewEntry
->UseGraphicsMode
= FALSE
;
574 NewEntry
->OSType
= 0;
576 NewEntry
->LoaderPath
= (Entry
->LoaderPath
) ? StrDuplicate(Entry
->LoaderPath
) : NULL
;
577 NewEntry
->VolName
= (Entry
->VolName
) ? StrDuplicate(Entry
->VolName
) : NULL
;
578 NewEntry
->DevicePath
= Entry
->DevicePath
;
579 NewEntry
->UseGraphicsMode
= Entry
->UseGraphicsMode
;
580 NewEntry
->LoadOptions
= (Entry
->LoadOptions
) ? StrDuplicate(Entry
->LoadOptions
) : NULL
;
581 NewEntry
->InitrdPath
= (Entry
->InitrdPath
) ? StrDuplicate(Entry
->InitrdPath
) : NULL
;
585 } // LOADER_ENTRY *InitializeLoaderEntry()
587 // Adds InitrdPath to Options, but only if Options doesn't already include an
588 // initrd= line. Done to enable overriding the default initrd selection in a
589 // refind_linux.conf file's options list.
590 // Returns a pointer to a new string. The calling function is responsible for
591 // freeing its memory.
592 static CHAR16
*AddInitrdToOptions(CHAR16
*Options
, CHAR16
*InitrdPath
) {
593 CHAR16
*NewOptions
= NULL
;
596 NewOptions
= StrDuplicate(Options
);
597 if ((InitrdPath
!= NULL
) && !StriSubCmp(L
"initrd=", Options
)) {
598 MergeStrings(&NewOptions
, L
"initrd=", L
' ');
599 MergeStrings(&NewOptions
, InitrdPath
, 0);
602 } // CHAR16 *AddInitrdToOptions()
604 // Prepare a REFIT_MENU_SCREEN data structure for a subscreen entry. This sets up
605 // the default entry that launches the boot loader using the same options as the
606 // main Entry does. Subsequent options can be added by the calling function.
607 // If a subscreen already exists in the Entry that's passed to this function,
608 // it's left unchanged and a pointer to it is returned.
609 // Returns a pointer to the new subscreen data structure, or NULL if there
610 // were problems allocating memory.
611 REFIT_MENU_SCREEN
*InitializeSubScreen(IN LOADER_ENTRY
*Entry
) {
612 CHAR16
*FileName
, *MainOptions
= NULL
;
613 REFIT_MENU_SCREEN
*SubScreen
= NULL
;
614 LOADER_ENTRY
*SubEntry
;
616 FileName
= Basename(Entry
->LoaderPath
);
617 if (Entry
->me
.SubScreen
== NULL
) { // No subscreen yet; initialize default entry....
618 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
619 if (SubScreen
!= NULL
) {
620 SubScreen
->Title
= AllocateZeroPool(sizeof(CHAR16
) * 256);
621 SPrint(SubScreen
->Title
, 255, L
"Boot Options for %s on %s",
622 (Entry
->Title
!= NULL
) ? Entry
->Title
: FileName
, Entry
->VolName
);
623 SubScreen
->TitleImage
= Entry
->me
.Image
;
625 SubEntry
= InitializeLoaderEntry(Entry
);
626 if (SubEntry
!= NULL
) {
627 SubEntry
->me
.Title
= StrDuplicate(L
"Boot using default options");
628 MainOptions
= SubEntry
->LoadOptions
;
629 SubEntry
->LoadOptions
= AddInitrdToOptions(MainOptions
, SubEntry
->InitrdPath
);
630 MyFreePool(MainOptions
);
631 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
632 } // if (SubEntry != NULL)
633 SubScreen
->Hint1
= StrDuplicate(SUBSCREEN_HINT1
);
634 if (GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_EDITOR
) {
635 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2_NO_EDITOR
);
637 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2
);
639 } // if (SubScreen != NULL)
640 } else { // existing subscreen; less initialization, and just add new entry later....
641 SubScreen
= Entry
->me
.SubScreen
;
644 } // REFIT_MENU_SCREEN *InitializeSubScreen()
646 VOID
GenerateSubScreen(LOADER_ENTRY
*Entry
, IN REFIT_VOLUME
*Volume
) {
647 REFIT_MENU_SCREEN
*SubScreen
;
648 LOADER_ENTRY
*SubEntry
;
650 CHAR16 DiagsFileName
[256];
655 // create the submenu
656 if (StrLen(Entry
->Title
) == 0) {
657 MyFreePool(Entry
->Title
);
660 SubScreen
= InitializeSubScreen(Entry
);
662 // loader-specific submenu entries
663 if (Entry
->OSType
== 'M') { // entries for Mac OS X
665 SubEntry
= InitializeLoaderEntry(Entry
);
666 if (SubEntry
!= NULL
) {
667 SubEntry
->me
.Title
= L
"Boot Mac OS X with a 64-bit kernel";
668 SubEntry
->LoadOptions
= L
"arch=x86_64";
669 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
670 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
673 SubEntry
= InitializeLoaderEntry(Entry
);
674 if (SubEntry
!= NULL
) {
675 SubEntry
->me
.Title
= L
"Boot Mac OS X with a 32-bit kernel";
676 SubEntry
->LoadOptions
= L
"arch=i386";
677 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
678 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
682 if (!(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_SINGLEUSER
)) {
683 SubEntry
= InitializeLoaderEntry(Entry
);
684 if (SubEntry
!= NULL
) {
685 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode";
686 SubEntry
->UseGraphicsMode
= FALSE
;
687 SubEntry
->LoadOptions
= L
"-v";
688 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
692 SubEntry
= InitializeLoaderEntry(Entry
);
693 if (SubEntry
!= NULL
) {
694 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode (64-bit)";
695 SubEntry
->UseGraphicsMode
= FALSE
;
696 SubEntry
->LoadOptions
= L
"-v arch=x86_64";
697 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
700 SubEntry
= InitializeLoaderEntry(Entry
);
701 if (SubEntry
!= NULL
) {
702 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode (32-bit)";
703 SubEntry
->UseGraphicsMode
= FALSE
;
704 SubEntry
->LoadOptions
= L
"-v arch=i386";
705 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
709 SubEntry
= InitializeLoaderEntry(Entry
);
710 if (SubEntry
!= NULL
) {
711 SubEntry
->me
.Title
= L
"Boot Mac OS X in single user mode";
712 SubEntry
->UseGraphicsMode
= FALSE
;
713 SubEntry
->LoadOptions
= L
"-v -s";
714 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
716 } // single-user mode allowed
718 if (!(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_SAFEMODE
)) {
719 SubEntry
= InitializeLoaderEntry(Entry
);
720 if (SubEntry
!= NULL
) {
721 SubEntry
->me
.Title
= L
"Boot Mac OS X in safe mode";
722 SubEntry
->UseGraphicsMode
= FALSE
;
723 SubEntry
->LoadOptions
= L
"-v -x";
724 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
726 } // safe mode allowed
728 // check for Apple hardware diagnostics
729 StrCpy(DiagsFileName
, L
"System\\Library\\CoreServices\\.diagnostics\\diags.efi");
730 if (FileExists(Volume
->RootDir
, DiagsFileName
) && !(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_HWTEST
)) {
731 SubEntry
= InitializeLoaderEntry(Entry
);
732 if (SubEntry
!= NULL
) {
733 SubEntry
->me
.Title
= L
"Run Apple Hardware Test";
734 MyFreePool(SubEntry
->LoaderPath
);
735 SubEntry
->LoaderPath
= StrDuplicate(DiagsFileName
);
736 SubEntry
->DevicePath
= FileDevicePath(Volume
->DeviceHandle
, SubEntry
->LoaderPath
);
737 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
738 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
740 } // if diagnostics entry found
742 } else if (Entry
->OSType
== 'L') { // entries for Linux kernels with EFI stub loaders
743 File
= ReadLinuxOptionsFile(Entry
->LoaderPath
, Volume
);
745 InitrdName
= FindInitrd(Entry
->LoaderPath
, Volume
);
746 TokenCount
= ReadTokenLine(File
, &TokenList
);
747 // first entry requires special processing, since it was initially set
748 // up with a default title but correct options by InitializeSubScreen(),
750 if ((SubScreen
->Entries
!= NULL
) && (SubScreen
->Entries
[0] != NULL
)) {
751 MyFreePool(SubScreen
->Entries
[0]->Title
);
752 SubScreen
->Entries
[0]->Title
= TokenList
[0] ? StrDuplicate(TokenList
[0]) : StrDuplicate(L
"Boot Linux");
754 FreeTokenLine(&TokenList
, &TokenCount
);
755 while ((TokenCount
= ReadTokenLine(File
, &TokenList
)) > 1) {
756 SubEntry
= InitializeLoaderEntry(Entry
);
757 SubEntry
->me
.Title
= TokenList
[0] ? StrDuplicate(TokenList
[0]) : StrDuplicate(L
"Boot Linux");
758 MyFreePool(SubEntry
->LoadOptions
);
759 SubEntry
->LoadOptions
= AddInitrdToOptions(TokenList
[1], InitrdName
);
760 FreeTokenLine(&TokenList
, &TokenCount
);
761 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_LINUX
;
762 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
764 MyFreePool(InitrdName
);
768 } else if (Entry
->OSType
== 'E') { // entries for ELILO
769 SubEntry
= InitializeLoaderEntry(Entry
);
770 if (SubEntry
!= NULL
) {
771 SubEntry
->me
.Title
= L
"Run ELILO in interactive mode";
772 SubEntry
->LoadOptions
= L
"-p";
773 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
774 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
777 SubEntry
= InitializeLoaderEntry(Entry
);
778 if (SubEntry
!= NULL
) {
779 SubEntry
->me
.Title
= L
"Boot Linux for a 17\" iMac or a 15\" MacBook Pro (*)";
780 SubEntry
->UseGraphicsMode
= TRUE
;
781 SubEntry
->LoadOptions
= L
"-d 0 i17";
782 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
783 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
786 SubEntry
= InitializeLoaderEntry(Entry
);
787 if (SubEntry
!= NULL
) {
788 SubEntry
->me
.Title
= L
"Boot Linux for a 20\" iMac (*)";
789 SubEntry
->UseGraphicsMode
= TRUE
;
790 SubEntry
->LoadOptions
= L
"-d 0 i20";
791 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
792 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
795 SubEntry
= InitializeLoaderEntry(Entry
);
796 if (SubEntry
!= NULL
) {
797 SubEntry
->me
.Title
= L
"Boot Linux for a Mac Mini (*)";
798 SubEntry
->UseGraphicsMode
= TRUE
;
799 SubEntry
->LoadOptions
= L
"-d 0 mini";
800 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
801 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
804 AddMenuInfoLine(SubScreen
, L
"NOTE: This is an example. Entries");
805 AddMenuInfoLine(SubScreen
, L
"marked with (*) may not work.");
807 } else if (Entry
->OSType
== 'X') { // entries for xom.efi
808 // by default, skip the built-in selection and boot from hard disk only
809 Entry
->LoadOptions
= L
"-s -h";
811 SubEntry
= InitializeLoaderEntry(Entry
);
812 if (SubEntry
!= NULL
) {
813 SubEntry
->me
.Title
= L
"Boot Windows from Hard Disk";
814 SubEntry
->LoadOptions
= L
"-s -h";
815 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
816 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
819 SubEntry
= InitializeLoaderEntry(Entry
);
820 if (SubEntry
!= NULL
) {
821 SubEntry
->me
.Title
= L
"Boot Windows from CD-ROM";
822 SubEntry
->LoadOptions
= L
"-s -c";
823 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
824 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
827 SubEntry
= InitializeLoaderEntry(Entry
);
828 if (SubEntry
!= NULL
) {
829 SubEntry
->me
.Title
= L
"Run XOM in text mode";
830 SubEntry
->UseGraphicsMode
= FALSE
;
831 SubEntry
->LoadOptions
= L
"-v";
832 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
833 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
835 } // entries for xom.efi
836 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
837 Entry
->me
.SubScreen
= SubScreen
;
838 } // VOID GenerateSubScreen()
840 // Returns options for a Linux kernel. Reads them from an options file in the
841 // kernel's directory; and if present, adds an initrd= option for an initial
842 // RAM disk file with the same version number as the kernel file.
843 static CHAR16
* GetMainLinuxOptions(IN CHAR16
* LoaderPath
, IN REFIT_VOLUME
*Volume
) {
844 CHAR16
*Options
= NULL
, *InitrdName
, *FullOptions
= NULL
;
846 Options
= GetFirstOptionsFromFile(LoaderPath
, Volume
);
847 InitrdName
= FindInitrd(LoaderPath
, Volume
);
848 FullOptions
= AddInitrdToOptions(Options
, InitrdName
);
851 MyFreePool(InitrdName
);
852 return (FullOptions
);
853 } // static CHAR16 * GetMainLinuxOptions()
855 // Try to guess the name of the Linux distribution & add that name to
857 static VOID
GuessLinuxDistribution(CHAR16
**OSIconName
, REFIT_VOLUME
*Volume
, CHAR16
*LoaderPath
) {
861 UINTN TokenCount
= 0;
863 // If on Linux root fs, /etc/os-release file probably has clues....
864 if (FileExists(Volume
->RootDir
, L
"etc\\os-release") &&
865 (ReadFile(Volume
->RootDir
, L
"etc\\os-release", &File
, &FileSize
) == EFI_SUCCESS
)) {
867 TokenCount
= ReadTokenLine(&File
, &TokenList
);
868 if ((TokenCount
> 1) && ((StriCmp(TokenList
[0], L
"ID") == 0) || (StriCmp(TokenList
[0], L
"NAME") == 0))) {
869 MergeStrings(OSIconName
, TokenList
[1], L
',');
871 FreeTokenLine(&TokenList
, &TokenCount
);
872 } while (TokenCount
> 0);
873 MyFreePool(File
.Buffer
);
876 // Search for clues in the kernel's filename....
877 if (StriSubCmp(L
".fc", LoaderPath
))
878 MergeStrings(OSIconName
, L
"fedora", L
',');
879 if (StriSubCmp(L
".el", LoaderPath
))
880 MergeStrings(OSIconName
, L
"redhat", L
',');
881 } // VOID GuessLinuxDistribution()
883 // Sets a few defaults for a loader entry -- mainly the icon, but also the OS type
884 // code and shortcut letter. For Linux EFI stub loaders, also sets kernel options
885 // that will (with luck) work fairly automatically.
886 VOID
SetLoaderDefaults(LOADER_ENTRY
*Entry
, CHAR16
*LoaderPath
, REFIT_VOLUME
*Volume
) {
887 CHAR16
*FileName
, *PathOnly
, *NoExtension
, *OSIconName
= NULL
, *Temp
, *SubString
;
888 CHAR16 ShortcutLetter
= 0;
891 FileName
= Basename(LoaderPath
);
892 PathOnly
= FindPath(LoaderPath
);
893 NoExtension
= StripEfiExtension(FileName
);
895 // locate a custom icon for the loader
896 // Anything found here takes precedence over the "hints" in the OSIconName variable
897 if (!Entry
->me
.Image
)
898 Entry
->me
.Image
= egLoadIconAnyType(Volume
->RootDir
, PathOnly
, NoExtension
, 128);
899 if (!Entry
->me
.Image
)
900 Entry
->me
.Image
= egCopyImage(Volume
->VolIconImage
);
902 // Begin creating icon "hints" by using last part of directory path leading
904 Temp
= FindLastDirName(LoaderPath
);
905 MergeStrings(&OSIconName
, Temp
, L
',');
908 if (OSIconName
!= NULL
) {
909 ShortcutLetter
= OSIconName
[0];
912 // Add every "word" in the volume label, delimited by spaces, dashes (-), or
913 // underscores (_), to the list of hints to be used in searching for OS
915 if ((Volume
->VolName
) && (StrLen(Volume
->VolName
) > 0)) {
916 Temp
= SubString
= StrDuplicate(Volume
->VolName
);
918 Length
= StrLen(Temp
);
919 for (i
= 0; i
< Length
; i
++) {
920 if ((Temp
[i
] == L
' ') || (Temp
[i
] == L
'_') || (Temp
[i
] == L
'-')) {
922 if (StrLen(SubString
) > 0)
923 MergeStrings(&OSIconName
, SubString
, L
',');
924 SubString
= Temp
+ i
+ 1;
927 MergeStrings(&OSIconName
, SubString
, L
',');
932 // detect specific loaders
933 if (StriSubCmp(L
"bzImage", LoaderPath
) || StriSubCmp(L
"vmlinuz", LoaderPath
)) {
934 GuessLinuxDistribution(&OSIconName
, Volume
, LoaderPath
);
935 MergeStrings(&OSIconName
, L
"linux", L
',');
937 if (ShortcutLetter
== 0)
938 ShortcutLetter
= 'L';
939 Entry
->LoadOptions
= GetMainLinuxOptions(LoaderPath
, Volume
);
940 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_LINUX
;
941 } else if (StriSubCmp(L
"refit", LoaderPath
)) {
942 MergeStrings(&OSIconName
, L
"refit", L
',');
944 ShortcutLetter
= 'R';
945 } else if (StriSubCmp(L
"refind", LoaderPath
)) {
946 MergeStrings(&OSIconName
, L
"refind", L
',');
948 ShortcutLetter
= 'R';
949 } else if (StriCmp(LoaderPath
, MACOSX_LOADER_PATH
) == 0) {
950 if (Volume
->VolIconImage
!= NULL
) { // custom icon file found
951 Entry
->me
.Image
= Volume
->VolIconImage
;
953 MergeStrings(&OSIconName
, L
"mac", L
',');
955 ShortcutLetter
= 'M';
956 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
957 } else if (StriCmp(FileName
, L
"diags.efi") == 0) {
958 MergeStrings(&OSIconName
, L
"hwtest", L
',');
959 } else if (StriCmp(FileName
, L
"e.efi") == 0 || StriCmp(FileName
, L
"elilo.efi") == 0 || StriSubCmp(L
"elilo", FileName
)) {
960 MergeStrings(&OSIconName
, L
"elilo,linux", L
',');
962 if (ShortcutLetter
== 0)
963 ShortcutLetter
= 'L';
964 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
965 } else if (StriSubCmp(L
"grub", FileName
)) {
967 ShortcutLetter
= 'G';
968 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_GRUB
;
969 } else if (StriCmp(FileName
, L
"cdboot.efi") == 0 ||
970 StriCmp(FileName
, L
"bootmgr.efi") == 0 ||
971 StriCmp(FileName
, L
"bootmgfw.efi") == 0 ||
972 StriCmp(FileName
, L
"bkpbootmgfw.efi") == 0) {
973 MergeStrings(&OSIconName
, L
"win", L
',');
975 ShortcutLetter
= 'W';
976 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
977 } else if (StriCmp(FileName
, L
"xom.efi") == 0) {
978 MergeStrings(&OSIconName
, L
"xom,win", L
',');
979 Entry
->UseGraphicsMode
= TRUE
;
981 ShortcutLetter
= 'W';
982 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
985 if ((ShortcutLetter
>= 'a') && (ShortcutLetter
<= 'z'))
986 ShortcutLetter
= ShortcutLetter
- 'a' + 'A'; // convert lowercase to uppercase
987 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
988 if (Entry
->me
.Image
== NULL
)
989 Entry
->me
.Image
= LoadOSIcon(OSIconName
, L
"unknown", FALSE
);
990 MyFreePool(PathOnly
);
991 } // VOID SetLoaderDefaults()
993 // Add a specified EFI boot loader to the list, using automatic settings
994 // for icons, options, etc.
995 LOADER_ENTRY
* AddLoaderEntry(IN CHAR16
*LoaderPath
, IN CHAR16
*LoaderTitle
, IN REFIT_VOLUME
*Volume
) {
998 CleanUpPathNameSlashes(LoaderPath
);
999 Entry
= InitializeLoaderEntry(NULL
);
1000 if (Entry
!= NULL
) {
1001 Entry
->Title
= StrDuplicate((LoaderTitle
!= NULL
) ? LoaderTitle
: LoaderPath
);
1002 Entry
->me
.Title
= AllocateZeroPool(sizeof(CHAR16
) * 256);
1003 SPrint(Entry
->me
.Title
, 255, L
"Boot %s from %s ", (LoaderTitle
!= NULL
) ? LoaderTitle
: LoaderPath
, Volume
->VolName
);
1005 Entry
->me
.BadgeImage
= Volume
->VolBadgeImage
;
1006 if ((LoaderPath
!= NULL
) && (LoaderPath
[0] != L
'\\')) {
1007 Entry
->LoaderPath
= StrDuplicate(L
"\\");
1009 Entry
->LoaderPath
= NULL
;
1011 MergeStrings(&(Entry
->LoaderPath
), LoaderPath
, 0);
1012 Entry
->VolName
= Volume
->VolName
;
1013 Entry
->DevicePath
= FileDevicePath(Volume
->DeviceHandle
, Entry
->LoaderPath
);
1014 SetLoaderDefaults(Entry
, LoaderPath
, Volume
);
1015 GenerateSubScreen(Entry
, Volume
);
1016 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1020 } // LOADER_ENTRY * AddLoaderEntry()
1022 // Returns -1 if (Time1 < Time2), +1 if (Time1 > Time2), or 0 if
1023 // (Time1 == Time2). Precision is only to the nearest second; since
1024 // this is used for sorting boot loader entries, differences smaller
1025 // than this are likely to be meaningless (and unlikely!).
1026 INTN
TimeComp(IN EFI_TIME
*Time1
, IN EFI_TIME
*Time2
) {
1027 INT64 Time1InSeconds
, Time2InSeconds
;
1029 // Following values are overestimates; I'm assuming 31 days in every month.
1030 // This is fine for the purpose of this function, which is limited
1031 Time1InSeconds
= Time1
->Second
+ (Time1
->Minute
* 60) + (Time1
->Hour
* 3600) + (Time1
->Day
* 86400) +
1032 (Time1
->Month
* 2678400) + ((Time1
->Year
- 1998) * 32140800);
1033 Time2InSeconds
= Time2
->Second
+ (Time2
->Minute
* 60) + (Time2
->Hour
* 3600) + (Time2
->Day
* 86400) +
1034 (Time2
->Month
* 2678400) + ((Time2
->Year
- 1998) * 32140800);
1035 if (Time1InSeconds
< Time2InSeconds
)
1037 else if (Time1InSeconds
> Time2InSeconds
)
1041 } // INTN TimeComp()
1043 // Adds a loader list element, keeping it sorted by date. Returns the new
1044 // first element (the one with the most recent date).
1045 static struct LOADER_LIST
* AddLoaderListEntry(struct LOADER_LIST
*LoaderList
, struct LOADER_LIST
*NewEntry
) {
1046 struct LOADER_LIST
*LatestEntry
, *CurrentEntry
, *PrevEntry
= NULL
;
1048 LatestEntry
= CurrentEntry
= LoaderList
;
1049 if (LoaderList
== NULL
) {
1050 LatestEntry
= NewEntry
;
1052 while ((CurrentEntry
!= NULL
) && (TimeComp(&(NewEntry
->TimeStamp
), &(CurrentEntry
->TimeStamp
)) < 0)) {
1053 PrevEntry
= CurrentEntry
;
1054 CurrentEntry
= CurrentEntry
->NextEntry
;
1056 NewEntry
->NextEntry
= CurrentEntry
;
1057 if (PrevEntry
== NULL
) {
1058 LatestEntry
= NewEntry
;
1060 PrevEntry
->NextEntry
= NewEntry
;
1063 return (LatestEntry
);
1064 } // static VOID AddLoaderListEntry()
1066 // Delete the LOADER_LIST linked list
1067 static VOID
CleanUpLoaderList(struct LOADER_LIST
*LoaderList
) {
1068 struct LOADER_LIST
*Temp
;
1070 while (LoaderList
!= NULL
) {
1072 LoaderList
= LoaderList
->NextEntry
;
1073 MyFreePool(Temp
->FileName
);
1076 } // static VOID CleanUpLoaderList()
1078 // Returns FALSE if the specified file/volume matches the GlobalConfig.DontScanDirs
1079 // or GlobalConfig.DontScanVolumes specification, or if Path points to a volume
1080 // other than the one specified by Volume, or if the specified path is SelfDir.
1081 // Returns TRUE if none of these conditions is met -- that is, if the path is
1082 // eligible for scanning.
1083 static BOOLEAN
ShouldScan(REFIT_VOLUME
*Volume
, CHAR16
*Path
) {
1084 CHAR16
*VolName
= NULL
, *DontScanDir
, *PathCopy
= NULL
;
1085 UINTN i
= 0, VolNum
;
1086 BOOLEAN ScanIt
= TRUE
;
1088 if (IsIn(Volume
->VolName
, GlobalConfig
.DontScanVolumes
))
1091 if ((StriCmp(Path
, SelfDirPath
) == 0) && (Volume
->DeviceHandle
== SelfVolume
->DeviceHandle
))
1094 // See if Path includes an explicit volume declaration that's NOT Volume....
1095 PathCopy
= StrDuplicate(Path
);
1096 if (SplitVolumeAndFilename(&PathCopy
, &VolName
)) {
1097 if (StriCmp(VolName
, Volume
->VolName
) != 0) {
1098 if ((StrLen(VolName
) > 2) && (VolName
[0] == L
'f') && (VolName
[1] == L
's') && (VolName
[2] >= L
'0') && (VolName
[2] <= L
'9')) {
1099 VolNum
= Atoi(VolName
+ 2);
1100 if (VolNum
!= Volume
->VolNumber
) {
1107 } // if Path includes volume specification
1108 MyFreePool(PathCopy
);
1109 MyFreePool(VolName
);
1112 // See if Volume is in GlobalConfig.DontScanDirs....
1113 while ((DontScanDir
= FindCommaDelimited(GlobalConfig
.DontScanDirs
, i
++)) && ScanIt
) {
1114 SplitVolumeAndFilename(&DontScanDir
, &VolName
);
1115 CleanUpPathNameSlashes(DontScanDir
);
1116 if (VolName
!= NULL
) {
1117 if ((StriCmp(VolName
, Volume
->VolName
) == 0) && (StriCmp(DontScanDir
, Path
) == 0))
1119 if ((StrLen(VolName
) > 2) && (VolName
[0] == L
'f') && (VolName
[1] == L
's') && (VolName
[2] >= L
'0') && (VolName
[2] <= L
'9')) {
1120 VolNum
= Atoi(VolName
+ 2);
1121 if ((VolNum
== Volume
->VolNumber
) && (StriCmp(DontScanDir
, Path
) == 0))
1125 if (StriCmp(DontScanDir
, Path
) == 0)
1128 MyFreePool(DontScanDir
);
1129 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 // Scan an individual directory for EFI boot loader files and, if found,
1232 // add them to the list. Exception: Ignores FALLBACK_FULLNAME, which is picked
1233 // up in ScanEfiFiles(). Sorts the entries within the loader directory so that
1234 // the most recent one appears first in the list.
1235 // Returns TRUE if a duplicate for FALLBACK_FILENAME was found, FALSE if not.
1236 static BOOLEAN
ScanLoaderDir(IN REFIT_VOLUME
*Volume
, IN CHAR16
*Path
, IN CHAR16
*Pattern
)
1239 REFIT_DIR_ITER DirIter
;
1240 EFI_FILE_INFO
*DirEntry
;
1241 CHAR16 FileName
[256], *Extension
;
1242 struct LOADER_LIST
*LoaderList
= NULL
, *NewLoader
;
1243 BOOLEAN FoundFallbackDuplicate
= FALSE
;
1245 if ((!SelfDirPath
|| !Path
|| ((StriCmp(Path
, SelfDirPath
) == 0) && (Volume
->DeviceHandle
!= SelfVolume
->DeviceHandle
)) ||
1246 (StriCmp(Path
, SelfDirPath
) != 0)) &&
1247 (ShouldScan(Volume
, Path
))) {
1248 // look through contents of the directory
1249 DirIterOpen(Volume
->RootDir
, Path
, &DirIter
);
1250 while (DirIterNext(&DirIter
, 2, Pattern
, &DirEntry
)) {
1251 Extension
= FindExtension(DirEntry
->FileName
);
1252 if (DirEntry
->FileName
[0] == '.' ||
1253 StriCmp(Extension
, L
".icns") == 0 ||
1254 StriCmp(Extension
, L
".png") == 0 ||
1255 (StriCmp(DirEntry
->FileName
, FALLBACK_BASENAME
) == 0 && (StriCmp(Path
, L
"EFI\\BOOT") == 0)) ||
1256 StriSubCmp(L
"shell", DirEntry
->FileName
) ||
1257 IsSymbolicLink(Volume
, Path
, DirEntry
) || /* is symbolic link */
1258 IsIn(DirEntry
->FileName
, GlobalConfig
.DontScanFiles
))
1259 continue; // skip this
1262 SPrint(FileName
, 255, L
"\\%s\\%s", Path
, DirEntry
->FileName
);
1264 SPrint(FileName
, 255, L
"\\%s", DirEntry
->FileName
);
1265 CleanUpPathNameSlashes(FileName
);
1267 if(!IsValidLoader(Volume
->RootDir
, FileName
))
1270 NewLoader
= AllocateZeroPool(sizeof(struct LOADER_LIST
));
1271 if (NewLoader
!= NULL
) {
1272 NewLoader
->FileName
= StrDuplicate(FileName
);
1273 NewLoader
->TimeStamp
= DirEntry
->ModificationTime
;
1274 LoaderList
= AddLoaderListEntry(LoaderList
, NewLoader
);
1275 if (DuplicatesFallback(Volume
, FileName
))
1276 FoundFallbackDuplicate
= TRUE
;
1278 MyFreePool(Extension
);
1281 NewLoader
= LoaderList
;
1282 while (NewLoader
!= NULL
) {
1283 AddLoaderEntry(NewLoader
->FileName
, NULL
, Volume
);
1284 NewLoader
= NewLoader
->NextEntry
;
1287 CleanUpLoaderList(LoaderList
);
1288 Status
= DirIterClose(&DirIter
);
1289 // NOTE: EFI_INVALID_PARAMETER really is an error that should be reported;
1290 // but I've gotten reports from users who are getting this error occasionally
1291 // and I can't find anything wrong or reproduce the problem, so I'm putting
1292 // it down to buggy EFI implementations and ignoring that particular error....
1293 if ((Status
!= EFI_NOT_FOUND
) && (Status
!= EFI_INVALID_PARAMETER
)) {
1295 SPrint(FileName
, 255, L
"while scanning the %s directory", Path
);
1297 StrCpy(FileName
, L
"while scanning the root directory");
1298 CheckError(Status
, FileName
);
1299 } // if (Status != EFI_NOT_FOUND)
1300 } // if not scanning a blacklisted directory
1302 return FoundFallbackDuplicate
;
1303 } /* static VOID ScanLoaderDir() */
1305 static VOID
ScanEfiFiles(REFIT_VOLUME
*Volume
) {
1307 REFIT_DIR_ITER EfiDirIter
;
1308 EFI_FILE_INFO
*EfiDirEntry
;
1309 CHAR16 FileName
[256], *Directory
, *MatchPatterns
, *VolName
= NULL
, *SelfPath
;
1311 BOOLEAN ScanFallbackLoader
= TRUE
;
1312 BOOLEAN FoundBRBackup
= FALSE
;
1314 MatchPatterns
= StrDuplicate(LOADER_MATCH_PATTERNS
);
1315 if (GlobalConfig
.ScanAllLinux
)
1316 MergeStrings(&MatchPatterns
, LINUX_MATCH_PATTERNS
, L
',');
1318 if ((Volume
->RootDir
!= NULL
) && (Volume
->VolName
!= NULL
)) {
1319 // check for Mac OS X boot loader
1320 if (ShouldScan(Volume
, L
"System\\Library\\CoreServices")) {
1321 StrCpy(FileName
, MACOSX_LOADER_PATH
);
1322 if (FileExists(Volume
->RootDir
, FileName
) && !IsIn(L
"boot.efi", GlobalConfig
.DontScanFiles
)) {
1323 AddLoaderEntry(FileName
, L
"Mac OS X", Volume
);
1324 if (DuplicatesFallback(Volume
, FileName
))
1325 ScanFallbackLoader
= FALSE
;
1329 StrCpy(FileName
, L
"System\\Library\\CoreServices\\xom.efi");
1330 if (FileExists(Volume
->RootDir
, FileName
) && !IsIn(L
"boot.efi", GlobalConfig
.DontScanFiles
)) {
1331 AddLoaderEntry(FileName
, L
"Windows XP (XoM)", Volume
);
1332 if (DuplicatesFallback(Volume
, FileName
))
1333 ScanFallbackLoader
= FALSE
;
1335 } // if should scan Mac directory
1337 // check for Microsoft boot loader/menu
1338 if (ShouldScan(Volume
, L
"EFI\\Microsoft\\Boot")) {
1339 StrCpy(FileName
, L
"EFI\\Microsoft\\Boot\\bkpbootmgfw.efi");
1340 if (FileExists(Volume
->RootDir
, FileName
) && !IsIn(L
"bkpbootmgfw.efi", GlobalConfig
.DontScanFiles
)) {
1341 AddLoaderEntry(FileName
, L
"Microsoft EFI boot (Boot Repair backup)", Volume
);
1342 FoundBRBackup
= TRUE
;
1343 if (DuplicatesFallback(Volume
, FileName
))
1344 ScanFallbackLoader
= FALSE
;
1346 StrCpy(FileName
, L
"EFI\\Microsoft\\Boot\\bootmgfw.efi");
1347 if (FileExists(Volume
->RootDir
, FileName
) && !IsIn(L
"bootmgfw.efi", GlobalConfig
.DontScanFiles
)) {
1349 AddLoaderEntry(FileName
, L
"Supposed Microsoft EFI boot (probably GRUB)", Volume
);
1351 AddLoaderEntry(FileName
, L
"Microsoft EFI boot", Volume
);
1352 if (DuplicatesFallback(Volume
, FileName
))
1353 ScanFallbackLoader
= FALSE
;
1357 // scan the root directory for EFI executables
1358 if (ScanLoaderDir(Volume
, L
"\\", MatchPatterns
))
1359 ScanFallbackLoader
= FALSE
;
1361 // scan subdirectories of the EFI directory (as per the standard)
1362 DirIterOpen(Volume
->RootDir
, L
"EFI", &EfiDirIter
);
1363 while (DirIterNext(&EfiDirIter
, 1, NULL
, &EfiDirEntry
)) {
1364 if (StriCmp(EfiDirEntry
->FileName
, L
"tools") == 0 || EfiDirEntry
->FileName
[0] == '.')
1365 continue; // skip this, doesn't contain boot loaders or is scanned later
1366 SPrint(FileName
, 255, L
"EFI\\%s", EfiDirEntry
->FileName
);
1367 if (ScanLoaderDir(Volume
, FileName
, MatchPatterns
))
1368 ScanFallbackLoader
= FALSE
;
1370 Status
= DirIterClose(&EfiDirIter
);
1371 if (Status
!= EFI_NOT_FOUND
)
1372 CheckError(Status
, L
"while scanning the EFI directory");
1374 // Scan user-specified (or additional default) directories....
1376 while ((Directory
= FindCommaDelimited(GlobalConfig
.AlsoScan
, i
++)) != NULL
) {
1377 if (ShouldScan(Volume
, Directory
)) {
1378 SplitVolumeAndFilename(&Directory
, &VolName
);
1379 CleanUpPathNameSlashes(Directory
);
1380 Length
= StrLen(Directory
);
1381 if ((Length
> 0) && ScanLoaderDir(Volume
, Directory
, MatchPatterns
))
1382 ScanFallbackLoader
= FALSE
;
1383 MyFreePool(VolName
);
1384 } // if should scan dir
1385 MyFreePool(Directory
);
1388 // Don't scan the fallback loader if it's on the same volume and a duplicate of rEFInd itself....
1389 SelfPath
= DevicePathToStr(SelfLoadedImage
->FilePath
);
1390 CleanUpPathNameSlashes(SelfPath
);
1391 if ((Volume
->DeviceHandle
== SelfLoadedImage
->DeviceHandle
) && DuplicatesFallback(Volume
, SelfPath
))
1392 ScanFallbackLoader
= FALSE
;
1394 // If not a duplicate & if it exists & if it's not us, create an entry
1395 // for the fallback boot loader
1396 if (ScanFallbackLoader
&& FileExists(Volume
->RootDir
, FALLBACK_FULLNAME
) && ShouldScan(Volume
, L
"EFI\\BOOT"))
1397 AddLoaderEntry(FALLBACK_FULLNAME
, L
"Fallback boot loader", Volume
);
1399 } // static VOID ScanEfiFiles()
1401 // Scan internal disks for valid EFI boot loaders....
1402 static VOID
ScanInternal(VOID
) {
1405 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1406 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_INTERNAL
) {
1407 ScanEfiFiles(Volumes
[VolumeIndex
]);
1410 } // static VOID ScanInternal()
1412 // Scan external disks for valid EFI boot loaders....
1413 static VOID
ScanExternal(VOID
) {
1416 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1417 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_EXTERNAL
) {
1418 ScanEfiFiles(Volumes
[VolumeIndex
]);
1421 } // static VOID ScanExternal()
1423 // Scan internal disks for valid EFI boot loaders....
1424 static VOID
ScanOptical(VOID
) {
1427 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1428 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_OPTICAL
) {
1429 ScanEfiFiles(Volumes
[VolumeIndex
]);
1432 } // static VOID ScanOptical()
1435 // legacy boot functions
1438 static EFI_STATUS
ActivateMbrPartition(IN EFI_BLOCK_IO
*BlockIO
, IN UINTN PartitionIndex
)
1441 UINT8 SectorBuffer
[512];
1442 MBR_PARTITION_INFO
*MbrTable
, *EMbrTable
;
1443 UINT32 ExtBase
, ExtCurrent
, NextExtCurrent
;
1444 UINTN LogicalPartitionIndex
= 4;
1446 BOOLEAN HaveBootCode
;
1449 Status
= refit_call5_wrapper(BlockIO
->ReadBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, 0, 512, SectorBuffer
);
1450 if (EFI_ERROR(Status
))
1452 if (*((UINT16
*)(SectorBuffer
+ 510)) != 0xaa55)
1453 return EFI_NOT_FOUND
; // safety measure #1
1455 // add boot code if necessary
1456 HaveBootCode
= FALSE
;
1457 for (i
= 0; i
< MBR_BOOTCODE_SIZE
; i
++) {
1458 if (SectorBuffer
[i
] != 0) {
1459 HaveBootCode
= TRUE
;
1463 if (!HaveBootCode
) {
1464 // no boot code found in the MBR, add the syslinux MBR code
1465 SetMem(SectorBuffer
, MBR_BOOTCODE_SIZE
, 0);
1466 CopyMem(SectorBuffer
, syslinux_mbr
, SYSLINUX_MBR_SIZE
);
1469 // set the partition active
1470 MbrTable
= (MBR_PARTITION_INFO
*)(SectorBuffer
+ 446);
1472 for (i
= 0; i
< 4; i
++) {
1473 if (MbrTable
[i
].Flags
!= 0x00 && MbrTable
[i
].Flags
!= 0x80)
1474 return EFI_NOT_FOUND
; // safety measure #2
1475 if (i
== PartitionIndex
)
1476 MbrTable
[i
].Flags
= 0x80;
1477 else if (PartitionIndex
>= 4 && IS_EXTENDED_PART_TYPE(MbrTable
[i
].Type
)) {
1478 MbrTable
[i
].Flags
= 0x80;
1479 ExtBase
= MbrTable
[i
].StartLBA
;
1481 MbrTable
[i
].Flags
= 0x00;
1485 Status
= refit_call5_wrapper(BlockIO
->WriteBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, 0, 512, SectorBuffer
);
1486 if (EFI_ERROR(Status
))
1489 if (PartitionIndex
>= 4) {
1490 // we have to activate a logical partition, so walk the EMBR chain
1492 // NOTE: ExtBase was set above while looking at the MBR table
1493 for (ExtCurrent
= ExtBase
; ExtCurrent
; ExtCurrent
= NextExtCurrent
) {
1494 // read current EMBR
1495 Status
= refit_call5_wrapper(BlockIO
->ReadBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, ExtCurrent
, 512, SectorBuffer
);
1496 if (EFI_ERROR(Status
))
1498 if (*((UINT16
*)(SectorBuffer
+ 510)) != 0xaa55)
1499 return EFI_NOT_FOUND
; // safety measure #3
1501 // scan EMBR, set appropriate partition active
1502 EMbrTable
= (MBR_PARTITION_INFO
*)(SectorBuffer
+ 446);
1504 for (i
= 0; i
< 4; i
++) {
1505 if (EMbrTable
[i
].Flags
!= 0x00 && EMbrTable
[i
].Flags
!= 0x80)
1506 return EFI_NOT_FOUND
; // safety measure #4
1507 if (EMbrTable
[i
].StartLBA
== 0 || EMbrTable
[i
].Size
== 0)
1509 if (IS_EXTENDED_PART_TYPE(EMbrTable
[i
].Type
)) {
1510 // link to next EMBR
1511 NextExtCurrent
= ExtBase
+ EMbrTable
[i
].StartLBA
;
1512 EMbrTable
[i
].Flags
= (PartitionIndex
>= LogicalPartitionIndex
) ? 0x80 : 0x00;
1515 // logical partition
1516 EMbrTable
[i
].Flags
= (PartitionIndex
== LogicalPartitionIndex
) ? 0x80 : 0x00;
1517 LogicalPartitionIndex
++;
1521 // write current EMBR
1522 Status
= refit_call5_wrapper(BlockIO
->WriteBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, ExtCurrent
, 512, SectorBuffer
);
1523 if (EFI_ERROR(Status
))
1526 if (PartitionIndex
< LogicalPartitionIndex
)
1527 break; // stop the loop, no need to touch further EMBRs
1533 } /* static EFI_STATUS ActivateMbrPartition() */
1535 // early 2006 Core Duo / Core Solo models
1536 static UINT8 LegacyLoaderDevicePath1Data
[] = {
1537 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1538 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1539 0xFF, 0xFF, 0xF9, 0xFF, 0x00, 0x00, 0x00, 0x00,
1540 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1541 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1542 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1544 // mid-2006 Mac Pro (and probably other Core 2 models)
1545 static UINT8 LegacyLoaderDevicePath2Data
[] = {
1546 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1547 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1548 0xFF, 0xFF, 0xF7, 0xFF, 0x00, 0x00, 0x00, 0x00,
1549 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1550 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1551 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1553 // mid-2007 MBP ("Santa Rosa" based models)
1554 static UINT8 LegacyLoaderDevicePath3Data
[] = {
1555 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1556 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1557 0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
1558 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1559 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1560 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1563 static UINT8 LegacyLoaderDevicePath4Data
[] = {
1564 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1565 0x00, 0x00, 0xC0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1566 0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
1567 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1568 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1569 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1571 // late-2008 MB/MBP (NVidia chipset)
1572 static UINT8 LegacyLoaderDevicePath5Data
[] = {
1573 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1574 0x00, 0x40, 0xCB, 0xFF, 0x00, 0x00, 0x00, 0x00,
1575 0xFF, 0xBF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
1576 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1577 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1578 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1581 static EFI_DEVICE_PATH
*LegacyLoaderList
[] = {
1582 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath1Data
,
1583 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath2Data
,
1584 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath3Data
,
1585 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath4Data
,
1586 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath5Data
,
1590 #define MAX_DISCOVERED_PATHS (16)
1592 static VOID
StartLegacy(IN LEGACY_ENTRY
*Entry
)
1595 EG_IMAGE
*BootLogoImage
;
1596 UINTN ErrorInStep
= 0;
1597 EFI_DEVICE_PATH
*DiscoveredPathList
[MAX_DISCOVERED_PATHS
];
1599 BeginExternalScreen(TRUE
, L
"Booting Legacy OS (Mac mode)");
1601 BootLogoImage
= LoadOSIcon(Entry
->Volume
->OSIconName
, L
"legacy", TRUE
);
1602 if (BootLogoImage
!= NULL
)
1603 BltImageAlpha(BootLogoImage
,
1604 (UGAWidth
- BootLogoImage
->Width
) >> 1,
1605 (UGAHeight
- BootLogoImage
->Height
) >> 1,
1606 &StdBackgroundPixel
);
1608 if (Entry
->Volume
->IsMbrPartition
) {
1609 ActivateMbrPartition(Entry
->Volume
->WholeDiskBlockIO
, Entry
->Volume
->MbrPartitionIndex
);
1612 ExtractLegacyLoaderPaths(DiscoveredPathList
, MAX_DISCOVERED_PATHS
, LegacyLoaderList
);
1614 Status
= StartEFIImageList(DiscoveredPathList
, Entry
->LoadOptions
, TYPE_LEGACY
, L
"legacy loader", 0, &ErrorInStep
, TRUE
);
1615 if (Status
== EFI_NOT_FOUND
) {
1616 if (ErrorInStep
== 1) {
1617 Print(L
"\nPlease make sure that you have the latest firmware update installed.\n");
1618 } else if (ErrorInStep
== 3) {
1619 Print(L
"\nThe firmware refused to boot from the selected volume. Note that external\n"
1620 L
"hard drives are not well-supported by Apple's firmware for legacy OS booting.\n");
1623 FinishExternalScreen();
1624 } /* static VOID StartLegacy() */
1626 // Start a device on a non-Mac using the EFI_LEGACY_BIOS_PROTOCOL
1627 #ifdef __MAKEWITH_TIANO
1628 static VOID
StartLegacyUEFI(LEGACY_ENTRY
*Entry
)
1630 UINTN ExitDataSize
= 0;
1631 CHAR16
*ExitData
= NULL
;
1634 BeginExternalScreen(TRUE
, L
"Booting Legacy OS (UEFI mode)");
1635 Print(L
"Launching from '%s'\n", DevicePathToStr(Entry
->BdsOption
->DevicePath
));
1638 // Status = BdsLibBootViaBootOption(Entry->BdsOption, Entry->BdsOption->DevicePath, &ExitDataSize, &ExitData);
1639 // Print(L"BdsLibBootViaBootOption() returned %d\n", Status);
1640 BdsLibConnectDevicePath (Entry
->BdsOption
->DevicePath
);
1641 BdsLibDoLegacyBoot(Entry
->BdsOption
);
1643 // If we get here, it means that there was a failure....
1644 Print(L
"Failure booting legacy (BIOS) OS.");
1646 FinishExternalScreen();
1647 } // static VOID StartLegacyUEFI()
1648 #endif // __MAKEWITH_TIANO
1650 static LEGACY_ENTRY
* AddLegacyEntry(IN CHAR16
*LoaderTitle
, IN REFIT_VOLUME
*Volume
)
1652 LEGACY_ENTRY
*Entry
, *SubEntry
;
1653 REFIT_MENU_SCREEN
*SubScreen
;
1655 CHAR16 ShortcutLetter
= 0;
1657 if (LoaderTitle
== NULL
) {
1658 if (Volume
->OSName
!= NULL
) {
1659 LoaderTitle
= Volume
->OSName
;
1660 if (LoaderTitle
[0] == 'W' || LoaderTitle
[0] == 'L')
1661 ShortcutLetter
= LoaderTitle
[0];
1663 LoaderTitle
= L
"Legacy OS";
1665 if (Volume
->VolName
!= NULL
)
1666 VolDesc
= Volume
->VolName
;
1668 VolDesc
= (Volume
->DiskKind
== DISK_KIND_OPTICAL
) ? L
"CD" : L
"HD";
1670 // prepare the menu entry
1671 Entry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1672 Entry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1673 SPrint(Entry
->me
.Title
, 255, L
"Boot %s from %s", LoaderTitle
, VolDesc
);
1674 Entry
->me
.Tag
= TAG_LEGACY
;
1676 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1677 Entry
->me
.Image
= LoadOSIcon(Volume
->OSIconName
, L
"legacy", FALSE
);
1678 Entry
->me
.BadgeImage
= Volume
->VolBadgeImage
;
1679 Entry
->Volume
= Volume
;
1680 Entry
->LoadOptions
= (Volume
->DiskKind
== DISK_KIND_OPTICAL
) ? L
"CD" :
1681 ((Volume
->DiskKind
== DISK_KIND_EXTERNAL
) ? L
"USB" : L
"HD");
1682 Entry
->Enabled
= TRUE
;
1684 // create the submenu
1685 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
1686 SubScreen
->Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1687 SPrint(SubScreen
->Title
, 255, L
"Boot Options for %s on %s", LoaderTitle
, VolDesc
);
1688 SubScreen
->TitleImage
= Entry
->me
.Image
;
1689 SubScreen
->Hint1
= StrDuplicate(SUBSCREEN_HINT1
);
1690 if (GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_EDITOR
) {
1691 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2_NO_EDITOR
);
1693 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2
);
1697 SubEntry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1698 SubEntry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1699 SPrint(SubEntry
->me
.Title
, 255, L
"Boot %s", LoaderTitle
);
1700 SubEntry
->me
.Tag
= TAG_LEGACY
;
1701 SubEntry
->Volume
= Entry
->Volume
;
1702 SubEntry
->LoadOptions
= Entry
->LoadOptions
;
1703 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
1705 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
1706 Entry
->me
.SubScreen
= SubScreen
;
1707 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1709 } /* static LEGACY_ENTRY * AddLegacyEntry() */
1712 #ifdef __MAKEWITH_GNUEFI
1713 static VOID
ScanLegacyUEFI(IN UINTN DiskType
){}
1715 // default volume badge icon based on disk kind
1716 static EG_IMAGE
* GetDiskBadge(IN UINTN DiskType
) {
1717 EG_IMAGE
* Badge
= NULL
;
1721 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_INTERNAL
);
1724 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_EXTERNAL
);
1727 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_OPTICAL
);
1731 } // static EG_IMAGE * GetDiskBadge()
1734 Create a rEFInd boot option from a Legacy BIOS protocol option.
1736 static LEGACY_ENTRY
* AddLegacyEntryUEFI(BDS_COMMON_OPTION
*BdsOption
, IN UINT16 DiskType
)
1738 LEGACY_ENTRY
*Entry
, *SubEntry
;
1739 REFIT_MENU_SCREEN
*SubScreen
;
1740 CHAR16 ShortcutLetter
= 0;
1741 CHAR16
*LegacyDescription
= BdsOption
->Description
;
1743 // prepare the menu entry
1744 Entry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1745 Entry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1746 SPrint(Entry
->me
.Title
, 255, L
"Boot legacy target %s", LegacyDescription
);
1747 Entry
->me
.Tag
= TAG_LEGACY_UEFI
;
1749 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1750 Entry
->me
.Image
= LoadOSIcon(L
"legacy", L
"legacy", TRUE
);
1751 Entry
->LoadOptions
= (DiskType
== BBS_CDROM
) ? L
"CD" :
1752 ((DiskType
== BBS_USB
) ? L
"USB" : L
"HD");
1753 Entry
->me
.BadgeImage
= GetDiskBadge(DiskType
);
1754 Entry
->BdsOption
= BdsOption
;
1755 Entry
->Enabled
= TRUE
;
1757 // create the submenu
1758 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
1759 SubScreen
->Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1760 SPrint(SubScreen
->Title
, 255, L
"No boot options for legacy target");
1761 SubScreen
->TitleImage
= Entry
->me
.Image
;
1762 SubScreen
->Hint1
= StrDuplicate(SUBSCREEN_HINT1
);
1763 if (GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_EDITOR
) {
1764 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2_NO_EDITOR
);
1766 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2
);
1770 SubEntry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1771 SubEntry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1772 SPrint(SubEntry
->me
.Title
, 255, L
"Boot %s", LegacyDescription
);
1773 SubEntry
->me
.Tag
= TAG_LEGACY_UEFI
;
1774 Entry
->BdsOption
= BdsOption
;
1775 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
1777 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
1778 Entry
->me
.SubScreen
= SubScreen
;
1779 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1781 } /* static LEGACY_ENTRY * AddLegacyEntryUEFI() */
1784 Scan for legacy BIOS targets on machines that implement EFI_LEGACY_BIOS_PROTOCOL.
1785 In testing, protocol has not been implemented on Macs but has been
1786 implemented on several Dell PCs and an ASUS motherboard.
1787 Restricts output to disks of the specified DiskType.
1789 static VOID
ScanLegacyUEFI(IN UINTN DiskType
)
1792 EFI_LEGACY_BIOS_PROTOCOL
*LegacyBios
;
1793 UINT16
*BootOrder
= NULL
;
1795 CHAR16 BootOption
[10];
1796 UINTN BootOrderSize
= 0;
1798 BDS_COMMON_OPTION
*BdsOption
;
1799 LIST_ENTRY TempList
;
1800 BBS_BBS_DEVICE_PATH
* BbsDevicePath
= NULL
;
1802 InitializeListHead (&TempList
);
1803 ZeroMem (Buffer
, sizeof (Buffer
));
1805 // If LegacyBios protocol is not implemented on this platform, then
1806 //we do not support this type of legacy boot on this machine.
1807 Status
= gBS
->LocateProtocol(&gEfiLegacyBootProtocolGuid
, NULL
, (VOID
**) &LegacyBios
);
1808 if (EFI_ERROR (Status
))
1811 // Grab the boot order
1812 BootOrder
= BdsLibGetVariableAndSize(L
"BootOrder", &gEfiGlobalVariableGuid
, &BootOrderSize
);
1813 if (BootOrder
== NULL
) {
1818 while (Index
< BootOrderSize
/ sizeof (UINT16
))
1820 // Grab each boot option variable from the boot order, and convert
1821 // the variable into a BDS boot option
1822 UnicodeSPrint (BootOption
, sizeof (BootOption
), L
"Boot%04x", BootOrder
[Index
]);
1823 BdsOption
= BdsLibVariableToOption (&TempList
, BootOption
);
1825 if (BdsOption
!= NULL
) {
1826 BbsDevicePath
= (BBS_BBS_DEVICE_PATH
*)BdsOption
->DevicePath
;
1828 // Only add the entry if it is of a requested type (e.g. USB, HD)
1830 // Two checks necessary because some systems return EFI boot loaders
1831 // with a DeviceType value that would inappropriately include them
1832 // as legacy loaders....
1833 if ((BbsDevicePath
->DeviceType
== DiskType
) && (BdsOption
->DevicePath
->Type
== DEVICE_TYPE_BIOS
)) {
1834 AddLegacyEntryUEFI(BdsOption
, BbsDevicePath
->DeviceType
);
1839 } /* static VOID ScanLegacyUEFI() */
1840 #endif // __MAKEWITH_GNUEFI
1842 static VOID
ScanLegacyVolume(REFIT_VOLUME
*Volume
, UINTN VolumeIndex
) {
1844 BOOLEAN ShowVolume
, HideIfOthersFound
;
1847 HideIfOthersFound
= FALSE
;
1848 if (Volume
->IsAppleLegacy
) {
1850 HideIfOthersFound
= TRUE
;
1851 } else if (Volume
->HasBootCode
) {
1853 if (Volume
->BlockIO
== Volume
->WholeDiskBlockIO
&&
1854 Volume
->BlockIOOffset
== 0 &&
1855 Volume
->OSName
== NULL
)
1856 // this is a whole disk (MBR) entry; hide if we have entries for partitions
1857 HideIfOthersFound
= TRUE
;
1859 if (HideIfOthersFound
) {
1860 // check for other bootable entries on the same disk
1861 for (VolumeIndex2
= 0; VolumeIndex2
< VolumesCount
; VolumeIndex2
++) {
1862 if (VolumeIndex2
!= VolumeIndex
&& Volumes
[VolumeIndex2
]->HasBootCode
&&
1863 Volumes
[VolumeIndex2
]->WholeDiskBlockIO
== Volume
->WholeDiskBlockIO
)
1869 AddLegacyEntry(NULL
, Volume
);
1870 } // static VOID ScanLegacyVolume()
1872 // Scan attached optical discs for legacy (BIOS) boot code
1873 // and add anything found to the list....
1874 static VOID
ScanLegacyDisc(VOID
)
1877 REFIT_VOLUME
*Volume
;
1879 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
) {
1880 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1881 Volume
= Volumes
[VolumeIndex
];
1882 if (Volume
->DiskKind
== DISK_KIND_OPTICAL
)
1883 ScanLegacyVolume(Volume
, VolumeIndex
);
1885 } else if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
1886 ScanLegacyUEFI(BBS_CDROM
);
1888 } /* static VOID ScanLegacyDisc() */
1890 // Scan internal hard disks for legacy (BIOS) boot code
1891 // and add anything found to the list....
1892 static VOID
ScanLegacyInternal(VOID
)
1895 REFIT_VOLUME
*Volume
;
1897 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
) {
1898 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1899 Volume
= Volumes
[VolumeIndex
];
1900 if (Volume
->DiskKind
== DISK_KIND_INTERNAL
)
1901 ScanLegacyVolume(Volume
, VolumeIndex
);
1903 } else if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
1904 ScanLegacyUEFI(BBS_HARDDISK
);
1906 } /* static VOID ScanLegacyInternal() */
1908 // Scan external disks for legacy (BIOS) boot code
1909 // and add anything found to the list....
1910 static VOID
ScanLegacyExternal(VOID
)
1913 REFIT_VOLUME
*Volume
;
1915 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
) {
1916 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1917 Volume
= Volumes
[VolumeIndex
];
1918 if (Volume
->DiskKind
== DISK_KIND_EXTERNAL
)
1919 ScanLegacyVolume(Volume
, VolumeIndex
);
1921 } else if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
1922 ScanLegacyUEFI(BBS_USB
);
1924 } /* static VOID ScanLegacyExternal() */
1927 // pre-boot tool functions
1930 static VOID
StartTool(IN LOADER_ENTRY
*Entry
)
1932 BeginExternalScreen(Entry
->UseGraphicsMode
, Entry
->me
.Title
+ 6); // assumes "Start <title>" as assigned below
1933 StartEFIImage(Entry
->DevicePath
, Entry
->LoadOptions
, TYPE_EFI
,
1934 Basename(Entry
->LoaderPath
), Entry
->OSType
, NULL
, TRUE
);
1935 FinishExternalScreen();
1936 } /* static VOID StartTool() */
1938 static LOADER_ENTRY
* AddToolEntry(EFI_HANDLE DeviceHandle
, IN CHAR16
*LoaderPath
, IN CHAR16
*LoaderTitle
, IN EG_IMAGE
*Image
,
1939 IN CHAR16 ShortcutLetter
, IN BOOLEAN UseGraphicsMode
)
1941 LOADER_ENTRY
*Entry
;
1942 CHAR16
*TitleStr
= NULL
;
1944 Entry
= AllocateZeroPool(sizeof(LOADER_ENTRY
));
1946 TitleStr
= PoolPrint(L
"Start %s", LoaderTitle
);
1947 Entry
->me
.Title
= TitleStr
;
1948 Entry
->me
.Tag
= TAG_TOOL
;
1950 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1951 Entry
->me
.Image
= Image
;
1952 Entry
->LoaderPath
= (LoaderPath
) ? StrDuplicate(LoaderPath
) : NULL
;
1953 Entry
->DevicePath
= FileDevicePath(DeviceHandle
, Entry
->LoaderPath
);
1954 Entry
->UseGraphicsMode
= UseGraphicsMode
;
1956 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1958 } /* static LOADER_ENTRY * AddToolEntry() */
1961 // pre-boot driver functions
1964 static UINTN
ScanDriverDir(IN CHAR16
*Path
)
1967 REFIT_DIR_ITER DirIter
;
1969 EFI_FILE_INFO
*DirEntry
;
1970 CHAR16 FileName
[256];
1972 CleanUpPathNameSlashes(Path
);
1973 // look through contents of the directory
1974 DirIterOpen(SelfRootDir
, Path
, &DirIter
);
1975 while (DirIterNext(&DirIter
, 2, LOADER_MATCH_PATTERNS
, &DirEntry
)) {
1976 if (DirEntry
->FileName
[0] == '.')
1977 continue; // skip this
1979 SPrint(FileName
, 255, L
"%s\\%s", Path
, DirEntry
->FileName
);
1981 Status
= StartEFIImage(FileDevicePath(SelfLoadedImage
->DeviceHandle
, FileName
),
1982 L
"", TYPE_EFI
, DirEntry
->FileName
, 0, NULL
, FALSE
);
1984 Status
= DirIterClose(&DirIter
);
1985 if (Status
!= EFI_NOT_FOUND
) {
1986 SPrint(FileName
, 255, L
"while scanning the %s directory", Path
);
1987 CheckError(Status
, FileName
);
1992 #ifdef __MAKEWITH_GNUEFI
1993 static EFI_STATUS
ConnectAllDriversToAllControllers(VOID
)
1996 UINTN AllHandleCount
;
1997 EFI_HANDLE
*AllHandleBuffer
;
2000 EFI_HANDLE
*HandleBuffer
;
2006 Status
= LibLocateHandle(AllHandles
,
2011 if (EFI_ERROR(Status
))
2014 for (Index
= 0; Index
< AllHandleCount
; Index
++) {
2016 // Scan the handle database
2018 Status
= LibScanHandleDatabase(NULL
,
2020 AllHandleBuffer
[Index
],
2025 if (EFI_ERROR (Status
))
2029 if (HandleType
[Index
] & EFI_HANDLE_TYPE_DRIVER_BINDING_HANDLE
)
2031 if (HandleType
[Index
] & EFI_HANDLE_TYPE_IMAGE_HANDLE
)
2036 for (HandleIndex
= 0; HandleIndex
< HandleCount
; HandleIndex
++) {
2037 if (HandleType
[HandleIndex
] & EFI_HANDLE_TYPE_PARENT_HANDLE
)
2042 if (HandleType
[Index
] & EFI_HANDLE_TYPE_DEVICE_HANDLE
) {
2043 Status
= refit_call4_wrapper(BS
->ConnectController
,
2044 AllHandleBuffer
[Index
],
2052 MyFreePool (HandleBuffer
);
2053 MyFreePool (HandleType
);
2057 MyFreePool (AllHandleBuffer
);
2059 } /* EFI_STATUS ConnectAllDriversToAllControllers() */
2061 static EFI_STATUS
ConnectAllDriversToAllControllers(VOID
) {
2062 BdsLibConnectAllDriversToAllControllers();
2067 // Load all EFI drivers from rEFInd's "drivers" subdirectory and from the
2068 // directories specified by the user in the "scan_driver_dirs" configuration
2070 static VOID
LoadDrivers(VOID
)
2072 CHAR16
*Directory
, *SelfDirectory
;
2073 UINTN i
= 0, Length
, NumFound
= 0;
2075 // load drivers from the subdirectories of rEFInd's home directory specified
2076 // in the DRIVER_DIRS constant.
2077 while ((Directory
= FindCommaDelimited(DRIVER_DIRS
, i
++)) != NULL
) {
2078 SelfDirectory
= SelfDirPath
? StrDuplicate(SelfDirPath
) : NULL
;
2079 CleanUpPathNameSlashes(SelfDirectory
);
2080 MergeStrings(&SelfDirectory
, Directory
, L
'\\');
2081 NumFound
+= ScanDriverDir(SelfDirectory
);
2082 MyFreePool(Directory
);
2083 MyFreePool(SelfDirectory
);
2086 // Scan additional user-specified driver directories....
2088 while ((Directory
= FindCommaDelimited(GlobalConfig
.DriverDirs
, i
++)) != NULL
) {
2089 CleanUpPathNameSlashes(Directory
);
2090 Length
= StrLen(Directory
);
2092 NumFound
+= ScanDriverDir(Directory
);
2094 MyFreePool(Directory
);
2097 // connect all devices
2099 ConnectAllDriversToAllControllers();
2100 } /* static VOID LoadDrivers() */
2102 // Determine what (if any) type of legacy (BIOS) boot support is available
2103 static VOID
FindLegacyBootType(VOID
) {
2104 #ifdef __MAKEWITH_TIANO
2106 EFI_LEGACY_BIOS_PROTOCOL
*LegacyBios
;
2109 GlobalConfig
.LegacyType
= LEGACY_TYPE_NONE
;
2111 // UEFI-style legacy BIOS support is available only with the TianoCore EDK2
2112 // build environment, and then only with some EFI implementations....
2113 #ifdef __MAKEWITH_TIANO
2114 Status
= gBS
->LocateProtocol (&gEfiLegacyBootProtocolGuid
, NULL
, (VOID
**) &LegacyBios
);
2115 if (!EFI_ERROR (Status
))
2116 GlobalConfig
.LegacyType
= LEGACY_TYPE_UEFI
;
2119 // Macs have their own system. If the firmware vendor code contains the
2120 // string "Apple", assume it's available. Note that this overrides the
2121 // UEFI type, and might yield false positives if the vendor string
2122 // contains "Apple" as part of something bigger, so this isn't 100%
2124 if (StriSubCmp(L
"Apple", ST
->FirmwareVendor
))
2125 GlobalConfig
.LegacyType
= LEGACY_TYPE_MAC
;
2126 } // static VOID FindLegacyBootType
2128 // Warn the user if legacy OS scans are enabled but the firmware or this
2129 // application can't support them....
2130 static VOID
WarnIfLegacyProblems() {
2131 BOOLEAN found
= FALSE
;
2134 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_NONE
) {
2136 if (GlobalConfig
.ScanFor
[i
] == 'h' || GlobalConfig
.ScanFor
[i
] == 'b' || GlobalConfig
.ScanFor
[i
] == 'c')
2139 } while ((i
< NUM_SCAN_OPTIONS
) && (!found
));
2141 Print(L
"NOTE: refind.conf's 'scanfor' line specifies scanning for one or more legacy\n");
2142 Print(L
"(BIOS) boot options; however, this is not possible because ");
2143 #ifdef __MAKEWITH_TIANO
2144 Print(L
"your computer lacks\n");
2145 Print(L
"the necessary Compatibility Support Module (CSM) support.\n");
2147 Print(L
"this program was\n");
2148 Print(L
"compiled without the necessary support. Please visit\n");
2149 Print(L
"http://www.rodsbooks.com/refind/getting.html and download and install a rEFInd\n");
2150 Print(L
"binary built with the TianoCore EDK2 to enable legacy boot support.\n");
2154 } // if no legacy support
2155 } // static VOID WarnIfLegacyProblems()
2157 // Locates boot loaders. NOTE: This assumes that GlobalConfig.LegacyType is set correctly.
2158 static VOID
ScanForBootloaders(VOID
) {
2161 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
2162 Print(L
"About to call BdsDeleteAllInvalidLegacyBootOptions()\n");
2163 BdsDeleteAllInvalidLegacyBootOptions();
2164 Print(L
"About to call BdsAddNonExistingLegacyBootOptions()\n");
2165 BdsAddNonExistingLegacyBootOptions();
2166 Print(L
"About to call BdsUpdateLegacyDevOrder()\n");
2167 // BdsUpdateLegacyDevOrder(); // EXTREME CAUTION: HOSED ONE FIRMWARE!
2168 Print(L
"Done with legacy boot updates!\n");
2174 // scan for loaders and tools, add them to the menu
2175 for (i
= 0; i
< NUM_SCAN_OPTIONS
; i
++) {
2176 switch(GlobalConfig
.ScanFor
[i
]) {
2181 ScanLegacyInternal();
2184 ScanLegacyExternal();
2187 ScanUserConfigured(CONFIG_FILE_NAME
);
2201 // assign shortcut keys
2202 for (i
= 0; i
< MainMenu
.EntryCount
&& MainMenu
.Entries
[i
]->Row
== 0 && i
< 9; i
++)
2203 MainMenu
.Entries
[i
]->ShortcutDigit
= (CHAR16
)('1' + i
);
2205 // wait for user ACK when there were errors
2206 FinishTextScreen(FALSE
);
2207 } // static VOID ScanForBootloaders()
2209 // Locate a single tool from the specified Locations using one of the
2210 // specified Names and add it to the menu.
2211 static VOID
FindTool(CHAR16
*Locations
, CHAR16
*Names
, CHAR16
*Description
, UINTN Icon
) {
2212 UINTN j
= 0, k
, VolumeIndex
;
2213 CHAR16
*DirName
, *FileName
, *PathName
, FullDescription
[256];
2215 while ((DirName
= FindCommaDelimited(Locations
, j
++)) != NULL
) {
2217 while ((FileName
= FindCommaDelimited(Names
, k
++)) != NULL
) {
2218 PathName
= StrDuplicate(DirName
);
2219 MergeStrings(&PathName
, FileName
, (StriCmp(PathName
, L
"\\") == 0) ? 0 : L
'\\');
2220 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
2221 if ((Volumes
[VolumeIndex
]->RootDir
!= NULL
) && (FileExists(Volumes
[VolumeIndex
]->RootDir
, PathName
))) {
2222 SPrint(FullDescription
, 255, L
"%s at %s on %s", Description
, PathName
, Volumes
[VolumeIndex
]->VolName
);
2223 AddToolEntry(Volumes
[VolumeIndex
]->DeviceHandle
, PathName
, FullDescription
, BuiltinIcon(Icon
), 'S', FALSE
);
2226 MyFreePool(PathName
);
2227 MyFreePool(FileName
);
2229 MyFreePool(DirName
);
2230 } // while Locations
2231 } // VOID FindTool()
2233 // Add the second-row tags containing built-in and external tools (EFI shell,
2235 static VOID
ScanForTools(VOID
) {
2236 CHAR16
*FileName
= NULL
, *MokLocations
, Description
[256];
2237 REFIT_MENU_ENTRY
*TempMenuEntry
;
2238 UINTN i
, j
, VolumeIndex
;
2242 MokLocations
= StrDuplicate(MOK_LOCATIONS
);
2243 if (MokLocations
!= NULL
)
2244 MergeStrings(&MokLocations
, SelfDirPath
, L
',');
2246 for (i
= 0; i
< NUM_TOOLS
; i
++) {
2247 switch(GlobalConfig
.ShowTools
[i
]) {
2248 // NOTE: Be sure that FileName is NULL at the end of each case.
2250 TempMenuEntry
= CopyMenuEntry(&MenuEntryShutdown
);
2251 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_SHUTDOWN
);
2252 AddMenuEntry(&MainMenu
, TempMenuEntry
);
2256 TempMenuEntry
= CopyMenuEntry(&MenuEntryReset
);
2257 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_RESET
);
2258 AddMenuEntry(&MainMenu
, TempMenuEntry
);
2262 TempMenuEntry
= CopyMenuEntry(&MenuEntryAbout
);
2263 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT
);
2264 AddMenuEntry(&MainMenu
, TempMenuEntry
);
2268 TempMenuEntry
= CopyMenuEntry(&MenuEntryExit
);
2269 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_EXIT
);
2270 AddMenuEntry(&MainMenu
, TempMenuEntry
);
2274 if (EfivarGetRaw(&GlobalGuid
, L
"OsIndicationsSupported", &b
, &j
) == EFI_SUCCESS
) {
2276 if (osind
& EFI_OS_INDICATIONS_BOOT_TO_FW_UI
) {
2277 TempMenuEntry
= CopyMenuEntry(&MenuEntryFirmware
);
2278 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_FIRMWARE
);
2279 AddMenuEntry(&MainMenu
, TempMenuEntry
);
2286 while ((FileName
= FindCommaDelimited(SHELL_NAMES
, j
++)) != NULL
) {
2287 if (FileExists(SelfRootDir
, FileName
)) {
2288 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, L
"EFI Shell", BuiltinIcon(BUILTIN_ICON_TOOL_SHELL
),
2291 MyFreePool(FileName
);
2297 while ((FileName
= FindCommaDelimited(GPTSYNC_NAMES
, j
++)) != NULL
) {
2298 if (FileExists(SelfRootDir
, FileName
)) {
2299 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, L
"Hybrid MBR tool", BuiltinIcon(BUILTIN_ICON_TOOL_PART
),
2302 MyFreePool(FileName
);
2307 case TAG_APPLE_RECOVERY
:
2308 FileName
= StrDuplicate(L
"\\com.apple.recovery.boot\\boot.efi");
2309 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
2310 if ((Volumes
[VolumeIndex
]->RootDir
!= NULL
) && (FileExists(Volumes
[VolumeIndex
]->RootDir
, FileName
))) {
2311 SPrint(Description
, 255, L
"Apple Recovery on %s", Volumes
[VolumeIndex
]->VolName
);
2312 AddToolEntry(Volumes
[VolumeIndex
]->DeviceHandle
, FileName
, Description
,
2313 BuiltinIcon(BUILTIN_ICON_TOOL_APPLE_RESCUE
), 'R', TRUE
);
2316 MyFreePool(FileName
);
2321 FindTool(MokLocations
, MOK_NAMES
, L
"MOK utility utility", BUILTIN_ICON_TOOL_MOK_TOOL
);
2325 FindTool(MEMTEST_LOCATIONS
, MEMTEST_NAMES
, L
"Memory test utility", BUILTIN_ICON_TOOL_MEMTEST
);
2330 } // static VOID ScanForTools
2332 // Rescan for boot loaders
2333 VOID
RescanAll(VOID
) {
2340 egDisplayMessage(L
"Scanning for new boot loaders; please wait....", &BGColor
);
2341 FreeList((VOID
***) &(MainMenu
.Entries
), &MainMenu
.EntryCount
);
2342 MainMenu
.Entries
= NULL
;
2343 MainMenu
.EntryCount
= 0;
2344 ReadConfig(CONFIG_FILE_NAME
);
2345 ConnectAllDriversToAllControllers();
2347 ScanForBootloaders();
2350 } // VOID RescanAll()
2352 #ifdef __MAKEWITH_TIANO
2354 // Minimal initialization function
2355 static VOID
InitializeLib(IN EFI_HANDLE ImageHandle
, IN EFI_SYSTEM_TABLE
*SystemTable
) {
2357 // gImageHandle = ImageHandle;
2358 gBS
= SystemTable
->BootServices
;
2359 // gRS = SystemTable->RuntimeServices;
2360 gRT
= SystemTable
->RuntimeServices
; // Some BDS functions need gRT to be set
2361 EfiGetSystemConfigurationTable (&gEfiDxeServicesTableGuid
, (VOID
**) &gDS
);
2363 InitializeConsoleSim();
2368 // Set up our own Secure Boot extensions....
2369 // Returns TRUE on success, FALSE otherwise
2370 static BOOLEAN
SecureBootSetup(VOID
) {
2372 BOOLEAN Success
= FALSE
;
2374 if (secure_mode() && ShimLoaded()) {
2375 Status
= security_policy_install();
2376 if (Status
== EFI_SUCCESS
) {
2379 Print(L
"Failed to install MOK Secure Boot extensions");
2383 } // VOID SecureBootSetup()
2385 // Remove our own Secure Boot extensions....
2386 // Returns TRUE on success, FALSE otherwise
2387 static BOOLEAN
SecureBootUninstall(VOID
) {
2389 BOOLEAN Success
= TRUE
;
2391 if (secure_mode()) {
2392 Status
= security_policy_uninstall();
2393 if (Status
!= EFI_SUCCESS
) {
2395 BeginTextScreen(L
"Secure Boot Policy Failure");
2396 Print(L
"Failed to uninstall MOK Secure Boot extensions; forcing a reboot.");
2398 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);
2402 } // VOID SecureBootUninstall
2409 efi_main (EFI_HANDLE ImageHandle
, EFI_SYSTEM_TABLE
*SystemTable
)
2412 BOOLEAN MainLoopRunning
= TRUE
;
2413 BOOLEAN MokProtocol
;
2414 REFIT_MENU_ENTRY
*ChosenEntry
;
2416 CHAR16
*Selection
= NULL
;
2420 InitializeLib(ImageHandle
, SystemTable
);
2421 Status
= InitRefitLib(ImageHandle
);
2422 if (EFI_ERROR(Status
))
2425 // read configuration
2426 CopyMem(GlobalConfig
.ScanFor
, "ieom ", NUM_SCAN_OPTIONS
);
2427 FindLegacyBootType();
2428 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
)
2429 CopyMem(GlobalConfig
.ScanFor
, "ihebocm ", NUM_SCAN_OPTIONS
);
2430 ReadConfig(CONFIG_FILE_NAME
);
2435 WarnIfLegacyProblems();
2436 MainMenu
.TimeoutSeconds
= GlobalConfig
.Timeout
;
2438 // disable EFI watchdog timer
2439 refit_call4_wrapper(BS
->SetWatchdogTimer
, 0x0000, 0x0000, 0x0000, NULL
);
2441 // further bootstrap (now with config available)
2442 MokProtocol
= SecureBootSetup();
2444 ScanForBootloaders();
2448 if (GlobalConfig
.ScanDelay
> 0) {
2453 egDisplayMessage(L
"Pausing before disk scan; please wait....", &BGColor
);
2454 for (i
= 0; i
< GlobalConfig
.ScanDelay
; i
++)
2455 refit_call1_wrapper(BS
->Stall
, 1000000);
2459 if (GlobalConfig
.DefaultSelection
)
2460 Selection
= StrDuplicate(GlobalConfig
.DefaultSelection
);
2462 while (MainLoopRunning
) {
2463 MenuExit
= RunMainMenu(&MainMenu
, Selection
, &ChosenEntry
);
2465 // The Escape key triggers a re-scan operation....
2466 if (MenuExit
== MENU_EXIT_ESCAPE
) {
2471 switch (ChosenEntry
->Tag
) {
2473 case TAG_REBOOT
: // Reboot
2475 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);
2476 MainLoopRunning
= FALSE
; // just in case we get this far
2479 case TAG_SHUTDOWN
: // Shut Down
2481 refit_call4_wrapper(RT
->ResetSystem
, EfiResetShutdown
, EFI_SUCCESS
, 0, NULL
);
2482 MainLoopRunning
= FALSE
; // just in case we get this far
2485 case TAG_ABOUT
: // About rEFInd
2489 case TAG_LOADER
: // Boot OS via .EFI loader
2490 StartLoader((LOADER_ENTRY
*)ChosenEntry
);
2493 case TAG_LEGACY
: // Boot legacy OS
2494 StartLegacy((LEGACY_ENTRY
*)ChosenEntry
);
2497 #ifdef __MAKEWITH_TIANO
2498 case TAG_LEGACY_UEFI
: // Boot a legacy OS on a non-Mac
2499 StartLegacyUEFI((LEGACY_ENTRY
*)ChosenEntry
);
2503 case TAG_TOOL
: // Start a EFI tool
2504 StartTool((LOADER_ENTRY
*)ChosenEntry
);
2507 case TAG_EXIT
: // Terminate rEFInd
2508 if ((MokProtocol
) && !SecureBootUninstall()) {
2509 MainLoopRunning
= FALSE
; // just in case we get this far
2511 BeginTextScreen(L
" ");
2516 case TAG_FIRMWARE
: // Reboot into firmware's user interface
2517 RebootIntoFirmware();
2521 MyFreePool(Selection
);
2522 Selection
= (ChosenEntry
->Title
) ? StrDuplicate(ChosenEntry
->Title
) : NULL
;
2525 // If we end up here, things have gone wrong. Try to reboot, and if that
2526 // fails, go into an endless loop.
2527 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);