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 #ifndef EFI_SECURITY_VIOLATION
60 #define EFI_SECURITY_VIOLATION EFIERR (26)
63 #include "../EfiLib/BdsHelper.h"
64 #include "../EfiLib/legacy.h"
65 #endif // __MAKEWITH_GNUEFI
67 #ifndef EFI_OS_INDICATIONS_BOOT_TO_FW_UI
68 #define EFI_OS_INDICATIONS_BOOT_TO_FW_UI 0x0000000000000001ULL
74 #define MACOSX_LOADER_PATH L"System\\Library\\CoreServices\\boot.efi"
76 #define SHELL_NAMES L"\\EFI\\tools\\shell.efi,\\EFI\\tools\\shellx64.efi,\\shell.efi,\\shellx64.efi"
77 #define GPTSYNC_NAMES L"\\EFI\\tools\\gptsync.efi,\\EFI\\tools\\gptsync_x64.efi"
78 #define MEMTEST_NAMES L"memtest86.efi,memtest86_x64.efi,memtest86x64.efi,bootx64.efi"
79 #define DRIVER_DIRS L"drivers,drivers_x64"
80 #define FALLBACK_FULLNAME L"EFI\\BOOT\\bootx64.efi"
81 #define FALLBACK_BASENAME L"bootx64.efi"
82 #define EFI_STUB_ARCH 0x8664
84 #define SHELL_NAMES L"\\EFI\\tools\\shell.efi,\\EFI\\tools\\shellia32.efi,\\shell.efi,\\shellia32.efi"
85 #define GPTSYNC_NAMES L"\\EFI\\tools\\gptsync.efi,\\EFI\\tools\\gptsync_ia32.efi"
86 #define MEMTEST_NAMES L"memtest86.efi,memtest86_ia32.efi,memtest86ia32.efi,bootia32.efi"
87 #define DRIVER_DIRS L"drivers,drivers_ia32"
88 #define FALLBACK_FULLNAME L"EFI\\BOOT\\bootia32.efi"
89 #define FALLBACK_BASENAME L"bootia32.efi"
90 #define EFI_STUB_ARCH 0x014c
92 #define SHELL_NAMES L"\\EFI\\tools\\shell.efi,\\shell.efi"
93 #define GPTSYNC_NAMES L"\\EFI\\tools\\gptsync.efi"
94 #define MEMTEST_NAMES L"memtest86.efi"
95 #define DRIVER_DIRS L"drivers"
96 #define FALLBACK_FULLNAME L"EFI\\BOOT\\boot.efi" /* Not really correct */
97 #define FALLBACK_BASENAME L"boot.efi" /* Not really correct */
99 #define FAT_ARCH 0x0ef1fab9 /* ID for Apple "fat" binary */
101 // Filename patterns that identify EFI boot loaders. Note that a single case (either L"*.efi" or
102 // L"*.EFI") is fine for most systems; but Gigabyte's buggy Hybrid EFI does a case-sensitive
103 // comparison when it should do a case-insensitive comparison, so I'm doubling this up. It does
104 // no harm on other computers, AFAIK. In theory, every case variation should be done for
105 // completeness, but that's ridiculous....
106 #define LOADER_MATCH_PATTERNS L"*.efi,*.EFI"
108 // Patterns that identify Linux kernels. Added to the loader match pattern when the
109 // scan_all_linux_kernels option is set in the configuration file. Causes kernels WITHOUT
110 // a ".efi" extension to be found when scanning for boot loaders.
111 #define LINUX_MATCH_PATTERNS L"vmlinuz*,bzImage*"
113 // Default hint text for program-launch submenus
114 #define SUBSCREEN_HINT1 L"Use arrow keys to move cursor; Enter to boot;"
115 #define SUBSCREEN_HINT2 L"Insert or F2 to edit options; Esc to return to main menu"
116 #define SUBSCREEN_HINT2_NO_EDITOR L"Esc to return to main menu"
120 #define TYPE_LEGACY 2
122 static REFIT_MENU_ENTRY MenuEntryAbout
= { L
"About rEFInd", TAG_ABOUT
, 1, 0, 'A', NULL
, NULL
, NULL
};
123 static REFIT_MENU_ENTRY MenuEntryReset
= { L
"Reboot Computer", TAG_REBOOT
, 1, 0, 'R', NULL
, NULL
, NULL
};
124 static REFIT_MENU_ENTRY MenuEntryShutdown
= { L
"Shut Down Computer", TAG_SHUTDOWN
, 1, 0, 'U', NULL
, NULL
, NULL
};
125 static REFIT_MENU_ENTRY MenuEntryReturn
= { L
"Return to Main Menu", TAG_RETURN
, 1, 0, 0, NULL
, NULL
, NULL
};
126 static REFIT_MENU_ENTRY MenuEntryExit
= { L
"Exit rEFInd", TAG_EXIT
, 1, 0, 0, NULL
, NULL
, NULL
};
127 static REFIT_MENU_ENTRY MenuEntryFirmware
= { L
"Reboot to Computer Setup Utility", TAG_FIRMWARE
, 1, 0, 0, NULL
, NULL
, NULL
};
129 static REFIT_MENU_SCREEN MainMenu
= { L
"Main Menu", NULL
, 0, NULL
, 0, NULL
, 0, L
"Automatic boot",
130 L
"Use arrow keys to move cursor; Enter to boot;",
131 L
"Insert or F2 for more options; Esc to refresh" };
132 static REFIT_MENU_SCREEN AboutMenu
= { L
"About", NULL
, 0, NULL
, 0, NULL
, 0, NULL
, L
"Press Enter to return to main menu", L
"" };
134 REFIT_CONFIG GlobalConfig
= { FALSE
, FALSE
, 0, 0, 0, DONT_CHANGE_TEXT_MODE
, 20, 0, 0, GRAPHICS_FOR_OSX
, LEGACY_TYPE_MAC
, 0, 0,
135 NULL
, NULL
, CONFIG_FILE_NAME
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
136 { TAG_SHELL
, TAG_MEMTEST
, TAG_APPLE_RECOVERY
, TAG_WINDOWS_RECOVERY
, TAG_MOK_TOOL
, TAG_ABOUT
,
137 TAG_SHUTDOWN
, TAG_REBOOT
, TAG_FIRMWARE
, 0, 0, 0, 0, 0, 0 }
140 EFI_GUID GlobalGuid
= EFI_GLOBAL_VARIABLE
;
142 // Structure used to hold boot loader filenames and time stamps in
143 // a linked list; used to sort entries within a directory.
147 struct LOADER_LIST
*NextEntry
;
154 static VOID
AboutrEFInd(VOID
)
156 if (AboutMenu
.EntryCount
== 0) {
157 AboutMenu
.TitleImage
= BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT
);
158 AddMenuInfoLine(&AboutMenu
, L
"rEFInd Version 0.7.6.3");
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 AddMenuInfoLine(&AboutMenu
, PoolPrint(L
" EFI Revision %d.%02d", ST
->Hdr
.Revision
>> 16, ST
->Hdr
.Revision
& ((1 << 16) - 1)));
168 AddMenuInfoLine(&AboutMenu
, L
" Platform: x86 (32 bit)");
169 #elif defined(EFIX64)
170 AddMenuInfoLine(&AboutMenu
, PoolPrint(L
" Platform: x86_64 (64 bit); Secure Boot %s",
171 secure_mode() ? L
"active" : L
"inactive"));
173 AddMenuInfoLine(&AboutMenu
, L
" Platform: unknown");
175 AddMenuInfoLine(&AboutMenu
, PoolPrint(L
" Firmware: %s %d.%02d", ST
->FirmwareVendor
, ST
->FirmwareRevision
>> 16,
176 ST
->FirmwareRevision
& ((1 << 16) - 1)));
177 AddMenuInfoLine(&AboutMenu
, PoolPrint(L
" Screen Output: %s", egScreenDescription()));
178 AddMenuInfoLine(&AboutMenu
, L
"");
179 #if defined(__MAKEWITH_GNUEFI)
180 AddMenuInfoLine(&AboutMenu
, L
"Built with GNU-EFI");
182 AddMenuInfoLine(&AboutMenu
, L
"Built with TianoCore EDK2");
184 AddMenuInfoLine(&AboutMenu
, L
"");
185 AddMenuInfoLine(&AboutMenu
, L
"For more information, see the rEFInd Web site:");
186 AddMenuInfoLine(&AboutMenu
, L
"http://www.rodsbooks.com/refind/");
187 AddMenuEntry(&AboutMenu
, &MenuEntryReturn
);
190 RunMenu(&AboutMenu
, NULL
);
191 } /* VOID AboutrEFInd() */
193 static VOID
WarnSecureBootError(CHAR16
*Name
, BOOLEAN Verbose
) {
195 Name
= L
"the loader";
197 refit_call2_wrapper(ST
->ConOut
->SetAttribute
, ST
->ConOut
, ATTR_ERROR
);
198 Print(L
"Secure Boot validation failure loading %s!\n", Name
);
199 refit_call2_wrapper(ST
->ConOut
->SetAttribute
, ST
->ConOut
, ATTR_BASIC
);
200 if (Verbose
&& secure_mode()) {
201 Print(L
"\nThis computer is configured with Secure Boot active, but\n%s has failed validation.\n", Name
);
202 Print(L
"\nYou can:\n * Launch another boot loader\n");
203 Print(L
" * Disable Secure Boot in your firmware\n");
204 Print(L
" * Sign %s with a machine owner key (MOK)\n", Name
);
205 Print(L
" * Use a MOK utility (often present on the second row) to add a MOK with which\n");
206 Print(L
" %s has already been signed.\n", Name
);
207 Print(L
" * Use a MOK utility to register %s (\"enroll its hash\") without\n", Name
);
208 Print(L
" signing it.\n");
209 Print(L
"\nSee http://www.rodsbooks.com/refind/secureboot.html for more information\n");
212 } // VOID WarnSecureBootError()
214 // Returns TRUE if this file is a valid EFI loader file, and is proper ARCH
215 static BOOLEAN
IsValidLoader(EFI_FILE
*RootDir
, CHAR16
*FileName
) {
216 BOOLEAN IsValid
= TRUE
;
217 #if defined (EFIX64) | defined (EFI32)
219 EFI_FILE_HANDLE FileHandle
;
221 UINTN Size
= sizeof(Header
);
223 if ((RootDir
== NULL
) || (FileName
== NULL
)) {
224 // Assume valid here, because Macs produce NULL RootDir (& maybe FileName)
225 // when launching from a Firewire drive. This should be handled better, but
226 // fix would have to be in StartEFIImageList() and/or in FindVolumeAndFilename().
230 Status
= refit_call5_wrapper(RootDir
->Open
, RootDir
, &FileHandle
, FileName
, EFI_FILE_MODE_READ
, 0);
231 if (EFI_ERROR(Status
))
234 Status
= refit_call3_wrapper(FileHandle
->Read
, FileHandle
, &Size
, Header
);
235 refit_call1_wrapper(FileHandle
->Close
, FileHandle
);
237 IsValid
= !EFI_ERROR(Status
) &&
238 Size
== sizeof(Header
) &&
239 ((Header
[0] == 'M' && Header
[1] == 'Z' &&
240 (Size
= *(UINT32
*)&Header
[0x3c]) < 0x180 &&
241 Header
[Size
] == 'P' && Header
[Size
+1] == 'E' &&
242 Header
[Size
+2] == 0 && Header
[Size
+3] == 0 &&
243 *(UINT16
*)&Header
[Size
+4] == EFI_STUB_ARCH
) ||
244 (*(UINT32
*)&Header
== FAT_ARCH
));
247 } // BOOLEAN IsValidLoader()
249 // Launch an EFI binary.
250 static EFI_STATUS
StartEFIImageList(IN EFI_DEVICE_PATH
**DevicePaths
,
251 IN CHAR16
*LoadOptions
, IN UINTN LoaderType
,
252 IN CHAR16
*ImageTitle
, IN CHAR8 OSType
,
253 OUT UINTN
*ErrorInStep
,
256 EFI_STATUS Status
, ReturnStatus
;
257 EFI_HANDLE ChildImageHandle
;
258 EFI_LOADED_IMAGE
*ChildLoadedImage
= NULL
;
259 REFIT_VOLUME
*Volume
= NULL
;
260 UINTN DevicePathIndex
;
261 CHAR16 ErrorInfo
[256];
262 CHAR16
*FullLoadOptions
= NULL
;
263 CHAR16
*Filename
= NULL
;
266 if (ErrorInStep
!= NULL
)
270 if (LoadOptions
!= NULL
) {
271 FullLoadOptions
= StrDuplicate(LoadOptions
);
272 if ((LoaderType
== TYPE_EFI
) && (OSType
== 'M')) {
273 MergeStrings(&FullLoadOptions
, L
" ", 0);
274 // NOTE: That last space is also added by the EFI shell and seems to be significant
275 // when passing options to Apple's boot.efi...
277 } // if (LoadOptions != NULL)
279 Print(L
"Starting %s\nUsing load options '%s'\n", ImageTitle
, FullLoadOptions
? FullLoadOptions
: L
"");
281 // load the image into memory (and execute it, in the case of a shim/MOK image).
282 ReturnStatus
= Status
= EFI_NOT_FOUND
; // in case the list is empty
283 for (DevicePathIndex
= 0; DevicePaths
[DevicePathIndex
] != NULL
; DevicePathIndex
++) {
284 FindVolumeAndFilename(DevicePaths
[DevicePathIndex
], &Volume
, &Filename
);
285 // Some EFIs crash if attempting to load driver for invalid architecture, so
286 // protect for this condition; but sometimes Volume comes back NULL, so provide
287 // an exception. (TODO: Handle this special condition better.)
288 if ((LoaderType
== TYPE_LEGACY
) || (Volume
== NULL
) || IsValidLoader(Volume
->RootDir
, Filename
)) {
289 if (Filename
&& (LoaderType
!= TYPE_LEGACY
)) {
290 Temp
= PoolPrint(L
"\\%s %s", Filename
, FullLoadOptions
? FullLoadOptions
: L
"");
292 MyFreePool(FullLoadOptions
);
293 FullLoadOptions
= Temp
;
297 // NOTE: Below commented-out line could be more efficient if file were read ahead of
298 // time and passed as a pre-loaded image to LoadImage(), but it doesn't work on my
299 // 32-bit Mac Mini or my 64-bit Intel box when launching a Linux kernel; the
300 // kernel returns a "Failed to handle fs_proto" error message.
301 // TODO: Track down the cause of this error and fix it, if possible.
302 // ReturnStatus = Status = refit_call6_wrapper(BS->LoadImage, FALSE, SelfImageHandle, DevicePaths[DevicePathIndex],
303 // ImageData, ImageSize, &ChildImageHandle);
304 ReturnStatus
= Status
= refit_call6_wrapper(BS
->LoadImage
, FALSE
, SelfImageHandle
, DevicePaths
[DevicePathIndex
],
305 NULL
, 0, &ChildImageHandle
);
307 Print(L
"Invalid loader file!\n");
308 ReturnStatus
= EFI_LOAD_ERROR
;
310 if (ReturnStatus
!= EFI_NOT_FOUND
) {
314 if ((Status
== EFI_ACCESS_DENIED
) || (Status
== EFI_SECURITY_VIOLATION
)) {
315 WarnSecureBootError(ImageTitle
, Verbose
);
318 SPrint(ErrorInfo
, 255, L
"while loading %s", ImageTitle
);
319 if (CheckError(Status
, ErrorInfo
)) {
320 if (ErrorInStep
!= NULL
)
325 ReturnStatus
= Status
= refit_call3_wrapper(BS
->HandleProtocol
, ChildImageHandle
, &LoadedImageProtocol
,
326 (VOID
**) &ChildLoadedImage
);
327 if (CheckError(Status
, L
"while getting a LoadedImageProtocol handle")) {
328 if (ErrorInStep
!= NULL
)
332 ChildLoadedImage
->LoadOptions
= (VOID
*)FullLoadOptions
;
333 ChildLoadedImage
->LoadOptionsSize
= FullLoadOptions
? ((UINT32
)StrLen(FullLoadOptions
) + 1) * sizeof(CHAR16
) : 0;
334 // turn control over to the image
335 // TODO: (optionally) re-enable the EFI watchdog timer!
337 // close open file handles
339 ReturnStatus
= Status
= refit_call3_wrapper(BS
->StartImage
, ChildImageHandle
, NULL
, NULL
);
341 // control returns here when the child image calls Exit()
342 SPrint(ErrorInfo
, 255, L
"returned from %s", ImageTitle
);
343 if (CheckError(Status
, ErrorInfo
)) {
344 if (ErrorInStep
!= NULL
)
348 // re-open file handles
352 // unload the image, we don't care if it works or not...
353 Status
= refit_call1_wrapper(BS
->UnloadImage
, ChildImageHandle
);
356 MyFreePool(FullLoadOptions
);
358 } /* static EFI_STATUS StartEFIImageList() */
360 static EFI_STATUS
StartEFIImage(IN EFI_DEVICE_PATH
*DevicePath
,
361 IN CHAR16
*LoadOptions
, IN UINTN LoaderType
,
362 IN CHAR16
*ImageTitle
, IN CHAR8 OSType
,
363 OUT UINTN
*ErrorInStep
,
366 EFI_DEVICE_PATH
*DevicePaths
[2];
368 DevicePaths
[0] = DevicePath
;
369 DevicePaths
[1] = NULL
;
370 return StartEFIImageList(DevicePaths
, LoadOptions
, LoaderType
, ImageTitle
, OSType
, ErrorInStep
, Verbose
);
371 } /* static EFI_STATUS StartEFIImage() */
373 // From gummiboot: Retrieve a raw EFI variable.
374 // Returns EFI status
375 static EFI_STATUS
EfivarGetRaw(EFI_GUID
*vendor
, CHAR16
*name
, CHAR8
**buffer
, UINTN
*size
) {
380 l
= sizeof(CHAR16
*) * EFI_MAXIMUM_VARIABLE_SIZE
;
381 buf
= AllocatePool(l
);
383 return EFI_OUT_OF_RESOURCES
;
385 err
= refit_call5_wrapper(RT
->GetVariable
, name
, vendor
, NULL
, &l
, buf
);
386 if (EFI_ERROR(err
) == EFI_SUCCESS
) {
393 } // EFI_STATUS EfivarGetRaw()
395 // From gummiboot: Set an EFI variable
396 static EFI_STATUS
EfivarSetRaw(EFI_GUID
*vendor
, CHAR16
*name
, CHAR8
*buf
, UINTN size
, BOOLEAN persistent
) {
399 flags
= EFI_VARIABLE_BOOTSERVICE_ACCESS
|EFI_VARIABLE_RUNTIME_ACCESS
;
401 flags
|= EFI_VARIABLE_NON_VOLATILE
;
403 return refit_call5_wrapper(RT
->SetVariable
, name
, vendor
, flags
, size
, buf
);
404 } // EFI_STATUS EfivarSetRaw()
406 // From gummiboot: Reboot the computer into its built-in user interface
407 static EFI_STATUS
RebootIntoFirmware(VOID
) {
413 osind
= EFI_OS_INDICATIONS_BOOT_TO_FW_UI
;
415 err
= EfivarGetRaw(&GlobalGuid
, L
"OsIndications", &b
, &size
);
416 if (err
== EFI_SUCCESS
)
420 err
= EfivarSetRaw(&GlobalGuid
, L
"OsIndications", (CHAR8
*)&osind
, sizeof(UINT64
), TRUE
);
421 if (err
!= EFI_SUCCESS
)
424 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);
425 Print(L
"Error calling ResetSystem: %r", err
);
432 // EFI OS loader functions
435 static VOID
StartLoader(LOADER_ENTRY
*Entry
)
437 UINTN ErrorInStep
= 0;
439 BeginExternalScreen(Entry
->UseGraphicsMode
, L
"Booting OS");
440 StartEFIImage(Entry
->DevicePath
, Entry
->LoadOptions
, TYPE_EFI
,
441 Basename(Entry
->LoaderPath
), Entry
->OSType
, &ErrorInStep
, !Entry
->UseGraphicsMode
);
442 FinishExternalScreen();
445 // Locate an initrd or initramfs file that matches the kernel specified by LoaderPath.
446 // The matching file has a name that begins with "init" and includes the same version
447 // number string as is found in LoaderPath -- but not a longer version number string.
448 // For instance, if LoaderPath is \EFI\kernels\bzImage-3.3.0.efi, and if \EFI\kernels
449 // has a file called initramfs-3.3.0.img, this function will return the string
450 // '\EFI\kernels\initramfs-3.3.0.img'. If the directory ALSO contains the file
451 // initramfs-3.3.0-rc7.img or initramfs-13.3.0.img, those files will NOT match;
452 // however, initmine-3.3.0.img might match. (FindInitrd() returns the first match it
453 // finds). Thus, care should be taken to avoid placing duplicate matching files in
454 // the kernel's directory.
455 // If no matching init file can be found, returns NULL.
456 static CHAR16
* FindInitrd(IN CHAR16
*LoaderPath
, IN REFIT_VOLUME
*Volume
) {
457 CHAR16
*InitrdName
= NULL
, *FileName
, *KernelVersion
, *InitrdVersion
, *Path
;
458 REFIT_DIR_ITER DirIter
;
459 EFI_FILE_INFO
*DirEntry
;
461 FileName
= Basename(LoaderPath
);
462 KernelVersion
= FindNumbers(FileName
);
463 Path
= FindPath(LoaderPath
);
465 // Add trailing backslash for root directory; necessary on some systems, but must
466 // NOT be added to all directories, since on other systems, a trailing backslash on
467 // anything but the root directory causes them to flake out!
468 if (StrLen(Path
) == 0) {
469 MergeStrings(&Path
, L
"\\", 0);
471 DirIterOpen(Volume
->RootDir
, Path
, &DirIter
);
472 // Now add a trailing backslash if it was NOT added earlier, for consistency in
473 // building the InitrdName later....
474 if ((StrLen(Path
) > 0) && (Path
[StrLen(Path
) - 1] != L
'\\'))
475 MergeStrings(&Path
, L
"\\", 0);
476 while ((DirIterNext(&DirIter
, 2, L
"init*", &DirEntry
)) && (InitrdName
== NULL
)) {
477 InitrdVersion
= FindNumbers(DirEntry
->FileName
);
478 if (KernelVersion
!= NULL
) {
479 if (StriCmp(InitrdVersion
, KernelVersion
) == 0) {
480 InitrdName
= PoolPrint(L
"%s%s", Path
, DirEntry
->FileName
);
483 if (InitrdVersion
== NULL
) {
484 InitrdName
= PoolPrint(L
"%s%s", Path
, DirEntry
->FileName
);
487 MyFreePool(InitrdVersion
);
489 DirIterClose(&DirIter
);
491 // Note: Don't FreePool(FileName), since Basename returns a pointer WITHIN the string it's passed.
492 MyFreePool(KernelVersion
);
495 } // static CHAR16 * FindInitrd()
497 LOADER_ENTRY
* AddPreparedLoaderEntry(LOADER_ENTRY
*Entry
) {
498 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
501 } // LOADER_ENTRY * AddPreparedLoaderEntry()
503 // Creates a copy of a menu screen.
504 // Returns a pointer to the copy of the menu screen.
505 static REFIT_MENU_SCREEN
* CopyMenuScreen(REFIT_MENU_SCREEN
*Entry
) {
506 REFIT_MENU_SCREEN
*NewEntry
;
509 NewEntry
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
510 if ((Entry
!= NULL
) && (NewEntry
!= NULL
)) {
511 CopyMem(NewEntry
, Entry
, sizeof(REFIT_MENU_SCREEN
));
512 NewEntry
->Title
= (Entry
->Title
) ? StrDuplicate(Entry
->Title
) : NULL
;
513 NewEntry
->TimeoutText
= (Entry
->TimeoutText
) ? StrDuplicate(Entry
->TimeoutText
) : NULL
;
514 if (Entry
->TitleImage
!= NULL
) {
515 NewEntry
->TitleImage
= AllocatePool(sizeof(EG_IMAGE
));
516 if (NewEntry
->TitleImage
!= NULL
)
517 CopyMem(NewEntry
->TitleImage
, Entry
->TitleImage
, sizeof(EG_IMAGE
));
519 NewEntry
->InfoLines
= (CHAR16
**) AllocateZeroPool(Entry
->InfoLineCount
* (sizeof(CHAR16
*)));
520 for (i
= 0; i
< Entry
->InfoLineCount
&& NewEntry
->InfoLines
; i
++) {
521 NewEntry
->InfoLines
[i
] = (Entry
->InfoLines
[i
]) ? StrDuplicate(Entry
->InfoLines
[i
]) : NULL
;
523 NewEntry
->Entries
= (REFIT_MENU_ENTRY
**) AllocateZeroPool(Entry
->EntryCount
* (sizeof (REFIT_MENU_ENTRY
*)));
524 for (i
= 0; i
< Entry
->EntryCount
&& NewEntry
->Entries
; i
++) {
525 AddMenuEntry(NewEntry
, Entry
->Entries
[i
]);
527 NewEntry
->Hint1
= (Entry
->Hint1
) ? StrDuplicate(Entry
->Hint1
) : NULL
;
528 NewEntry
->Hint2
= (Entry
->Hint2
) ? StrDuplicate(Entry
->Hint2
) : NULL
;
531 } // static REFIT_MENU_SCREEN* CopyMenuScreen()
533 // Creates a copy of a menu entry. Intended to enable moving a stack-based
534 // menu entry (such as the ones for the "reboot" and "exit" functions) to
535 // to the heap. This enables easier deletion of the whole set of menu
536 // entries when re-scanning.
537 // Returns a pointer to the copy of the menu entry.
538 static REFIT_MENU_ENTRY
* CopyMenuEntry(REFIT_MENU_ENTRY
*Entry
) {
539 REFIT_MENU_ENTRY
*NewEntry
;
541 NewEntry
= AllocateZeroPool(sizeof(REFIT_MENU_ENTRY
));
542 if ((Entry
!= NULL
) && (NewEntry
!= NULL
)) {
543 CopyMem(NewEntry
, Entry
, sizeof(REFIT_MENU_ENTRY
));
544 NewEntry
->Title
= (Entry
->Title
) ? StrDuplicate(Entry
->Title
) : NULL
;
545 if (Entry
->BadgeImage
!= NULL
) {
546 NewEntry
->BadgeImage
= AllocatePool(sizeof(EG_IMAGE
));
547 if (NewEntry
->BadgeImage
!= NULL
)
548 CopyMem(NewEntry
->BadgeImage
, Entry
->BadgeImage
, sizeof(EG_IMAGE
));
550 if (Entry
->Image
!= NULL
) {
551 NewEntry
->Image
= AllocatePool(sizeof(EG_IMAGE
));
552 if (NewEntry
->Image
!= NULL
)
553 CopyMem(NewEntry
->Image
, Entry
->Image
, sizeof(EG_IMAGE
));
555 if (Entry
->SubScreen
!= NULL
) {
556 NewEntry
->SubScreen
= CopyMenuScreen(Entry
->SubScreen
);
560 } // REFIT_MENU_ENTRY* CopyMenuEntry()
562 // Creates a new LOADER_ENTRY data structure and populates it with
563 // default values from the specified Entry, or NULL values if Entry
564 // is unspecified (NULL).
565 // Returns a pointer to the new data structure, or NULL if it
566 // couldn't be allocated
567 LOADER_ENTRY
*InitializeLoaderEntry(IN LOADER_ENTRY
*Entry
) {
568 LOADER_ENTRY
*NewEntry
= NULL
;
570 NewEntry
= AllocateZeroPool(sizeof(LOADER_ENTRY
));
571 if (NewEntry
!= NULL
) {
572 NewEntry
->me
.Title
= NULL
;
573 NewEntry
->me
.Tag
= TAG_LOADER
;
574 NewEntry
->Enabled
= TRUE
;
575 NewEntry
->UseGraphicsMode
= FALSE
;
576 NewEntry
->OSType
= 0;
578 NewEntry
->LoaderPath
= (Entry
->LoaderPath
) ? StrDuplicate(Entry
->LoaderPath
) : NULL
;
579 NewEntry
->VolName
= (Entry
->VolName
) ? StrDuplicate(Entry
->VolName
) : NULL
;
580 NewEntry
->DevicePath
= Entry
->DevicePath
;
581 NewEntry
->UseGraphicsMode
= Entry
->UseGraphicsMode
;
582 NewEntry
->LoadOptions
= (Entry
->LoadOptions
) ? StrDuplicate(Entry
->LoadOptions
) : NULL
;
583 NewEntry
->InitrdPath
= (Entry
->InitrdPath
) ? StrDuplicate(Entry
->InitrdPath
) : NULL
;
587 } // LOADER_ENTRY *InitializeLoaderEntry()
589 // Adds InitrdPath to Options, but only if Options doesn't already include an
590 // initrd= line. Done to enable overriding the default initrd selection in a
591 // refind_linux.conf file's options list.
592 // Returns a pointer to a new string. The calling function is responsible for
593 // freeing its memory.
594 static CHAR16
*AddInitrdToOptions(CHAR16
*Options
, CHAR16
*InitrdPath
) {
595 CHAR16
*NewOptions
= NULL
;
598 NewOptions
= StrDuplicate(Options
);
599 if ((InitrdPath
!= NULL
) && !StriSubCmp(L
"initrd=", Options
)) {
600 MergeStrings(&NewOptions
, L
"initrd=", L
' ');
601 MergeStrings(&NewOptions
, InitrdPath
, 0);
604 } // CHAR16 *AddInitrdToOptions()
606 // Prepare a REFIT_MENU_SCREEN data structure for a subscreen entry. This sets up
607 // the default entry that launches the boot loader using the same options as the
608 // main Entry does. Subsequent options can be added by the calling function.
609 // If a subscreen already exists in the Entry that's passed to this function,
610 // it's left unchanged and a pointer to it is returned.
611 // Returns a pointer to the new subscreen data structure, or NULL if there
612 // were problems allocating memory.
613 REFIT_MENU_SCREEN
*InitializeSubScreen(IN LOADER_ENTRY
*Entry
) {
614 CHAR16
*FileName
, *MainOptions
= NULL
;
615 REFIT_MENU_SCREEN
*SubScreen
= NULL
;
616 LOADER_ENTRY
*SubEntry
;
618 FileName
= Basename(Entry
->LoaderPath
);
619 if (Entry
->me
.SubScreen
== NULL
) { // No subscreen yet; initialize default entry....
620 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
621 if (SubScreen
!= NULL
) {
622 SubScreen
->Title
= AllocateZeroPool(sizeof(CHAR16
) * 256);
623 SPrint(SubScreen
->Title
, 255, L
"Boot Options for %s on %s",
624 (Entry
->Title
!= NULL
) ? Entry
->Title
: FileName
, Entry
->VolName
);
625 SubScreen
->TitleImage
= Entry
->me
.Image
;
627 SubEntry
= InitializeLoaderEntry(Entry
);
628 if (SubEntry
!= NULL
) {
629 SubEntry
->me
.Title
= StrDuplicate(L
"Boot using default options");
630 MainOptions
= SubEntry
->LoadOptions
;
631 SubEntry
->LoadOptions
= AddInitrdToOptions(MainOptions
, SubEntry
->InitrdPath
);
632 MyFreePool(MainOptions
);
633 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
634 } // if (SubEntry != NULL)
635 SubScreen
->Hint1
= StrDuplicate(SUBSCREEN_HINT1
);
636 if (GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_EDITOR
) {
637 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2_NO_EDITOR
);
639 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2
);
641 } // if (SubScreen != NULL)
642 } else { // existing subscreen; less initialization, and just add new entry later....
643 SubScreen
= Entry
->me
.SubScreen
;
646 } // REFIT_MENU_SCREEN *InitializeSubScreen()
648 VOID
GenerateSubScreen(LOADER_ENTRY
*Entry
, IN REFIT_VOLUME
*Volume
) {
649 REFIT_MENU_SCREEN
*SubScreen
;
650 LOADER_ENTRY
*SubEntry
;
652 CHAR16 DiagsFileName
[256];
657 // create the submenu
658 if (StrLen(Entry
->Title
) == 0) {
659 MyFreePool(Entry
->Title
);
662 SubScreen
= InitializeSubScreen(Entry
);
664 // loader-specific submenu entries
665 if (Entry
->OSType
== 'M') { // entries for Mac OS X
667 SubEntry
= InitializeLoaderEntry(Entry
);
668 if (SubEntry
!= NULL
) {
669 SubEntry
->me
.Title
= L
"Boot Mac OS X with a 64-bit kernel";
670 SubEntry
->LoadOptions
= L
"arch=x86_64";
671 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
672 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
675 SubEntry
= InitializeLoaderEntry(Entry
);
676 if (SubEntry
!= NULL
) {
677 SubEntry
->me
.Title
= L
"Boot Mac OS X with a 32-bit kernel";
678 SubEntry
->LoadOptions
= L
"arch=i386";
679 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
680 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
684 if (!(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_SINGLEUSER
)) {
685 SubEntry
= InitializeLoaderEntry(Entry
);
686 if (SubEntry
!= NULL
) {
687 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode";
688 SubEntry
->UseGraphicsMode
= FALSE
;
689 SubEntry
->LoadOptions
= L
"-v";
690 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
694 SubEntry
= InitializeLoaderEntry(Entry
);
695 if (SubEntry
!= NULL
) {
696 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode (64-bit)";
697 SubEntry
->UseGraphicsMode
= FALSE
;
698 SubEntry
->LoadOptions
= L
"-v arch=x86_64";
699 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
702 SubEntry
= InitializeLoaderEntry(Entry
);
703 if (SubEntry
!= NULL
) {
704 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode (32-bit)";
705 SubEntry
->UseGraphicsMode
= FALSE
;
706 SubEntry
->LoadOptions
= L
"-v arch=i386";
707 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
711 SubEntry
= InitializeLoaderEntry(Entry
);
712 if (SubEntry
!= NULL
) {
713 SubEntry
->me
.Title
= L
"Boot Mac OS X in single user mode";
714 SubEntry
->UseGraphicsMode
= FALSE
;
715 SubEntry
->LoadOptions
= L
"-v -s";
716 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
718 } // single-user mode allowed
720 if (!(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_SAFEMODE
)) {
721 SubEntry
= InitializeLoaderEntry(Entry
);
722 if (SubEntry
!= NULL
) {
723 SubEntry
->me
.Title
= L
"Boot Mac OS X in safe mode";
724 SubEntry
->UseGraphicsMode
= FALSE
;
725 SubEntry
->LoadOptions
= L
"-v -x";
726 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
728 } // safe mode allowed
730 // check for Apple hardware diagnostics
731 StrCpy(DiagsFileName
, L
"System\\Library\\CoreServices\\.diagnostics\\diags.efi");
732 if (FileExists(Volume
->RootDir
, DiagsFileName
) && !(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_HWTEST
)) {
733 SubEntry
= InitializeLoaderEntry(Entry
);
734 if (SubEntry
!= NULL
) {
735 SubEntry
->me
.Title
= L
"Run Apple Hardware Test";
736 MyFreePool(SubEntry
->LoaderPath
);
737 SubEntry
->LoaderPath
= StrDuplicate(DiagsFileName
);
738 SubEntry
->DevicePath
= FileDevicePath(Volume
->DeviceHandle
, SubEntry
->LoaderPath
);
739 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
740 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
742 } // if diagnostics entry found
744 } else if (Entry
->OSType
== 'L') { // entries for Linux kernels with EFI stub loaders
745 File
= ReadLinuxOptionsFile(Entry
->LoaderPath
, Volume
);
747 InitrdName
= FindInitrd(Entry
->LoaderPath
, Volume
);
748 TokenCount
= ReadTokenLine(File
, &TokenList
);
749 // first entry requires special processing, since it was initially set
750 // up with a default title but correct options by InitializeSubScreen(),
752 if ((SubScreen
->Entries
!= NULL
) && (SubScreen
->Entries
[0] != NULL
)) {
753 MyFreePool(SubScreen
->Entries
[0]->Title
);
754 SubScreen
->Entries
[0]->Title
= TokenList
[0] ? StrDuplicate(TokenList
[0]) : StrDuplicate(L
"Boot Linux");
756 FreeTokenLine(&TokenList
, &TokenCount
);
757 while ((TokenCount
= ReadTokenLine(File
, &TokenList
)) > 1) {
758 SubEntry
= InitializeLoaderEntry(Entry
);
759 SubEntry
->me
.Title
= TokenList
[0] ? StrDuplicate(TokenList
[0]) : StrDuplicate(L
"Boot Linux");
760 MyFreePool(SubEntry
->LoadOptions
);
761 SubEntry
->LoadOptions
= AddInitrdToOptions(TokenList
[1], InitrdName
);
762 FreeTokenLine(&TokenList
, &TokenCount
);
763 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_LINUX
;
764 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
766 MyFreePool(InitrdName
);
770 } else if (Entry
->OSType
== 'E') { // entries for ELILO
771 SubEntry
= InitializeLoaderEntry(Entry
);
772 if (SubEntry
!= NULL
) {
773 SubEntry
->me
.Title
= L
"Run ELILO in interactive mode";
774 SubEntry
->LoadOptions
= L
"-p";
775 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
776 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
779 SubEntry
= InitializeLoaderEntry(Entry
);
780 if (SubEntry
!= NULL
) {
781 SubEntry
->me
.Title
= L
"Boot Linux for a 17\" iMac or a 15\" MacBook Pro (*)";
782 SubEntry
->UseGraphicsMode
= TRUE
;
783 SubEntry
->LoadOptions
= L
"-d 0 i17";
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 20\" iMac (*)";
791 SubEntry
->UseGraphicsMode
= TRUE
;
792 SubEntry
->LoadOptions
= L
"-d 0 i20";
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 Mac Mini (*)";
800 SubEntry
->UseGraphicsMode
= TRUE
;
801 SubEntry
->LoadOptions
= L
"-d 0 mini";
802 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
803 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
806 AddMenuInfoLine(SubScreen
, L
"NOTE: This is an example. Entries");
807 AddMenuInfoLine(SubScreen
, L
"marked with (*) may not work.");
809 } else if (Entry
->OSType
== 'X') { // entries for xom.efi
810 // by default, skip the built-in selection and boot from hard disk only
811 Entry
->LoadOptions
= L
"-s -h";
813 SubEntry
= InitializeLoaderEntry(Entry
);
814 if (SubEntry
!= NULL
) {
815 SubEntry
->me
.Title
= L
"Boot Windows from Hard Disk";
816 SubEntry
->LoadOptions
= L
"-s -h";
817 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
818 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
821 SubEntry
= InitializeLoaderEntry(Entry
);
822 if (SubEntry
!= NULL
) {
823 SubEntry
->me
.Title
= L
"Boot Windows from CD-ROM";
824 SubEntry
->LoadOptions
= L
"-s -c";
825 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
826 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
829 SubEntry
= InitializeLoaderEntry(Entry
);
830 if (SubEntry
!= NULL
) {
831 SubEntry
->me
.Title
= L
"Run XOM in text mode";
832 SubEntry
->UseGraphicsMode
= FALSE
;
833 SubEntry
->LoadOptions
= L
"-v";
834 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
835 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
837 } // entries for xom.efi
838 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
839 Entry
->me
.SubScreen
= SubScreen
;
840 } // VOID GenerateSubScreen()
842 // Returns options for a Linux kernel. Reads them from an options file in the
843 // kernel's directory; and if present, adds an initrd= option for an initial
844 // RAM disk file with the same version number as the kernel file.
845 static CHAR16
* GetMainLinuxOptions(IN CHAR16
* LoaderPath
, IN REFIT_VOLUME
*Volume
) {
846 CHAR16
*Options
= NULL
, *InitrdName
, *FullOptions
= NULL
;
848 Options
= GetFirstOptionsFromFile(LoaderPath
, Volume
);
849 InitrdName
= FindInitrd(LoaderPath
, Volume
);
850 FullOptions
= AddInitrdToOptions(Options
, InitrdName
);
853 MyFreePool(InitrdName
);
854 return (FullOptions
);
855 } // static CHAR16 * GetMainLinuxOptions()
857 // Try to guess the name of the Linux distribution & add that name to
859 static VOID
GuessLinuxDistribution(CHAR16
**OSIconName
, REFIT_VOLUME
*Volume
, CHAR16
*LoaderPath
) {
863 UINTN TokenCount
= 0;
865 // If on Linux root fs, /etc/os-release file probably has clues....
866 if (FileExists(Volume
->RootDir
, L
"etc\\os-release") &&
867 (ReadFile(Volume
->RootDir
, L
"etc\\os-release", &File
, &FileSize
) == EFI_SUCCESS
)) {
869 TokenCount
= ReadTokenLine(&File
, &TokenList
);
870 if ((TokenCount
> 1) && ((StriCmp(TokenList
[0], L
"ID") == 0) || (StriCmp(TokenList
[0], L
"NAME") == 0))) {
871 MergeStrings(OSIconName
, TokenList
[1], L
',');
873 FreeTokenLine(&TokenList
, &TokenCount
);
874 } while (TokenCount
> 0);
875 MyFreePool(File
.Buffer
);
878 // Search for clues in the kernel's filename....
879 if (StriSubCmp(L
".fc", LoaderPath
))
880 MergeStrings(OSIconName
, L
"fedora", L
',');
881 if (StriSubCmp(L
".el", LoaderPath
))
882 MergeStrings(OSIconName
, L
"redhat", L
',');
883 } // VOID GuessLinuxDistribution()
885 // Sets a few defaults for a loader entry -- mainly the icon, but also the OS type
886 // code and shortcut letter. For Linux EFI stub loaders, also sets kernel options
887 // that will (with luck) work fairly automatically.
888 VOID
SetLoaderDefaults(LOADER_ENTRY
*Entry
, CHAR16
*LoaderPath
, REFIT_VOLUME
*Volume
) {
889 CHAR16
*FileName
, *PathOnly
, *NoExtension
, *OSIconName
= NULL
, *Temp
, *SubString
;
890 CHAR16 ShortcutLetter
= 0;
893 FileName
= Basename(LoaderPath
);
894 PathOnly
= FindPath(LoaderPath
);
895 NoExtension
= StripEfiExtension(FileName
);
897 // locate a custom icon for the loader
898 // Anything found here takes precedence over the "hints" in the OSIconName variable
899 if (!Entry
->me
.Image
)
900 Entry
->me
.Image
= egLoadIconAnyType(Volume
->RootDir
, PathOnly
, NoExtension
, 128);
901 if (!Entry
->me
.Image
)
902 Entry
->me
.Image
= egCopyImage(Volume
->VolIconImage
);
904 // Begin creating icon "hints" by using last part of directory path leading
906 Temp
= FindLastDirName(LoaderPath
);
907 MergeStrings(&OSIconName
, Temp
, L
',');
910 if (OSIconName
!= NULL
) {
911 ShortcutLetter
= OSIconName
[0];
914 // Add every "word" in the volume label, delimited by spaces, dashes (-), or
915 // underscores (_), to the list of hints to be used in searching for OS
917 if ((Volume
->VolName
) && (StrLen(Volume
->VolName
) > 0)) {
918 Temp
= SubString
= StrDuplicate(Volume
->VolName
);
920 Length
= StrLen(Temp
);
921 for (i
= 0; i
< Length
; i
++) {
922 if ((Temp
[i
] == L
' ') || (Temp
[i
] == L
'_') || (Temp
[i
] == L
'-')) {
924 if (StrLen(SubString
) > 0)
925 MergeStrings(&OSIconName
, SubString
, L
',');
926 SubString
= Temp
+ i
+ 1;
929 MergeStrings(&OSIconName
, SubString
, L
',');
934 // detect specific loaders
935 if (StriSubCmp(L
"bzImage", FileName
) || StriSubCmp(L
"vmlinuz", FileName
)) {
936 GuessLinuxDistribution(&OSIconName
, Volume
, LoaderPath
);
937 MergeStrings(&OSIconName
, L
"linux", L
',');
939 if (ShortcutLetter
== 0)
940 ShortcutLetter
= 'L';
941 Entry
->LoadOptions
= GetMainLinuxOptions(LoaderPath
, Volume
);
942 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_LINUX
;
943 } else if (StriSubCmp(L
"refit", LoaderPath
)) {
944 MergeStrings(&OSIconName
, L
"refit", L
',');
946 ShortcutLetter
= 'R';
947 } else if (StriSubCmp(L
"refind", LoaderPath
)) {
948 MergeStrings(&OSIconName
, L
"refind", L
',');
950 ShortcutLetter
= 'R';
951 } else if (StriCmp(LoaderPath
, MACOSX_LOADER_PATH
) == 0) {
952 if (Volume
->VolIconImage
!= NULL
) { // custom icon file found
953 Entry
->me
.Image
= Volume
->VolIconImage
;
955 MergeStrings(&OSIconName
, L
"mac", L
',');
957 ShortcutLetter
= 'M';
958 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
959 } else if (StriCmp(FileName
, L
"diags.efi") == 0) {
960 MergeStrings(&OSIconName
, L
"hwtest", L
',');
961 } else if (StriCmp(FileName
, L
"e.efi") == 0 || StriCmp(FileName
, L
"elilo.efi") == 0 || StriSubCmp(L
"elilo", FileName
)) {
962 MergeStrings(&OSIconName
, L
"elilo,linux", L
',');
964 if (ShortcutLetter
== 0)
965 ShortcutLetter
= 'L';
966 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
967 } else if (StriSubCmp(L
"grub", FileName
)) {
969 ShortcutLetter
= 'G';
970 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_GRUB
;
971 } else if (StriCmp(FileName
, L
"cdboot.efi") == 0 ||
972 StriCmp(FileName
, L
"bootmgr.efi") == 0 ||
973 StriCmp(FileName
, L
"bootmgfw.efi") == 0 ||
974 StriCmp(FileName
, L
"bkpbootmgfw.efi") == 0) {
975 MergeStrings(&OSIconName
, L
"win", L
',');
977 ShortcutLetter
= 'W';
978 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
979 } else if (StriCmp(FileName
, L
"xom.efi") == 0) {
980 MergeStrings(&OSIconName
, L
"xom,win", L
',');
981 Entry
->UseGraphicsMode
= TRUE
;
983 ShortcutLetter
= 'W';
984 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
987 if ((ShortcutLetter
>= 'a') && (ShortcutLetter
<= 'z'))
988 ShortcutLetter
= ShortcutLetter
- 'a' + 'A'; // convert lowercase to uppercase
989 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
990 if (Entry
->me
.Image
== NULL
)
991 Entry
->me
.Image
= LoadOSIcon(OSIconName
, L
"unknown", FALSE
);
992 MyFreePool(PathOnly
);
993 } // VOID SetLoaderDefaults()
995 // Add a specified EFI boot loader to the list, using automatic settings
996 // for icons, options, etc.
997 LOADER_ENTRY
* AddLoaderEntry(IN CHAR16
*LoaderPath
, IN CHAR16
*LoaderTitle
, IN REFIT_VOLUME
*Volume
) {
1000 CleanUpPathNameSlashes(LoaderPath
);
1001 Entry
= InitializeLoaderEntry(NULL
);
1002 if (Entry
!= NULL
) {
1003 Entry
->Title
= StrDuplicate((LoaderTitle
!= NULL
) ? LoaderTitle
: LoaderPath
);
1004 Entry
->me
.Title
= AllocateZeroPool(sizeof(CHAR16
) * 256);
1005 SPrint(Entry
->me
.Title
, 255, L
"Boot %s from %s ", (LoaderTitle
!= NULL
) ? LoaderTitle
: LoaderPath
, Volume
->VolName
);
1007 Entry
->me
.BadgeImage
= Volume
->VolBadgeImage
;
1008 if ((LoaderPath
!= NULL
) && (LoaderPath
[0] != L
'\\')) {
1009 Entry
->LoaderPath
= StrDuplicate(L
"\\");
1011 Entry
->LoaderPath
= NULL
;
1013 MergeStrings(&(Entry
->LoaderPath
), LoaderPath
, 0);
1014 Entry
->VolName
= Volume
->VolName
;
1015 Entry
->DevicePath
= FileDevicePath(Volume
->DeviceHandle
, Entry
->LoaderPath
);
1016 SetLoaderDefaults(Entry
, LoaderPath
, Volume
);
1017 GenerateSubScreen(Entry
, Volume
);
1018 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1022 } // LOADER_ENTRY * AddLoaderEntry()
1024 // Returns -1 if (Time1 < Time2), +1 if (Time1 > Time2), or 0 if
1025 // (Time1 == Time2). Precision is only to the nearest second; since
1026 // this is used for sorting boot loader entries, differences smaller
1027 // than this are likely to be meaningless (and unlikely!).
1028 INTN
TimeComp(IN EFI_TIME
*Time1
, IN EFI_TIME
*Time2
) {
1029 INT64 Time1InSeconds
, Time2InSeconds
;
1031 // Following values are overestimates; I'm assuming 31 days in every month.
1032 // This is fine for the purpose of this function, which is limited
1033 Time1InSeconds
= Time1
->Second
+ (Time1
->Minute
* 60) + (Time1
->Hour
* 3600) + (Time1
->Day
* 86400) +
1034 (Time1
->Month
* 2678400) + ((Time1
->Year
- 1998) * 32140800);
1035 Time2InSeconds
= Time2
->Second
+ (Time2
->Minute
* 60) + (Time2
->Hour
* 3600) + (Time2
->Day
* 86400) +
1036 (Time2
->Month
* 2678400) + ((Time2
->Year
- 1998) * 32140800);
1037 if (Time1InSeconds
< Time2InSeconds
)
1039 else if (Time1InSeconds
> Time2InSeconds
)
1043 } // INTN TimeComp()
1045 // Adds a loader list element, keeping it sorted by date. Returns the new
1046 // first element (the one with the most recent date).
1047 static struct LOADER_LIST
* AddLoaderListEntry(struct LOADER_LIST
*LoaderList
, struct LOADER_LIST
*NewEntry
) {
1048 struct LOADER_LIST
*LatestEntry
, *CurrentEntry
, *PrevEntry
= NULL
;
1050 LatestEntry
= CurrentEntry
= LoaderList
;
1051 if (LoaderList
== NULL
) {
1052 LatestEntry
= NewEntry
;
1054 while ((CurrentEntry
!= NULL
) && (TimeComp(&(NewEntry
->TimeStamp
), &(CurrentEntry
->TimeStamp
)) < 0)) {
1055 PrevEntry
= CurrentEntry
;
1056 CurrentEntry
= CurrentEntry
->NextEntry
;
1058 NewEntry
->NextEntry
= CurrentEntry
;
1059 if (PrevEntry
== NULL
) {
1060 LatestEntry
= NewEntry
;
1062 PrevEntry
->NextEntry
= NewEntry
;
1065 return (LatestEntry
);
1066 } // static VOID AddLoaderListEntry()
1068 // Delete the LOADER_LIST linked list
1069 static VOID
CleanUpLoaderList(struct LOADER_LIST
*LoaderList
) {
1070 struct LOADER_LIST
*Temp
;
1072 while (LoaderList
!= NULL
) {
1074 LoaderList
= LoaderList
->NextEntry
;
1075 MyFreePool(Temp
->FileName
);
1078 } // static VOID CleanUpLoaderList()
1080 // Returns FALSE if the specified file/volume matches the GlobalConfig.DontScanDirs
1081 // or GlobalConfig.DontScanVolumes specification, or if Path points to a volume
1082 // other than the one specified by Volume, or if the specified path is SelfDir.
1083 // Returns TRUE if none of these conditions is met -- that is, if the path is
1084 // eligible for scanning.
1085 static BOOLEAN
ShouldScan(REFIT_VOLUME
*Volume
, CHAR16
*Path
) {
1086 CHAR16
*VolName
= NULL
, *DontScanDir
, *PathCopy
= NULL
;
1088 BOOLEAN ScanIt
= TRUE
;
1090 if (IsIn(Volume
->VolName
, GlobalConfig
.DontScanVolumes
))
1093 if ((StriCmp(Path
, SelfDirPath
) == 0) && (Volume
->DeviceHandle
== SelfVolume
->DeviceHandle
))
1096 // See if Path includes an explicit volume declaration that's NOT Volume....
1097 PathCopy
= StrDuplicate(Path
);
1098 if (SplitVolumeAndFilename(&PathCopy
, &VolName
)) {
1099 VolumeNumberToName(Volume
, &VolName
);
1100 if (VolName
&& StriCmp(VolName
, Volume
->VolName
) != 0) {
1103 } // if Path includes volume specification
1104 MyFreePool(PathCopy
);
1105 MyFreePool(VolName
);
1108 // See if Volume is in GlobalConfig.DontScanDirs....
1109 while ((DontScanDir
= FindCommaDelimited(GlobalConfig
.DontScanDirs
, i
++)) && ScanIt
) {
1110 SplitVolumeAndFilename(&DontScanDir
, &VolName
);
1111 CleanUpPathNameSlashes(DontScanDir
);
1112 VolumeNumberToName(Volume
, &VolName
);
1113 if (VolName
!= NULL
) {
1114 if ((StriCmp(VolName
, Volume
->VolName
) == 0) && (StriCmp(DontScanDir
, Path
) == 0))
1117 if (StriCmp(DontScanDir
, Path
) == 0)
1120 MyFreePool(DontScanDir
);
1121 MyFreePool(VolName
);
1126 } // BOOLEAN ShouldScan()
1128 // Returns TRUE if the file is byte-for-byte identical with the fallback file
1129 // on the volume AND if the file is not itself the fallback file; returns
1130 // FALSE if the file is not identical to the fallback file OR if the file
1131 // IS the fallback file. Intended for use in excluding the fallback boot
1132 // loader when it's a duplicate of another boot loader.
1133 static BOOLEAN
DuplicatesFallback(IN REFIT_VOLUME
*Volume
, IN CHAR16
*FileName
) {
1134 CHAR8
*FileContents
, *FallbackContents
;
1135 EFI_FILE_HANDLE FileHandle
, FallbackHandle
;
1136 EFI_FILE_INFO
*FileInfo
, *FallbackInfo
;
1137 UINTN FileSize
= 0, FallbackSize
= 0;
1139 BOOLEAN AreIdentical
= FALSE
;
1141 if (!FileExists(Volume
->RootDir
, FileName
) || !FileExists(Volume
->RootDir
, FALLBACK_FULLNAME
))
1144 CleanUpPathNameSlashes(FileName
);
1146 if (StriCmp(FileName
, FALLBACK_FULLNAME
) == 0)
1147 return FALSE
; // identical filenames, so not a duplicate....
1149 Status
= refit_call5_wrapper(Volume
->RootDir
->Open
, Volume
->RootDir
, &FileHandle
, FileName
, EFI_FILE_MODE_READ
, 0);
1150 if (Status
== EFI_SUCCESS
) {
1151 FileInfo
= LibFileInfo(FileHandle
);
1152 FileSize
= FileInfo
->FileSize
;
1157 Status
= refit_call5_wrapper(Volume
->RootDir
->Open
, Volume
->RootDir
, &FallbackHandle
, FALLBACK_FULLNAME
, EFI_FILE_MODE_READ
, 0);
1158 if (Status
== EFI_SUCCESS
) {
1159 FallbackInfo
= LibFileInfo(FallbackHandle
);
1160 FallbackSize
= FallbackInfo
->FileSize
;
1162 refit_call1_wrapper(FileHandle
->Close
, FileHandle
);
1166 if (FallbackSize
!= FileSize
) { // not same size, so can't be identical
1167 AreIdentical
= FALSE
;
1168 } else { // could be identical; do full check....
1169 FileContents
= AllocatePool(FileSize
);
1170 FallbackContents
= AllocatePool(FallbackSize
);
1171 if (FileContents
&& FallbackContents
) {
1172 Status
= refit_call3_wrapper(FileHandle
->Read
, FileHandle
, &FileSize
, FileContents
);
1173 if (Status
== EFI_SUCCESS
) {
1174 Status
= refit_call3_wrapper(FallbackHandle
->Read
, FallbackHandle
, &FallbackSize
, FallbackContents
);
1176 if (Status
== EFI_SUCCESS
) {
1177 AreIdentical
= (CompareMem(FileContents
, FallbackContents
, FileSize
) == 0);
1180 MyFreePool(FileContents
);
1181 MyFreePool(FallbackContents
);
1184 // BUG ALERT: Some systems (e.g., DUET, some Macs with large displays) crash if the
1185 // following two calls are reversed. Go figure....
1186 refit_call1_wrapper(FileHandle
->Close
, FallbackHandle
);
1187 refit_call1_wrapper(FileHandle
->Close
, FileHandle
);
1188 return AreIdentical
;
1189 } // BOOLEAN DuplicatesFallback()
1191 // Returns FALSE if two measures of file size are identical for a single file,
1192 // TRUE if not or if the file can't be opened and the other measure is non-0.
1193 // Despite the function's name, this isn't really a direct test of symbolic
1194 // link status, since EFI doesn't officially support symlinks. It does seem
1195 // to be a reliable indicator, though. (OTOH, some disk errors might cause a
1196 // file to fail to open, which would return a false positive -- but as I use
1197 // this function to exclude symbolic links from the list of boot loaders,
1198 // that would be fine, since such boot loaders wouldn't work.)
1199 static BOOLEAN
IsSymbolicLink(REFIT_VOLUME
*Volume
, CHAR16
*Path
, EFI_FILE_INFO
*DirEntry
) {
1200 EFI_FILE_HANDLE FileHandle
;
1201 EFI_FILE_INFO
*FileInfo
= NULL
;
1203 UINTN FileSize2
= 0;
1206 FileName
= StrDuplicate(Path
);
1207 MergeStrings(&FileName
, DirEntry
->FileName
, L
'\\');
1208 CleanUpPathNameSlashes(FileName
);
1210 Status
= refit_call5_wrapper(Volume
->RootDir
->Open
, Volume
->RootDir
, &FileHandle
, FileName
, EFI_FILE_MODE_READ
, 0);
1211 if (Status
== EFI_SUCCESS
) {
1212 FileInfo
= LibFileInfo(FileHandle
);
1213 if (FileInfo
!= NULL
)
1214 FileSize2
= FileInfo
->FileSize
;
1217 MyFreePool(FileName
);
1218 MyFreePool(FileInfo
);
1220 return (DirEntry
->FileSize
!= FileSize2
);
1221 } // BOOLEAN IsSymbolicLink()
1223 // Scan an individual directory for EFI boot loader files and, if found,
1224 // add them to the list. Exception: Ignores FALLBACK_FULLNAME, which is picked
1225 // up in ScanEfiFiles(). Sorts the entries within the loader directory so that
1226 // the most recent one appears first in the list.
1227 // Returns TRUE if a duplicate for FALLBACK_FILENAME was found, FALSE if not.
1228 static BOOLEAN
ScanLoaderDir(IN REFIT_VOLUME
*Volume
, IN CHAR16
*Path
, IN CHAR16
*Pattern
)
1231 REFIT_DIR_ITER DirIter
;
1232 EFI_FILE_INFO
*DirEntry
;
1233 CHAR16 FileName
[256], *Extension
;
1234 struct LOADER_LIST
*LoaderList
= NULL
, *NewLoader
;
1235 BOOLEAN FoundFallbackDuplicate
= FALSE
;
1237 if ((!SelfDirPath
|| !Path
|| ((StriCmp(Path
, SelfDirPath
) == 0) && (Volume
->DeviceHandle
!= SelfVolume
->DeviceHandle
)) ||
1238 (StriCmp(Path
, SelfDirPath
) != 0)) &&
1239 (ShouldScan(Volume
, Path
))) {
1240 // look through contents of the directory
1241 DirIterOpen(Volume
->RootDir
, Path
, &DirIter
);
1242 while (DirIterNext(&DirIter
, 2, Pattern
, &DirEntry
)) {
1243 Extension
= FindExtension(DirEntry
->FileName
);
1244 if (DirEntry
->FileName
[0] == '.' ||
1245 StriCmp(Extension
, L
".icns") == 0 ||
1246 StriCmp(Extension
, L
".png") == 0 ||
1247 (StriCmp(DirEntry
->FileName
, FALLBACK_BASENAME
) == 0 && (StriCmp(Path
, L
"EFI\\BOOT") == 0)) ||
1248 StriSubCmp(L
"shell", DirEntry
->FileName
) ||
1249 IsSymbolicLink(Volume
, Path
, DirEntry
) || /* is symbolic link */
1250 FilenameIn(Volume
, Path
, DirEntry
->FileName
, GlobalConfig
.DontScanFiles
))
1251 continue; // skip this
1254 SPrint(FileName
, 255, L
"\\%s\\%s", Path
, DirEntry
->FileName
);
1256 SPrint(FileName
, 255, L
"\\%s", DirEntry
->FileName
);
1257 CleanUpPathNameSlashes(FileName
);
1259 if(!IsValidLoader(Volume
->RootDir
, FileName
))
1262 NewLoader
= AllocateZeroPool(sizeof(struct LOADER_LIST
));
1263 if (NewLoader
!= NULL
) {
1264 NewLoader
->FileName
= StrDuplicate(FileName
);
1265 NewLoader
->TimeStamp
= DirEntry
->ModificationTime
;
1266 LoaderList
= AddLoaderListEntry(LoaderList
, NewLoader
);
1267 if (DuplicatesFallback(Volume
, FileName
))
1268 FoundFallbackDuplicate
= TRUE
;
1270 MyFreePool(Extension
);
1273 NewLoader
= LoaderList
;
1274 while (NewLoader
!= NULL
) {
1275 AddLoaderEntry(NewLoader
->FileName
, NULL
, Volume
);
1276 NewLoader
= NewLoader
->NextEntry
;
1279 CleanUpLoaderList(LoaderList
);
1280 Status
= DirIterClose(&DirIter
);
1281 // NOTE: EFI_INVALID_PARAMETER really is an error that should be reported;
1282 // but I've gotten reports from users who are getting this error occasionally
1283 // and I can't find anything wrong or reproduce the problem, so I'm putting
1284 // it down to buggy EFI implementations and ignoring that particular error....
1285 if ((Status
!= EFI_NOT_FOUND
) && (Status
!= EFI_INVALID_PARAMETER
)) {
1287 SPrint(FileName
, 255, L
"while scanning the %s directory", Path
);
1289 StrCpy(FileName
, L
"while scanning the root directory");
1290 CheckError(Status
, FileName
);
1291 } // if (Status != EFI_NOT_FOUND)
1292 } // if not scanning a blacklisted directory
1294 return FoundFallbackDuplicate
;
1295 } /* static VOID ScanLoaderDir() */
1297 static VOID
ScanEfiFiles(REFIT_VOLUME
*Volume
) {
1299 REFIT_DIR_ITER EfiDirIter
;
1300 EFI_FILE_INFO
*EfiDirEntry
;
1301 CHAR16 FileName
[256], *Directory
= NULL
, *MatchPatterns
, *VolName
= NULL
, *SelfPath
;
1303 BOOLEAN ScanFallbackLoader
= TRUE
;
1304 BOOLEAN FoundBRBackup
= FALSE
;
1306 MatchPatterns
= StrDuplicate(LOADER_MATCH_PATTERNS
);
1307 if (GlobalConfig
.ScanAllLinux
)
1308 MergeStrings(&MatchPatterns
, LINUX_MATCH_PATTERNS
, L
',');
1310 if ((Volume
->RootDir
!= NULL
) && (Volume
->VolName
!= NULL
) && (Volume
->IsReadable
)) {
1311 // check for Mac OS X boot loader
1312 if (ShouldScan(Volume
, L
"System\\Library\\CoreServices")) {
1313 StrCpy(FileName
, MACOSX_LOADER_PATH
);
1314 if (FileExists(Volume
->RootDir
, FileName
) && !FilenameIn(Volume
, Directory
, L
"boot.efi", GlobalConfig
.DontScanFiles
)) {
1315 AddLoaderEntry(FileName
, L
"Mac OS X", Volume
);
1316 if (DuplicatesFallback(Volume
, FileName
))
1317 ScanFallbackLoader
= FALSE
;
1321 StrCpy(FileName
, L
"System\\Library\\CoreServices\\xom.efi");
1322 if (FileExists(Volume
->RootDir
, FileName
) && !FilenameIn(Volume
, Directory
, L
"boot.efi", GlobalConfig
.DontScanFiles
)) {
1323 AddLoaderEntry(FileName
, L
"Windows XP (XoM)", Volume
);
1324 if (DuplicatesFallback(Volume
, FileName
))
1325 ScanFallbackLoader
= FALSE
;
1327 } // if should scan Mac directory
1329 // check for Microsoft boot loader/menu
1330 if (ShouldScan(Volume
, L
"EFI\\Microsoft\\Boot")) {
1331 StrCpy(FileName
, L
"EFI\\Microsoft\\Boot\\bkpbootmgfw.efi");
1332 if (FileExists(Volume
->RootDir
, FileName
) && !FilenameIn(Volume
, Directory
, L
"bkpbootmgfw.efi", GlobalConfig
.DontScanFiles
)) {
1333 AddLoaderEntry(FileName
, L
"Microsoft EFI boot (Boot Repair backup)", Volume
);
1334 FoundBRBackup
= TRUE
;
1335 if (DuplicatesFallback(Volume
, FileName
))
1336 ScanFallbackLoader
= FALSE
;
1338 StrCpy(FileName
, L
"EFI\\Microsoft\\Boot\\bootmgfw.efi");
1339 if (FileExists(Volume
->RootDir
, FileName
) && !FilenameIn(Volume
, Directory
, L
"bootmgfw.efi", GlobalConfig
.DontScanFiles
)) {
1341 AddLoaderEntry(FileName
, L
"Supposed Microsoft EFI boot (probably GRUB)", Volume
);
1343 AddLoaderEntry(FileName
, L
"Microsoft EFI boot", Volume
);
1344 if (DuplicatesFallback(Volume
, FileName
))
1345 ScanFallbackLoader
= FALSE
;
1349 // scan the root directory for EFI executables
1350 if (ScanLoaderDir(Volume
, L
"\\", MatchPatterns
))
1351 ScanFallbackLoader
= FALSE
;
1353 // scan subdirectories of the EFI directory (as per the standard)
1354 DirIterOpen(Volume
->RootDir
, L
"EFI", &EfiDirIter
);
1355 while (DirIterNext(&EfiDirIter
, 1, NULL
, &EfiDirEntry
)) {
1356 if (StriCmp(EfiDirEntry
->FileName
, L
"tools") == 0 || EfiDirEntry
->FileName
[0] == '.')
1357 continue; // skip this, doesn't contain boot loaders or is scanned later
1358 SPrint(FileName
, 255, L
"EFI\\%s", EfiDirEntry
->FileName
);
1359 if (ScanLoaderDir(Volume
, FileName
, MatchPatterns
))
1360 ScanFallbackLoader
= FALSE
;
1362 Status
= DirIterClose(&EfiDirIter
);
1363 if (Status
!= EFI_NOT_FOUND
)
1364 CheckError(Status
, L
"while scanning the EFI directory");
1366 // Scan user-specified (or additional default) directories....
1368 while ((Directory
= FindCommaDelimited(GlobalConfig
.AlsoScan
, i
++)) != NULL
) {
1369 if (ShouldScan(Volume
, Directory
)) {
1370 SplitVolumeAndFilename(&Directory
, &VolName
);
1371 CleanUpPathNameSlashes(Directory
);
1372 Length
= StrLen(Directory
);
1373 if ((Length
> 0) && ScanLoaderDir(Volume
, Directory
, MatchPatterns
))
1374 ScanFallbackLoader
= FALSE
;
1375 MyFreePool(VolName
);
1376 } // if should scan dir
1377 MyFreePool(Directory
);
1380 // Don't scan the fallback loader if it's on the same volume and a duplicate of rEFInd itself....
1381 SelfPath
= DevicePathToStr(SelfLoadedImage
->FilePath
);
1382 CleanUpPathNameSlashes(SelfPath
);
1383 if ((Volume
->DeviceHandle
== SelfLoadedImage
->DeviceHandle
) && DuplicatesFallback(Volume
, SelfPath
))
1384 ScanFallbackLoader
= FALSE
;
1386 // If not a duplicate & if it exists & if it's not us, create an entry
1387 // for the fallback boot loader
1388 if (ScanFallbackLoader
&& FileExists(Volume
->RootDir
, FALLBACK_FULLNAME
) && ShouldScan(Volume
, L
"EFI\\BOOT"))
1389 AddLoaderEntry(FALLBACK_FULLNAME
, L
"Fallback boot loader", Volume
);
1391 } // static VOID ScanEfiFiles()
1393 // Scan internal disks for valid EFI boot loaders....
1394 static VOID
ScanInternal(VOID
) {
1397 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1398 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_INTERNAL
) {
1399 ScanEfiFiles(Volumes
[VolumeIndex
]);
1402 } // static VOID ScanInternal()
1404 // Scan external disks for valid EFI boot loaders....
1405 static VOID
ScanExternal(VOID
) {
1408 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1409 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_EXTERNAL
) {
1410 ScanEfiFiles(Volumes
[VolumeIndex
]);
1413 } // static VOID ScanExternal()
1415 // Scan internal disks for valid EFI boot loaders....
1416 static VOID
ScanOptical(VOID
) {
1419 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1420 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_OPTICAL
) {
1421 ScanEfiFiles(Volumes
[VolumeIndex
]);
1424 } // static VOID ScanOptical()
1427 // legacy boot functions
1430 static EFI_STATUS
ActivateMbrPartition(IN EFI_BLOCK_IO
*BlockIO
, IN UINTN PartitionIndex
)
1433 UINT8 SectorBuffer
[512];
1434 MBR_PARTITION_INFO
*MbrTable
, *EMbrTable
;
1435 UINT32 ExtBase
, ExtCurrent
, NextExtCurrent
;
1436 UINTN LogicalPartitionIndex
= 4;
1438 BOOLEAN HaveBootCode
;
1441 Status
= refit_call5_wrapper(BlockIO
->ReadBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, 0, 512, SectorBuffer
);
1442 if (EFI_ERROR(Status
))
1444 if (*((UINT16
*)(SectorBuffer
+ 510)) != 0xaa55)
1445 return EFI_NOT_FOUND
; // safety measure #1
1447 // add boot code if necessary
1448 HaveBootCode
= FALSE
;
1449 for (i
= 0; i
< MBR_BOOTCODE_SIZE
; i
++) {
1450 if (SectorBuffer
[i
] != 0) {
1451 HaveBootCode
= TRUE
;
1455 if (!HaveBootCode
) {
1456 // no boot code found in the MBR, add the syslinux MBR code
1457 SetMem(SectorBuffer
, MBR_BOOTCODE_SIZE
, 0);
1458 CopyMem(SectorBuffer
, syslinux_mbr
, SYSLINUX_MBR_SIZE
);
1461 // set the partition active
1462 MbrTable
= (MBR_PARTITION_INFO
*)(SectorBuffer
+ 446);
1464 for (i
= 0; i
< 4; i
++) {
1465 if (MbrTable
[i
].Flags
!= 0x00 && MbrTable
[i
].Flags
!= 0x80)
1466 return EFI_NOT_FOUND
; // safety measure #2
1467 if (i
== PartitionIndex
)
1468 MbrTable
[i
].Flags
= 0x80;
1469 else if (PartitionIndex
>= 4 && IS_EXTENDED_PART_TYPE(MbrTable
[i
].Type
)) {
1470 MbrTable
[i
].Flags
= 0x80;
1471 ExtBase
= MbrTable
[i
].StartLBA
;
1473 MbrTable
[i
].Flags
= 0x00;
1477 Status
= refit_call5_wrapper(BlockIO
->WriteBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, 0, 512, SectorBuffer
);
1478 if (EFI_ERROR(Status
))
1481 if (PartitionIndex
>= 4) {
1482 // we have to activate a logical partition, so walk the EMBR chain
1484 // NOTE: ExtBase was set above while looking at the MBR table
1485 for (ExtCurrent
= ExtBase
; ExtCurrent
; ExtCurrent
= NextExtCurrent
) {
1486 // read current EMBR
1487 Status
= refit_call5_wrapper(BlockIO
->ReadBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, ExtCurrent
, 512, SectorBuffer
);
1488 if (EFI_ERROR(Status
))
1490 if (*((UINT16
*)(SectorBuffer
+ 510)) != 0xaa55)
1491 return EFI_NOT_FOUND
; // safety measure #3
1493 // scan EMBR, set appropriate partition active
1494 EMbrTable
= (MBR_PARTITION_INFO
*)(SectorBuffer
+ 446);
1496 for (i
= 0; i
< 4; i
++) {
1497 if (EMbrTable
[i
].Flags
!= 0x00 && EMbrTable
[i
].Flags
!= 0x80)
1498 return EFI_NOT_FOUND
; // safety measure #4
1499 if (EMbrTable
[i
].StartLBA
== 0 || EMbrTable
[i
].Size
== 0)
1501 if (IS_EXTENDED_PART_TYPE(EMbrTable
[i
].Type
)) {
1502 // link to next EMBR
1503 NextExtCurrent
= ExtBase
+ EMbrTable
[i
].StartLBA
;
1504 EMbrTable
[i
].Flags
= (PartitionIndex
>= LogicalPartitionIndex
) ? 0x80 : 0x00;
1507 // logical partition
1508 EMbrTable
[i
].Flags
= (PartitionIndex
== LogicalPartitionIndex
) ? 0x80 : 0x00;
1509 LogicalPartitionIndex
++;
1513 // write current EMBR
1514 Status
= refit_call5_wrapper(BlockIO
->WriteBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, ExtCurrent
, 512, SectorBuffer
);
1515 if (EFI_ERROR(Status
))
1518 if (PartitionIndex
< LogicalPartitionIndex
)
1519 break; // stop the loop, no need to touch further EMBRs
1525 } /* static EFI_STATUS ActivateMbrPartition() */
1527 // early 2006 Core Duo / Core Solo models
1528 static UINT8 LegacyLoaderDevicePath1Data
[] = {
1529 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1530 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1531 0xFF, 0xFF, 0xF9, 0xFF, 0x00, 0x00, 0x00, 0x00,
1532 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1533 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1534 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1536 // mid-2006 Mac Pro (and probably other Core 2 models)
1537 static UINT8 LegacyLoaderDevicePath2Data
[] = {
1538 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1539 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1540 0xFF, 0xFF, 0xF7, 0xFF, 0x00, 0x00, 0x00, 0x00,
1541 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1542 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1543 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1545 // mid-2007 MBP ("Santa Rosa" based models)
1546 static UINT8 LegacyLoaderDevicePath3Data
[] = {
1547 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1548 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1549 0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
1550 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1551 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1552 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1555 static UINT8 LegacyLoaderDevicePath4Data
[] = {
1556 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1557 0x00, 0x00, 0xC0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1558 0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
1559 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1560 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1561 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1563 // late-2008 MB/MBP (NVidia chipset)
1564 static UINT8 LegacyLoaderDevicePath5Data
[] = {
1565 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1566 0x00, 0x40, 0xCB, 0xFF, 0x00, 0x00, 0x00, 0x00,
1567 0xFF, 0xBF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
1568 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1569 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1570 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1573 static EFI_DEVICE_PATH
*LegacyLoaderList
[] = {
1574 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath1Data
,
1575 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath2Data
,
1576 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath3Data
,
1577 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath4Data
,
1578 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath5Data
,
1582 #define MAX_DISCOVERED_PATHS (16)
1584 static VOID
StartLegacy(IN LEGACY_ENTRY
*Entry
)
1587 EG_IMAGE
*BootLogoImage
;
1588 UINTN ErrorInStep
= 0;
1589 EFI_DEVICE_PATH
*DiscoveredPathList
[MAX_DISCOVERED_PATHS
];
1591 BeginExternalScreen(TRUE
, L
"Booting Legacy OS (Mac mode)");
1593 BootLogoImage
= LoadOSIcon(Entry
->Volume
->OSIconName
, L
"legacy", TRUE
);
1594 if (BootLogoImage
!= NULL
)
1595 BltImageAlpha(BootLogoImage
,
1596 (UGAWidth
- BootLogoImage
->Width
) >> 1,
1597 (UGAHeight
- BootLogoImage
->Height
) >> 1,
1598 &StdBackgroundPixel
);
1600 if (Entry
->Volume
->IsMbrPartition
) {
1601 ActivateMbrPartition(Entry
->Volume
->WholeDiskBlockIO
, Entry
->Volume
->MbrPartitionIndex
);
1604 ExtractLegacyLoaderPaths(DiscoveredPathList
, MAX_DISCOVERED_PATHS
, LegacyLoaderList
);
1606 Status
= StartEFIImageList(DiscoveredPathList
, Entry
->LoadOptions
, TYPE_LEGACY
, L
"legacy loader", 0, &ErrorInStep
, TRUE
);
1607 if (Status
== EFI_NOT_FOUND
) {
1608 if (ErrorInStep
== 1) {
1609 Print(L
"\nPlease make sure that you have the latest firmware update installed.\n");
1610 } else if (ErrorInStep
== 3) {
1611 Print(L
"\nThe firmware refused to boot from the selected volume. Note that external\n"
1612 L
"hard drives are not well-supported by Apple's firmware for legacy OS booting.\n");
1615 FinishExternalScreen();
1616 } /* static VOID StartLegacy() */
1618 // Start a device on a non-Mac using the EFI_LEGACY_BIOS_PROTOCOL
1619 #ifdef __MAKEWITH_TIANO
1620 static VOID
StartLegacyUEFI(LEGACY_ENTRY
*Entry
)
1622 // UINTN ExitDataSize = 0;
1623 // CHAR16 *ExitData = NULL;
1624 // EFI_STATUS Status;
1626 BeginExternalScreen(TRUE
, L
"Booting Legacy OS (UEFI mode)");
1627 // Print(L"Launching from '%s'\n", DevicePathToStr(Entry->BdsOption->DevicePath));
1630 // Status = BdsLibBootViaBootOption(Entry->BdsOption, Entry->BdsOption->DevicePath, &ExitDataSize, &ExitData);
1631 // Print(L"BdsLibBootViaBootOption() returned %d\n", Status);
1632 BdsLibConnectDevicePath (Entry
->BdsOption
->DevicePath
);
1633 BdsLibDoLegacyBoot(Entry
->BdsOption
);
1635 // If we get here, it means that there was a failure....
1636 Print(L
"Failure booting legacy (BIOS) OS.");
1638 FinishExternalScreen();
1639 } // static VOID StartLegacyUEFI()
1640 #endif // __MAKEWITH_TIANO
1642 static LEGACY_ENTRY
* AddLegacyEntry(IN CHAR16
*LoaderTitle
, IN REFIT_VOLUME
*Volume
)
1644 LEGACY_ENTRY
*Entry
, *SubEntry
;
1645 REFIT_MENU_SCREEN
*SubScreen
;
1647 CHAR16 ShortcutLetter
= 0;
1649 if (LoaderTitle
== NULL
) {
1650 if (Volume
->OSName
!= NULL
) {
1651 LoaderTitle
= Volume
->OSName
;
1652 if (LoaderTitle
[0] == 'W' || LoaderTitle
[0] == 'L')
1653 ShortcutLetter
= LoaderTitle
[0];
1655 LoaderTitle
= L
"Legacy OS";
1657 if (Volume
->VolName
!= NULL
)
1658 VolDesc
= Volume
->VolName
;
1660 VolDesc
= (Volume
->DiskKind
== DISK_KIND_OPTICAL
) ? L
"CD" : L
"HD";
1662 // prepare the menu entry
1663 Entry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1664 Entry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1665 SPrint(Entry
->me
.Title
, 255, L
"Boot %s from %s", LoaderTitle
, VolDesc
);
1666 Entry
->me
.Tag
= TAG_LEGACY
;
1668 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1669 Entry
->me
.Image
= LoadOSIcon(Volume
->OSIconName
, L
"legacy", FALSE
);
1670 Entry
->me
.BadgeImage
= Volume
->VolBadgeImage
;
1671 Entry
->Volume
= Volume
;
1672 Entry
->LoadOptions
= (Volume
->DiskKind
== DISK_KIND_OPTICAL
) ? L
"CD" :
1673 ((Volume
->DiskKind
== DISK_KIND_EXTERNAL
) ? L
"USB" : L
"HD");
1674 Entry
->Enabled
= TRUE
;
1676 // create the submenu
1677 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
1678 SubScreen
->Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1679 SPrint(SubScreen
->Title
, 255, L
"Boot Options for %s on %s", LoaderTitle
, VolDesc
);
1680 SubScreen
->TitleImage
= Entry
->me
.Image
;
1681 SubScreen
->Hint1
= StrDuplicate(SUBSCREEN_HINT1
);
1682 if (GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_EDITOR
) {
1683 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2_NO_EDITOR
);
1685 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2
);
1689 SubEntry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1690 SubEntry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1691 SPrint(SubEntry
->me
.Title
, 255, L
"Boot %s", LoaderTitle
);
1692 SubEntry
->me
.Tag
= TAG_LEGACY
;
1693 SubEntry
->Volume
= Entry
->Volume
;
1694 SubEntry
->LoadOptions
= Entry
->LoadOptions
;
1695 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
1697 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
1698 Entry
->me
.SubScreen
= SubScreen
;
1699 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1701 } /* static LEGACY_ENTRY * AddLegacyEntry() */
1704 #ifdef __MAKEWITH_GNUEFI
1705 static VOID
ScanLegacyUEFI(IN UINTN DiskType
){}
1707 // default volume badge icon based on disk kind
1708 static EG_IMAGE
* GetDiskBadge(IN UINTN DiskType
) {
1709 EG_IMAGE
* Badge
= NULL
;
1713 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_INTERNAL
);
1716 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_EXTERNAL
);
1719 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_OPTICAL
);
1723 } // static EG_IMAGE * GetDiskBadge()
1726 Create a rEFInd boot option from a Legacy BIOS protocol option.
1728 static LEGACY_ENTRY
* AddLegacyEntryUEFI(BDS_COMMON_OPTION
*BdsOption
, IN UINT16 DiskType
)
1730 LEGACY_ENTRY
*Entry
, *SubEntry
;
1731 REFIT_MENU_SCREEN
*SubScreen
;
1732 CHAR16 ShortcutLetter
= 0;
1733 CHAR16
*LegacyDescription
= BdsOption
->Description
;
1735 // prepare the menu entry
1736 Entry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1737 Entry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1738 SPrint(Entry
->me
.Title
, 255, L
"Boot legacy target %s", LegacyDescription
);
1739 Entry
->me
.Tag
= TAG_LEGACY_UEFI
;
1741 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1742 Entry
->me
.Image
= LoadOSIcon(L
"legacy", L
"legacy", TRUE
);
1743 Entry
->LoadOptions
= (DiskType
== BBS_CDROM
) ? L
"CD" :
1744 ((DiskType
== BBS_USB
) ? L
"USB" : L
"HD");
1745 Entry
->me
.BadgeImage
= GetDiskBadge(DiskType
);
1746 Entry
->BdsOption
= BdsOption
;
1747 Entry
->Enabled
= TRUE
;
1749 // create the submenu
1750 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
1751 SubScreen
->Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1752 SPrint(SubScreen
->Title
, 255, L
"No boot options for legacy target");
1753 SubScreen
->TitleImage
= Entry
->me
.Image
;
1754 SubScreen
->Hint1
= StrDuplicate(SUBSCREEN_HINT1
);
1755 if (GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_EDITOR
) {
1756 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2_NO_EDITOR
);
1758 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2
);
1762 SubEntry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1763 SubEntry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1764 SPrint(SubEntry
->me
.Title
, 255, L
"Boot %s", LegacyDescription
);
1765 SubEntry
->me
.Tag
= TAG_LEGACY_UEFI
;
1766 Entry
->BdsOption
= BdsOption
;
1767 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
1769 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
1770 Entry
->me
.SubScreen
= SubScreen
;
1771 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1773 } /* static LEGACY_ENTRY * AddLegacyEntryUEFI() */
1776 Scan for legacy BIOS targets on machines that implement EFI_LEGACY_BIOS_PROTOCOL.
1777 In testing, protocol has not been implemented on Macs but has been
1778 implemented on several Dell PCs and an ASUS motherboard.
1779 Restricts output to disks of the specified DiskType.
1781 static VOID
ScanLegacyUEFI(IN UINTN DiskType
)
1784 EFI_LEGACY_BIOS_PROTOCOL
*LegacyBios
;
1785 UINT16
*BootOrder
= NULL
;
1787 CHAR16 BootOption
[10];
1788 UINTN BootOrderSize
= 0;
1790 BDS_COMMON_OPTION
*BdsOption
;
1791 LIST_ENTRY TempList
;
1792 BBS_BBS_DEVICE_PATH
* BbsDevicePath
= NULL
;
1794 InitializeListHead (&TempList
);
1795 ZeroMem (Buffer
, sizeof (Buffer
));
1797 // If LegacyBios protocol is not implemented on this platform, then
1798 //we do not support this type of legacy boot on this machine.
1799 Status
= gBS
->LocateProtocol(&gEfiLegacyBootProtocolGuid
, NULL
, (VOID
**) &LegacyBios
);
1800 if (EFI_ERROR (Status
))
1803 // Grab the boot order
1804 BootOrder
= BdsLibGetVariableAndSize(L
"BootOrder", &gEfiGlobalVariableGuid
, &BootOrderSize
);
1805 if (BootOrder
== NULL
) {
1810 while (Index
< BootOrderSize
/ sizeof (UINT16
))
1812 // Grab each boot option variable from the boot order, and convert
1813 // the variable into a BDS boot option
1814 UnicodeSPrint (BootOption
, sizeof (BootOption
), L
"Boot%04x", BootOrder
[Index
]);
1815 BdsOption
= BdsLibVariableToOption (&TempList
, BootOption
);
1817 if (BdsOption
!= NULL
) {
1818 BbsDevicePath
= (BBS_BBS_DEVICE_PATH
*)BdsOption
->DevicePath
;
1820 // Only add the entry if it is of a requested type (e.g. USB, HD)
1822 // Two checks necessary because some systems return EFI boot loaders
1823 // with a DeviceType value that would inappropriately include them
1824 // as legacy loaders....
1825 if ((BbsDevicePath
->DeviceType
== DiskType
) && (BdsOption
->DevicePath
->Type
== DEVICE_TYPE_BIOS
)) {
1826 AddLegacyEntryUEFI(BdsOption
, BbsDevicePath
->DeviceType
);
1831 } /* static VOID ScanLegacyUEFI() */
1832 #endif // __MAKEWITH_GNUEFI
1834 static VOID
ScanLegacyVolume(REFIT_VOLUME
*Volume
, UINTN VolumeIndex
) {
1836 BOOLEAN ShowVolume
, HideIfOthersFound
;
1839 HideIfOthersFound
= FALSE
;
1840 if (Volume
->IsAppleLegacy
) {
1842 HideIfOthersFound
= TRUE
;
1843 } else if (Volume
->HasBootCode
) {
1845 if (Volume
->BlockIO
== Volume
->WholeDiskBlockIO
&&
1846 Volume
->BlockIOOffset
== 0 &&
1847 Volume
->OSName
== NULL
)
1848 // this is a whole disk (MBR) entry; hide if we have entries for partitions
1849 HideIfOthersFound
= TRUE
;
1851 if (HideIfOthersFound
) {
1852 // check for other bootable entries on the same disk
1853 for (VolumeIndex2
= 0; VolumeIndex2
< VolumesCount
; VolumeIndex2
++) {
1854 if (VolumeIndex2
!= VolumeIndex
&& Volumes
[VolumeIndex2
]->HasBootCode
&&
1855 Volumes
[VolumeIndex2
]->WholeDiskBlockIO
== Volume
->WholeDiskBlockIO
)
1861 AddLegacyEntry(NULL
, Volume
);
1862 } // static VOID ScanLegacyVolume()
1864 // Scan attached optical discs for legacy (BIOS) boot code
1865 // and add anything found to the list....
1866 static VOID
ScanLegacyDisc(VOID
)
1869 REFIT_VOLUME
*Volume
;
1871 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
) {
1872 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1873 Volume
= Volumes
[VolumeIndex
];
1874 if (Volume
->DiskKind
== DISK_KIND_OPTICAL
)
1875 ScanLegacyVolume(Volume
, VolumeIndex
);
1877 } else if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
1878 ScanLegacyUEFI(BBS_CDROM
);
1880 } /* static VOID ScanLegacyDisc() */
1882 // Scan internal hard disks for legacy (BIOS) boot code
1883 // and add anything found to the list....
1884 static VOID
ScanLegacyInternal(VOID
)
1887 REFIT_VOLUME
*Volume
;
1889 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
) {
1890 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1891 Volume
= Volumes
[VolumeIndex
];
1892 if (Volume
->DiskKind
== DISK_KIND_INTERNAL
)
1893 ScanLegacyVolume(Volume
, VolumeIndex
);
1895 } else if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
1896 ScanLegacyUEFI(BBS_HARDDISK
);
1898 } /* static VOID ScanLegacyInternal() */
1900 // Scan external disks for legacy (BIOS) boot code
1901 // and add anything found to the list....
1902 static VOID
ScanLegacyExternal(VOID
)
1905 REFIT_VOLUME
*Volume
;
1907 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
) {
1908 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1909 Volume
= Volumes
[VolumeIndex
];
1910 if (Volume
->DiskKind
== DISK_KIND_EXTERNAL
)
1911 ScanLegacyVolume(Volume
, VolumeIndex
);
1913 } else if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
1914 ScanLegacyUEFI(BBS_USB
);
1916 } /* static VOID ScanLegacyExternal() */
1919 // pre-boot tool functions
1922 static VOID
StartTool(IN LOADER_ENTRY
*Entry
)
1924 BeginExternalScreen(Entry
->UseGraphicsMode
, Entry
->me
.Title
+ 6); // assumes "Start <title>" as assigned below
1925 StartEFIImage(Entry
->DevicePath
, Entry
->LoadOptions
, TYPE_EFI
,
1926 Basename(Entry
->LoaderPath
), Entry
->OSType
, NULL
, TRUE
);
1927 FinishExternalScreen();
1928 } /* static VOID StartTool() */
1930 static LOADER_ENTRY
* AddToolEntry(EFI_HANDLE DeviceHandle
, IN CHAR16
*LoaderPath
, IN CHAR16
*LoaderTitle
, IN EG_IMAGE
*Image
,
1931 IN CHAR16 ShortcutLetter
, IN BOOLEAN UseGraphicsMode
)
1933 LOADER_ENTRY
*Entry
;
1934 CHAR16
*TitleStr
= NULL
;
1936 Entry
= AllocateZeroPool(sizeof(LOADER_ENTRY
));
1938 TitleStr
= PoolPrint(L
"Start %s", LoaderTitle
);
1939 Entry
->me
.Title
= TitleStr
;
1940 Entry
->me
.Tag
= TAG_TOOL
;
1942 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1943 Entry
->me
.Image
= Image
;
1944 Entry
->LoaderPath
= (LoaderPath
) ? StrDuplicate(LoaderPath
) : NULL
;
1945 Entry
->DevicePath
= FileDevicePath(DeviceHandle
, Entry
->LoaderPath
);
1946 Entry
->UseGraphicsMode
= UseGraphicsMode
;
1948 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1950 } /* static LOADER_ENTRY * AddToolEntry() */
1953 // pre-boot driver functions
1956 static UINTN
ScanDriverDir(IN CHAR16
*Path
)
1959 REFIT_DIR_ITER DirIter
;
1961 EFI_FILE_INFO
*DirEntry
;
1962 CHAR16 FileName
[256];
1964 CleanUpPathNameSlashes(Path
);
1965 // look through contents of the directory
1966 DirIterOpen(SelfRootDir
, Path
, &DirIter
);
1967 while (DirIterNext(&DirIter
, 2, LOADER_MATCH_PATTERNS
, &DirEntry
)) {
1968 if (DirEntry
->FileName
[0] == '.')
1969 continue; // skip this
1971 SPrint(FileName
, 255, L
"%s\\%s", Path
, DirEntry
->FileName
);
1973 Status
= StartEFIImage(FileDevicePath(SelfLoadedImage
->DeviceHandle
, FileName
),
1974 L
"", TYPE_EFI
, DirEntry
->FileName
, 0, NULL
, FALSE
);
1976 Status
= DirIterClose(&DirIter
);
1977 if (Status
!= EFI_NOT_FOUND
) {
1978 SPrint(FileName
, 255, L
"while scanning the %s directory", Path
);
1979 CheckError(Status
, FileName
);
1984 #ifdef __MAKEWITH_GNUEFI
1985 static EFI_STATUS
ConnectAllDriversToAllControllers(VOID
)
1988 UINTN AllHandleCount
;
1989 EFI_HANDLE
*AllHandleBuffer
;
1992 EFI_HANDLE
*HandleBuffer
;
1998 Status
= LibLocateHandle(AllHandles
,
2003 if (EFI_ERROR(Status
))
2006 for (Index
= 0; Index
< AllHandleCount
; Index
++) {
2008 // Scan the handle database
2010 Status
= LibScanHandleDatabase(NULL
,
2012 AllHandleBuffer
[Index
],
2017 if (EFI_ERROR (Status
))
2021 if (HandleType
[Index
] & EFI_HANDLE_TYPE_DRIVER_BINDING_HANDLE
)
2023 if (HandleType
[Index
] & EFI_HANDLE_TYPE_IMAGE_HANDLE
)
2028 for (HandleIndex
= 0; HandleIndex
< HandleCount
; HandleIndex
++) {
2029 if (HandleType
[HandleIndex
] & EFI_HANDLE_TYPE_PARENT_HANDLE
)
2034 if (HandleType
[Index
] & EFI_HANDLE_TYPE_DEVICE_HANDLE
) {
2035 Status
= refit_call4_wrapper(BS
->ConnectController
,
2036 AllHandleBuffer
[Index
],
2044 MyFreePool (HandleBuffer
);
2045 MyFreePool (HandleType
);
2049 MyFreePool (AllHandleBuffer
);
2051 } /* EFI_STATUS ConnectAllDriversToAllControllers() */
2053 static EFI_STATUS
ConnectAllDriversToAllControllers(VOID
) {
2054 BdsLibConnectAllDriversToAllControllers();
2059 // Load all EFI drivers from rEFInd's "drivers" subdirectory and from the
2060 // directories specified by the user in the "scan_driver_dirs" configuration
2062 static VOID
LoadDrivers(VOID
)
2064 CHAR16
*Directory
, *SelfDirectory
;
2065 UINTN i
= 0, Length
, NumFound
= 0;
2067 // load drivers from the subdirectories of rEFInd's home directory specified
2068 // in the DRIVER_DIRS constant.
2069 while ((Directory
= FindCommaDelimited(DRIVER_DIRS
, i
++)) != NULL
) {
2070 SelfDirectory
= SelfDirPath
? StrDuplicate(SelfDirPath
) : NULL
;
2071 CleanUpPathNameSlashes(SelfDirectory
);
2072 MergeStrings(&SelfDirectory
, Directory
, L
'\\');
2073 NumFound
+= ScanDriverDir(SelfDirectory
);
2074 MyFreePool(Directory
);
2075 MyFreePool(SelfDirectory
);
2078 // Scan additional user-specified driver directories....
2080 while ((Directory
= FindCommaDelimited(GlobalConfig
.DriverDirs
, i
++)) != NULL
) {
2081 CleanUpPathNameSlashes(Directory
);
2082 Length
= StrLen(Directory
);
2084 NumFound
+= ScanDriverDir(Directory
);
2086 MyFreePool(Directory
);
2089 // connect all devices
2091 ConnectAllDriversToAllControllers();
2092 } /* static VOID LoadDrivers() */
2094 // Determine what (if any) type of legacy (BIOS) boot support is available
2095 static VOID
FindLegacyBootType(VOID
) {
2096 #ifdef __MAKEWITH_TIANO
2098 EFI_LEGACY_BIOS_PROTOCOL
*LegacyBios
;
2101 GlobalConfig
.LegacyType
= LEGACY_TYPE_NONE
;
2103 // UEFI-style legacy BIOS support is available only with the TianoCore EDK2
2104 // build environment, and then only with some EFI implementations....
2105 #ifdef __MAKEWITH_TIANO
2106 Status
= gBS
->LocateProtocol (&gEfiLegacyBootProtocolGuid
, NULL
, (VOID
**) &LegacyBios
);
2107 if (!EFI_ERROR (Status
))
2108 GlobalConfig
.LegacyType
= LEGACY_TYPE_UEFI
;
2111 // Macs have their own system. If the firmware vendor code contains the
2112 // string "Apple", assume it's available. Note that this overrides the
2113 // UEFI type, and might yield false positives if the vendor string
2114 // contains "Apple" as part of something bigger, so this isn't 100%
2116 if (StriSubCmp(L
"Apple", ST
->FirmwareVendor
))
2117 GlobalConfig
.LegacyType
= LEGACY_TYPE_MAC
;
2118 } // static VOID FindLegacyBootType
2120 // Warn the user if legacy OS scans are enabled but the firmware or this
2121 // application can't support them....
2122 static VOID
WarnIfLegacyProblems() {
2123 BOOLEAN found
= FALSE
;
2126 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_NONE
) {
2128 if (GlobalConfig
.ScanFor
[i
] == 'h' || GlobalConfig
.ScanFor
[i
] == 'b' || GlobalConfig
.ScanFor
[i
] == 'c')
2131 } while ((i
< NUM_SCAN_OPTIONS
) && (!found
));
2133 Print(L
"NOTE: refind.conf's 'scanfor' line specifies scanning for one or more legacy\n");
2134 Print(L
"(BIOS) boot options; however, this is not possible because ");
2135 #ifdef __MAKEWITH_TIANO
2136 Print(L
"your computer lacks\n");
2137 Print(L
"the necessary Compatibility Support Module (CSM) support.\n");
2139 Print(L
"this program was\n");
2140 Print(L
"compiled without the necessary support. Please visit\n");
2141 Print(L
"http://www.rodsbooks.com/refind/getting.html and download and install a rEFInd\n");
2142 Print(L
"binary built with the TianoCore EDK2 to enable legacy boot support.\n");
2146 } // if no legacy support
2147 } // static VOID WarnIfLegacyProblems()
2149 // Locates boot loaders. NOTE: This assumes that GlobalConfig.LegacyType is set correctly.
2150 static VOID
ScanForBootloaders(VOID
) {
2153 // if (GlobalConfig.LegacyType == LEGACY_TYPE_UEFI) {
2154 // Print(L"About to call BdsDeleteAllInvalidLegacyBootOptions()\n");
2155 // BdsDeleteAllInvalidLegacyBootOptions();
2156 // Print(L"About to call BdsAddNonExistingLegacyBootOptions()\n");
2157 // BdsAddNonExistingLegacyBootOptions();
2158 // Print(L"About to call BdsUpdateLegacyDevOrder()\n");
2159 // // BdsUpdateLegacyDevOrder(); // EXTREME CAUTION: HOSED ONE FIRMWARE!
2160 // Print(L"Done with legacy boot updates!\n");
2166 // scan for loaders and tools, add them to the menu
2167 for (i
= 0; i
< NUM_SCAN_OPTIONS
; i
++) {
2168 switch(GlobalConfig
.ScanFor
[i
]) {
2173 ScanLegacyInternal();
2176 ScanLegacyExternal();
2179 ScanUserConfigured(GlobalConfig
.ConfigFilename
);
2193 // assign shortcut keys
2194 for (i
= 0; i
< MainMenu
.EntryCount
&& MainMenu
.Entries
[i
]->Row
== 0 && i
< 9; i
++)
2195 MainMenu
.Entries
[i
]->ShortcutDigit
= (CHAR16
)('1' + i
);
2197 // wait for user ACK when there were errors
2198 FinishTextScreen(FALSE
);
2199 } // static VOID ScanForBootloaders()
2201 // Locate a single tool from the specified Locations using one of the
2202 // specified Names and add it to the menu.
2203 static VOID
FindTool(CHAR16
*Locations
, CHAR16
*Names
, CHAR16
*Description
, UINTN Icon
) {
2204 UINTN j
= 0, k
, VolumeIndex
;
2205 CHAR16
*DirName
, *FileName
, *PathName
, FullDescription
[256];
2207 while ((DirName
= FindCommaDelimited(Locations
, j
++)) != NULL
) {
2209 while ((FileName
= FindCommaDelimited(Names
, k
++)) != NULL
) {
2210 PathName
= StrDuplicate(DirName
);
2211 MergeStrings(&PathName
, FileName
, (StriCmp(PathName
, L
"\\") == 0) ? 0 : L
'\\');
2212 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
2213 if ((Volumes
[VolumeIndex
]->RootDir
!= NULL
) && (FileExists(Volumes
[VolumeIndex
]->RootDir
, PathName
))) {
2214 SPrint(FullDescription
, 255, L
"%s at %s on %s", Description
, PathName
, Volumes
[VolumeIndex
]->VolName
);
2215 AddToolEntry(Volumes
[VolumeIndex
]->DeviceHandle
, PathName
, FullDescription
, BuiltinIcon(Icon
), 'S', FALSE
);
2218 MyFreePool(PathName
);
2219 MyFreePool(FileName
);
2221 MyFreePool(DirName
);
2222 } // while Locations
2223 } // VOID FindTool()
2225 // Add the second-row tags containing built-in and external tools (EFI shell,
2227 static VOID
ScanForTools(VOID
) {
2228 CHAR16
*FileName
= NULL
, *VolName
= NULL
, *MokLocations
, Description
[256];
2229 REFIT_MENU_ENTRY
*TempMenuEntry
;
2230 UINTN i
, j
, VolumeIndex
;
2234 MokLocations
= StrDuplicate(MOK_LOCATIONS
);
2235 if (MokLocations
!= NULL
)
2236 MergeStrings(&MokLocations
, SelfDirPath
, L
',');
2238 for (i
= 0; i
< NUM_TOOLS
; i
++) {
2239 switch(GlobalConfig
.ShowTools
[i
]) {
2240 // NOTE: Be sure that FileName is NULL at the end of each case.
2242 TempMenuEntry
= CopyMenuEntry(&MenuEntryShutdown
);
2243 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_SHUTDOWN
);
2244 AddMenuEntry(&MainMenu
, TempMenuEntry
);
2248 TempMenuEntry
= CopyMenuEntry(&MenuEntryReset
);
2249 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_RESET
);
2250 AddMenuEntry(&MainMenu
, TempMenuEntry
);
2254 TempMenuEntry
= CopyMenuEntry(&MenuEntryAbout
);
2255 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT
);
2256 AddMenuEntry(&MainMenu
, TempMenuEntry
);
2260 TempMenuEntry
= CopyMenuEntry(&MenuEntryExit
);
2261 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_EXIT
);
2262 AddMenuEntry(&MainMenu
, TempMenuEntry
);
2266 if (EfivarGetRaw(&GlobalGuid
, L
"OsIndicationsSupported", &b
, &j
) == EFI_SUCCESS
) {
2268 if (osind
& EFI_OS_INDICATIONS_BOOT_TO_FW_UI
) {
2269 TempMenuEntry
= CopyMenuEntry(&MenuEntryFirmware
);
2270 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_FIRMWARE
);
2271 AddMenuEntry(&MainMenu
, TempMenuEntry
);
2278 while ((FileName
= FindCommaDelimited(SHELL_NAMES
, j
++)) != NULL
) {
2279 if (FileExists(SelfRootDir
, FileName
)) {
2280 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, L
"EFI Shell", BuiltinIcon(BUILTIN_ICON_TOOL_SHELL
),
2283 MyFreePool(FileName
);
2289 while ((FileName
= FindCommaDelimited(GPTSYNC_NAMES
, j
++)) != NULL
) {
2290 if (FileExists(SelfRootDir
, FileName
)) {
2291 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, L
"Hybrid MBR tool", BuiltinIcon(BUILTIN_ICON_TOOL_PART
),
2294 MyFreePool(FileName
);
2299 case TAG_APPLE_RECOVERY
:
2300 FileName
= StrDuplicate(L
"\\com.apple.recovery.boot\\boot.efi");
2301 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
2302 if ((Volumes
[VolumeIndex
]->RootDir
!= NULL
) && (FileExists(Volumes
[VolumeIndex
]->RootDir
, FileName
))) {
2303 SPrint(Description
, 255, L
"Apple Recovery on %s", Volumes
[VolumeIndex
]->VolName
);
2304 AddToolEntry(Volumes
[VolumeIndex
]->DeviceHandle
, FileName
, Description
,
2305 BuiltinIcon(BUILTIN_ICON_TOOL_APPLE_RESCUE
), 'R', TRUE
);
2308 MyFreePool(FileName
);
2312 case TAG_WINDOWS_RECOVERY
:
2314 while ((FileName
= FindCommaDelimited(GlobalConfig
.WindowsRecoveryFiles
, j
++)) != NULL
) {
2315 SplitVolumeAndFilename(&FileName
, &VolName
);
2316 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
2317 if ((Volumes
[VolumeIndex
]->RootDir
!= NULL
) && (FileExists(Volumes
[VolumeIndex
]->RootDir
, FileName
)) &&
2318 ((VolName
== NULL
) || (StriCmp(VolName
, Volumes
[VolumeIndex
]->VolName
) == 0))) {
2319 SPrint(Description
, 255, L
"Microsoft Recovery on %s", Volumes
[VolumeIndex
]->VolName
);
2320 AddToolEntry(Volumes
[VolumeIndex
]->DeviceHandle
, FileName
, Description
,
2321 BuiltinIcon(BUILTIN_ICON_TOOL_WINDOWS_RESCUE
), 'R', TRUE
);
2325 MyFreePool(FileName
);
2327 MyFreePool(VolName
);
2332 FindTool(MokLocations
, MOK_NAMES
, L
"MOK utility utility", BUILTIN_ICON_TOOL_MOK_TOOL
);
2336 FindTool(MEMTEST_LOCATIONS
, MEMTEST_NAMES
, L
"Memory test utility", BUILTIN_ICON_TOOL_MEMTEST
);
2341 } // static VOID ScanForTools
2343 // Rescan for boot loaders
2344 VOID
RescanAll(VOID
) {
2351 egDisplayMessage(L
"Scanning for new boot loaders; please wait....", &BGColor
);
2352 FreeList((VOID
***) &(MainMenu
.Entries
), &MainMenu
.EntryCount
);
2353 MainMenu
.Entries
= NULL
;
2354 MainMenu
.EntryCount
= 0;
2355 ReadConfig(GlobalConfig
.ConfigFilename
);
2356 ConnectAllDriversToAllControllers();
2358 ScanForBootloaders();
2361 } // VOID RescanAll()
2363 #ifdef __MAKEWITH_TIANO
2365 // Minimal initialization function
2366 static VOID
InitializeLib(IN EFI_HANDLE ImageHandle
, IN EFI_SYSTEM_TABLE
*SystemTable
) {
2368 // gImageHandle = ImageHandle;
2369 gBS
= SystemTable
->BootServices
;
2370 // gRS = SystemTable->RuntimeServices;
2371 gRT
= SystemTable
->RuntimeServices
; // Some BDS functions need gRT to be set
2372 EfiGetSystemConfigurationTable (&gEfiDxeServicesTableGuid
, (VOID
**) &gDS
);
2374 InitializeConsoleSim();
2379 // Set up our own Secure Boot extensions....
2380 // Returns TRUE on success, FALSE otherwise
2381 static BOOLEAN
SecureBootSetup(VOID
) {
2383 BOOLEAN Success
= FALSE
;
2385 if (secure_mode() && ShimLoaded()) {
2386 Status
= security_policy_install();
2387 if (Status
== EFI_SUCCESS
) {
2390 Print(L
"Failed to install MOK Secure Boot extensions");
2394 } // VOID SecureBootSetup()
2396 // Remove our own Secure Boot extensions....
2397 // Returns TRUE on success, FALSE otherwise
2398 static BOOLEAN
SecureBootUninstall(VOID
) {
2400 BOOLEAN Success
= TRUE
;
2402 if (secure_mode()) {
2403 Status
= security_policy_uninstall();
2404 if (Status
!= EFI_SUCCESS
) {
2406 BeginTextScreen(L
"Secure Boot Policy Failure");
2407 Print(L
"Failed to uninstall MOK Secure Boot extensions; forcing a reboot.");
2409 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);
2413 } // VOID SecureBootUninstall
2415 // Sets the global configuration filename; will be CONFIG_FILE_NAME unless the
2416 // "-c" command-line option is set, in which case that takes precedence.
2417 // If an error is encountered, leaves the value alone (it should be set to
2418 // CONFIG_FILE_NAME when GlobalConfig is initialized).
2419 static VOID
SetConfigFilename(EFI_HANDLE ImageHandle
) {
2420 EFI_LOADED_IMAGE
*Info
;
2421 CHAR16
*Options
, *FileName
;
2425 Status
= uefi_call_wrapper(BS
->HandleProtocol
, 3, ImageHandle
,
2426 &LoadedImageProtocol
, (VOID
**) &Info
);
2427 if ((Status
== EFI_SUCCESS
) && (Info
->LoadOptionsSize
> 0)) {
2428 Options
= (CHAR16
*) Info
->LoadOptions
;
2429 Where
= FindSubString(L
" -c ", Options
);
2431 FileName
= StrDuplicate(&Options
[Where
+ 4]);
2432 Where
= FindSubString(L
" ", FileName
);
2434 FileName
[Where
] = L
'\0';
2436 if (FileExists(SelfDir
, FileName
)) {
2437 GlobalConfig
.ConfigFilename
= FileName
;
2439 Print(L
"Specified configuration file (%s) doesn't exist; using\n'refind.conf' default\n", FileName
);
2440 MyFreePool(FileName
);
2444 } // VOID SetConfigFilename()
2451 efi_main (EFI_HANDLE ImageHandle
, EFI_SYSTEM_TABLE
*SystemTable
)
2454 BOOLEAN MainLoopRunning
= TRUE
;
2455 BOOLEAN MokProtocol
;
2456 REFIT_MENU_ENTRY
*ChosenEntry
;
2458 CHAR16
*Selection
= NULL
;
2462 InitializeLib(ImageHandle
, SystemTable
);
2463 Status
= InitRefitLib(ImageHandle
);
2464 if (EFI_ERROR(Status
))
2467 // read configuration
2468 CopyMem(GlobalConfig
.ScanFor
, "ieom ", NUM_SCAN_OPTIONS
);
2469 FindLegacyBootType();
2470 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
)
2471 CopyMem(GlobalConfig
.ScanFor
, "ihebocm ", NUM_SCAN_OPTIONS
);
2472 SetConfigFilename(ImageHandle
);
2473 ReadConfig(GlobalConfig
.ConfigFilename
);
2477 WarnIfLegacyProblems();
2478 MainMenu
.TimeoutSeconds
= GlobalConfig
.Timeout
;
2480 // disable EFI watchdog timer
2481 refit_call4_wrapper(BS
->SetWatchdogTimer
, 0x0000, 0x0000, 0x0000, NULL
);
2483 // further bootstrap (now with config available)
2484 MokProtocol
= SecureBootSetup();
2486 ScanForBootloaders();
2490 if (GlobalConfig
.ScanDelay
> 0) {
2495 egDisplayMessage(L
"Pausing before disk scan; please wait....", &BGColor
);
2496 for (i
= 0; i
< GlobalConfig
.ScanDelay
; i
++)
2497 refit_call1_wrapper(BS
->Stall
, 1000000);
2501 if (GlobalConfig
.DefaultSelection
)
2502 Selection
= StrDuplicate(GlobalConfig
.DefaultSelection
);
2504 while (MainLoopRunning
) {
2505 MenuExit
= RunMainMenu(&MainMenu
, Selection
, &ChosenEntry
);
2507 // The Escape key triggers a re-scan operation....
2508 if (MenuExit
== MENU_EXIT_ESCAPE
) {
2513 switch (ChosenEntry
->Tag
) {
2515 case TAG_REBOOT
: // Reboot
2517 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);
2518 MainLoopRunning
= FALSE
; // just in case we get this far
2521 case TAG_SHUTDOWN
: // Shut Down
2523 refit_call4_wrapper(RT
->ResetSystem
, EfiResetShutdown
, EFI_SUCCESS
, 0, NULL
);
2524 MainLoopRunning
= FALSE
; // just in case we get this far
2527 case TAG_ABOUT
: // About rEFInd
2531 case TAG_LOADER
: // Boot OS via .EFI loader
2532 StartLoader((LOADER_ENTRY
*)ChosenEntry
);
2535 case TAG_LEGACY
: // Boot legacy OS
2536 StartLegacy((LEGACY_ENTRY
*)ChosenEntry
);
2539 #ifdef __MAKEWITH_TIANO
2540 case TAG_LEGACY_UEFI
: // Boot a legacy OS on a non-Mac
2541 StartLegacyUEFI((LEGACY_ENTRY
*)ChosenEntry
);
2545 case TAG_TOOL
: // Start a EFI tool
2546 StartTool((LOADER_ENTRY
*)ChosenEntry
);
2549 case TAG_EXIT
: // Terminate rEFInd
2550 if ((MokProtocol
) && !SecureBootUninstall()) {
2551 MainLoopRunning
= FALSE
; // just in case we get this far
2553 BeginTextScreen(L
" ");
2558 case TAG_FIRMWARE
: // Reboot into firmware's user interface
2559 RebootIntoFirmware();
2563 MyFreePool(Selection
);
2564 Selection
= (ChosenEntry
->Title
) ? StrDuplicate(ChosenEntry
->Title
) : NULL
;
2567 // If we end up here, things have gone wrong. Try to reboot, and if that
2568 // fails, go into an endless loop.
2569 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);