3 * Main code for the boot menu
5 * Copyright (c) 2006-2010 Christoph Pfisterer
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions are
12 * * Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
15 * * Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the
20 * * Neither the name of Christoph Pfisterer nor the names of the
21 * contributors may be used to endorse or promote products derived
22 * from this software without specific prior written permission.
24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
26 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
27 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
28 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
29 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
30 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
31 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
32 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
33 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
34 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37 * Modifications copyright (c) 2012-2013 Roderick W. Smith
39 * Modifications distributed under the terms of the GNU General Public
40 * License (GPL) version 3 (GPLv3), a copy of which must be distributed
41 * with this source code or binaries made from it.
52 #include "security_policy.h"
53 #include "../include/Handle.h"
54 #include "../include/refit_call_wrapper.h"
55 #include "driver_support.h"
56 #include "../include/syslinux_mbr.h"
58 #ifdef __MAKEWITH_GNUEFI
59 #define EFI_SECURITY_VIOLATION EFIERR (26)
61 #include "../EfiLib/BdsHelper.h"
62 #endif // __MAKEWITH_GNUEFI
64 #ifndef EFI_OS_INDICATIONS_BOOT_TO_FW_UI
65 #define EFI_OS_INDICATIONS_BOOT_TO_FW_UI 0x0000000000000001ULL
71 #define MACOSX_LOADER_PATH L"System\\Library\\CoreServices\\boot.efi"
73 #define SHELL_NAMES L"\\EFI\\tools\\shell.efi,\\EFI\\tools\\shellx64.efi,\\shell.efi,\\shellx64.efi"
74 #define GPTSYNC_NAMES L"\\EFI\\tools\\gptsync.efi,\\EFI\\tools\\gptsync_x64.efi"
75 #define DRIVER_DIRS L"drivers,drivers_x64"
76 #define FALLBACK_FULLNAME L"EFI\\BOOT\\bootx64.efi"
77 #define FALLBACK_BASENAME L"bootx64.efi"
78 #define EFI_STUB_ARCH 0x8664
80 #define SHELL_NAMES L"\\EFI\\tools\\shell.efi,\\EFI\\tools\\shellia32.efi,\\shell.efi,\\shellia32.efi"
81 #define GPTSYNC_NAMES L"\\EFI\\tools\\gptsync.efi,\\EFI\\tools\\gptsync_ia32.efi"
82 #define DRIVER_DIRS L"drivers,drivers_ia32"
83 #define FALLBACK_FULLNAME L"EFI\\BOOT\\bootia32.efi"
84 #define FALLBACK_BASENAME L"bootia32.efi"
85 #define EFI_STUB_ARCH 0x014c
87 #define SHELL_NAMES L"\\EFI\\tools\\shell.efi,\\shell.efi"
88 #define GPTSYNC_NAMES L"\\EFI\\tools\\gptsync.efi"
89 #define DRIVER_DIRS L"drivers"
90 #define FALLBACK_FULLNAME L"EFI\\BOOT\\boot.efi" /* Not really correct */
91 #define FALLBACK_BASENAME L"boot.efi" /* Not really correct */
93 #define FAT_ARCH 0x0ef1fab9 /* ID for Apple "fat" binary */
95 // Filename patterns that identify EFI boot loaders. Note that a single case (either L"*.efi" or
96 // L"*.EFI") is fine for most systems; but Gigabyte's buggy Hybrid EFI does a case-sensitive
97 // comparison when it should do a case-insensitive comparison, so I'm doubling this up. It does
98 // no harm on other computers, AFAIK. In theory, every case variation should be done for
99 // completeness, but that's ridiculous....
100 #define LOADER_MATCH_PATTERNS L"*.efi,*.EFI"
102 // Patterns that identify Linux kernels. Added to the loader match pattern when the
103 // scan_all_linux_kernels option is set in the configuration file. Causes kernels WITHOUT
104 // a ".efi" extension to be found when scanning for boot loaders.
105 #define LINUX_MATCH_PATTERNS L"vmlinuz*,bzImage*"
107 // Default hint text for program-launch submenus
108 #define SUBSCREEN_HINT1 L"Use arrow keys to move cursor; Enter to boot;"
109 #define SUBSCREEN_HINT2 L"Insert or F2 to edit options; Esc to return to main menu"
110 #define SUBSCREEN_HINT2_NO_EDITOR L"Esc to return to main menu"
114 #define TYPE_LEGACY 2
116 static REFIT_MENU_ENTRY MenuEntryAbout
= { L
"About rEFInd", TAG_ABOUT
, 1, 0, 'A', NULL
, NULL
, NULL
};
117 static REFIT_MENU_ENTRY MenuEntryReset
= { L
"Reboot Computer", TAG_REBOOT
, 1, 0, 'R', NULL
, NULL
, NULL
};
118 static REFIT_MENU_ENTRY MenuEntryShutdown
= { L
"Shut Down Computer", TAG_SHUTDOWN
, 1, 0, 'U', NULL
, NULL
, NULL
};
119 static REFIT_MENU_ENTRY MenuEntryReturn
= { L
"Return to Main Menu", TAG_RETURN
, 1, 0, 0, NULL
, NULL
, NULL
};
120 static REFIT_MENU_ENTRY MenuEntryExit
= { L
"Exit rEFInd", TAG_EXIT
, 1, 0, 0, NULL
, NULL
, NULL
};
121 static REFIT_MENU_ENTRY MenuEntryFirmware
= { L
"Reboot to Computer Setup Utility", TAG_FIRMWARE
, 1, 0, 0, NULL
, NULL
, NULL
};
123 static REFIT_MENU_SCREEN MainMenu
= { L
"Main Menu", NULL
, 0, NULL
, 0, NULL
, 0, L
"Automatic boot",
124 L
"Use arrow keys to move cursor; Enter to boot;",
125 L
"Insert or F2 for more options; Esc to refresh" };
126 static REFIT_MENU_SCREEN AboutMenu
= { L
"About", NULL
, 0, NULL
, 0, NULL
, 0, NULL
, L
"Press Enter to return to main menu", L
"" };
128 REFIT_CONFIG GlobalConfig
= { FALSE
, FALSE
, 0, 0, 0, DONT_CHANGE_TEXT_MODE
, 20, 0, 0, GRAPHICS_FOR_OSX
, LEGACY_TYPE_MAC
, 0, 0,
129 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
130 { TAG_SHELL
, TAG_APPLE_RECOVERY
, TAG_MOK_TOOL
, TAG_ABOUT
, TAG_SHUTDOWN
, TAG_REBOOT
, TAG_FIRMWARE
,
134 EFI_GUID GlobalGuid
= EFI_GLOBAL_VARIABLE
;
136 // Structure used to hold boot loader filenames and time stamps in
137 // a linked list; used to sort entries within a directory.
141 struct LOADER_LIST
*NextEntry
;
148 static VOID
AboutrEFInd(VOID
)
150 CHAR16
*TempStr
; // Note: Don't deallocate; moved to menu structure
152 if (AboutMenu
.EntryCount
== 0) {
153 AboutMenu
.TitleImage
= BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT
);
154 AddMenuInfoLine(&AboutMenu
, L
"rEFInd Version 0.7.3");
155 AddMenuInfoLine(&AboutMenu
, L
"");
156 AddMenuInfoLine(&AboutMenu
, L
"Copyright (c) 2006-2010 Christoph Pfisterer");
157 AddMenuInfoLine(&AboutMenu
, L
"Copyright (c) 2012-2013 Roderick W. Smith");
158 AddMenuInfoLine(&AboutMenu
, L
"Portions Copyright (c) Intel Corporation and others");
159 AddMenuInfoLine(&AboutMenu
, L
"Distributed under the terms of the GNU GPLv3 license");
160 AddMenuInfoLine(&AboutMenu
, L
"");
161 AddMenuInfoLine(&AboutMenu
, L
"Running on:");
162 TempStr
= AllocateZeroPool(256 * sizeof(CHAR16
));
163 SPrint(TempStr
, 255, L
" EFI Revision %d.%02d", ST
->Hdr
.Revision
>> 16, ST
->Hdr
.Revision
& ((1 << 16) - 1));
164 AddMenuInfoLine(&AboutMenu
, TempStr
);
166 AddMenuInfoLine(&AboutMenu
, L
" Platform: x86 (32 bit)");
167 #elif defined(EFIX64)
168 TempStr
= AllocateZeroPool(256 * sizeof(CHAR16
));
169 SPrint(TempStr
, 255, L
" Platform: x86_64 (64 bit); Secure Boot %s", secure_mode() ? L
"active" : L
"inactive");
170 AddMenuInfoLine(&AboutMenu
, TempStr
);
172 AddMenuInfoLine(&AboutMenu
, L
" Platform: unknown");
174 TempStr
= AllocateZeroPool(256 * sizeof(CHAR16
));
175 SPrint(TempStr
, 255, L
" Firmware: %s %d.%02d",
176 ST
->FirmwareVendor
, ST
->FirmwareRevision
>> 16, ST
->FirmwareRevision
& ((1 << 16) - 1));
177 AddMenuInfoLine(&AboutMenu
, TempStr
);
178 TempStr
= AllocateZeroPool(256 * sizeof(CHAR16
));
179 SPrint(TempStr
, 255, L
" Screen Output: %s", egScreenDescription());
180 AddMenuInfoLine(&AboutMenu
, TempStr
);
181 AddMenuInfoLine(&AboutMenu
, L
"");
182 #if defined(__MAKEWITH_GNUEFI)
183 AddMenuInfoLine(&AboutMenu
, L
"Built with GNU-EFI");
185 AddMenuInfoLine(&AboutMenu
, L
"Built with TianoCore EDK2");
187 AddMenuInfoLine(&AboutMenu
, L
"");
188 AddMenuInfoLine(&AboutMenu
, L
"For more information, see the rEFInd Web site:");
189 AddMenuInfoLine(&AboutMenu
, L
"http://www.rodsbooks.com/refind/");
190 AddMenuEntry(&AboutMenu
, &MenuEntryReturn
);
193 RunMenu(&AboutMenu
, NULL
);
194 } /* VOID AboutrEFInd() */
196 static VOID
WarnSecureBootError(CHAR16
*Name
, BOOLEAN Verbose
) {
198 Name
= L
"the loader";
200 refit_call2_wrapper(ST
->ConOut
->SetAttribute
, ST
->ConOut
, ATTR_ERROR
);
201 Print(L
"Secure Boot validation failure loading %s!\n", Name
);
202 refit_call2_wrapper(ST
->ConOut
->SetAttribute
, ST
->ConOut
, ATTR_BASIC
);
203 if (Verbose
&& secure_mode()) {
204 Print(L
"\nThis computer is configured with Secure Boot active, but\n%s has failed validation.\n", Name
);
205 Print(L
"\nYou can:\n * Launch another boot loader\n");
206 Print(L
" * Disable Secure Boot in your firmware\n");
207 Print(L
" * Sign %s with a machine owner key (MOK)\n", Name
);
208 Print(L
" * Use a MOK utility (often present on the second row) to add a MOK with which\n");
209 Print(L
" %s has already been signed.\n", Name
);
210 Print(L
" * Use a MOK utility to register %s (\"enroll its hash\") without\n", Name
);
211 Print(L
" signing it.\n");
212 Print(L
"\nSee http://www.rodsbooks.com/refind/secureboot.html for more information\n");
215 } // VOID WarnSecureBootError()
217 // Returns TRUE if this file is a valid EFI loader file, and is proper ARCH
218 static BOOLEAN
IsValidLoader(EFI_FILE
*RootDir
, CHAR16
*FileName
) {
219 BOOLEAN IsValid
= TRUE
;
220 #if defined (EFIX64) | defined (EFI32)
222 EFI_FILE_HANDLE FileHandle
;
224 UINTN Size
= sizeof(Header
);
226 Status
= refit_call5_wrapper(RootDir
->Open
, RootDir
, &FileHandle
, FileName
, EFI_FILE_MODE_READ
, 0);
227 if (EFI_ERROR(Status
))
230 Status
= refit_call3_wrapper(FileHandle
->Read
, FileHandle
, &Size
, Header
);
231 refit_call1_wrapper(FileHandle
->Close
, FileHandle
);
233 IsValid
= !EFI_ERROR(Status
) &&
234 Size
== sizeof(Header
) &&
235 ((Header
[0] == 'M' && Header
[1] == 'Z' &&
236 (Size
= *(UINT32
*)&Header
[0x3c]) < 0x180 &&
237 Header
[Size
] == 'P' && Header
[Size
+1] == 'E' &&
238 Header
[Size
+2] == 0 && Header
[Size
+3] == 0 &&
239 *(UINT16
*)&Header
[Size
+4] == EFI_STUB_ARCH
) ||
240 (*(UINT32
*)&Header
== FAT_ARCH
));
243 } // BOOLEAN IsValidLoader()
245 // Launch an EFI binary.
246 static EFI_STATUS
StartEFIImageList(IN EFI_DEVICE_PATH
**DevicePaths
,
247 IN CHAR16
*LoadOptions
, IN UINTN LoaderType
,
248 IN CHAR16
*ImageTitle
, IN CHAR8 OSType
,
249 OUT UINTN
*ErrorInStep
,
252 EFI_STATUS Status
, ReturnStatus
;
253 EFI_HANDLE ChildImageHandle
;
254 EFI_LOADED_IMAGE
*ChildLoadedImage
= NULL
;
255 REFIT_VOLUME
*Volume
= NULL
;
256 UINTN DevicePathIndex
;
257 CHAR16 ErrorInfo
[256];
258 CHAR16
*FullLoadOptions
= NULL
;
259 CHAR16
*Filename
= NULL
;
261 if (ErrorInStep
!= NULL
)
265 if (LoadOptions
!= NULL
) {
266 if (LoaderType
== TYPE_EFI
) {
267 MergeStrings(&FullLoadOptions
, LoadOptions
, L
' ');
269 MergeStrings(&FullLoadOptions
, L
" ", 0);
270 // NOTE: That last space is also added by the EFI shell and seems to be significant
271 // when passing options to Apple's boot.efi...
274 MergeStrings(&FullLoadOptions
, LoadOptions
, 0);
276 } else { // LoadOptions == NULL
277 // NOTE: We provide a non-null string when no options are specified for safety;
278 // some systems (at least DUET) can hang when launching some programs (such as
279 // an EFI shell) without this.
280 FullLoadOptions
= StrDuplicate(L
" ");
283 Print(L
"Starting %s\nUsing load options '%s'\n", ImageTitle
, FullLoadOptions
);
285 // load the image into memory (and execute it, in the case of a shim/MOK image).
286 ReturnStatus
= Status
= EFI_NOT_FOUND
; // in case the list is empty
287 for (DevicePathIndex
= 0; DevicePaths
[DevicePathIndex
] != NULL
; DevicePathIndex
++) {
288 FindVolumeAndFilename(DevicePaths
[DevicePathIndex
], &Volume
, &Filename
);
289 // Some EFIs crash if attempting to load driver for invalid architecture, so
290 // protect for this condition....
291 if ((LoaderType
== TYPE_LEGACY
) || IsValidLoader(Volume
->RootDir
, Filename
)) {
292 // NOTE: Below commented-out line could be more efficient if file were read ahead of
293 // time and passed as a pre-loaded image to LoadImage(), but it doesn't work on my
294 // 32-bit Mac Mini or my 64-bit Intel box when launching a Linux kernel; the
295 // kernel returns a "Failed to handle fs_proto" error message.
296 // TODO: Track down the cause of this error and fix it, if possible.
297 // ReturnStatus = Status = refit_call6_wrapper(BS->LoadImage, FALSE, SelfImageHandle, DevicePaths[DevicePathIndex],
298 // ImageData, ImageSize, &ChildImageHandle);
299 ReturnStatus
= Status
= refit_call6_wrapper(BS
->LoadImage
, FALSE
, SelfImageHandle
, DevicePaths
[DevicePathIndex
],
300 NULL
, 0, &ChildImageHandle
);
302 Print(L
"Invalid loader file!\n");
303 ReturnStatus
= EFI_LOAD_ERROR
;
305 if (ReturnStatus
!= EFI_NOT_FOUND
) {
309 if ((Status
== EFI_ACCESS_DENIED
) || (Status
== EFI_SECURITY_VIOLATION
)) {
310 WarnSecureBootError(ImageTitle
, Verbose
);
313 SPrint(ErrorInfo
, 255, L
"while loading %s", ImageTitle
);
314 if (CheckError(Status
, ErrorInfo
)) {
315 if (ErrorInStep
!= NULL
)
320 ReturnStatus
= Status
= refit_call3_wrapper(BS
->HandleProtocol
, ChildImageHandle
, &LoadedImageProtocol
,
321 (VOID
**) &ChildLoadedImage
);
322 if (CheckError(Status
, L
"while getting a LoadedImageProtocol handle")) {
323 if (ErrorInStep
!= NULL
)
327 ChildLoadedImage
->LoadOptions
= (VOID
*)FullLoadOptions
;
328 ChildLoadedImage
->LoadOptionsSize
= ((UINT32
)StrLen(FullLoadOptions
) + 1) * sizeof(CHAR16
);
329 // turn control over to the image
330 // TODO: (optionally) re-enable the EFI watchdog timer!
332 // close open file handles
334 ReturnStatus
= Status
= refit_call3_wrapper(BS
->StartImage
, ChildImageHandle
, NULL
, NULL
);
336 // control returns here when the child image calls Exit()
337 SPrint(ErrorInfo
, 255, L
"returned from %s", ImageTitle
);
338 if (CheckError(Status
, ErrorInfo
)) {
339 if (ErrorInStep
!= NULL
)
343 // re-open file handles
347 // unload the image, we don't care if it works or not...
348 Status
= refit_call1_wrapper(BS
->UnloadImage
, ChildImageHandle
);
351 MyFreePool(FullLoadOptions
);
353 } /* static EFI_STATUS StartEFIImageList() */
355 static EFI_STATUS
StartEFIImage(IN EFI_DEVICE_PATH
*DevicePath
,
356 IN CHAR16
*LoadOptions
, IN UINTN LoaderType
,
357 IN CHAR16
*ImageTitle
, IN CHAR8 OSType
,
358 OUT UINTN
*ErrorInStep
,
361 EFI_DEVICE_PATH
*DevicePaths
[2];
363 DevicePaths
[0] = DevicePath
;
364 DevicePaths
[1] = NULL
;
365 return StartEFIImageList(DevicePaths
, LoadOptions
, LoaderType
, ImageTitle
, OSType
, ErrorInStep
, Verbose
);
366 } /* static EFI_STATUS StartEFIImage() */
368 // From gummiboot: Retrieve a raw EFI variable.
369 // Returns EFI status
370 static EFI_STATUS
EfivarGetRaw(EFI_GUID
*vendor
, CHAR16
*name
, CHAR8
**buffer
, UINTN
*size
) {
375 l
= sizeof(CHAR16
*) * EFI_MAXIMUM_VARIABLE_SIZE
;
376 buf
= AllocatePool(l
);
378 return EFI_OUT_OF_RESOURCES
;
380 err
= refit_call5_wrapper(RT
->GetVariable
, name
, vendor
, NULL
, &l
, buf
);
381 if (EFI_ERROR(err
) == EFI_SUCCESS
) {
388 } // EFI_STATUS EfivarGetRaw()
390 // From gummiboot: Set an EFI variable
391 static EFI_STATUS
EfivarSetRaw(EFI_GUID
*vendor
, CHAR16
*name
, CHAR8
*buf
, UINTN size
, BOOLEAN persistent
) {
394 flags
= EFI_VARIABLE_BOOTSERVICE_ACCESS
|EFI_VARIABLE_RUNTIME_ACCESS
;
396 flags
|= EFI_VARIABLE_NON_VOLATILE
;
398 return refit_call5_wrapper(RT
->SetVariable
, name
, vendor
, flags
, size
, buf
);
399 } // EFI_STATUS EfivarSetRaw()
401 // From gummiboot: Reboot the computer into its built-in user interface
402 static EFI_STATUS
RebootIntoFirmware(VOID
) {
408 osind
= EFI_OS_INDICATIONS_BOOT_TO_FW_UI
;
410 err
= EfivarGetRaw(&GlobalGuid
, L
"OsIndications", &b
, &size
);
411 if (err
== EFI_SUCCESS
)
415 err
= EfivarSetRaw(&GlobalGuid
, L
"OsIndications", (CHAR8
*)&osind
, sizeof(UINT64
), TRUE
);
416 if (err
!= EFI_SUCCESS
)
419 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);
420 Print(L
"Error calling ResetSystem: %r", err
);
427 // EFI OS loader functions
430 static VOID
StartLoader(LOADER_ENTRY
*Entry
)
432 UINTN ErrorInStep
= 0;
434 BeginExternalScreen(Entry
->UseGraphicsMode
, L
"Booting OS");
435 StartEFIImage(Entry
->DevicePath
, Entry
->LoadOptions
, TYPE_EFI
,
436 Basename(Entry
->LoaderPath
), Entry
->OSType
, &ErrorInStep
, !Entry
->UseGraphicsMode
);
437 FinishExternalScreen();
440 // Locate an initrd or initramfs file that matches the kernel specified by LoaderPath.
441 // The matching file has a name that begins with "init" and includes the same version
442 // number string as is found in LoaderPath -- but not a longer version number string.
443 // For instance, if LoaderPath is \EFI\kernels\bzImage-3.3.0.efi, and if \EFI\kernels
444 // has a file called initramfs-3.3.0.img, this function will return the string
445 // '\EFI\kernels\initramfs-3.3.0.img'. If the directory ALSO contains the file
446 // initramfs-3.3.0-rc7.img or initramfs-13.3.0.img, those files will NOT match;
447 // however, initmine-3.3.0.img might match. (FindInitrd() returns the first match it
448 // finds). Thus, care should be taken to avoid placing duplicate matching files in
449 // the kernel's directory.
450 // If no matching init file can be found, returns NULL.
451 static CHAR16
* FindInitrd(IN CHAR16
*LoaderPath
, IN REFIT_VOLUME
*Volume
) {
452 CHAR16
*InitrdName
= NULL
, *FileName
, *KernelVersion
, *InitrdVersion
, *Path
;
453 REFIT_DIR_ITER DirIter
;
454 EFI_FILE_INFO
*DirEntry
;
456 FileName
= Basename(LoaderPath
);
457 KernelVersion
= FindNumbers(FileName
);
458 Path
= FindPath(LoaderPath
);
460 // Add trailing backslash for root directory; necessary on some systems, but must
461 // NOT be added to all directories, since on other systems, a trailing backslash on
462 // anything but the root directory causes them to flake out!
463 if (StrLen(Path
) == 0) {
464 MergeStrings(&Path
, L
"\\", 0);
466 DirIterOpen(Volume
->RootDir
, Path
, &DirIter
);
467 // Now add a trailing backslash if it was NOT added earlier, for consistency in
468 // building the InitrdName later....
469 if ((StrLen(Path
) > 0) && (Path
[StrLen(Path
) - 1] != L
'\\'))
470 MergeStrings(&Path
, L
"\\", 0);
471 while ((DirIterNext(&DirIter
, 2, L
"init*", &DirEntry
)) && (InitrdName
== NULL
)) {
472 InitrdVersion
= FindNumbers(DirEntry
->FileName
);
473 if (KernelVersion
!= NULL
) {
474 if (StriCmp(InitrdVersion
, KernelVersion
) == 0) {
475 MergeStrings(&InitrdName
, Path
, 0);
476 MergeStrings(&InitrdName
, DirEntry
->FileName
, 0);
479 if (InitrdVersion
== NULL
) {
480 MergeStrings(&InitrdName
, Path
, 0);
481 MergeStrings(&InitrdName
, DirEntry
->FileName
, 0);
484 MyFreePool(InitrdVersion
);
486 DirIterClose(&DirIter
);
488 // Note: Don't FreePool(FileName), since Basename returns a pointer WITHIN the string it's passed.
489 MyFreePool(KernelVersion
);
492 } // static CHAR16 * FindInitrd()
494 LOADER_ENTRY
* AddPreparedLoaderEntry(LOADER_ENTRY
*Entry
) {
495 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
498 } // LOADER_ENTRY * AddPreparedLoaderEntry()
500 // Creates a copy of a menu screen.
501 // Returns a pointer to the copy of the menu screen.
502 static REFIT_MENU_SCREEN
* CopyMenuScreen(REFIT_MENU_SCREEN
*Entry
) {
503 REFIT_MENU_SCREEN
*NewEntry
;
506 NewEntry
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
507 if ((Entry
!= NULL
) && (NewEntry
!= NULL
)) {
508 CopyMem(NewEntry
, Entry
, sizeof(REFIT_MENU_SCREEN
));
509 NewEntry
->Title
= (Entry
->Title
) ? StrDuplicate(Entry
->Title
) : NULL
;
510 NewEntry
->TimeoutText
= (Entry
->TimeoutText
) ? StrDuplicate(Entry
->TimeoutText
) : NULL
;
511 if (Entry
->TitleImage
!= NULL
) {
512 NewEntry
->TitleImage
= AllocatePool(sizeof(EG_IMAGE
));
513 if (NewEntry
->TitleImage
!= NULL
)
514 CopyMem(NewEntry
->TitleImage
, Entry
->TitleImage
, sizeof(EG_IMAGE
));
516 NewEntry
->InfoLines
= (CHAR16
**) AllocateZeroPool(Entry
->InfoLineCount
* (sizeof(CHAR16
*)));
517 for (i
= 0; i
< Entry
->InfoLineCount
&& NewEntry
->InfoLines
; i
++) {
518 NewEntry
->InfoLines
[i
] = (Entry
->InfoLines
[i
]) ? StrDuplicate(Entry
->InfoLines
[i
]) : NULL
;
520 NewEntry
->Entries
= (REFIT_MENU_ENTRY
**) AllocateZeroPool(Entry
->EntryCount
* (sizeof (REFIT_MENU_ENTRY
*)));
521 for (i
= 0; i
< Entry
->EntryCount
&& NewEntry
->Entries
; i
++) {
522 AddMenuEntry(NewEntry
, Entry
->Entries
[i
]);
524 NewEntry
->Hint1
= (Entry
->Hint1
) ? StrDuplicate(Entry
->Hint1
) : NULL
;
525 NewEntry
->Hint2
= (Entry
->Hint2
) ? StrDuplicate(Entry
->Hint2
) : NULL
;
528 } // static REFIT_MENU_SCREEN* CopyMenuScreen()
530 // Creates a copy of a menu entry. Intended to enable moving a stack-based
531 // menu entry (such as the ones for the "reboot" and "exit" functions) to
532 // to the heap. This enables easier deletion of the whole set of menu
533 // entries when re-scanning.
534 // Returns a pointer to the copy of the menu entry.
535 static REFIT_MENU_ENTRY
* CopyMenuEntry(REFIT_MENU_ENTRY
*Entry
) {
536 REFIT_MENU_ENTRY
*NewEntry
;
538 NewEntry
= AllocateZeroPool(sizeof(REFIT_MENU_ENTRY
));
539 if ((Entry
!= NULL
) && (NewEntry
!= NULL
)) {
540 CopyMem(NewEntry
, Entry
, sizeof(REFIT_MENU_ENTRY
));
541 NewEntry
->Title
= (Entry
->Title
) ? StrDuplicate(Entry
->Title
) : NULL
;
542 if (Entry
->BadgeImage
!= NULL
) {
543 NewEntry
->BadgeImage
= AllocatePool(sizeof(EG_IMAGE
));
544 if (NewEntry
->BadgeImage
!= NULL
)
545 CopyMem(NewEntry
->BadgeImage
, Entry
->BadgeImage
, sizeof(EG_IMAGE
));
547 if (Entry
->Image
!= NULL
) {
548 NewEntry
->Image
= AllocatePool(sizeof(EG_IMAGE
));
549 if (NewEntry
->Image
!= NULL
)
550 CopyMem(NewEntry
->Image
, Entry
->Image
, sizeof(EG_IMAGE
));
552 if (Entry
->SubScreen
!= NULL
) {
553 NewEntry
->SubScreen
= CopyMenuScreen(Entry
->SubScreen
);
557 } // REFIT_MENU_ENTRY* CopyMenuEntry()
559 // Creates a new LOADER_ENTRY data structure and populates it with
560 // default values from the specified Entry, or NULL values if Entry
561 // is unspecified (NULL).
562 // Returns a pointer to the new data structure, or NULL if it
563 // couldn't be allocated
564 LOADER_ENTRY
*InitializeLoaderEntry(IN LOADER_ENTRY
*Entry
) {
565 LOADER_ENTRY
*NewEntry
= NULL
;
567 NewEntry
= AllocateZeroPool(sizeof(LOADER_ENTRY
));
568 if (NewEntry
!= NULL
) {
569 NewEntry
->me
.Title
= NULL
;
570 NewEntry
->me
.Tag
= TAG_LOADER
;
571 NewEntry
->Enabled
= TRUE
;
572 NewEntry
->UseGraphicsMode
= FALSE
;
573 NewEntry
->OSType
= 0;
575 NewEntry
->LoaderPath
= (Entry
->LoaderPath
) ? StrDuplicate(Entry
->LoaderPath
) : NULL
;
576 NewEntry
->VolName
= (Entry
->VolName
) ? StrDuplicate(Entry
->VolName
) : NULL
;
577 NewEntry
->DevicePath
= Entry
->DevicePath
;
578 NewEntry
->UseGraphicsMode
= Entry
->UseGraphicsMode
;
579 NewEntry
->LoadOptions
= (Entry
->LoadOptions
) ? StrDuplicate(Entry
->LoadOptions
) : NULL
;
580 NewEntry
->InitrdPath
= (Entry
->InitrdPath
) ? StrDuplicate(Entry
->InitrdPath
) : NULL
;
584 } // LOADER_ENTRY *InitializeLoaderEntry()
586 // Adds InitrdPath to Options, but only if Options doesn't already include an
587 // initrd= line. Done to enable overriding the default initrd selection in a
588 // refind_linux.conf file's options list.
589 // Returns a pointer to a new string. The calling function is responsible for
590 // freeing its memory.
591 static CHAR16
*AddInitrdToOptions(CHAR16
*Options
, CHAR16
*InitrdPath
) {
592 CHAR16
*NewOptions
= NULL
;
595 NewOptions
= StrDuplicate(Options
);
596 if ((InitrdPath
!= NULL
) && !StriSubCmp(L
"initrd=", Options
)) {
597 MergeStrings(&NewOptions
, L
"initrd=", L
' ');
598 MergeStrings(&NewOptions
, InitrdPath
, 0);
601 } // CHAR16 *AddInitrdToOptions()
603 // Prepare a REFIT_MENU_SCREEN data structure for a subscreen entry. This sets up
604 // the default entry that launches the boot loader using the same options as the
605 // main Entry does. Subsequent options can be added by the calling function.
606 // If a subscreen already exists in the Entry that's passed to this function,
607 // it's left unchanged and a pointer to it is returned.
608 // Returns a pointer to the new subscreen data structure, or NULL if there
609 // were problems allocating memory.
610 REFIT_MENU_SCREEN
*InitializeSubScreen(IN LOADER_ENTRY
*Entry
) {
611 CHAR16
*FileName
, *MainOptions
= NULL
;
612 REFIT_MENU_SCREEN
*SubScreen
= NULL
;
613 LOADER_ENTRY
*SubEntry
;
615 FileName
= Basename(Entry
->LoaderPath
);
616 if (Entry
->me
.SubScreen
== NULL
) { // No subscreen yet; initialize default entry....
617 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
618 if (SubScreen
!= NULL
) {
619 SubScreen
->Title
= AllocateZeroPool(sizeof(CHAR16
) * 256);
620 SPrint(SubScreen
->Title
, 255, L
"Boot Options for %s on %s",
621 (Entry
->Title
!= NULL
) ? Entry
->Title
: FileName
, Entry
->VolName
);
622 SubScreen
->TitleImage
= Entry
->me
.Image
;
624 SubEntry
= InitializeLoaderEntry(Entry
);
625 if (SubEntry
!= NULL
) {
626 SubEntry
->me
.Title
= StrDuplicate(L
"Boot using default options");
627 MainOptions
= SubEntry
->LoadOptions
;
628 SubEntry
->LoadOptions
= AddInitrdToOptions(MainOptions
, SubEntry
->InitrdPath
);
629 MyFreePool(MainOptions
);
630 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
631 } // if (SubEntry != NULL)
632 SubScreen
->Hint1
= StrDuplicate(SUBSCREEN_HINT1
);
633 if (GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_EDITOR
) {
634 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2_NO_EDITOR
);
636 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2
);
638 } // if (SubScreen != NULL)
639 } else { // existing subscreen; less initialization, and just add new entry later....
640 SubScreen
= Entry
->me
.SubScreen
;
643 } // REFIT_MENU_SCREEN *InitializeSubScreen()
645 VOID
GenerateSubScreen(LOADER_ENTRY
*Entry
, IN REFIT_VOLUME
*Volume
) {
646 REFIT_MENU_SCREEN
*SubScreen
;
647 LOADER_ENTRY
*SubEntry
;
649 CHAR16 DiagsFileName
[256];
654 // create the submenu
655 if (StrLen(Entry
->Title
) == 0) {
656 MyFreePool(Entry
->Title
);
659 SubScreen
= InitializeSubScreen(Entry
);
661 // loader-specific submenu entries
662 if (Entry
->OSType
== 'M') { // entries for Mac OS X
664 SubEntry
= InitializeLoaderEntry(Entry
);
665 if (SubEntry
!= NULL
) {
666 SubEntry
->me
.Title
= L
"Boot Mac OS X with a 64-bit kernel";
667 SubEntry
->LoadOptions
= L
"arch=x86_64";
668 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
669 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
672 SubEntry
= InitializeLoaderEntry(Entry
);
673 if (SubEntry
!= NULL
) {
674 SubEntry
->me
.Title
= L
"Boot Mac OS X with a 32-bit kernel";
675 SubEntry
->LoadOptions
= L
"arch=i386";
676 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
677 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
681 if (!(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_SINGLEUSER
)) {
682 SubEntry
= InitializeLoaderEntry(Entry
);
683 if (SubEntry
!= NULL
) {
684 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode";
685 SubEntry
->UseGraphicsMode
= FALSE
;
686 SubEntry
->LoadOptions
= L
"-v";
687 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
691 SubEntry
= InitializeLoaderEntry(Entry
);
692 if (SubEntry
!= NULL
) {
693 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode (64-bit)";
694 SubEntry
->UseGraphicsMode
= FALSE
;
695 SubEntry
->LoadOptions
= L
"-v arch=x86_64";
696 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
699 SubEntry
= InitializeLoaderEntry(Entry
);
700 if (SubEntry
!= NULL
) {
701 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode (32-bit)";
702 SubEntry
->UseGraphicsMode
= FALSE
;
703 SubEntry
->LoadOptions
= L
"-v arch=i386";
704 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
708 SubEntry
= InitializeLoaderEntry(Entry
);
709 if (SubEntry
!= NULL
) {
710 SubEntry
->me
.Title
= L
"Boot Mac OS X in single user mode";
711 SubEntry
->UseGraphicsMode
= FALSE
;
712 SubEntry
->LoadOptions
= L
"-v -s";
713 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
715 } // single-user mode allowed
717 if (!(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_SAFEMODE
)) {
718 SubEntry
= InitializeLoaderEntry(Entry
);
719 if (SubEntry
!= NULL
) {
720 SubEntry
->me
.Title
= L
"Boot Mac OS X in safe mode";
721 SubEntry
->UseGraphicsMode
= FALSE
;
722 SubEntry
->LoadOptions
= L
"-v -x";
723 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
725 } // safe mode allowed
727 // check for Apple hardware diagnostics
728 StrCpy(DiagsFileName
, L
"System\\Library\\CoreServices\\.diagnostics\\diags.efi");
729 if (FileExists(Volume
->RootDir
, DiagsFileName
) && !(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_HWTEST
)) {
730 SubEntry
= InitializeLoaderEntry(Entry
);
731 if (SubEntry
!= NULL
) {
732 SubEntry
->me
.Title
= L
"Run Apple Hardware Test";
733 MyFreePool(SubEntry
->LoaderPath
);
734 SubEntry
->LoaderPath
= StrDuplicate(DiagsFileName
);
735 SubEntry
->DevicePath
= FileDevicePath(Volume
->DeviceHandle
, SubEntry
->LoaderPath
);
736 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
737 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
739 } // if diagnostics entry found
741 } else if (Entry
->OSType
== 'L') { // entries for Linux kernels with EFI stub loaders
742 File
= ReadLinuxOptionsFile(Entry
->LoaderPath
, Volume
);
744 InitrdName
= FindInitrd(Entry
->LoaderPath
, Volume
);
745 TokenCount
= ReadTokenLine(File
, &TokenList
);
746 // first entry requires special processing, since it was initially set
747 // up with a default title but correct options by InitializeSubScreen(),
749 if ((SubScreen
->Entries
!= NULL
) && (SubScreen
->Entries
[0] != NULL
)) {
750 MyFreePool(SubScreen
->Entries
[0]->Title
);
751 SubScreen
->Entries
[0]->Title
= TokenList
[0] ? StrDuplicate(TokenList
[0]) : StrDuplicate(L
"Boot Linux");
753 FreeTokenLine(&TokenList
, &TokenCount
);
754 while ((TokenCount
= ReadTokenLine(File
, &TokenList
)) > 1) {
755 SubEntry
= InitializeLoaderEntry(Entry
);
756 SubEntry
->me
.Title
= TokenList
[0] ? StrDuplicate(TokenList
[0]) : StrDuplicate(L
"Boot Linux");
757 MyFreePool(SubEntry
->LoadOptions
);
758 SubEntry
->LoadOptions
= AddInitrdToOptions(TokenList
[1], InitrdName
);
759 FreeTokenLine(&TokenList
, &TokenCount
);
760 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_LINUX
;
761 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
763 MyFreePool(InitrdName
);
767 } else if (Entry
->OSType
== 'E') { // entries for ELILO
768 SubEntry
= InitializeLoaderEntry(Entry
);
769 if (SubEntry
!= NULL
) {
770 SubEntry
->me
.Title
= L
"Run ELILO in interactive mode";
771 SubEntry
->LoadOptions
= L
"-p";
772 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
773 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
776 SubEntry
= InitializeLoaderEntry(Entry
);
777 if (SubEntry
!= NULL
) {
778 SubEntry
->me
.Title
= L
"Boot Linux for a 17\" iMac or a 15\" MacBook Pro (*)";
779 SubEntry
->UseGraphicsMode
= TRUE
;
780 SubEntry
->LoadOptions
= L
"-d 0 i17";
781 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
782 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
785 SubEntry
= InitializeLoaderEntry(Entry
);
786 if (SubEntry
!= NULL
) {
787 SubEntry
->me
.Title
= L
"Boot Linux for a 20\" iMac (*)";
788 SubEntry
->UseGraphicsMode
= TRUE
;
789 SubEntry
->LoadOptions
= L
"-d 0 i20";
790 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
791 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
794 SubEntry
= InitializeLoaderEntry(Entry
);
795 if (SubEntry
!= NULL
) {
796 SubEntry
->me
.Title
= L
"Boot Linux for a Mac Mini (*)";
797 SubEntry
->UseGraphicsMode
= TRUE
;
798 SubEntry
->LoadOptions
= L
"-d 0 mini";
799 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
800 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
803 AddMenuInfoLine(SubScreen
, L
"NOTE: This is an example. Entries");
804 AddMenuInfoLine(SubScreen
, L
"marked with (*) may not work.");
806 } else if (Entry
->OSType
== 'X') { // entries for xom.efi
807 // by default, skip the built-in selection and boot from hard disk only
808 Entry
->LoadOptions
= L
"-s -h";
810 SubEntry
= InitializeLoaderEntry(Entry
);
811 if (SubEntry
!= NULL
) {
812 SubEntry
->me
.Title
= L
"Boot Windows from Hard Disk";
813 SubEntry
->LoadOptions
= L
"-s -h";
814 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
815 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
818 SubEntry
= InitializeLoaderEntry(Entry
);
819 if (SubEntry
!= NULL
) {
820 SubEntry
->me
.Title
= L
"Boot Windows from CD-ROM";
821 SubEntry
->LoadOptions
= L
"-s -c";
822 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
823 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
826 SubEntry
= InitializeLoaderEntry(Entry
);
827 if (SubEntry
!= NULL
) {
828 SubEntry
->me
.Title
= L
"Run XOM in text mode";
829 SubEntry
->UseGraphicsMode
= FALSE
;
830 SubEntry
->LoadOptions
= L
"-v";
831 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
832 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
834 } // entries for xom.efi
835 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
836 Entry
->me
.SubScreen
= SubScreen
;
837 } // VOID GenerateSubScreen()
839 // Returns options for a Linux kernel. Reads them from an options file in the
840 // kernel's directory; and if present, adds an initrd= option for an initial
841 // RAM disk file with the same version number as the kernel file.
842 static CHAR16
* GetMainLinuxOptions(IN CHAR16
* LoaderPath
, IN REFIT_VOLUME
*Volume
) {
843 CHAR16
*Options
= NULL
, *InitrdName
, *FullOptions
= NULL
;
845 Options
= GetFirstOptionsFromFile(LoaderPath
, Volume
);
846 InitrdName
= FindInitrd(LoaderPath
, Volume
);
847 FullOptions
= AddInitrdToOptions(Options
, InitrdName
);
850 MyFreePool(InitrdName
);
851 return (FullOptions
);
852 } // static CHAR16 * GetMainLinuxOptions()
854 // Try to guess the name of the Linux distribution & add that name to
856 static VOID
GuessLinuxDistribution(CHAR16
**OSIconName
, REFIT_VOLUME
*Volume
, CHAR16
*LoaderPath
) {
860 UINTN TokenCount
= 0;
862 // If on Linux root fs, /etc/os-release file probably has clues....
863 if (FileExists(Volume
->RootDir
, L
"etc\\os-release") &&
864 (ReadFile(Volume
->RootDir
, L
"etc\\os-release", &File
, &FileSize
) == EFI_SUCCESS
)) {
866 TokenCount
= ReadTokenLine(&File
, &TokenList
);
867 if ((TokenCount
> 1) && ((StriCmp(TokenList
[0], L
"ID") == 0) || (StriCmp(TokenList
[0], L
"NAME") == 0))) {
868 MergeStrings(OSIconName
, TokenList
[1], L
',');
870 FreeTokenLine(&TokenList
, &TokenCount
);
871 } while (TokenCount
> 0);
872 MyFreePool(File
.Buffer
);
875 // Search for clues in the kernel's filename....
876 if (StriSubCmp(L
".fc", LoaderPath
))
877 MergeStrings(OSIconName
, L
"fedora", L
',');
878 if (StriSubCmp(L
".el", LoaderPath
))
879 MergeStrings(OSIconName
, L
"redhat", L
',');
880 } // VOID GuessLinuxDistribution()
882 // Sets a few defaults for a loader entry -- mainly the icon, but also the OS type
883 // code and shortcut letter. For Linux EFI stub loaders, also sets kernel options
884 // that will (with luck) work fairly automatically.
885 VOID
SetLoaderDefaults(LOADER_ENTRY
*Entry
, CHAR16
*LoaderPath
, REFIT_VOLUME
*Volume
) {
886 CHAR16
*FileName
, *PathOnly
, *NoExtension
, *OSIconName
= NULL
, *Temp
, *SubString
;
887 CHAR16 ShortcutLetter
= 0;
890 FileName
= Basename(LoaderPath
);
891 PathOnly
= FindPath(LoaderPath
);
892 NoExtension
= StripEfiExtension(FileName
);
894 // locate a custom icon for the loader
895 // Anything found here takes precedence over the "hints" in the OSIconName variable
896 if (!Entry
->me
.Image
)
897 Entry
->me
.Image
= egLoadIconAnyType(Volume
->RootDir
, PathOnly
, NoExtension
, 128);
898 if (!Entry
->me
.Image
)
899 Entry
->me
.Image
= egCopyImage(Volume
->VolIconImage
);
901 // Begin creating icon "hints" by using last part of directory path leading
903 Temp
= FindLastDirName(LoaderPath
);
904 MergeStrings(&OSIconName
, Temp
, L
',');
907 if (OSIconName
!= NULL
) {
908 ShortcutLetter
= OSIconName
[0];
911 // Add every "word" in the volume label, delimited by spaces, dashes (-), or
912 // underscores (_), to the list of hints to be used in searching for OS
914 if ((Volume
->VolName
) && (StrLen(Volume
->VolName
) > 0)) {
915 Temp
= SubString
= StrDuplicate(Volume
->VolName
);
917 Length
= StrLen(Temp
);
918 for (i
= 0; i
< Length
; i
++) {
919 if ((Temp
[i
] == L
' ') || (Temp
[i
] == L
'_') || (Temp
[i
] == L
'-')) {
921 if (StrLen(SubString
) > 0)
922 MergeStrings(&OSIconName
, SubString
, L
',');
923 SubString
= Temp
+ i
+ 1;
926 MergeStrings(&OSIconName
, SubString
, L
',');
931 // detect specific loaders
932 if (StriSubCmp(L
"bzImage", LoaderPath
) || StriSubCmp(L
"vmlinuz", LoaderPath
)) {
933 GuessLinuxDistribution(&OSIconName
, Volume
, LoaderPath
);
934 MergeStrings(&OSIconName
, L
"linux", L
',');
936 if (ShortcutLetter
== 0)
937 ShortcutLetter
= 'L';
938 Entry
->LoadOptions
= GetMainLinuxOptions(LoaderPath
, Volume
);
939 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_LINUX
;
940 } else if (StriSubCmp(L
"refit", LoaderPath
)) {
941 MergeStrings(&OSIconName
, L
"refit", L
',');
943 ShortcutLetter
= 'R';
944 } else if (StriSubCmp(L
"refind", LoaderPath
)) {
945 MergeStrings(&OSIconName
, L
"refind", L
',');
947 ShortcutLetter
= 'R';
948 } else if (StriCmp(LoaderPath
, MACOSX_LOADER_PATH
) == 0) {
949 if (Volume
->VolIconImage
!= NULL
) { // custom icon file found
950 Entry
->me
.Image
= Volume
->VolIconImage
;
952 MergeStrings(&OSIconName
, L
"mac", L
',');
954 ShortcutLetter
= 'M';
955 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
956 } else if (StriCmp(FileName
, L
"diags.efi") == 0) {
957 MergeStrings(&OSIconName
, L
"hwtest", L
',');
958 } else if (StriCmp(FileName
, L
"e.efi") == 0 || StriCmp(FileName
, L
"elilo.efi") == 0 || StriSubCmp(L
"elilo", FileName
)) {
959 MergeStrings(&OSIconName
, L
"elilo,linux", L
',');
961 if (ShortcutLetter
== 0)
962 ShortcutLetter
= 'L';
963 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
964 } else if (StriSubCmp(L
"grub", FileName
)) {
966 ShortcutLetter
= 'G';
967 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_GRUB
;
968 } else if (StriCmp(FileName
, L
"cdboot.efi") == 0 ||
969 StriCmp(FileName
, L
"bootmgr.efi") == 0 ||
970 StriCmp(FileName
, L
"bootmgfw.efi") == 0 ||
971 StriCmp(FileName
, L
"bkpbootmgfw.efi") == 0) {
972 MergeStrings(&OSIconName
, L
"win", L
',');
974 ShortcutLetter
= 'W';
975 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
976 } else if (StriCmp(FileName
, L
"xom.efi") == 0) {
977 MergeStrings(&OSIconName
, L
"xom,win", L
',');
978 Entry
->UseGraphicsMode
= TRUE
;
980 ShortcutLetter
= 'W';
981 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
984 if ((ShortcutLetter
>= 'a') && (ShortcutLetter
<= 'z'))
985 ShortcutLetter
= ShortcutLetter
- 'a' + 'A'; // convert lowercase to uppercase
986 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
987 if (Entry
->me
.Image
== NULL
)
988 Entry
->me
.Image
= LoadOSIcon(OSIconName
, L
"unknown", FALSE
);
989 MyFreePool(PathOnly
);
990 } // VOID SetLoaderDefaults()
992 // Add a specified EFI boot loader to the list, using automatic settings
993 // for icons, options, etc.
994 LOADER_ENTRY
* AddLoaderEntry(IN CHAR16
*LoaderPath
, IN CHAR16
*LoaderTitle
, IN REFIT_VOLUME
*Volume
) {
997 CleanUpPathNameSlashes(LoaderPath
);
998 Entry
= InitializeLoaderEntry(NULL
);
1000 Entry
->Title
= StrDuplicate((LoaderTitle
!= NULL
) ? LoaderTitle
: LoaderPath
);
1001 Entry
->me
.Title
= AllocateZeroPool(sizeof(CHAR16
) * 256);
1002 SPrint(Entry
->me
.Title
, 255, L
"Boot %s from %s", (LoaderTitle
!= NULL
) ? LoaderTitle
: LoaderPath
, Volume
->VolName
);
1004 Entry
->me
.BadgeImage
= Volume
->VolBadgeImage
;
1005 if ((LoaderPath
!= NULL
) && (LoaderPath
[0] != L
'\\')) {
1006 Entry
->LoaderPath
= StrDuplicate(L
"\\");
1008 Entry
->LoaderPath
= NULL
;
1010 MergeStrings(&(Entry
->LoaderPath
), LoaderPath
, 0);
1011 Entry
->VolName
= Volume
->VolName
;
1012 Entry
->DevicePath
= FileDevicePath(Volume
->DeviceHandle
, Entry
->LoaderPath
);
1013 SetLoaderDefaults(Entry
, LoaderPath
, Volume
);
1014 GenerateSubScreen(Entry
, Volume
);
1015 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1019 } // LOADER_ENTRY * AddLoaderEntry()
1021 // Returns -1 if (Time1 < Time2), +1 if (Time1 > Time2), or 0 if
1022 // (Time1 == Time2). Precision is only to the nearest second; since
1023 // this is used for sorting boot loader entries, differences smaller
1024 // than this are likely to be meaningless (and unlikely!).
1025 INTN
TimeComp(IN EFI_TIME
*Time1
, IN EFI_TIME
*Time2
) {
1026 INT64 Time1InSeconds
, Time2InSeconds
;
1028 // Following values are overestimates; I'm assuming 31 days in every month.
1029 // This is fine for the purpose of this function, which is limited
1030 Time1InSeconds
= Time1
->Second
+ (Time1
->Minute
* 60) + (Time1
->Hour
* 3600) + (Time1
->Day
* 86400) +
1031 (Time1
->Month
* 2678400) + ((Time1
->Year
- 1998) * 32140800);
1032 Time2InSeconds
= Time2
->Second
+ (Time2
->Minute
* 60) + (Time2
->Hour
* 3600) + (Time2
->Day
* 86400) +
1033 (Time2
->Month
* 2678400) + ((Time2
->Year
- 1998) * 32140800);
1034 if (Time1InSeconds
< Time2InSeconds
)
1036 else if (Time1InSeconds
> Time2InSeconds
)
1040 } // INTN TimeComp()
1042 // Adds a loader list element, keeping it sorted by date. Returns the new
1043 // first element (the one with the most recent date).
1044 static struct LOADER_LIST
* AddLoaderListEntry(struct LOADER_LIST
*LoaderList
, struct LOADER_LIST
*NewEntry
) {
1045 struct LOADER_LIST
*LatestEntry
, *CurrentEntry
, *PrevEntry
= NULL
;
1047 LatestEntry
= CurrentEntry
= LoaderList
;
1048 if (LoaderList
== NULL
) {
1049 LatestEntry
= NewEntry
;
1051 while ((CurrentEntry
!= NULL
) && (TimeComp(&(NewEntry
->TimeStamp
), &(CurrentEntry
->TimeStamp
)) < 0)) {
1052 PrevEntry
= CurrentEntry
;
1053 CurrentEntry
= CurrentEntry
->NextEntry
;
1055 NewEntry
->NextEntry
= CurrentEntry
;
1056 if (PrevEntry
== NULL
) {
1057 LatestEntry
= NewEntry
;
1059 PrevEntry
->NextEntry
= NewEntry
;
1062 return (LatestEntry
);
1063 } // static VOID AddLoaderListEntry()
1065 // Delete the LOADER_LIST linked list
1066 static VOID
CleanUpLoaderList(struct LOADER_LIST
*LoaderList
) {
1067 struct LOADER_LIST
*Temp
;
1069 while (LoaderList
!= NULL
) {
1071 LoaderList
= LoaderList
->NextEntry
;
1072 MyFreePool(Temp
->FileName
);
1075 } // static VOID CleanUpLoaderList()
1077 // Returns FALSE if the specified file/volume matches the GlobalConfig.DontScanDirs
1078 // or GlobalConfig.DontScanVolumes specification, or if Path points to a volume
1079 // other than the one specified by Volume, or if the specified path is SelfDir.
1080 // Returns TRUE if none of these conditions is met -- that is, if the path is
1081 // eligible for scanning.
1082 static BOOLEAN
ShouldScan(REFIT_VOLUME
*Volume
, CHAR16
*Path
) {
1083 CHAR16
*VolName
= NULL
, *DontScanDir
, *PathCopy
= NULL
;
1084 UINTN i
= 0, VolNum
;
1085 BOOLEAN ScanIt
= TRUE
;
1087 if (IsIn(Volume
->VolName
, GlobalConfig
.DontScanVolumes
))
1090 if ((StriCmp(Path
, SelfDirPath
) == 0) && (Volume
->DeviceHandle
== SelfVolume
->DeviceHandle
))
1093 // See if Path includes an explicit volume declaration that's NOT Volume....
1094 PathCopy
= StrDuplicate(Path
);
1095 if (SplitVolumeAndFilename(&PathCopy
, &VolName
)) {
1096 if (StriCmp(VolName
, Volume
->VolName
) != 0) {
1097 if ((StrLen(VolName
) > 2) && (VolName
[0] == L
'f') && (VolName
[1] == L
's') && (VolName
[2] >= L
'0') && (VolName
[2] <= L
'9')) {
1098 VolNum
= Atoi(VolName
+ 2);
1099 if (VolNum
!= Volume
->VolNumber
) {
1106 } // if Path includes volume specification
1107 MyFreePool(PathCopy
);
1108 MyFreePool(VolName
);
1111 // See if Volume is in GlobalConfig.DontScanDirs....
1112 while ((DontScanDir
= FindCommaDelimited(GlobalConfig
.DontScanDirs
, i
++)) && ScanIt
) {
1113 SplitVolumeAndFilename(&DontScanDir
, &VolName
);
1114 CleanUpPathNameSlashes(DontScanDir
);
1115 if (VolName
!= NULL
) {
1116 if ((StriCmp(VolName
, Volume
->VolName
) == 0) && (StriCmp(DontScanDir
, Path
) == 0))
1118 if ((StrLen(VolName
) > 2) && (VolName
[0] == L
'f') && (VolName
[1] == L
's') && (VolName
[2] >= L
'0') && (VolName
[2] <= L
'9')) {
1119 VolNum
= Atoi(VolName
+ 2);
1120 if ((VolNum
== Volume
->VolNumber
) && (StriCmp(DontScanDir
, Path
) == 0))
1124 if (StriCmp(DontScanDir
, Path
) == 0)
1127 MyFreePool(DontScanDir
);
1128 MyFreePool(VolName
);
1133 } // BOOLEAN ShouldScan()
1135 // Returns TRUE if the file is byte-for-byte identical with the fallback file
1136 // on the volume AND if the file is not itself the fallback file; returns
1137 // FALSE if the file is not identical to the fallback file OR if the file
1138 // IS the fallback file. Intended for use in excluding the fallback boot
1139 // loader when it's a duplicate of another boot loader.
1140 static BOOLEAN
DuplicatesFallback(IN REFIT_VOLUME
*Volume
, IN CHAR16
*FileName
) {
1141 CHAR8
*FileContents
, *FallbackContents
;
1142 EFI_FILE_HANDLE FileHandle
, FallbackHandle
;
1143 EFI_FILE_INFO
*FileInfo
, *FallbackInfo
;
1144 UINTN FileSize
= 0, FallbackSize
= 0;
1146 BOOLEAN AreIdentical
= FALSE
;
1148 if (!FileExists(Volume
->RootDir
, FileName
) || !FileExists(Volume
->RootDir
, FALLBACK_FULLNAME
))
1151 CleanUpPathNameSlashes(FileName
);
1153 if (StriCmp(FileName
, FALLBACK_FULLNAME
) == 0)
1154 return FALSE
; // identical filenames, so not a duplicate....
1156 Status
= refit_call5_wrapper(Volume
->RootDir
->Open
, Volume
->RootDir
, &FileHandle
, FileName
, EFI_FILE_MODE_READ
, 0);
1157 if (Status
== EFI_SUCCESS
) {
1158 FileInfo
= LibFileInfo(FileHandle
);
1159 FileSize
= FileInfo
->FileSize
;
1164 Status
= refit_call5_wrapper(Volume
->RootDir
->Open
, Volume
->RootDir
, &FallbackHandle
, FALLBACK_FULLNAME
, EFI_FILE_MODE_READ
, 0);
1165 if (Status
== EFI_SUCCESS
) {
1166 FallbackInfo
= LibFileInfo(FallbackHandle
);
1167 FallbackSize
= FallbackInfo
->FileSize
;
1169 refit_call1_wrapper(FileHandle
->Close
, FileHandle
);
1173 if (FallbackSize
!= FileSize
) { // not same size, so can't be identical
1174 AreIdentical
= FALSE
;
1175 } else { // could be identical; do full check....
1176 FileContents
= AllocatePool(FileSize
);
1177 FallbackContents
= AllocatePool(FallbackSize
);
1178 if (FileContents
&& FallbackContents
) {
1179 Status
= refit_call3_wrapper(FileHandle
->Read
, FileHandle
, &FileSize
, FileContents
);
1180 if (Status
== EFI_SUCCESS
) {
1181 Status
= refit_call3_wrapper(FallbackHandle
->Read
, FallbackHandle
, &FallbackSize
, FallbackContents
);
1183 if (Status
== EFI_SUCCESS
) {
1184 AreIdentical
= (CompareMem(FileContents
, FallbackContents
, FileSize
) == 0);
1187 MyFreePool(FileContents
);
1188 MyFreePool(FallbackContents
);
1191 // BUG ALERT: Some systems (e.g., DUET, some Macs with large displays) crash if the
1192 // following two calls are reversed. Go figure....
1193 refit_call1_wrapper(FileHandle
->Close
, FallbackHandle
);
1194 refit_call1_wrapper(FileHandle
->Close
, FileHandle
);
1195 return AreIdentical
;
1196 } // BOOLEAN DuplicatesFallback()
1198 // Returns FALSE if two measures of file size are identical for a single file,
1199 // TRUE if not or if the file can't be opened and the other measure is non-0.
1200 // Despite the function's name, this isn't really a direct test of symbolic
1201 // link status, since EFI doesn't officially support symlinks. It does seem
1202 // to be a reliable indicator, though. (OTOH, some disk errors might cause a
1203 // file to fail to open, which would return a false positive -- but as I use
1204 // this function to exclude symbolic links from the list of boot loaders,
1205 // that would be fine, since such boot loaders wouldn't work.)
1206 static BOOLEAN
IsSymbolicLink(REFIT_VOLUME
*Volume
, CHAR16
*Path
, EFI_FILE_INFO
*DirEntry
) {
1207 EFI_FILE_HANDLE FileHandle
;
1208 EFI_FILE_INFO
*FileInfo
= NULL
;
1210 UINTN FileSize2
= 0;
1213 FileName
= StrDuplicate(Path
);
1214 MergeStrings(&FileName
, DirEntry
->FileName
, L
'\\');
1215 CleanUpPathNameSlashes(FileName
);
1217 Status
= refit_call5_wrapper(Volume
->RootDir
->Open
, Volume
->RootDir
, &FileHandle
, FileName
, EFI_FILE_MODE_READ
, 0);
1218 if (Status
== EFI_SUCCESS
) {
1219 FileInfo
= LibFileInfo(FileHandle
);
1220 if (FileInfo
!= NULL
)
1221 FileSize2
= FileInfo
->FileSize
;
1224 MyFreePool(FileName
);
1225 MyFreePool(FileInfo
);
1227 return (DirEntry
->FileSize
!= FileSize2
);
1228 } // BOOLEAN IsSymbolicLink()
1230 // Scan an individual directory for EFI boot loader files and, if found,
1231 // add them to the list. Exception: Ignores FALLBACK_FULLNAME, which is picked
1232 // up in ScanEfiFiles(). Sorts the entries within the loader directory so that
1233 // the most recent one appears first in the list.
1234 // Returns TRUE if a duplicate for FALLBACK_FILENAME was found, FALSE if not.
1235 static BOOLEAN
ScanLoaderDir(IN REFIT_VOLUME
*Volume
, IN CHAR16
*Path
, IN CHAR16
*Pattern
)
1238 REFIT_DIR_ITER DirIter
;
1239 EFI_FILE_INFO
*DirEntry
;
1240 CHAR16 FileName
[256], *Extension
;
1241 struct LOADER_LIST
*LoaderList
= NULL
, *NewLoader
;
1242 BOOLEAN FoundFallbackDuplicate
= FALSE
;
1244 if ((!SelfDirPath
|| !Path
|| ((StriCmp(Path
, SelfDirPath
) == 0) && (Volume
->DeviceHandle
!= SelfVolume
->DeviceHandle
)) ||
1245 (StriCmp(Path
, SelfDirPath
) != 0)) &&
1246 (ShouldScan(Volume
, Path
))) {
1247 // look through contents of the directory
1248 DirIterOpen(Volume
->RootDir
, Path
, &DirIter
);
1249 while (DirIterNext(&DirIter
, 2, Pattern
, &DirEntry
)) {
1250 Extension
= FindExtension(DirEntry
->FileName
);
1251 if (DirEntry
->FileName
[0] == '.' ||
1252 StriCmp(Extension
, L
".icns") == 0 ||
1253 StriCmp(Extension
, L
".png") == 0 ||
1254 (StriCmp(DirEntry
->FileName
, FALLBACK_BASENAME
) == 0 && (StriCmp(Path
, L
"EFI\\BOOT") == 0)) ||
1255 StriSubCmp(L
"shell", DirEntry
->FileName
) ||
1256 IsSymbolicLink(Volume
, Path
, DirEntry
) || /* is symbolic link */
1257 IsIn(DirEntry
->FileName
, GlobalConfig
.DontScanFiles
))
1258 continue; // skip this
1261 SPrint(FileName
, 255, L
"\\%s\\%s", Path
, DirEntry
->FileName
);
1263 SPrint(FileName
, 255, L
"\\%s", DirEntry
->FileName
);
1264 CleanUpPathNameSlashes(FileName
);
1266 if(!IsValidLoader(Volume
->RootDir
, FileName
))
1269 NewLoader
= AllocateZeroPool(sizeof(struct LOADER_LIST
));
1270 if (NewLoader
!= NULL
) {
1271 NewLoader
->FileName
= StrDuplicate(FileName
);
1272 NewLoader
->TimeStamp
= DirEntry
->ModificationTime
;
1273 LoaderList
= AddLoaderListEntry(LoaderList
, NewLoader
);
1274 if (DuplicatesFallback(Volume
, FileName
))
1275 FoundFallbackDuplicate
= TRUE
;
1277 MyFreePool(Extension
);
1280 NewLoader
= LoaderList
;
1281 while (NewLoader
!= NULL
) {
1282 AddLoaderEntry(NewLoader
->FileName
, NULL
, Volume
);
1283 NewLoader
= NewLoader
->NextEntry
;
1286 CleanUpLoaderList(LoaderList
);
1287 Status
= DirIterClose(&DirIter
);
1288 // NOTE: EFI_INVALID_PARAMETER really is an error that should be reported;
1289 // but I've gotten reports from users who are getting this error occasionally
1290 // and I can't find anything wrong or reproduce the problem, so I'm putting
1291 // it down to buggy EFI implementations and ignoring that particular error....
1292 if ((Status
!= EFI_NOT_FOUND
) && (Status
!= EFI_INVALID_PARAMETER
)) {
1294 SPrint(FileName
, 255, L
"while scanning the %s directory", Path
);
1296 StrCpy(FileName
, L
"while scanning the root directory");
1297 CheckError(Status
, FileName
);
1298 } // if (Status != EFI_NOT_FOUND)
1299 } // if not scanning a blacklisted directory
1301 return FoundFallbackDuplicate
;
1302 } /* static VOID ScanLoaderDir() */
1304 static VOID
ScanEfiFiles(REFIT_VOLUME
*Volume
) {
1306 REFIT_DIR_ITER EfiDirIter
;
1307 EFI_FILE_INFO
*EfiDirEntry
;
1308 CHAR16 FileName
[256], *Directory
, *MatchPatterns
, *VolName
= NULL
, *SelfPath
;
1310 BOOLEAN ScanFallbackLoader
= TRUE
;
1311 BOOLEAN FoundBRBackup
= FALSE
;
1313 MatchPatterns
= StrDuplicate(LOADER_MATCH_PATTERNS
);
1314 if (GlobalConfig
.ScanAllLinux
)
1315 MergeStrings(&MatchPatterns
, LINUX_MATCH_PATTERNS
, L
',');
1317 if ((Volume
->RootDir
!= NULL
) && (Volume
->VolName
!= NULL
)) {
1318 // check for Mac OS X boot loader
1319 if (ShouldScan(Volume
, L
"System\\Library\\CoreServices")) {
1320 StrCpy(FileName
, MACOSX_LOADER_PATH
);
1321 if (FileExists(Volume
->RootDir
, FileName
) && !IsIn(L
"boot.efi", GlobalConfig
.DontScanFiles
)) {
1322 AddLoaderEntry(FileName
, L
"Mac OS X", Volume
);
1323 if (DuplicatesFallback(Volume
, FileName
))
1324 ScanFallbackLoader
= FALSE
;
1328 StrCpy(FileName
, L
"System\\Library\\CoreServices\\xom.efi");
1329 if (FileExists(Volume
->RootDir
, FileName
) && !IsIn(L
"boot.efi", GlobalConfig
.DontScanFiles
)) {
1330 AddLoaderEntry(FileName
, L
"Windows XP (XoM)", Volume
);
1331 if (DuplicatesFallback(Volume
, FileName
))
1332 ScanFallbackLoader
= FALSE
;
1334 } // if should scan Mac directory
1336 // check for Microsoft boot loader/menu
1337 if (ShouldScan(Volume
, L
"EFI\\Microsoft\\Boot")) {
1338 StrCpy(FileName
, L
"EFI\\Microsoft\\Boot\\bkpbootmgfw.efi");
1339 if (FileExists(Volume
->RootDir
, FileName
) && !IsIn(L
"bkpbootmgfw.efi", GlobalConfig
.DontScanFiles
)) {
1340 AddLoaderEntry(FileName
, L
"Microsoft EFI boot (Boot Repair backup)", Volume
);
1341 FoundBRBackup
= TRUE
;
1342 if (DuplicatesFallback(Volume
, FileName
))
1343 ScanFallbackLoader
= FALSE
;
1345 StrCpy(FileName
, L
"EFI\\Microsoft\\Boot\\bootmgfw.efi");
1346 if (FileExists(Volume
->RootDir
, FileName
) && !IsIn(L
"bootmgfw.efi", GlobalConfig
.DontScanFiles
)) {
1348 AddLoaderEntry(FileName
, L
"Supposed Microsoft EFI boot (probably GRUB)", Volume
);
1350 AddLoaderEntry(FileName
, L
"Microsoft EFI boot", Volume
);
1351 if (DuplicatesFallback(Volume
, FileName
))
1352 ScanFallbackLoader
= FALSE
;
1356 // scan the root directory for EFI executables
1357 if (ScanLoaderDir(Volume
, L
"\\", MatchPatterns
))
1358 ScanFallbackLoader
= FALSE
;
1360 // scan subdirectories of the EFI directory (as per the standard)
1361 DirIterOpen(Volume
->RootDir
, L
"EFI", &EfiDirIter
);
1362 while (DirIterNext(&EfiDirIter
, 1, NULL
, &EfiDirEntry
)) {
1363 if (StriCmp(EfiDirEntry
->FileName
, L
"tools") == 0 || EfiDirEntry
->FileName
[0] == '.')
1364 continue; // skip this, doesn't contain boot loaders or is scanned later
1365 SPrint(FileName
, 255, L
"EFI\\%s", EfiDirEntry
->FileName
);
1366 if (ScanLoaderDir(Volume
, FileName
, MatchPatterns
))
1367 ScanFallbackLoader
= FALSE
;
1369 Status
= DirIterClose(&EfiDirIter
);
1370 if (Status
!= EFI_NOT_FOUND
)
1371 CheckError(Status
, L
"while scanning the EFI directory");
1373 // Scan user-specified (or additional default) directories....
1375 while ((Directory
= FindCommaDelimited(GlobalConfig
.AlsoScan
, i
++)) != NULL
) {
1376 if (ShouldScan(Volume
, Directory
)) {
1377 SplitVolumeAndFilename(&Directory
, &VolName
);
1378 CleanUpPathNameSlashes(Directory
);
1379 Length
= StrLen(Directory
);
1380 if ((Length
> 0) && ScanLoaderDir(Volume
, Directory
, MatchPatterns
))
1381 ScanFallbackLoader
= FALSE
;
1382 MyFreePool(VolName
);
1383 } // if should scan dir
1384 MyFreePool(Directory
);
1387 // Don't scan the fallback loader if it's on the same volume and a duplicate of rEFInd itself....
1388 SelfPath
= DevicePathToStr(SelfLoadedImage
->FilePath
);
1389 CleanUpPathNameSlashes(SelfPath
);
1390 if ((Volume
->DeviceHandle
== SelfLoadedImage
->DeviceHandle
) && DuplicatesFallback(Volume
, SelfPath
))
1391 ScanFallbackLoader
= FALSE
;
1393 // If not a duplicate & if it exists & if it's not us, create an entry
1394 // for the fallback boot loader
1395 if (ScanFallbackLoader
&& FileExists(Volume
->RootDir
, FALLBACK_FULLNAME
) && ShouldScan(Volume
, L
"EFI\\BOOT"))
1396 AddLoaderEntry(FALLBACK_FULLNAME
, L
"Fallback boot loader", Volume
);
1398 } // static VOID ScanEfiFiles()
1400 // Scan internal disks for valid EFI boot loaders....
1401 static VOID
ScanInternal(VOID
) {
1404 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1405 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_INTERNAL
) {
1406 ScanEfiFiles(Volumes
[VolumeIndex
]);
1409 } // static VOID ScanInternal()
1411 // Scan external disks for valid EFI boot loaders....
1412 static VOID
ScanExternal(VOID
) {
1415 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1416 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_EXTERNAL
) {
1417 ScanEfiFiles(Volumes
[VolumeIndex
]);
1420 } // static VOID ScanExternal()
1422 // Scan internal disks for valid EFI boot loaders....
1423 static VOID
ScanOptical(VOID
) {
1426 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1427 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_OPTICAL
) {
1428 ScanEfiFiles(Volumes
[VolumeIndex
]);
1431 } // static VOID ScanOptical()
1434 // legacy boot functions
1437 static EFI_STATUS
ActivateMbrPartition(IN EFI_BLOCK_IO
*BlockIO
, IN UINTN PartitionIndex
)
1440 UINT8 SectorBuffer
[512];
1441 MBR_PARTITION_INFO
*MbrTable
, *EMbrTable
;
1442 UINT32 ExtBase
, ExtCurrent
, NextExtCurrent
;
1443 UINTN LogicalPartitionIndex
= 4;
1445 BOOLEAN HaveBootCode
;
1448 Status
= refit_call5_wrapper(BlockIO
->ReadBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, 0, 512, SectorBuffer
);
1449 if (EFI_ERROR(Status
))
1451 if (*((UINT16
*)(SectorBuffer
+ 510)) != 0xaa55)
1452 return EFI_NOT_FOUND
; // safety measure #1
1454 // add boot code if necessary
1455 HaveBootCode
= FALSE
;
1456 for (i
= 0; i
< MBR_BOOTCODE_SIZE
; i
++) {
1457 if (SectorBuffer
[i
] != 0) {
1458 HaveBootCode
= TRUE
;
1462 if (!HaveBootCode
) {
1463 // no boot code found in the MBR, add the syslinux MBR code
1464 SetMem(SectorBuffer
, MBR_BOOTCODE_SIZE
, 0);
1465 CopyMem(SectorBuffer
, syslinux_mbr
, SYSLINUX_MBR_SIZE
);
1468 // set the partition active
1469 MbrTable
= (MBR_PARTITION_INFO
*)(SectorBuffer
+ 446);
1471 for (i
= 0; i
< 4; i
++) {
1472 if (MbrTable
[i
].Flags
!= 0x00 && MbrTable
[i
].Flags
!= 0x80)
1473 return EFI_NOT_FOUND
; // safety measure #2
1474 if (i
== PartitionIndex
)
1475 MbrTable
[i
].Flags
= 0x80;
1476 else if (PartitionIndex
>= 4 && IS_EXTENDED_PART_TYPE(MbrTable
[i
].Type
)) {
1477 MbrTable
[i
].Flags
= 0x80;
1478 ExtBase
= MbrTable
[i
].StartLBA
;
1480 MbrTable
[i
].Flags
= 0x00;
1484 Status
= refit_call5_wrapper(BlockIO
->WriteBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, 0, 512, SectorBuffer
);
1485 if (EFI_ERROR(Status
))
1488 if (PartitionIndex
>= 4) {
1489 // we have to activate a logical partition, so walk the EMBR chain
1491 // NOTE: ExtBase was set above while looking at the MBR table
1492 for (ExtCurrent
= ExtBase
; ExtCurrent
; ExtCurrent
= NextExtCurrent
) {
1493 // read current EMBR
1494 Status
= refit_call5_wrapper(BlockIO
->ReadBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, ExtCurrent
, 512, SectorBuffer
);
1495 if (EFI_ERROR(Status
))
1497 if (*((UINT16
*)(SectorBuffer
+ 510)) != 0xaa55)
1498 return EFI_NOT_FOUND
; // safety measure #3
1500 // scan EMBR, set appropriate partition active
1501 EMbrTable
= (MBR_PARTITION_INFO
*)(SectorBuffer
+ 446);
1503 for (i
= 0; i
< 4; i
++) {
1504 if (EMbrTable
[i
].Flags
!= 0x00 && EMbrTable
[i
].Flags
!= 0x80)
1505 return EFI_NOT_FOUND
; // safety measure #4
1506 if (EMbrTable
[i
].StartLBA
== 0 || EMbrTable
[i
].Size
== 0)
1508 if (IS_EXTENDED_PART_TYPE(EMbrTable
[i
].Type
)) {
1509 // link to next EMBR
1510 NextExtCurrent
= ExtBase
+ EMbrTable
[i
].StartLBA
;
1511 EMbrTable
[i
].Flags
= (PartitionIndex
>= LogicalPartitionIndex
) ? 0x80 : 0x00;
1514 // logical partition
1515 EMbrTable
[i
].Flags
= (PartitionIndex
== LogicalPartitionIndex
) ? 0x80 : 0x00;
1516 LogicalPartitionIndex
++;
1520 // write current EMBR
1521 Status
= refit_call5_wrapper(BlockIO
->WriteBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, ExtCurrent
, 512, SectorBuffer
);
1522 if (EFI_ERROR(Status
))
1525 if (PartitionIndex
< LogicalPartitionIndex
)
1526 break; // stop the loop, no need to touch further EMBRs
1532 } /* static EFI_STATUS ActivateMbrPartition() */
1534 // early 2006 Core Duo / Core Solo models
1535 static UINT8 LegacyLoaderDevicePath1Data
[] = {
1536 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1537 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1538 0xFF, 0xFF, 0xF9, 0xFF, 0x00, 0x00, 0x00, 0x00,
1539 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1540 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1541 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1543 // mid-2006 Mac Pro (and probably other Core 2 models)
1544 static UINT8 LegacyLoaderDevicePath2Data
[] = {
1545 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1546 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1547 0xFF, 0xFF, 0xF7, 0xFF, 0x00, 0x00, 0x00, 0x00,
1548 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1549 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1550 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1552 // mid-2007 MBP ("Santa Rosa" based models)
1553 static UINT8 LegacyLoaderDevicePath3Data
[] = {
1554 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1555 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1556 0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
1557 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1558 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1559 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1562 static UINT8 LegacyLoaderDevicePath4Data
[] = {
1563 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1564 0x00, 0x00, 0xC0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1565 0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
1566 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1567 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1568 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1570 // late-2008 MB/MBP (NVidia chipset)
1571 static UINT8 LegacyLoaderDevicePath5Data
[] = {
1572 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1573 0x00, 0x40, 0xCB, 0xFF, 0x00, 0x00, 0x00, 0x00,
1574 0xFF, 0xBF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
1575 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1576 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1577 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1580 static EFI_DEVICE_PATH
*LegacyLoaderList
[] = {
1581 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath1Data
,
1582 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath2Data
,
1583 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath3Data
,
1584 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath4Data
,
1585 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath5Data
,
1589 #define MAX_DISCOVERED_PATHS (16)
1591 static VOID
StartLegacy(IN LEGACY_ENTRY
*Entry
)
1594 EG_IMAGE
*BootLogoImage
;
1595 UINTN ErrorInStep
= 0;
1596 EFI_DEVICE_PATH
*DiscoveredPathList
[MAX_DISCOVERED_PATHS
];
1598 BeginExternalScreen(TRUE
, L
"Booting Legacy OS (Mac mode)");
1600 BootLogoImage
= LoadOSIcon(Entry
->Volume
->OSIconName
, L
"legacy", TRUE
);
1601 if (BootLogoImage
!= NULL
)
1602 BltImageAlpha(BootLogoImage
,
1603 (UGAWidth
- BootLogoImage
->Width
) >> 1,
1604 (UGAHeight
- BootLogoImage
->Height
) >> 1,
1605 &StdBackgroundPixel
);
1607 if (Entry
->Volume
->IsMbrPartition
) {
1608 ActivateMbrPartition(Entry
->Volume
->WholeDiskBlockIO
, Entry
->Volume
->MbrPartitionIndex
);
1611 ExtractLegacyLoaderPaths(DiscoveredPathList
, MAX_DISCOVERED_PATHS
, LegacyLoaderList
);
1613 Status
= StartEFIImageList(DiscoveredPathList
, Entry
->LoadOptions
, TYPE_LEGACY
, L
"legacy loader", 0, &ErrorInStep
, TRUE
);
1614 if (Status
== EFI_NOT_FOUND
) {
1615 if (ErrorInStep
== 1) {
1616 Print(L
"\nPlease make sure that you have the latest firmware update installed.\n");
1617 } else if (ErrorInStep
== 3) {
1618 Print(L
"\nThe firmware refused to boot from the selected volume. Note that external\n"
1619 L
"hard drives are not well-supported by Apple's firmware for legacy OS booting.\n");
1622 FinishExternalScreen();
1623 } /* static VOID StartLegacy() */
1625 // Start a device on a non-Mac using the EFI_LEGACY_BIOS_PROTOCOL
1626 #ifdef __MAKEWITH_TIANO
1627 static VOID
StartLegacyUEFI(IN LEGACY_ENTRY
*Entry
)
1629 BeginExternalScreen(TRUE
, L
"Booting Legacy OS (UEFI mode)");
1631 BdsLibConnectDevicePath (Entry
->BdsOption
->DevicePath
);
1632 BdsLibDoLegacyBoot(Entry
->BdsOption
);
1634 // If we get here, it means that there was a failure....
1635 Print(L
"Failure booting legacy (BIOS) OS.");
1637 FinishExternalScreen();
1638 } // static VOID StartLegacyUEFI()
1639 #endif // __MAKEWITH_TIANO
1641 static LEGACY_ENTRY
* AddLegacyEntry(IN CHAR16
*LoaderTitle
, IN REFIT_VOLUME
*Volume
)
1643 LEGACY_ENTRY
*Entry
, *SubEntry
;
1644 REFIT_MENU_SCREEN
*SubScreen
;
1646 CHAR16 ShortcutLetter
= 0;
1648 if (LoaderTitle
== NULL
) {
1649 if (Volume
->OSName
!= NULL
) {
1650 LoaderTitle
= Volume
->OSName
;
1651 if (LoaderTitle
[0] == 'W' || LoaderTitle
[0] == 'L')
1652 ShortcutLetter
= LoaderTitle
[0];
1654 LoaderTitle
= L
"Legacy OS";
1656 if (Volume
->VolName
!= NULL
)
1657 VolDesc
= Volume
->VolName
;
1659 VolDesc
= (Volume
->DiskKind
== DISK_KIND_OPTICAL
) ? L
"CD" : L
"HD";
1661 // prepare the menu entry
1662 Entry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1663 Entry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1664 SPrint(Entry
->me
.Title
, 255, L
"Boot %s from %s", LoaderTitle
, VolDesc
);
1665 Entry
->me
.Tag
= TAG_LEGACY
;
1667 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1668 Entry
->me
.Image
= LoadOSIcon(Volume
->OSIconName
, L
"legacy", FALSE
);
1669 Entry
->me
.BadgeImage
= Volume
->VolBadgeImage
;
1670 Entry
->Volume
= Volume
;
1671 Entry
->LoadOptions
= (Volume
->DiskKind
== DISK_KIND_OPTICAL
) ? L
"CD" :
1672 ((Volume
->DiskKind
== DISK_KIND_EXTERNAL
) ? L
"USB" : L
"HD");
1673 Entry
->Enabled
= TRUE
;
1675 // create the submenu
1676 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
1677 SubScreen
->Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1678 SPrint(SubScreen
->Title
, 255, L
"Boot Options for %s on %s", LoaderTitle
, VolDesc
);
1679 SubScreen
->TitleImage
= Entry
->me
.Image
;
1680 SubScreen
->Hint1
= StrDuplicate(SUBSCREEN_HINT1
);
1681 if (GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_EDITOR
) {
1682 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2_NO_EDITOR
);
1684 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2
);
1688 SubEntry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1689 SubEntry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1690 SPrint(SubEntry
->me
.Title
, 255, L
"Boot %s", LoaderTitle
);
1691 SubEntry
->me
.Tag
= TAG_LEGACY
;
1692 SubEntry
->Volume
= Entry
->Volume
;
1693 SubEntry
->LoadOptions
= Entry
->LoadOptions
;
1694 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
1696 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
1697 Entry
->me
.SubScreen
= SubScreen
;
1698 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1700 } /* static LEGACY_ENTRY * AddLegacyEntry() */
1703 #ifdef __MAKEWITH_GNUEFI
1704 static VOID
ScanLegacyUEFI(IN UINTN DiskType
){}
1706 // default volume badge icon based on disk kind
1707 static EG_IMAGE
* GetDiskBadge(IN UINTN DiskType
) {
1708 EG_IMAGE
* Badge
= NULL
;
1712 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_INTERNAL
);
1715 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_EXTERNAL
);
1718 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_OPTICAL
);
1722 } // static EG_IMAGE * GetDiskBadge()
1725 Create a rEFInd boot option from a Legacy BIOS protocol option.
1727 static LEGACY_ENTRY
* AddLegacyEntryUEFI(BDS_COMMON_OPTION
*BdsOption
, IN UINT16 DiskType
)
1729 LEGACY_ENTRY
*Entry
, *SubEntry
;
1730 REFIT_MENU_SCREEN
*SubScreen
;
1731 CHAR16 ShortcutLetter
= 0;
1732 CHAR16
*LegacyDescription
= BdsOption
->Description
;
1734 // prepare the menu entry
1735 Entry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1736 Entry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1737 SPrint(Entry
->me
.Title
, 255, L
"Boot legacy target %s", LegacyDescription
);
1738 Entry
->me
.Tag
= TAG_LEGACY_UEFI
;
1740 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1741 Entry
->me
.Image
= LoadOSIcon(L
"legacy", L
"legacy", TRUE
);
1742 Entry
->LoadOptions
= (DiskType
== BBS_CDROM
) ? L
"CD" :
1743 ((DiskType
== BBS_USB
) ? L
"USB" : L
"HD");
1744 Entry
->me
.BadgeImage
= GetDiskBadge(DiskType
);
1745 Entry
->BdsOption
= BdsOption
;
1746 Entry
->Enabled
= TRUE
;
1748 // create the submenu
1749 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
1750 SubScreen
->Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1751 SPrint(SubScreen
->Title
, 255, L
"No boot options for legacy target");
1752 SubScreen
->TitleImage
= Entry
->me
.Image
;
1753 SubScreen
->Hint1
= StrDuplicate(SUBSCREEN_HINT1
);
1754 if (GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_EDITOR
) {
1755 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2_NO_EDITOR
);
1757 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2
);
1761 SubEntry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1762 SubEntry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1763 SPrint(SubEntry
->me
.Title
, 255, L
"Boot %s", LegacyDescription
);
1764 SubEntry
->me
.Tag
= TAG_LEGACY_UEFI
;
1765 Entry
->BdsOption
= BdsOption
;
1766 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
1768 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
1769 Entry
->me
.SubScreen
= SubScreen
;
1770 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1772 } /* static LEGACY_ENTRY * AddLegacyEntryUEFI() */
1775 Scan for legacy BIOS targets on machines that implement EFI_LEGACY_BIOS_PROTOCOL.
1776 In testing, protocol has not been implemented on Macs but has been
1777 implemented on several Dell PCs and an ASUS motherboard.
1778 Restricts output to disks of the specified DiskType.
1780 static VOID
ScanLegacyUEFI(IN UINTN DiskType
)
1783 EFI_LEGACY_BIOS_PROTOCOL
*LegacyBios
;
1784 UINT16
*BootOrder
= NULL
;
1786 CHAR16 BootOption
[10];
1787 UINTN BootOrderSize
= 0;
1789 BDS_COMMON_OPTION
*BdsOption
;
1790 LIST_ENTRY TempList
;
1791 BBS_BBS_DEVICE_PATH
* BbsDevicePath
= NULL
;
1793 InitializeListHead (&TempList
);
1794 ZeroMem (Buffer
, sizeof (Buffer
));
1796 // If LegacyBios protocol is not implemented on this platform, then
1797 //we do not support this type of legacy boot on this machine.
1798 Status
= gBS
->LocateProtocol(&gEfiLegacyBootProtocolGuid
, NULL
, (VOID
**) &LegacyBios
);
1799 if (EFI_ERROR (Status
))
1802 // Grab the boot order
1803 BootOrder
= BdsLibGetVariableAndSize(L
"BootOrder", &gEfiGlobalVariableGuid
, &BootOrderSize
);
1804 if (BootOrder
== NULL
) {
1809 while (Index
< BootOrderSize
/ sizeof (UINT16
))
1811 // Grab each boot option variable from the boot order, and convert
1812 // the variable into a BDS boot option
1813 UnicodeSPrint (BootOption
, sizeof (BootOption
), L
"Boot%04x", BootOrder
[Index
]);
1814 BdsOption
= BdsLibVariableToOption (&TempList
, BootOption
);
1816 if (BdsOption
!= NULL
) {
1817 BbsDevicePath
= (BBS_BBS_DEVICE_PATH
*)BdsOption
->DevicePath
;
1819 // Only add the entry if it is of a requested type (e.g. USB, HD)
1821 // Two checks necessary because some systems return EFI boot loaders
1822 // with a DeviceType value that would inappropriately include them
1823 // as legacy loaders....
1824 if ((BbsDevicePath
->DeviceType
== DiskType
) && (BdsOption
->DevicePath
->Type
== DEVICE_TYPE_BIOS
)) {
1825 AddLegacyEntryUEFI(BdsOption
, BbsDevicePath
->DeviceType
);
1830 } /* static VOID ScanLegacyUEFI() */
1831 #endif // __MAKEWITH_GNUEFI
1833 static VOID
ScanLegacyVolume(REFIT_VOLUME
*Volume
, UINTN VolumeIndex
) {
1835 BOOLEAN ShowVolume
, HideIfOthersFound
;
1838 HideIfOthersFound
= FALSE
;
1839 if (Volume
->IsAppleLegacy
) {
1841 HideIfOthersFound
= TRUE
;
1842 } else if (Volume
->HasBootCode
) {
1844 if (Volume
->BlockIO
== Volume
->WholeDiskBlockIO
&&
1845 Volume
->BlockIOOffset
== 0 &&
1846 Volume
->OSName
== NULL
)
1847 // this is a whole disk (MBR) entry; hide if we have entries for partitions
1848 HideIfOthersFound
= TRUE
;
1850 if (HideIfOthersFound
) {
1851 // check for other bootable entries on the same disk
1852 for (VolumeIndex2
= 0; VolumeIndex2
< VolumesCount
; VolumeIndex2
++) {
1853 if (VolumeIndex2
!= VolumeIndex
&& Volumes
[VolumeIndex2
]->HasBootCode
&&
1854 Volumes
[VolumeIndex2
]->WholeDiskBlockIO
== Volume
->WholeDiskBlockIO
)
1860 AddLegacyEntry(NULL
, Volume
);
1861 } // static VOID ScanLegacyVolume()
1863 // Scan attached optical discs for legacy (BIOS) boot code
1864 // and add anything found to the list....
1865 static VOID
ScanLegacyDisc(VOID
)
1868 REFIT_VOLUME
*Volume
;
1870 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
) {
1871 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1872 Volume
= Volumes
[VolumeIndex
];
1873 if (Volume
->DiskKind
== DISK_KIND_OPTICAL
)
1874 ScanLegacyVolume(Volume
, VolumeIndex
);
1876 } else if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
1877 ScanLegacyUEFI(BBS_CDROM
);
1879 } /* static VOID ScanLegacyDisc() */
1881 // Scan internal hard disks for legacy (BIOS) boot code
1882 // and add anything found to the list....
1883 static VOID
ScanLegacyInternal(VOID
)
1886 REFIT_VOLUME
*Volume
;
1888 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
) {
1889 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1890 Volume
= Volumes
[VolumeIndex
];
1891 if (Volume
->DiskKind
== DISK_KIND_INTERNAL
)
1892 ScanLegacyVolume(Volume
, VolumeIndex
);
1894 } else if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
1895 ScanLegacyUEFI(BBS_HARDDISK
);
1897 } /* static VOID ScanLegacyInternal() */
1899 // Scan external disks for legacy (BIOS) boot code
1900 // and add anything found to the list....
1901 static VOID
ScanLegacyExternal(VOID
)
1904 REFIT_VOLUME
*Volume
;
1906 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
) {
1907 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1908 Volume
= Volumes
[VolumeIndex
];
1909 if (Volume
->DiskKind
== DISK_KIND_EXTERNAL
)
1910 ScanLegacyVolume(Volume
, VolumeIndex
);
1912 } else if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
1913 ScanLegacyUEFI(BBS_USB
);
1915 } /* static VOID ScanLegacyExternal() */
1918 // pre-boot tool functions
1921 static VOID
StartTool(IN LOADER_ENTRY
*Entry
)
1923 BeginExternalScreen(Entry
->UseGraphicsMode
, Entry
->me
.Title
+ 6); // assumes "Start <title>" as assigned below
1924 StartEFIImage(Entry
->DevicePath
, Entry
->LoadOptions
, TYPE_EFI
,
1925 Basename(Entry
->LoaderPath
), Entry
->OSType
, NULL
, TRUE
);
1926 FinishExternalScreen();
1927 } /* static VOID StartTool() */
1929 static LOADER_ENTRY
* AddToolEntry(EFI_HANDLE DeviceHandle
, IN CHAR16
*LoaderPath
, IN CHAR16
*LoaderTitle
, IN EG_IMAGE
*Image
,
1930 IN CHAR16 ShortcutLetter
, IN BOOLEAN UseGraphicsMode
)
1932 LOADER_ENTRY
*Entry
;
1933 CHAR16
*TitleStr
= NULL
;
1935 Entry
= AllocateZeroPool(sizeof(LOADER_ENTRY
));
1937 MergeStrings(&TitleStr
, L
"Start ", 0);
1938 MergeStrings(&TitleStr
, LoaderTitle
, 0);
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
) {
2155 // scan for loaders and tools, add them to the menu
2156 for (i
= 0; i
< NUM_SCAN_OPTIONS
; i
++) {
2157 switch(GlobalConfig
.ScanFor
[i
]) {
2162 ScanLegacyInternal();
2165 ScanLegacyExternal();
2168 ScanUserConfigured(CONFIG_FILE_NAME
);
2182 // assign shortcut keys
2183 for (i
= 0; i
< MainMenu
.EntryCount
&& MainMenu
.Entries
[i
]->Row
== 0 && i
< 9; i
++)
2184 MainMenu
.Entries
[i
]->ShortcutDigit
= (CHAR16
)('1' + i
);
2186 // wait for user ACK when there were errors
2187 FinishTextScreen(FALSE
);
2188 } // static VOID ScanForBootloaders()
2190 // Add the second-row tags containing built-in and external tools (EFI shell,
2192 static VOID
ScanForTools(VOID
) {
2193 CHAR16
*FileName
= NULL
, *MokLocations
, *MokName
, *PathName
, Description
[256];
2194 REFIT_MENU_ENTRY
*TempMenuEntry
;
2195 UINTN i
, j
, k
, VolumeIndex
;
2199 MokLocations
= StrDuplicate(MOK_LOCATIONS
);
2200 if (MokLocations
!= NULL
)
2201 MergeStrings(&MokLocations
, SelfDirPath
, L
',');
2203 for (i
= 0; i
< NUM_TOOLS
; i
++) {
2204 switch(GlobalConfig
.ShowTools
[i
]) {
2205 // NOTE: Be sure that FileName is NULL at the end of each case.
2207 TempMenuEntry
= CopyMenuEntry(&MenuEntryShutdown
);
2208 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_SHUTDOWN
);
2209 AddMenuEntry(&MainMenu
, TempMenuEntry
);
2212 TempMenuEntry
= CopyMenuEntry(&MenuEntryReset
);
2213 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_RESET
);
2214 AddMenuEntry(&MainMenu
, TempMenuEntry
);
2217 TempMenuEntry
= CopyMenuEntry(&MenuEntryAbout
);
2218 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT
);
2219 AddMenuEntry(&MainMenu
, TempMenuEntry
);
2222 TempMenuEntry
= CopyMenuEntry(&MenuEntryExit
);
2223 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_EXIT
);
2224 AddMenuEntry(&MainMenu
, TempMenuEntry
);
2227 if (EfivarGetRaw(&GlobalGuid
, L
"OsIndicationsSupported", &b
, &j
) == EFI_SUCCESS
) {
2229 if (osind
& EFI_OS_INDICATIONS_BOOT_TO_FW_UI
) {
2230 TempMenuEntry
= CopyMenuEntry(&MenuEntryFirmware
);
2231 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_FIRMWARE
);
2232 AddMenuEntry(&MainMenu
, TempMenuEntry
);
2238 while ((FileName
= FindCommaDelimited(SHELL_NAMES
, j
++)) != NULL
) {
2239 if (FileExists(SelfRootDir
, FileName
)) {
2240 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, L
"EFI Shell", BuiltinIcon(BUILTIN_ICON_TOOL_SHELL
),
2243 MyFreePool(FileName
);
2248 while ((FileName
= FindCommaDelimited(GPTSYNC_NAMES
, j
++)) != NULL
) {
2249 if (FileExists(SelfRootDir
, FileName
)) {
2250 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, L
"Hybrid MBR tool", BuiltinIcon(BUILTIN_ICON_TOOL_PART
),
2253 MyFreePool(FileName
);
2257 case TAG_APPLE_RECOVERY
:
2258 FileName
= StrDuplicate(L
"\\com.apple.recovery.boot\\boot.efi");
2259 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
2260 if ((Volumes
[VolumeIndex
]->RootDir
!= NULL
) && (FileExists(Volumes
[VolumeIndex
]->RootDir
, FileName
))) {
2261 SPrint(Description
, 255, L
"Apple Recovery on %s", Volumes
[VolumeIndex
]->VolName
);
2262 AddToolEntry(Volumes
[VolumeIndex
]->DeviceHandle
, FileName
, Description
,
2263 BuiltinIcon(BUILTIN_ICON_TOOL_APPLE_RESCUE
), 'R', TRUE
);
2266 MyFreePool(FileName
);
2271 while ((FileName
= FindCommaDelimited(MokLocations
, j
++)) != NULL
) {
2273 while ((MokName
= FindCommaDelimited(MOK_NAMES
, k
++)) != NULL
) {
2274 PathName
= StrDuplicate(FileName
);
2275 MergeStrings(&PathName
, MokName
, (StriCmp(PathName
, L
"\\") == 0) ? 0 : L
'\\');
2276 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
2277 if ((Volumes
[VolumeIndex
]->RootDir
!= NULL
) && (FileExists(Volumes
[VolumeIndex
]->RootDir
, PathName
))) {
2278 SPrint(Description
, 255, L
"MOK utility at %s on %s", PathName
, Volumes
[VolumeIndex
]->VolName
);
2279 AddToolEntry(Volumes
[VolumeIndex
]->DeviceHandle
, PathName
, Description
,
2280 BuiltinIcon(BUILTIN_ICON_TOOL_MOK_TOOL
), 'S', FALSE
);
2283 MyFreePool(PathName
);
2284 MyFreePool(MokName
);
2285 } // while MOK_NAMES
2286 MyFreePool(FileName
);
2287 } // while MokLocations
2292 } // static VOID ScanForTools
2294 // Rescan for boot loaders
2295 VOID
RescanAll(VOID
) {
2302 egDisplayMessage(L
"Scanning for new boot loaders; please wait....", &BGColor
);
2303 FreeList((VOID
***) &(MainMenu
.Entries
), &MainMenu
.EntryCount
);
2304 MainMenu
.Entries
= NULL
;
2305 MainMenu
.EntryCount
= 0;
2306 ReadConfig(CONFIG_FILE_NAME
);
2307 ConnectAllDriversToAllControllers();
2309 ScanForBootloaders();
2312 } // VOID RescanAll()
2314 #ifdef __MAKEWITH_TIANO
2316 // Minimal initialization function
2317 static VOID
InitializeLib(IN EFI_HANDLE ImageHandle
, IN EFI_SYSTEM_TABLE
*SystemTable
) {
2319 // gImageHandle = ImageHandle;
2320 gBS
= SystemTable
->BootServices
;
2321 // gRS = SystemTable->RuntimeServices;
2322 gRT
= SystemTable
->RuntimeServices
; // Some BDS functions need gRT to be set
2323 EfiGetSystemConfigurationTable (&gEfiDxeServicesTableGuid
, (VOID
**) &gDS
);
2325 InitializeConsoleSim();
2330 // Set up our own Secure Boot extensions....
2331 // Returns TRUE on success, FALSE otherwise
2332 static BOOLEAN
SecureBootSetup(VOID
) {
2334 BOOLEAN Success
= FALSE
;
2336 if (secure_mode() && ShimLoaded()) {
2337 Status
= security_policy_install();
2338 if (Status
== EFI_SUCCESS
) {
2341 Print(L
"Failed to install MOK Secure Boot extensions");
2345 } // VOID SecureBootSetup()
2347 // Remove our own Secure Boot extensions....
2348 // Returns TRUE on success, FALSE otherwise
2349 static BOOLEAN
SecureBootUninstall(VOID
) {
2351 BOOLEAN Success
= TRUE
;
2353 if (secure_mode()) {
2354 Status
= security_policy_uninstall();
2355 if (Status
!= EFI_SUCCESS
) {
2357 BeginTextScreen(L
"Secure Boot Policy Failure");
2358 Print(L
"Failed to uninstall MOK Secure Boot extensions; forcing a reboot.");
2360 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);
2364 } // VOID SecureBootUninstall
2371 efi_main (EFI_HANDLE ImageHandle
, EFI_SYSTEM_TABLE
*SystemTable
)
2374 BOOLEAN MainLoopRunning
= TRUE
;
2375 BOOLEAN MokProtocol
;
2376 REFIT_MENU_ENTRY
*ChosenEntry
;
2378 CHAR16
*Selection
= NULL
;
2382 InitializeLib(ImageHandle
, SystemTable
);
2383 Status
= InitRefitLib(ImageHandle
);
2384 if (EFI_ERROR(Status
))
2387 // read configuration
2388 CopyMem(GlobalConfig
.ScanFor
, "ieom ", NUM_SCAN_OPTIONS
);
2389 FindLegacyBootType();
2390 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
)
2391 CopyMem(GlobalConfig
.ScanFor
, "ihebocm ", NUM_SCAN_OPTIONS
);
2392 ReadConfig(CONFIG_FILE_NAME
);
2396 WarnIfLegacyProblems();
2397 MainMenu
.TimeoutSeconds
= GlobalConfig
.Timeout
;
2399 // disable EFI watchdog timer
2400 refit_call4_wrapper(BS
->SetWatchdogTimer
, 0x0000, 0x0000, 0x0000, NULL
);
2402 // further bootstrap (now with config available)
2403 MokProtocol
= SecureBootSetup();
2405 ScanForBootloaders();
2409 if (GlobalConfig
.ScanDelay
> 0) {
2414 egDisplayMessage(L
"Pausing before disk scan; please wait....", &BGColor
);
2415 for (i
= 0; i
< GlobalConfig
.ScanDelay
; i
++)
2416 refit_call1_wrapper(BS
->Stall
, 1000000);
2420 if (GlobalConfig
.DefaultSelection
)
2421 Selection
= StrDuplicate(GlobalConfig
.DefaultSelection
);
2423 while (MainLoopRunning
) {
2424 MenuExit
= RunMainMenu(&MainMenu
, Selection
, &ChosenEntry
);
2426 // The Escape key triggers a re-scan operation....
2427 if (MenuExit
== MENU_EXIT_ESCAPE
) {
2432 switch (ChosenEntry
->Tag
) {
2434 case TAG_REBOOT
: // Reboot
2436 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);
2437 MainLoopRunning
= FALSE
; // just in case we get this far
2440 case TAG_SHUTDOWN
: // Shut Down
2442 refit_call4_wrapper(RT
->ResetSystem
, EfiResetShutdown
, EFI_SUCCESS
, 0, NULL
);
2443 MainLoopRunning
= FALSE
; // just in case we get this far
2446 case TAG_ABOUT
: // About rEFInd
2450 case TAG_LOADER
: // Boot OS via .EFI loader
2451 StartLoader((LOADER_ENTRY
*)ChosenEntry
);
2454 case TAG_LEGACY
: // Boot legacy OS
2455 StartLegacy((LEGACY_ENTRY
*)ChosenEntry
);
2458 #ifdef __MAKEWITH_TIANO
2459 case TAG_LEGACY_UEFI
: // Boot a legacy OS on a non-Mac
2460 StartLegacyUEFI((LEGACY_ENTRY
*)ChosenEntry
);
2464 case TAG_TOOL
: // Start a EFI tool
2465 StartTool((LOADER_ENTRY
*)ChosenEntry
);
2468 case TAG_EXIT
: // Terminate rEFInd
2469 if ((MokProtocol
) && !SecureBootUninstall()) {
2470 MainLoopRunning
= FALSE
; // just in case we get this far
2472 BeginTextScreen(L
" ");
2477 case TAG_FIRMWARE
: // Reboot into firmware's user interface
2478 RebootIntoFirmware();
2482 MyFreePool(Selection
);
2483 Selection
= (ChosenEntry
->Title
) ? StrDuplicate(ChosenEntry
->Title
) : NULL
;
2486 // If we end up here, things have gone wrong. Try to reboot, and if that
2487 // fails, go into an endless loop.
2488 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);