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"
79 #define SHELL_NAMES L"\\EFI\\tools\\shell.efi,\\EFI\\tools\\shellia32.efi,\\shell.efi,\\shellia32.efi"
80 #define GPTSYNC_NAMES L"\\EFI\\tools\\gptsync.efi,\\EFI\\tools\\gptsync_ia32.efi"
81 #define DRIVER_DIRS L"drivers,drivers_ia32"
82 #define FALLBACK_FULLNAME L"EFI\\BOOT\\bootia32.efi"
83 #define FALLBACK_BASENAME L"bootia32.efi"
85 #define SHELL_NAMES L"\\EFI\\tools\\shell.efi,\\shell.efi"
86 #define GPTSYNC_NAMES L"\\EFI\\tools\\gptsync.efi"
87 #define DRIVER_DIRS L"drivers"
88 #define FALLBACK_FULLNAME L"EFI\\BOOT\\boot.efi" /* Not really correct */
89 #define FALLBACK_BASENAME L"boot.efi" /* Not really correct */
92 // Filename patterns that identify EFI boot loaders. Note that a single case (either L"*.efi" or
93 // L"*.EFI") is fine for most systems; but Gigabyte's buggy Hybrid EFI does a case-sensitive
94 // comparison when it should do a case-insensitive comparison, so I'm doubling this up. It does
95 // no harm on other computers, AFAIK. In theory, every case variation should be done for
96 // completeness, but that's ridiculous....
97 #define LOADER_MATCH_PATTERNS L"*.efi,*.EFI"
99 // Patterns that identify Linux kernels. Added to the loader match pattern when the
100 // scan_all_linux_kernels option is set in the configuration file. Causes kernels WITHOUT
101 // a ".efi" extension to be found when scanning for boot loaders.
102 #define LINUX_MATCH_PATTERNS L"vmlinuz*,bzImage*"
104 // Default hint text for program-launch submenus
105 #define SUBSCREEN_HINT1 L"Use arrow keys to move cursor; Enter to boot;"
106 #define SUBSCREEN_HINT2 L"Insert or F2 to edit options; Esc to return to main menu"
107 #define SUBSCREEN_HINT2_NO_EDITOR L"Esc to return to main menu"
109 static REFIT_MENU_ENTRY MenuEntryAbout
= { L
"About rEFInd", TAG_ABOUT
, 1, 0, 'A', NULL
, NULL
, NULL
};
110 static REFIT_MENU_ENTRY MenuEntryReset
= { L
"Reboot Computer", TAG_REBOOT
, 1, 0, 'R', NULL
, NULL
, NULL
};
111 static REFIT_MENU_ENTRY MenuEntryShutdown
= { L
"Shut Down Computer", TAG_SHUTDOWN
, 1, 0, 'U', NULL
, NULL
, NULL
};
112 static REFIT_MENU_ENTRY MenuEntryReturn
= { L
"Return to Main Menu", TAG_RETURN
, 1, 0, 0, NULL
, NULL
, NULL
};
113 static REFIT_MENU_ENTRY MenuEntryExit
= { L
"Exit rEFInd", TAG_EXIT
, 1, 0, 0, NULL
, NULL
, NULL
};
114 static REFIT_MENU_ENTRY MenuEntryFirmware
= { L
"Reboot to Computer Setup Utility", TAG_FIRMWARE
, 1, 0, 0, NULL
, NULL
, NULL
};
116 static REFIT_MENU_SCREEN MainMenu
= { L
"Main Menu", NULL
, 0, NULL
, 0, NULL
, 0, L
"Automatic boot",
117 L
"Use arrow keys to move cursor; Enter to boot;",
118 L
"Insert or F2 for more options; Esc to refresh" };
119 static REFIT_MENU_SCREEN AboutMenu
= { L
"About", NULL
, 0, NULL
, 0, NULL
, 0, NULL
, L
"Press Enter to return to main menu", L
"" };
121 REFIT_CONFIG GlobalConfig
= { FALSE
, FALSE
, 0, 0, 0, DONT_CHANGE_TEXT_MODE
, 20, 0, 0, GRAPHICS_FOR_OSX
, LEGACY_TYPE_MAC
, 0, 0,
122 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
123 { TAG_SHELL
, TAG_APPLE_RECOVERY
, TAG_MOK_TOOL
, TAG_ABOUT
, TAG_SHUTDOWN
, TAG_REBOOT
, TAG_FIRMWARE
,
127 EFI_GUID GlobalGuid
= EFI_GLOBAL_VARIABLE
;
129 // Structure used to hold boot loader filenames and time stamps in
130 // a linked list; used to sort entries within a directory.
134 struct LOADER_LIST
*NextEntry
;
141 static VOID
AboutrEFInd(VOID
)
143 CHAR16
*TempStr
; // Note: Don't deallocate; moved to menu structure
145 if (AboutMenu
.EntryCount
== 0) {
146 AboutMenu
.TitleImage
= BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT
);
147 AddMenuInfoLine(&AboutMenu
, L
"rEFInd Version 0.6.11");
148 AddMenuInfoLine(&AboutMenu
, L
"");
149 AddMenuInfoLine(&AboutMenu
, L
"Copyright (c) 2006-2010 Christoph Pfisterer");
150 AddMenuInfoLine(&AboutMenu
, L
"Copyright (c) 2012-2013 Roderick W. Smith");
151 AddMenuInfoLine(&AboutMenu
, L
"Portions Copyright (c) Intel Corporation and others");
152 AddMenuInfoLine(&AboutMenu
, L
"Distributed under the terms of the GNU GPLv3 license");
153 AddMenuInfoLine(&AboutMenu
, L
"");
154 AddMenuInfoLine(&AboutMenu
, L
"Running on:");
155 TempStr
= AllocateZeroPool(256 * sizeof(CHAR16
));
156 SPrint(TempStr
, 255, L
" EFI Revision %d.%02d", ST
->Hdr
.Revision
>> 16, ST
->Hdr
.Revision
& ((1 << 16) - 1));
157 AddMenuInfoLine(&AboutMenu
, TempStr
);
159 AddMenuInfoLine(&AboutMenu
, L
" Platform: x86 (32 bit)");
160 #elif defined(EFIX64)
161 TempStr
= AllocateZeroPool(256 * sizeof(CHAR16
));
162 SPrint(TempStr
, 255, L
" Platform: x86_64 (64 bit); Secure Boot %s", secure_mode() ? L
"active" : L
"inactive");
163 AddMenuInfoLine(&AboutMenu
, TempStr
);
165 AddMenuInfoLine(&AboutMenu
, L
" Platform: unknown");
167 TempStr
= AllocateZeroPool(256 * sizeof(CHAR16
));
168 SPrint(TempStr
, 255, L
" Firmware: %s %d.%02d",
169 ST
->FirmwareVendor
, ST
->FirmwareRevision
>> 16, ST
->FirmwareRevision
& ((1 << 16) - 1));
170 AddMenuInfoLine(&AboutMenu
, TempStr
);
171 TempStr
= AllocateZeroPool(256 * sizeof(CHAR16
));
172 SPrint(TempStr
, 255, L
" Screen Output: %s", egScreenDescription());
173 AddMenuInfoLine(&AboutMenu
, TempStr
);
174 AddMenuInfoLine(&AboutMenu
, L
"");
175 #if defined(__MAKEWITH_GNUEFI)
176 AddMenuInfoLine(&AboutMenu
, L
"Built with GNU-EFI");
178 AddMenuInfoLine(&AboutMenu
, L
"Built with TianoCore EDK2");
180 AddMenuInfoLine(&AboutMenu
, L
"");
181 AddMenuInfoLine(&AboutMenu
, L
"For more information, see the rEFInd Web site:");
182 AddMenuInfoLine(&AboutMenu
, L
"http://www.rodsbooks.com/refind/");
183 AddMenuEntry(&AboutMenu
, &MenuEntryReturn
);
186 RunMenu(&AboutMenu
, NULL
);
187 } /* VOID AboutrEFInd() */
189 static VOID
WarnSecureBootError(CHAR16
*Name
, BOOLEAN Verbose
) {
191 Name
= L
"the loader";
193 refit_call2_wrapper(ST
->ConOut
->SetAttribute
, ST
->ConOut
, ATTR_ERROR
);
194 Print(L
"Secure Boot validation failure loading %s!\n", Name
);
195 refit_call2_wrapper(ST
->ConOut
->SetAttribute
, ST
->ConOut
, ATTR_BASIC
);
196 if (Verbose
&& secure_mode()) {
197 Print(L
"\nThis computer is configured with Secure Boot active, but\n%s has failed validation.\n", Name
);
198 Print(L
"\nYou can:\n * Launch another boot loader\n");
199 Print(L
" * Disable Secure Boot in your firmware\n");
200 Print(L
" * Sign %s with a machine owner key (MOK)\n", Name
);
201 Print(L
" * Use a MOK utility (often present on the second row) to add a MOK with which\n");
202 Print(L
" %s has already been signed.\n", Name
);
203 Print(L
" * Use a MOK utility to register %s (\"enroll its hash\") without\n", Name
);
204 Print(L
" signing it.\n");
205 Print(L
"\nSee http://www.rodsbooks.com/refind/secureboot.html for more information\n");
208 } // VOID WarnSecureBootError()
210 // Launch an EFI binary.
211 static EFI_STATUS
StartEFIImageList(IN EFI_DEVICE_PATH
**DevicePaths
,
212 IN CHAR16
*LoadOptions
, IN CHAR16
*LoadOptionsPrefix
,
213 IN CHAR16
*ImageTitle
, IN CHAR8 OSType
,
214 OUT UINTN
*ErrorInStep
,
217 EFI_STATUS Status
, ReturnStatus
;
218 EFI_HANDLE ChildImageHandle
;
219 EFI_LOADED_IMAGE
*ChildLoadedImage
= NULL
;
220 UINTN DevicePathIndex
;
221 CHAR16 ErrorInfo
[256];
222 CHAR16
*FullLoadOptions
= NULL
;
224 if (ErrorInStep
!= NULL
)
228 if (LoadOptions
!= NULL
) {
229 if (LoadOptionsPrefix
!= NULL
) {
230 MergeStrings(&FullLoadOptions
, LoadOptions
, L
' ');
232 MergeStrings(&FullLoadOptions
, L
" ", 0);
233 // NOTE: That last space is also added by the EFI shell and seems to be significant
234 // when passing options to Apple's boot.efi...
237 MergeStrings(&FullLoadOptions
, LoadOptions
, 0);
239 } else { // LoadOptions == NULL
240 // NOTE: We provide a non-null string when no options are specified for safety;
241 // some systems (at least DUET) can hang when launching some programs (such as
242 // an EFI shell) without this.
243 FullLoadOptions
= StrDuplicate(L
" ");
246 Print(L
"Starting %s\nUsing load options '%s'\n", ImageTitle
, FullLoadOptions
);
248 // load the image into memory (and execute it, in the case of a shim/MOK image).
249 ReturnStatus
= Status
= EFI_NOT_FOUND
; // in case the list is empty
250 for (DevicePathIndex
= 0; DevicePaths
[DevicePathIndex
] != NULL
; DevicePathIndex
++) {
251 // NOTE: Below commented-out line could be more efficient if file were read ahead of
252 // time and passed as a pre-loaded image to LoadImage(), but it doesn't work on my
253 // 32-bit Mac Mini or my 64-bit Intel box when launching a Linux kernel; the
254 // kernel returns a "Failed to handle fs_proto" error message.
255 // TODO: Track down the cause of this error and fix it, if possible.
256 // ReturnStatus = Status = refit_call6_wrapper(BS->LoadImage, FALSE, SelfImageHandle, DevicePaths[DevicePathIndex],
257 // ImageData, ImageSize, &ChildImageHandle);
258 ReturnStatus
= Status
= refit_call6_wrapper(BS
->LoadImage
, FALSE
, SelfImageHandle
, DevicePaths
[DevicePathIndex
],
259 NULL
, 0, &ChildImageHandle
);
260 if (ReturnStatus
!= EFI_NOT_FOUND
) {
264 if ((Status
== EFI_ACCESS_DENIED
) || (Status
== EFI_SECURITY_VIOLATION
)) {
265 WarnSecureBootError(ImageTitle
, Verbose
);
268 SPrint(ErrorInfo
, 255, L
"while loading %s", ImageTitle
);
269 if (CheckError(Status
, ErrorInfo
)) {
270 if (ErrorInStep
!= NULL
)
275 ReturnStatus
= Status
= refit_call3_wrapper(BS
->HandleProtocol
, ChildImageHandle
, &LoadedImageProtocol
,
276 (VOID
**) &ChildLoadedImage
);
277 if (CheckError(Status
, L
"while getting a LoadedImageProtocol handle")) {
278 if (ErrorInStep
!= NULL
)
282 ChildLoadedImage
->LoadOptions
= (VOID
*)FullLoadOptions
;
283 ChildLoadedImage
->LoadOptionsSize
= ((UINT32
)StrLen(FullLoadOptions
) + 1) * sizeof(CHAR16
);
284 // turn control over to the image
285 // TODO: (optionally) re-enable the EFI watchdog timer!
287 // close open file handles
289 ReturnStatus
= Status
= refit_call3_wrapper(BS
->StartImage
, ChildImageHandle
, NULL
, NULL
);
291 // control returns here when the child image calls Exit()
292 SPrint(ErrorInfo
, 255, L
"returned from %s", ImageTitle
);
293 if (CheckError(Status
, ErrorInfo
)) {
294 if (ErrorInStep
!= NULL
)
298 // re-open file handles
302 // unload the image, we don't care if it works or not...
303 Status
= refit_call1_wrapper(BS
->UnloadImage
, ChildImageHandle
);
306 MyFreePool(FullLoadOptions
);
308 } /* static EFI_STATUS StartEFIImageList() */
310 static EFI_STATUS
StartEFIImage(IN EFI_DEVICE_PATH
*DevicePath
,
311 IN CHAR16
*LoadOptions
, IN CHAR16
*LoadOptionsPrefix
,
312 IN CHAR16
*ImageTitle
, IN CHAR8 OSType
,
313 OUT UINTN
*ErrorInStep
,
316 EFI_DEVICE_PATH
*DevicePaths
[2];
318 DevicePaths
[0] = DevicePath
;
319 DevicePaths
[1] = NULL
;
320 return StartEFIImageList(DevicePaths
, LoadOptions
, LoadOptionsPrefix
, ImageTitle
, OSType
, ErrorInStep
, Verbose
);
321 } /* static EFI_STATUS StartEFIImage() */
323 // From gummiboot: Retrieve a raw EFI variable.
324 // Returns EFI status
325 static EFI_STATUS
EfivarGetRaw(EFI_GUID
*vendor
, CHAR16
*name
, CHAR8
**buffer
, UINTN
*size
) {
330 l
= sizeof(CHAR16
*) * EFI_MAXIMUM_VARIABLE_SIZE
;
331 buf
= AllocatePool(l
);
333 return EFI_OUT_OF_RESOURCES
;
335 err
= refit_call5_wrapper(RT
->GetVariable
, name
, vendor
, NULL
, &l
, buf
);
336 if (EFI_ERROR(err
) == EFI_SUCCESS
) {
343 } // EFI_STATUS EfivarGetRaw()
345 // From gummiboot: Set an EFI variable
346 static EFI_STATUS
EfivarSetRaw(EFI_GUID
*vendor
, CHAR16
*name
, CHAR8
*buf
, UINTN size
, BOOLEAN persistent
) {
349 flags
= EFI_VARIABLE_BOOTSERVICE_ACCESS
|EFI_VARIABLE_RUNTIME_ACCESS
;
351 flags
|= EFI_VARIABLE_NON_VOLATILE
;
353 return refit_call5_wrapper(RT
->SetVariable
, name
, vendor
, flags
, size
, buf
);
354 } // EFI_STATUS EfivarSetRaw()
356 // From gummiboot: Reboot the computer into its built-in user interface
357 static EFI_STATUS
RebootIntoFirmware(VOID
) {
363 osind
= EFI_OS_INDICATIONS_BOOT_TO_FW_UI
;
365 err
= EfivarGetRaw(&GlobalGuid
, L
"OsIndications", &b
, &size
);
366 if (err
== EFI_SUCCESS
)
370 err
= EfivarSetRaw(&GlobalGuid
, L
"OsIndications", (CHAR8
*)&osind
, sizeof(UINT64
), TRUE
);
371 if (err
!= EFI_SUCCESS
)
374 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);
375 Print(L
"Error calling ResetSystem: %r", err
);
377 // uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
383 // EFI OS loader functions
386 static VOID
StartLoader(IN LOADER_ENTRY
*Entry
)
388 UINTN ErrorInStep
= 0;
390 BeginExternalScreen(Entry
->UseGraphicsMode
, L
"Booting OS");
391 StartEFIImage(Entry
->DevicePath
, Entry
->LoadOptions
, Basename(Entry
->LoaderPath
),
392 Basename(Entry
->LoaderPath
), Entry
->OSType
, &ErrorInStep
, !Entry
->UseGraphicsMode
);
393 FinishExternalScreen();
396 // Locate an initrd or initramfs file that matches the kernel specified by LoaderPath.
397 // The matching file has a name that begins with "init" and includes the same version
398 // number string as is found in LoaderPath -- but not a longer version number string.
399 // For instance, if LoaderPath is \EFI\kernels\bzImage-3.3.0.efi, and if \EFI\kernels
400 // has a file called initramfs-3.3.0.img, this function will return the string
401 // '\EFI\kernels\initramfs-3.3.0.img'. If the directory ALSO contains the file
402 // initramfs-3.3.0-rc7.img or initramfs-13.3.0.img, those files will NOT match;
403 // however, initmine-3.3.0.img might match. (FindInitrd() returns the first match it
404 // finds). Thus, care should be taken to avoid placing duplicate matching files in
405 // the kernel's directory.
406 // If no matching init file can be found, returns NULL.
407 static CHAR16
* FindInitrd(IN CHAR16
*LoaderPath
, IN REFIT_VOLUME
*Volume
) {
408 CHAR16
*InitrdName
= NULL
, *FileName
, *KernelVersion
, *InitrdVersion
, *Path
;
409 REFIT_DIR_ITER DirIter
;
410 EFI_FILE_INFO
*DirEntry
;
412 FileName
= Basename(LoaderPath
);
413 KernelVersion
= FindNumbers(FileName
);
414 Path
= FindPath(LoaderPath
);
416 // Add trailing backslash for root directory; necessary on some systems, but must
417 // NOT be added to all directories, since on other systems, a trailing backslash on
418 // anything but the root directory causes them to flake out!
419 if (StrLen(Path
) == 0) {
420 MergeStrings(&Path
, L
"\\", 0);
422 DirIterOpen(Volume
->RootDir
, Path
, &DirIter
);
423 // Now add a trailing backslash if it was NOT added earlier, for consistency in
424 // building the InitrdName later....
425 if ((StrLen(Path
) > 0) && (Path
[StrLen(Path
) - 1] != L
'\\'))
426 MergeStrings(&Path
, L
"\\", 0);
427 while ((DirIterNext(&DirIter
, 2, L
"init*", &DirEntry
)) && (InitrdName
== NULL
)) {
428 InitrdVersion
= FindNumbers(DirEntry
->FileName
);
429 if (KernelVersion
!= NULL
) {
430 if (StriCmp(InitrdVersion
, KernelVersion
) == 0) {
431 MergeStrings(&InitrdName
, Path
, 0);
432 MergeStrings(&InitrdName
, DirEntry
->FileName
, 0);
435 if (InitrdVersion
== NULL
) {
436 MergeStrings(&InitrdName
, Path
, 0);
437 MergeStrings(&InitrdName
, DirEntry
->FileName
, 0);
440 MyFreePool(InitrdVersion
);
442 DirIterClose(&DirIter
);
444 // Note: Don't FreePool(FileName), since Basename returns a pointer WITHIN the string it's passed.
445 MyFreePool(KernelVersion
);
448 } // static CHAR16 * FindInitrd()
450 LOADER_ENTRY
* AddPreparedLoaderEntry(LOADER_ENTRY
*Entry
) {
451 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
454 } // LOADER_ENTRY * AddPreparedLoaderEntry()
456 // Creates a copy of a menu screen.
457 // Returns a pointer to the copy of the menu screen.
458 static REFIT_MENU_SCREEN
* CopyMenuScreen(REFIT_MENU_SCREEN
*Entry
) {
459 REFIT_MENU_SCREEN
*NewEntry
;
462 NewEntry
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
463 if ((Entry
!= NULL
) && (NewEntry
!= NULL
)) {
464 CopyMem(NewEntry
, Entry
, sizeof(REFIT_MENU_SCREEN
));
465 NewEntry
->Title
= (Entry
->Title
) ? StrDuplicate(Entry
->Title
) : NULL
;
466 NewEntry
->TimeoutText
= (Entry
->TimeoutText
) ? StrDuplicate(Entry
->TimeoutText
) : NULL
;
467 if (Entry
->TitleImage
!= NULL
) {
468 NewEntry
->TitleImage
= AllocatePool(sizeof(EG_IMAGE
));
469 if (NewEntry
->TitleImage
!= NULL
)
470 CopyMem(NewEntry
->TitleImage
, Entry
->TitleImage
, sizeof(EG_IMAGE
));
472 NewEntry
->InfoLines
= (CHAR16
**) AllocateZeroPool(Entry
->InfoLineCount
* (sizeof(CHAR16
*)));
473 for (i
= 0; i
< Entry
->InfoLineCount
&& NewEntry
->InfoLines
; i
++) {
474 NewEntry
->InfoLines
[i
] = (Entry
->InfoLines
[i
]) ? StrDuplicate(Entry
->InfoLines
[i
]) : NULL
;
476 NewEntry
->Entries
= (REFIT_MENU_ENTRY
**) AllocateZeroPool(Entry
->EntryCount
* (sizeof (REFIT_MENU_ENTRY
*)));
477 for (i
= 0; i
< Entry
->EntryCount
&& NewEntry
->Entries
; i
++) {
478 AddMenuEntry(NewEntry
, Entry
->Entries
[i
]);
480 NewEntry
->Hint1
= (Entry
->Hint1
) ? StrDuplicate(Entry
->Hint1
) : NULL
;
481 NewEntry
->Hint2
= (Entry
->Hint2
) ? StrDuplicate(Entry
->Hint2
) : NULL
;
484 } // static REFIT_MENU_SCREEN* CopyMenuScreen()
486 // Creates a copy of a menu entry. Intended to enable moving a stack-based
487 // menu entry (such as the ones for the "reboot" and "exit" functions) to
488 // to the heap. This enables easier deletion of the whole set of menu
489 // entries when re-scanning.
490 // Returns a pointer to the copy of the menu entry.
491 static REFIT_MENU_ENTRY
* CopyMenuEntry(REFIT_MENU_ENTRY
*Entry
) {
492 REFIT_MENU_ENTRY
*NewEntry
;
494 NewEntry
= AllocateZeroPool(sizeof(REFIT_MENU_ENTRY
));
495 if ((Entry
!= NULL
) && (NewEntry
!= NULL
)) {
496 CopyMem(NewEntry
, Entry
, sizeof(REFIT_MENU_ENTRY
));
497 NewEntry
->Title
= (Entry
->Title
) ? StrDuplicate(Entry
->Title
) : NULL
;
498 if (Entry
->BadgeImage
!= NULL
) {
499 NewEntry
->BadgeImage
= AllocatePool(sizeof(EG_IMAGE
));
500 if (NewEntry
->BadgeImage
!= NULL
)
501 CopyMem(NewEntry
->BadgeImage
, Entry
->BadgeImage
, sizeof(EG_IMAGE
));
503 if (Entry
->Image
!= NULL
) {
504 NewEntry
->Image
= AllocatePool(sizeof(EG_IMAGE
));
505 if (NewEntry
->Image
!= NULL
)
506 CopyMem(NewEntry
->Image
, Entry
->Image
, sizeof(EG_IMAGE
));
508 if (Entry
->SubScreen
!= NULL
) {
509 NewEntry
->SubScreen
= CopyMenuScreen(Entry
->SubScreen
);
513 } // REFIT_MENU_ENTRY* CopyMenuEntry()
515 // Creates a new LOADER_ENTRY data structure and populates it with
516 // default values from the specified Entry, or NULL values if Entry
517 // is unspecified (NULL).
518 // Returns a pointer to the new data structure, or NULL if it
519 // couldn't be allocated
520 LOADER_ENTRY
*InitializeLoaderEntry(IN LOADER_ENTRY
*Entry
) {
521 LOADER_ENTRY
*NewEntry
= NULL
;
523 NewEntry
= AllocateZeroPool(sizeof(LOADER_ENTRY
));
524 if (NewEntry
!= NULL
) {
525 NewEntry
->me
.Title
= NULL
;
526 NewEntry
->me
.Tag
= TAG_LOADER
;
527 NewEntry
->Enabled
= TRUE
;
528 NewEntry
->UseGraphicsMode
= FALSE
;
529 NewEntry
->OSType
= 0;
531 NewEntry
->LoaderPath
= (Entry
->LoaderPath
) ? StrDuplicate(Entry
->LoaderPath
) : NULL
;
532 NewEntry
->VolName
= (Entry
->VolName
) ? StrDuplicate(Entry
->VolName
) : NULL
;
533 NewEntry
->DevicePath
= Entry
->DevicePath
;
534 NewEntry
->UseGraphicsMode
= Entry
->UseGraphicsMode
;
535 NewEntry
->LoadOptions
= (Entry
->LoadOptions
) ? StrDuplicate(Entry
->LoadOptions
) : NULL
;
536 NewEntry
->InitrdPath
= (Entry
->InitrdPath
) ? StrDuplicate(Entry
->InitrdPath
) : NULL
;
540 } // LOADER_ENTRY *InitializeLoaderEntry()
542 // Adds InitrdPath to Options, but only if Options doesn't already include an
543 // initrd= line. Done to enable overriding the default initrd selection in a
544 // refind_linux.conf file's options list.
545 // Returns a pointer to a new string. The calling function is responsible for
546 // freeing its memory.
547 static CHAR16
*AddInitrdToOptions(CHAR16
*Options
, CHAR16
*InitrdPath
) {
548 CHAR16
*NewOptions
= NULL
;
551 NewOptions
= StrDuplicate(Options
);
552 if ((InitrdPath
!= NULL
) && !StriSubCmp(L
"initrd=", Options
)) {
553 MergeStrings(&NewOptions
, L
"initrd=", L
' ');
554 MergeStrings(&NewOptions
, InitrdPath
, 0);
557 } // CHAR16 *AddInitrdToOptions()
559 // Prepare a REFIT_MENU_SCREEN data structure for a subscreen entry. This sets up
560 // the default entry that launches the boot loader using the same options as the
561 // main Entry does. Subsequent options can be added by the calling function.
562 // If a subscreen already exists in the Entry that's passed to this function,
563 // it's left unchanged and a pointer to it is returned.
564 // Returns a pointer to the new subscreen data structure, or NULL if there
565 // were problems allocating memory.
566 REFIT_MENU_SCREEN
*InitializeSubScreen(IN LOADER_ENTRY
*Entry
) {
567 CHAR16
*FileName
, *MainOptions
= NULL
;
568 REFIT_MENU_SCREEN
*SubScreen
= NULL
;
569 LOADER_ENTRY
*SubEntry
;
571 FileName
= Basename(Entry
->LoaderPath
);
572 if (Entry
->me
.SubScreen
== NULL
) { // No subscreen yet; initialize default entry....
573 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
574 if (SubScreen
!= NULL
) {
575 SubScreen
->Title
= AllocateZeroPool(sizeof(CHAR16
) * 256);
576 SPrint(SubScreen
->Title
, 255, L
"Boot Options for %s on %s",
577 (Entry
->Title
!= NULL
) ? Entry
->Title
: FileName
, Entry
->VolName
);
578 SubScreen
->TitleImage
= Entry
->me
.Image
;
580 SubEntry
= InitializeLoaderEntry(Entry
);
581 if (SubEntry
!= NULL
) {
582 SubEntry
->me
.Title
= StrDuplicate(L
"Boot using default options");
583 MainOptions
= SubEntry
->LoadOptions
;
584 SubEntry
->LoadOptions
= AddInitrdToOptions(MainOptions
, SubEntry
->InitrdPath
);
585 MyFreePool(MainOptions
);
586 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
587 } // if (SubEntry != NULL)
588 SubScreen
->Hint1
= StrDuplicate(SUBSCREEN_HINT1
);
589 if (GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_EDITOR
) {
590 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2_NO_EDITOR
);
592 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2
);
594 } // if (SubScreen != NULL)
595 } else { // existing subscreen; less initialization, and just add new entry later....
596 SubScreen
= Entry
->me
.SubScreen
;
599 } // REFIT_MENU_SCREEN *InitializeSubScreen()
601 VOID
GenerateSubScreen(LOADER_ENTRY
*Entry
, IN REFIT_VOLUME
*Volume
) {
602 REFIT_MENU_SCREEN
*SubScreen
;
603 LOADER_ENTRY
*SubEntry
;
605 CHAR16 DiagsFileName
[256];
610 // create the submenu
611 if (StrLen(Entry
->Title
) == 0) {
612 MyFreePool(Entry
->Title
);
615 SubScreen
= InitializeSubScreen(Entry
);
617 // loader-specific submenu entries
618 if (Entry
->OSType
== 'M') { // entries for Mac OS X
620 SubEntry
= InitializeLoaderEntry(Entry
);
621 if (SubEntry
!= NULL
) {
622 SubEntry
->me
.Title
= L
"Boot Mac OS X with a 64-bit kernel";
623 SubEntry
->LoadOptions
= L
"arch=x86_64";
624 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
625 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
628 SubEntry
= InitializeLoaderEntry(Entry
);
629 if (SubEntry
!= NULL
) {
630 SubEntry
->me
.Title
= L
"Boot Mac OS X with a 32-bit kernel";
631 SubEntry
->LoadOptions
= L
"arch=i386";
632 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
633 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
637 if (!(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_SINGLEUSER
)) {
638 SubEntry
= InitializeLoaderEntry(Entry
);
639 if (SubEntry
!= NULL
) {
640 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode";
641 SubEntry
->UseGraphicsMode
= FALSE
;
642 SubEntry
->LoadOptions
= L
"-v";
643 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
647 SubEntry
= InitializeLoaderEntry(Entry
);
648 if (SubEntry
!= NULL
) {
649 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode (64-bit)";
650 SubEntry
->UseGraphicsMode
= FALSE
;
651 SubEntry
->LoadOptions
= L
"-v arch=x86_64";
652 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
655 SubEntry
= InitializeLoaderEntry(Entry
);
656 if (SubEntry
!= NULL
) {
657 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode (32-bit)";
658 SubEntry
->UseGraphicsMode
= FALSE
;
659 SubEntry
->LoadOptions
= L
"-v arch=i386";
660 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
664 SubEntry
= InitializeLoaderEntry(Entry
);
665 if (SubEntry
!= NULL
) {
666 SubEntry
->me
.Title
= L
"Boot Mac OS X in single user mode";
667 SubEntry
->UseGraphicsMode
= FALSE
;
668 SubEntry
->LoadOptions
= L
"-v -s";
669 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
671 } // single-user mode allowed
673 if (!(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_SAFEMODE
)) {
674 SubEntry
= InitializeLoaderEntry(Entry
);
675 if (SubEntry
!= NULL
) {
676 SubEntry
->me
.Title
= L
"Boot Mac OS X in safe mode";
677 SubEntry
->UseGraphicsMode
= FALSE
;
678 SubEntry
->LoadOptions
= L
"-v -x";
679 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
681 } // safe mode allowed
683 // check for Apple hardware diagnostics
684 StrCpy(DiagsFileName
, L
"System\\Library\\CoreServices\\.diagnostics\\diags.efi");
685 if (FileExists(Volume
->RootDir
, DiagsFileName
) && !(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_HWTEST
)) {
686 SubEntry
= InitializeLoaderEntry(Entry
);
687 if (SubEntry
!= NULL
) {
688 SubEntry
->me
.Title
= L
"Run Apple Hardware Test";
689 MyFreePool(SubEntry
->LoaderPath
);
690 SubEntry
->LoaderPath
= StrDuplicate(DiagsFileName
);
691 SubEntry
->DevicePath
= FileDevicePath(Volume
->DeviceHandle
, SubEntry
->LoaderPath
);
692 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
693 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
695 } // if diagnostics entry found
697 } else if (Entry
->OSType
== 'L') { // entries for Linux kernels with EFI stub loaders
698 File
= ReadLinuxOptionsFile(Entry
->LoaderPath
, Volume
);
700 InitrdName
= FindInitrd(Entry
->LoaderPath
, Volume
);
701 TokenCount
= ReadTokenLine(File
, &TokenList
);
702 // first entry requires special processing, since it was initially set
703 // up with a default title but correct options by InitializeSubScreen(),
705 if ((SubScreen
->Entries
!= NULL
) && (SubScreen
->Entries
[0] != NULL
)) {
706 MyFreePool(SubScreen
->Entries
[0]->Title
);
707 SubScreen
->Entries
[0]->Title
= TokenList
[0] ? StrDuplicate(TokenList
[0]) : StrDuplicate(L
"Boot Linux");
709 FreeTokenLine(&TokenList
, &TokenCount
);
710 while ((TokenCount
= ReadTokenLine(File
, &TokenList
)) > 1) {
711 SubEntry
= InitializeLoaderEntry(Entry
);
712 SubEntry
->me
.Title
= TokenList
[0] ? StrDuplicate(TokenList
[0]) : StrDuplicate(L
"Boot Linux");
713 MyFreePool(SubEntry
->LoadOptions
);
714 SubEntry
->LoadOptions
= AddInitrdToOptions(TokenList
[1], InitrdName
);
715 FreeTokenLine(&TokenList
, &TokenCount
);
716 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_LINUX
;
717 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
719 MyFreePool(InitrdName
);
721 } // if Linux options file exists
723 } else if (Entry
->OSType
== 'E') { // entries for ELILO
724 SubEntry
= InitializeLoaderEntry(Entry
);
725 if (SubEntry
!= NULL
) {
726 SubEntry
->me
.Title
= L
"Run ELILO in interactive mode";
727 SubEntry
->LoadOptions
= L
"-p";
728 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
729 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
732 SubEntry
= InitializeLoaderEntry(Entry
);
733 if (SubEntry
!= NULL
) {
734 SubEntry
->me
.Title
= L
"Boot Linux for a 17\" iMac or a 15\" MacBook Pro (*)";
735 SubEntry
->UseGraphicsMode
= TRUE
;
736 SubEntry
->LoadOptions
= L
"-d 0 i17";
737 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
738 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
741 SubEntry
= InitializeLoaderEntry(Entry
);
742 if (SubEntry
!= NULL
) {
743 SubEntry
->me
.Title
= L
"Boot Linux for a 20\" iMac (*)";
744 SubEntry
->UseGraphicsMode
= TRUE
;
745 SubEntry
->LoadOptions
= L
"-d 0 i20";
746 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
747 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
750 SubEntry
= InitializeLoaderEntry(Entry
);
751 if (SubEntry
!= NULL
) {
752 SubEntry
->me
.Title
= L
"Boot Linux for a Mac Mini (*)";
753 SubEntry
->UseGraphicsMode
= TRUE
;
754 SubEntry
->LoadOptions
= L
"-d 0 mini";
755 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
756 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
759 AddMenuInfoLine(SubScreen
, L
"NOTE: This is an example. Entries");
760 AddMenuInfoLine(SubScreen
, L
"marked with (*) may not work.");
762 } else if (Entry
->OSType
== 'X') { // entries for xom.efi
763 // by default, skip the built-in selection and boot from hard disk only
764 Entry
->LoadOptions
= L
"-s -h";
766 SubEntry
= InitializeLoaderEntry(Entry
);
767 if (SubEntry
!= NULL
) {
768 SubEntry
->me
.Title
= L
"Boot Windows from Hard Disk";
769 SubEntry
->LoadOptions
= L
"-s -h";
770 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
771 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
774 SubEntry
= InitializeLoaderEntry(Entry
);
775 if (SubEntry
!= NULL
) {
776 SubEntry
->me
.Title
= L
"Boot Windows from CD-ROM";
777 SubEntry
->LoadOptions
= L
"-s -c";
778 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
779 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
782 SubEntry
= InitializeLoaderEntry(Entry
);
783 if (SubEntry
!= NULL
) {
784 SubEntry
->me
.Title
= L
"Run XOM in text mode";
785 SubEntry
->UseGraphicsMode
= FALSE
;
786 SubEntry
->LoadOptions
= L
"-v";
787 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
788 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
790 } // entries for xom.efi
791 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
792 Entry
->me
.SubScreen
= SubScreen
;
793 } // VOID GenerateSubScreen()
795 // Returns options for a Linux kernel. Reads them from an options file in the
796 // kernel's directory; and if present, adds an initrd= option for an initial
797 // RAM disk file with the same version number as the kernel file.
798 static CHAR16
* GetMainLinuxOptions(IN CHAR16
* LoaderPath
, IN REFIT_VOLUME
*Volume
) {
799 CHAR16
*Options
= NULL
, *InitrdName
, *FullOptions
= NULL
;
801 Options
= GetFirstOptionsFromFile(LoaderPath
, Volume
);
802 InitrdName
= FindInitrd(LoaderPath
, Volume
);
803 FullOptions
= AddInitrdToOptions(Options
, InitrdName
);
806 MyFreePool(InitrdName
);
807 return (FullOptions
);
808 } // static CHAR16 * GetMainLinuxOptions()
810 // Try to guess the name of the Linux distribution & add that name to
812 static VOID
GuessLinuxDistribution(CHAR16
**OSIconName
, REFIT_VOLUME
*Volume
, CHAR16
*LoaderPath
) {
816 UINTN TokenCount
= 0;
818 // If on Linux root fs, /etc/os-release file probably has clues....
819 if (FileExists(Volume
->RootDir
, L
"etc\\os-release") &&
820 (ReadFile(Volume
->RootDir
, L
"etc\\os-release", &File
, &FileSize
) == EFI_SUCCESS
)) {
822 TokenCount
= ReadTokenLine(&File
, &TokenList
);
823 if ((TokenCount
> 1) && ((StriCmp(TokenList
[0], L
"ID") == 0) || (StriCmp(TokenList
[0], L
"NAME") == 0))) {
824 MergeStrings(OSIconName
, TokenList
[1], L
',');
826 FreeTokenLine(&TokenList
, &TokenCount
);
827 } while (TokenCount
> 0);
828 MyFreePool(File
.Buffer
);
831 // Search for clues in the kernel's filename....
832 if (StriSubCmp(L
".fc", LoaderPath
))
833 MergeStrings(OSIconName
, L
"fedora", L
',');
834 if (StriSubCmp(L
".el", LoaderPath
))
835 MergeStrings(OSIconName
, L
"redhat", L
',');
836 } // VOID GuessLinuxDistribution()
838 // Sets a few defaults for a loader entry -- mainly the icon, but also the OS type
839 // code and shortcut letter. For Linux EFI stub loaders, also sets kernel options
840 // that will (with luck) work fairly automatically.
841 VOID
SetLoaderDefaults(LOADER_ENTRY
*Entry
, CHAR16
*LoaderPath
, REFIT_VOLUME
*Volume
) {
842 CHAR16
*FileName
, *PathOnly
, *NoExtension
, *OSIconName
= NULL
, *Temp
, *SubString
;
843 CHAR16 ShortcutLetter
= 0;
846 FileName
= Basename(LoaderPath
);
847 PathOnly
= FindPath(LoaderPath
);
848 NoExtension
= StripEfiExtension(FileName
);
850 // locate a custom icon for the loader
851 // Anything found here takes precedence over the "hints" in the OSIconName variable
852 if (!Entry
->me
.Image
)
853 Entry
->me
.Image
= egLoadIconAnyType(Volume
->RootDir
, PathOnly
, NoExtension
, 128);
854 if (!Entry
->me
.Image
)
855 Entry
->me
.Image
= egCopyImage(Volume
->VolIconImage
);
857 // Begin creating icon "hints" by using last part of directory path leading
859 Temp
= FindLastDirName(LoaderPath
);
860 MergeStrings(&OSIconName
, Temp
, L
',');
863 if (OSIconName
!= NULL
) {
864 ShortcutLetter
= OSIconName
[0];
867 // Add every "word" in the volume label, delimited by spaces, dashes (-), or
868 // underscores (_), to the list of hints to be used in searching for OS
870 if ((Volume
->VolName
) && (StrLen(Volume
->VolName
) > 0)) {
871 Temp
= SubString
= StrDuplicate(Volume
->VolName
);
873 Length
= StrLen(Temp
);
874 for (i
= 0; i
< Length
; i
++) {
875 if ((Temp
[i
] == L
' ') || (Temp
[i
] == L
'_') || (Temp
[i
] == L
'-')) {
877 if (StrLen(SubString
) > 0)
878 MergeStrings(&OSIconName
, SubString
, L
',');
879 SubString
= Temp
+ i
+ 1;
882 MergeStrings(&OSIconName
, SubString
, L
',');
887 // detect specific loaders
888 if (StriSubCmp(L
"bzImage", LoaderPath
) || StriSubCmp(L
"vmlinuz", LoaderPath
)) {
889 GuessLinuxDistribution(&OSIconName
, Volume
, LoaderPath
);
890 MergeStrings(&OSIconName
, L
"linux", L
',');
892 if (ShortcutLetter
== 0)
893 ShortcutLetter
= 'L';
894 Entry
->LoadOptions
= GetMainLinuxOptions(LoaderPath
, Volume
);
895 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_LINUX
;
896 } else if (StriSubCmp(L
"refit", LoaderPath
)) {
897 MergeStrings(&OSIconName
, L
"refit", L
',');
899 ShortcutLetter
= 'R';
900 } else if (StriSubCmp(L
"refind", LoaderPath
)) {
901 MergeStrings(&OSIconName
, L
"refind", L
',');
903 ShortcutLetter
= 'R';
904 } else if (StriCmp(LoaderPath
, MACOSX_LOADER_PATH
) == 0) {
905 if (Volume
->VolIconImage
!= NULL
) { // custom icon file found
906 Entry
->me
.Image
= Volume
->VolIconImage
;
908 MergeStrings(&OSIconName
, L
"mac", L
',');
910 ShortcutLetter
= 'M';
911 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
912 } else if (StriCmp(FileName
, L
"diags.efi") == 0) {
913 MergeStrings(&OSIconName
, L
"hwtest", L
',');
914 } else if (StriCmp(FileName
, L
"e.efi") == 0 || StriCmp(FileName
, L
"elilo.efi") == 0 || StriSubCmp(L
"elilo", FileName
)) {
915 MergeStrings(&OSIconName
, L
"elilo,linux", L
',');
917 if (ShortcutLetter
== 0)
918 ShortcutLetter
= 'L';
919 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
920 } else if (StriSubCmp(L
"grub", FileName
)) {
922 ShortcutLetter
= 'G';
923 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_GRUB
;
924 } else if (StriCmp(FileName
, L
"cdboot.efi") == 0 ||
925 StriCmp(FileName
, L
"bootmgr.efi") == 0 ||
926 StriCmp(FileName
, L
"bootmgfw.efi") == 0) {
927 MergeStrings(&OSIconName
, L
"win", L
',');
929 ShortcutLetter
= 'W';
930 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
931 } else if (StriCmp(FileName
, L
"xom.efi") == 0) {
932 MergeStrings(&OSIconName
, L
"xom,win", L
',');
933 Entry
->UseGraphicsMode
= TRUE
;
935 ShortcutLetter
= 'W';
936 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
939 if ((ShortcutLetter
>= 'a') && (ShortcutLetter
<= 'z'))
940 ShortcutLetter
= ShortcutLetter
- 'a' + 'A'; // convert lowercase to uppercase
941 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
942 if (Entry
->me
.Image
== NULL
)
943 Entry
->me
.Image
= LoadOSIcon(OSIconName
, L
"unknown", FALSE
);
944 MyFreePool(PathOnly
);
945 } // VOID SetLoaderDefaults()
947 // Add a specified EFI boot loader to the list, using automatic settings
948 // for icons, options, etc.
949 LOADER_ENTRY
* AddLoaderEntry(IN CHAR16
*LoaderPath
, IN CHAR16
*LoaderTitle
, IN REFIT_VOLUME
*Volume
) {
952 CleanUpPathNameSlashes(LoaderPath
);
953 Entry
= InitializeLoaderEntry(NULL
);
955 Entry
->Title
= StrDuplicate((LoaderTitle
!= NULL
) ? LoaderTitle
: LoaderPath
);
956 Entry
->me
.Title
= AllocateZeroPool(sizeof(CHAR16
) * 256);
957 SPrint(Entry
->me
.Title
, 255, L
"Boot %s from %s", (LoaderTitle
!= NULL
) ? LoaderTitle
: LoaderPath
, Volume
->VolName
);
959 Entry
->me
.BadgeImage
= Volume
->VolBadgeImage
;
960 if ((LoaderPath
!= NULL
) && (LoaderPath
[0] != L
'\\')) {
961 Entry
->LoaderPath
= StrDuplicate(L
"\\");
963 Entry
->LoaderPath
= NULL
;
965 MergeStrings(&(Entry
->LoaderPath
), LoaderPath
, 0);
966 Entry
->VolName
= Volume
->VolName
;
967 Entry
->DevicePath
= FileDevicePath(Volume
->DeviceHandle
, Entry
->LoaderPath
);
968 SetLoaderDefaults(Entry
, LoaderPath
, Volume
);
969 GenerateSubScreen(Entry
, Volume
);
970 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
974 } // LOADER_ENTRY * AddLoaderEntry()
976 // Returns -1 if (Time1 < Time2), +1 if (Time1 > Time2), or 0 if
977 // (Time1 == Time2). Precision is only to the nearest second; since
978 // this is used for sorting boot loader entries, differences smaller
979 // than this are likely to be meaningless (and unlikely!).
980 INTN
TimeComp(IN EFI_TIME
*Time1
, IN EFI_TIME
*Time2
) {
981 INT64 Time1InSeconds
, Time2InSeconds
;
983 // Following values are overestimates; I'm assuming 31 days in every month.
984 // This is fine for the purpose of this function, which is limited
985 Time1InSeconds
= Time1
->Second
+ (Time1
->Minute
* 60) + (Time1
->Hour
* 3600) + (Time1
->Day
* 86400) +
986 (Time1
->Month
* 2678400) + ((Time1
->Year
- 1998) * 32140800);
987 Time2InSeconds
= Time2
->Second
+ (Time2
->Minute
* 60) + (Time2
->Hour
* 3600) + (Time2
->Day
* 86400) +
988 (Time2
->Month
* 2678400) + ((Time2
->Year
- 1998) * 32140800);
989 if (Time1InSeconds
< Time2InSeconds
)
991 else if (Time1InSeconds
> Time2InSeconds
)
997 // Adds a loader list element, keeping it sorted by date. Returns the new
998 // first element (the one with the most recent date).
999 static struct LOADER_LIST
* AddLoaderListEntry(struct LOADER_LIST
*LoaderList
, struct LOADER_LIST
*NewEntry
) {
1000 struct LOADER_LIST
*LatestEntry
, *CurrentEntry
, *PrevEntry
= NULL
;
1002 LatestEntry
= CurrentEntry
= LoaderList
;
1003 if (LoaderList
== NULL
) {
1004 LatestEntry
= NewEntry
;
1006 while ((CurrentEntry
!= NULL
) && (TimeComp(&(NewEntry
->TimeStamp
), &(CurrentEntry
->TimeStamp
)) < 0)) {
1007 PrevEntry
= CurrentEntry
;
1008 CurrentEntry
= CurrentEntry
->NextEntry
;
1010 NewEntry
->NextEntry
= CurrentEntry
;
1011 if (PrevEntry
== NULL
) {
1012 LatestEntry
= NewEntry
;
1014 PrevEntry
->NextEntry
= NewEntry
;
1017 return (LatestEntry
);
1018 } // static VOID AddLoaderListEntry()
1020 // Delete the LOADER_LIST linked list
1021 static VOID
CleanUpLoaderList(struct LOADER_LIST
*LoaderList
) {
1022 struct LOADER_LIST
*Temp
;
1024 while (LoaderList
!= NULL
) {
1026 LoaderList
= LoaderList
->NextEntry
;
1027 MyFreePool(Temp
->FileName
);
1030 } // static VOID CleanUpLoaderList()
1032 // Returns FALSE if the specified file/volume matches the GlobalConfig.DontScanDirs
1033 // or GlobalConfig.DontScanVolumes specification, or if Path points to a volume
1034 // other than the one specified by Volume, or if the specified path is SelfDir.
1035 // Returns TRUE if none of these conditions is met -- that is, if the path is
1036 // eligible for scanning.
1037 static BOOLEAN
ShouldScan(REFIT_VOLUME
*Volume
, CHAR16
*Path
) {
1038 CHAR16
*VolName
= NULL
, *DontScanDir
;
1039 UINTN i
= 0, VolNum
;
1040 BOOLEAN ScanIt
= TRUE
;
1042 if (IsIn(Volume
->VolName
, GlobalConfig
.DontScanVolumes
))
1045 if ((StriCmp(Path
, SelfDirPath
) == 0) && (Volume
->DeviceHandle
== SelfVolume
->DeviceHandle
))
1048 while ((DontScanDir
= FindCommaDelimited(GlobalConfig
.DontScanDirs
, i
++)) && ScanIt
) {
1049 SplitVolumeAndFilename(&DontScanDir
, &VolName
);
1050 CleanUpPathNameSlashes(DontScanDir
);
1051 if (VolName
!= NULL
) {
1052 if ((StriCmp(VolName
, Volume
->VolName
) == 0) && (StriCmp(DontScanDir
, Path
) == 0))
1054 if ((StrLen(VolName
) > 2) && (VolName
[0] == L
'f') && (VolName
[1] == L
's') && (VolName
[2] >= L
'0') && (VolName
[2] <= L
'9')) {
1055 VolNum
= Atoi(VolName
+ 2);
1056 if ((VolNum
== Volume
->VolNumber
) && (StriCmp(DontScanDir
, Path
) == 0))
1060 if (StriCmp(DontScanDir
, Path
) == 0)
1063 MyFreePool(DontScanDir
);
1067 } // BOOLEAN ShouldScan()
1069 // Returns TRUE if the file is byte-for-byte identical with the fallback file
1070 // on the volume AND if the file is not itself the fallback file; returns
1071 // FALSE if the file is not identical to the fallback file OR if the file
1072 // IS the fallback file. Intended for use in excluding the fallback boot
1073 // loader when it's a duplicate of another boot loader.
1074 static BOOLEAN
DuplicatesFallback(IN REFIT_VOLUME
*Volume
, IN CHAR16
*FileName
) {
1075 CHAR8
*FileContents
, *FallbackContents
;
1076 EFI_FILE_HANDLE FileHandle
, FallbackHandle
;
1077 EFI_FILE_INFO
*FileInfo
, *FallbackInfo
;
1078 UINTN FileSize
= 0, FallbackSize
= 0;
1080 BOOLEAN AreIdentical
= FALSE
;
1082 if (!FileExists(Volume
->RootDir
, FileName
) || !FileExists(Volume
->RootDir
, FALLBACK_FULLNAME
))
1085 CleanUpPathNameSlashes(FileName
);
1087 if (StriCmp(FileName
, FALLBACK_FULLNAME
) == 0)
1088 return FALSE
; // identical filenames, so not a duplicate....
1090 Status
= refit_call5_wrapper(Volume
->RootDir
->Open
, Volume
->RootDir
, &FileHandle
, FileName
, EFI_FILE_MODE_READ
, 0);
1091 if (Status
== EFI_SUCCESS
) {
1092 FileInfo
= LibFileInfo(FileHandle
);
1093 FileSize
= FileInfo
->FileSize
;
1098 Status
= refit_call5_wrapper(Volume
->RootDir
->Open
, Volume
->RootDir
, &FallbackHandle
, FALLBACK_FULLNAME
, EFI_FILE_MODE_READ
, 0);
1099 if (Status
== EFI_SUCCESS
) {
1100 FallbackInfo
= LibFileInfo(FallbackHandle
);
1101 FallbackSize
= FallbackInfo
->FileSize
;
1103 refit_call1_wrapper(FileHandle
->Close
, FileHandle
);
1107 if (FallbackSize
!= FileSize
) { // not same size, so can't be identical
1108 AreIdentical
= FALSE
;
1109 } else { // could be identical; do full check....
1110 FileContents
= AllocatePool(FileSize
);
1111 FallbackContents
= AllocatePool(FallbackSize
);
1112 if (FileContents
&& FallbackContents
) {
1113 Status
= refit_call3_wrapper(FileHandle
->Read
, FileHandle
, &FileSize
, FileContents
);
1114 if (Status
== EFI_SUCCESS
) {
1115 Status
= refit_call3_wrapper(FallbackHandle
->Read
, FallbackHandle
, &FallbackSize
, FallbackContents
);
1117 if (Status
== EFI_SUCCESS
) {
1118 AreIdentical
= (CompareMem(FileContents
, FallbackContents
, FileSize
) == 0);
1121 MyFreePool(FileContents
);
1122 MyFreePool(FallbackContents
);
1125 // BUG ALERT: Some systems (e.g., DUET, some Macs with large displays) crash if the
1126 // following two calls are reversed. Go figure....
1127 refit_call1_wrapper(FileHandle
->Close
, FallbackHandle
);
1128 refit_call1_wrapper(FileHandle
->Close
, FileHandle
);
1129 return AreIdentical
;
1130 } // BOOLEAN DuplicatesFallback()
1132 // Returns FALSE if two measures of file size are identical for a single file,
1133 // TRUE if not or if the file can't be opened and the other measure is non-0.
1134 // Despite the function's name, this isn't really a direct test of symbolic
1135 // link status, since EFI doesn't officially support symlinks. It does seem
1136 // to be a reliable indicator, though. (OTOH, some disk errors might cause a
1137 // file to fail to open, which would return a false positive -- but as I use
1138 // this function to exclude symbolic links from the list of boot loaders,
1139 // that would be fine, since such boot loaders wouldn't work.)
1140 static BOOLEAN
IsSymbolicLink(REFIT_VOLUME
*Volume
, CHAR16
*Path
, EFI_FILE_INFO
*DirEntry
) {
1141 EFI_FILE_HANDLE FileHandle
;
1142 EFI_FILE_INFO
*FileInfo
= NULL
;
1144 UINTN FileSize2
= 0;
1147 FileName
= StrDuplicate(Path
);
1148 MergeStrings(&FileName
, DirEntry
->FileName
, L
'\\');
1149 CleanUpPathNameSlashes(FileName
);
1151 Status
= refit_call5_wrapper(Volume
->RootDir
->Open
, Volume
->RootDir
, &FileHandle
, FileName
, EFI_FILE_MODE_READ
, 0);
1152 if (Status
== EFI_SUCCESS
) {
1153 FileInfo
= LibFileInfo(FileHandle
);
1154 if (FileInfo
!= NULL
)
1155 FileSize2
= FileInfo
->FileSize
;
1158 MyFreePool(FileName
);
1159 MyFreePool(FileInfo
);
1161 return (DirEntry
->FileSize
!= FileSize2
);
1162 } // BOOLEAN IsSymbolicLink()
1164 // Scan an individual directory for EFI boot loader files and, if found,
1165 // add them to the list. Exception: Ignores FALLBACK_FULLNAME, which is picked
1166 // up in ScanEfiFiles(). Sorts the entries within the loader directory so that
1167 // the most recent one appears first in the list.
1168 // Returns TRUE if a duplicate for FALLBACK_FILENAME was found, FALSE if not.
1169 static BOOLEAN
ScanLoaderDir(IN REFIT_VOLUME
*Volume
, IN CHAR16
*Path
, IN CHAR16
*Pattern
)
1172 REFIT_DIR_ITER DirIter
;
1173 EFI_FILE_INFO
*DirEntry
;
1174 CHAR16 FileName
[256], *Extension
;
1175 struct LOADER_LIST
*LoaderList
= NULL
, *NewLoader
;
1176 BOOLEAN FoundFallbackDuplicate
= FALSE
;
1178 if ((!SelfDirPath
|| !Path
|| ((StriCmp(Path
, SelfDirPath
) == 0) && (Volume
->DeviceHandle
!= SelfVolume
->DeviceHandle
)) ||
1179 (StriCmp(Path
, SelfDirPath
) != 0)) &&
1180 (ShouldScan(Volume
, Path
))) {
1181 // look through contents of the directory
1182 DirIterOpen(Volume
->RootDir
, Path
, &DirIter
);
1183 while (DirIterNext(&DirIter
, 2, Pattern
, &DirEntry
)) {
1184 Extension
= FindExtension(DirEntry
->FileName
);
1185 if (DirEntry
->FileName
[0] == '.' ||
1186 StriCmp(Extension
, L
".icns") == 0 ||
1187 StriCmp(Extension
, L
".png") == 0 ||
1188 (StriCmp(DirEntry
->FileName
, FALLBACK_BASENAME
) == 0 && (StriCmp(Path
, L
"EFI\\BOOT") == 0)) ||
1189 StriSubCmp(L
"shell", DirEntry
->FileName
) ||
1190 IsSymbolicLink(Volume
, Path
, DirEntry
) || /* is symbolic link */
1191 IsIn(DirEntry
->FileName
, GlobalConfig
.DontScanFiles
))
1192 continue; // skip this
1195 SPrint(FileName
, 255, L
"\\%s\\%s", Path
, DirEntry
->FileName
);
1197 SPrint(FileName
, 255, L
"\\%s", DirEntry
->FileName
);
1198 CleanUpPathNameSlashes(FileName
);
1199 NewLoader
= AllocateZeroPool(sizeof(struct LOADER_LIST
));
1200 if (NewLoader
!= NULL
) {
1201 NewLoader
->FileName
= StrDuplicate(FileName
);
1202 NewLoader
->TimeStamp
= DirEntry
->ModificationTime
;
1203 LoaderList
= AddLoaderListEntry(LoaderList
, NewLoader
);
1204 if (DuplicatesFallback(Volume
, FileName
))
1205 FoundFallbackDuplicate
= TRUE
;
1207 MyFreePool(Extension
);
1210 NewLoader
= LoaderList
;
1211 while (NewLoader
!= NULL
) {
1212 AddLoaderEntry(NewLoader
->FileName
, NULL
, Volume
);
1213 NewLoader
= NewLoader
->NextEntry
;
1216 CleanUpLoaderList(LoaderList
);
1217 Status
= DirIterClose(&DirIter
);
1218 // NOTE: EFI_INVALID_PARAMETER really is an error that should be reported;
1219 // but I've gotten reports from users who are getting this error occasionally
1220 // and I can't find anything wrong or reproduce the problem, so I'm putting
1221 // it down to buggy EFI implementations and ignoring that particular error....
1222 if ((Status
!= EFI_NOT_FOUND
) && (Status
!= EFI_INVALID_PARAMETER
)) {
1224 SPrint(FileName
, 255, L
"while scanning the %s directory", Path
);
1226 StrCpy(FileName
, L
"while scanning the root directory");
1227 CheckError(Status
, FileName
);
1228 } // if (Status != EFI_NOT_FOUND)
1229 } // if not scanning a blacklisted directory
1231 return FoundFallbackDuplicate
;
1232 } /* static VOID ScanLoaderDir() */
1234 static VOID
ScanEfiFiles(REFIT_VOLUME
*Volume
) {
1236 REFIT_DIR_ITER EfiDirIter
;
1237 EFI_FILE_INFO
*EfiDirEntry
;
1238 CHAR16 FileName
[256], *Directory
, *MatchPatterns
, *VolName
= NULL
, *SelfPath
;
1240 BOOLEAN ScanFallbackLoader
= TRUE
;
1242 MatchPatterns
= StrDuplicate(LOADER_MATCH_PATTERNS
);
1243 if (GlobalConfig
.ScanAllLinux
)
1244 MergeStrings(&MatchPatterns
, LINUX_MATCH_PATTERNS
, L
',');
1246 if ((Volume
->RootDir
!= NULL
) && (Volume
->VolName
!= NULL
)) {
1247 // check for Mac OS X boot loader
1248 if (ShouldScan(Volume
, L
"System\\Library\\CoreServices")) {
1249 StrCpy(FileName
, MACOSX_LOADER_PATH
);
1250 if (FileExists(Volume
->RootDir
, FileName
) && !IsIn(L
"boot.efi", GlobalConfig
.DontScanFiles
)) {
1251 AddLoaderEntry(FileName
, L
"Mac OS X", Volume
);
1252 if (DuplicatesFallback(Volume
, FileName
))
1253 ScanFallbackLoader
= FALSE
;
1257 StrCpy(FileName
, L
"System\\Library\\CoreServices\\xom.efi");
1258 if (FileExists(Volume
->RootDir
, FileName
) && !IsIn(L
"boot.efi", GlobalConfig
.DontScanFiles
)) {
1259 AddLoaderEntry(FileName
, L
"Windows XP (XoM)", Volume
);
1260 if (DuplicatesFallback(Volume
, FileName
))
1261 ScanFallbackLoader
= FALSE
;
1263 } // if should scan Mac directory
1265 // check for Microsoft boot loader/menu
1266 StrCpy(FileName
, L
"EFI\\Microsoft\\Boot\\bootmgfw.efi");
1267 if (FileExists(Volume
->RootDir
, FileName
) && ShouldScan(Volume
, L
"EFI\\Microsoft\\Boot") &&
1268 !IsIn(L
"bootmgfw.efi", GlobalConfig
.DontScanFiles
)) {
1269 AddLoaderEntry(FileName
, L
"Microsoft EFI boot", Volume
);
1270 if (DuplicatesFallback(Volume
, FileName
))
1271 ScanFallbackLoader
= FALSE
;
1274 // scan the root directory for EFI executables
1275 if (ScanLoaderDir(Volume
, L
"\\", MatchPatterns
))
1276 ScanFallbackLoader
= FALSE
;
1278 // scan subdirectories of the EFI directory (as per the standard)
1279 DirIterOpen(Volume
->RootDir
, L
"EFI", &EfiDirIter
);
1280 while (DirIterNext(&EfiDirIter
, 1, NULL
, &EfiDirEntry
)) {
1281 if (StriCmp(EfiDirEntry
->FileName
, L
"tools") == 0 || EfiDirEntry
->FileName
[0] == '.')
1282 continue; // skip this, doesn't contain boot loaders or is scanned later
1283 SPrint(FileName
, 255, L
"EFI\\%s", EfiDirEntry
->FileName
);
1284 if (ScanLoaderDir(Volume
, FileName
, MatchPatterns
))
1285 ScanFallbackLoader
= FALSE
;
1287 Status
= DirIterClose(&EfiDirIter
);
1288 if (Status
!= EFI_NOT_FOUND
)
1289 CheckError(Status
, L
"while scanning the EFI directory");
1291 // Scan user-specified (or additional default) directories....
1293 while ((Directory
= FindCommaDelimited(GlobalConfig
.AlsoScan
, i
++)) != NULL
) {
1294 SplitVolumeAndFilename(&Directory
, &VolName
);
1295 CleanUpPathNameSlashes(Directory
);
1296 Length
= StrLen(Directory
);
1297 if ((Length
> 0) && ScanLoaderDir(Volume
, Directory
, MatchPatterns
))
1298 ScanFallbackLoader
= FALSE
;
1299 MyFreePool(Directory
);
1300 MyFreePool(VolName
);
1303 // Don't scan the fallback loader if it's on the same volume and a duplicate of rEFInd itself....
1304 SelfPath
= DevicePathToStr(SelfLoadedImage
->FilePath
);
1305 CleanUpPathNameSlashes(SelfPath
);
1306 if ((Volume
->DeviceHandle
== SelfLoadedImage
->DeviceHandle
) && DuplicatesFallback(Volume
, SelfPath
))
1307 ScanFallbackLoader
= FALSE
;
1309 // If not a duplicate & if it exists & if it's not us, create an entry
1310 // for the fallback boot loader
1311 if (ScanFallbackLoader
&& FileExists(Volume
->RootDir
, FALLBACK_FULLNAME
) && ShouldScan(Volume
, L
"EFI\\BOOT"))
1312 AddLoaderEntry(FALLBACK_FULLNAME
, L
"Fallback boot loader", Volume
);
1314 } // static VOID ScanEfiFiles()
1316 // Scan internal disks for valid EFI boot loaders....
1317 static VOID
ScanInternal(VOID
) {
1320 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1321 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_INTERNAL
) {
1322 ScanEfiFiles(Volumes
[VolumeIndex
]);
1325 } // static VOID ScanInternal()
1327 // Scan external disks for valid EFI boot loaders....
1328 static VOID
ScanExternal(VOID
) {
1331 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1332 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_EXTERNAL
) {
1333 ScanEfiFiles(Volumes
[VolumeIndex
]);
1336 } // static VOID ScanExternal()
1338 // Scan internal disks for valid EFI boot loaders....
1339 static VOID
ScanOptical(VOID
) {
1342 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1343 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_OPTICAL
) {
1344 ScanEfiFiles(Volumes
[VolumeIndex
]);
1347 } // static VOID ScanOptical()
1350 // legacy boot functions
1353 static EFI_STATUS
ActivateMbrPartition(IN EFI_BLOCK_IO
*BlockIO
, IN UINTN PartitionIndex
)
1356 UINT8 SectorBuffer
[512];
1357 MBR_PARTITION_INFO
*MbrTable
, *EMbrTable
;
1358 UINT32 ExtBase
, ExtCurrent
, NextExtCurrent
;
1359 UINTN LogicalPartitionIndex
= 4;
1361 BOOLEAN HaveBootCode
;
1364 Status
= refit_call5_wrapper(BlockIO
->ReadBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, 0, 512, SectorBuffer
);
1365 if (EFI_ERROR(Status
))
1367 if (*((UINT16
*)(SectorBuffer
+ 510)) != 0xaa55)
1368 return EFI_NOT_FOUND
; // safety measure #1
1370 // add boot code if necessary
1371 HaveBootCode
= FALSE
;
1372 for (i
= 0; i
< MBR_BOOTCODE_SIZE
; i
++) {
1373 if (SectorBuffer
[i
] != 0) {
1374 HaveBootCode
= TRUE
;
1378 if (!HaveBootCode
) {
1379 // no boot code found in the MBR, add the syslinux MBR code
1380 SetMem(SectorBuffer
, MBR_BOOTCODE_SIZE
, 0);
1381 CopyMem(SectorBuffer
, syslinux_mbr
, SYSLINUX_MBR_SIZE
);
1384 // set the partition active
1385 MbrTable
= (MBR_PARTITION_INFO
*)(SectorBuffer
+ 446);
1387 for (i
= 0; i
< 4; i
++) {
1388 if (MbrTable
[i
].Flags
!= 0x00 && MbrTable
[i
].Flags
!= 0x80)
1389 return EFI_NOT_FOUND
; // safety measure #2
1390 if (i
== PartitionIndex
)
1391 MbrTable
[i
].Flags
= 0x80;
1392 else if (PartitionIndex
>= 4 && IS_EXTENDED_PART_TYPE(MbrTable
[i
].Type
)) {
1393 MbrTable
[i
].Flags
= 0x80;
1394 ExtBase
= MbrTable
[i
].StartLBA
;
1396 MbrTable
[i
].Flags
= 0x00;
1400 Status
= refit_call5_wrapper(BlockIO
->WriteBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, 0, 512, SectorBuffer
);
1401 if (EFI_ERROR(Status
))
1404 if (PartitionIndex
>= 4) {
1405 // we have to activate a logical partition, so walk the EMBR chain
1407 // NOTE: ExtBase was set above while looking at the MBR table
1408 for (ExtCurrent
= ExtBase
; ExtCurrent
; ExtCurrent
= NextExtCurrent
) {
1409 // read current EMBR
1410 Status
= refit_call5_wrapper(BlockIO
->ReadBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, ExtCurrent
, 512, SectorBuffer
);
1411 if (EFI_ERROR(Status
))
1413 if (*((UINT16
*)(SectorBuffer
+ 510)) != 0xaa55)
1414 return EFI_NOT_FOUND
; // safety measure #3
1416 // scan EMBR, set appropriate partition active
1417 EMbrTable
= (MBR_PARTITION_INFO
*)(SectorBuffer
+ 446);
1419 for (i
= 0; i
< 4; i
++) {
1420 if (EMbrTable
[i
].Flags
!= 0x00 && EMbrTable
[i
].Flags
!= 0x80)
1421 return EFI_NOT_FOUND
; // safety measure #4
1422 if (EMbrTable
[i
].StartLBA
== 0 || EMbrTable
[i
].Size
== 0)
1424 if (IS_EXTENDED_PART_TYPE(EMbrTable
[i
].Type
)) {
1425 // link to next EMBR
1426 NextExtCurrent
= ExtBase
+ EMbrTable
[i
].StartLBA
;
1427 EMbrTable
[i
].Flags
= (PartitionIndex
>= LogicalPartitionIndex
) ? 0x80 : 0x00;
1430 // logical partition
1431 EMbrTable
[i
].Flags
= (PartitionIndex
== LogicalPartitionIndex
) ? 0x80 : 0x00;
1432 LogicalPartitionIndex
++;
1436 // write current EMBR
1437 Status
= refit_call5_wrapper(BlockIO
->WriteBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, ExtCurrent
, 512, SectorBuffer
);
1438 if (EFI_ERROR(Status
))
1441 if (PartitionIndex
< LogicalPartitionIndex
)
1442 break; // stop the loop, no need to touch further EMBRs
1448 } /* static EFI_STATUS ActivateMbrPartition() */
1450 // early 2006 Core Duo / Core Solo models
1451 static UINT8 LegacyLoaderDevicePath1Data
[] = {
1452 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1453 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1454 0xFF, 0xFF, 0xF9, 0xFF, 0x00, 0x00, 0x00, 0x00,
1455 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1456 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1457 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1459 // mid-2006 Mac Pro (and probably other Core 2 models)
1460 static UINT8 LegacyLoaderDevicePath2Data
[] = {
1461 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1462 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1463 0xFF, 0xFF, 0xF7, 0xFF, 0x00, 0x00, 0x00, 0x00,
1464 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1465 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1466 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1468 // mid-2007 MBP ("Santa Rosa" based models)
1469 static UINT8 LegacyLoaderDevicePath3Data
[] = {
1470 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1471 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1472 0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
1473 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1474 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1475 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1478 static UINT8 LegacyLoaderDevicePath4Data
[] = {
1479 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1480 0x00, 0x00, 0xC0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1481 0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
1482 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1483 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1484 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1486 // late-2008 MB/MBP (NVidia chipset)
1487 static UINT8 LegacyLoaderDevicePath5Data
[] = {
1488 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1489 0x00, 0x40, 0xCB, 0xFF, 0x00, 0x00, 0x00, 0x00,
1490 0xFF, 0xBF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
1491 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1492 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1493 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1496 static EFI_DEVICE_PATH
*LegacyLoaderList
[] = {
1497 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath1Data
,
1498 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath2Data
,
1499 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath3Data
,
1500 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath4Data
,
1501 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath5Data
,
1505 #define MAX_DISCOVERED_PATHS (16)
1507 static VOID
StartLegacy(IN LEGACY_ENTRY
*Entry
)
1510 EG_IMAGE
*BootLogoImage
;
1511 UINTN ErrorInStep
= 0;
1512 EFI_DEVICE_PATH
*DiscoveredPathList
[MAX_DISCOVERED_PATHS
];
1514 BeginExternalScreen(TRUE
, L
"Booting Legacy OS (Mac mode)");
1516 BootLogoImage
= LoadOSIcon(Entry
->Volume
->OSIconName
, L
"legacy", TRUE
);
1517 if (BootLogoImage
!= NULL
)
1518 BltImageAlpha(BootLogoImage
,
1519 (UGAWidth
- BootLogoImage
->Width
) >> 1,
1520 (UGAHeight
- BootLogoImage
->Height
) >> 1,
1521 &StdBackgroundPixel
);
1523 if (Entry
->Volume
->IsMbrPartition
) {
1524 ActivateMbrPartition(Entry
->Volume
->WholeDiskBlockIO
, Entry
->Volume
->MbrPartitionIndex
);
1527 ExtractLegacyLoaderPaths(DiscoveredPathList
, MAX_DISCOVERED_PATHS
, LegacyLoaderList
);
1529 Status
= StartEFIImageList(DiscoveredPathList
, Entry
->LoadOptions
, NULL
, L
"legacy loader", 0, &ErrorInStep
, TRUE
);
1530 if (Status
== EFI_NOT_FOUND
) {
1531 if (ErrorInStep
== 1) {
1532 Print(L
"\nPlease make sure that you have the latest firmware update installed.\n");
1533 } else if (ErrorInStep
== 3) {
1534 Print(L
"\nThe firmware refused to boot from the selected volume. Note that external\n"
1535 L
"hard drives are not well-supported by Apple's firmware for legacy OS booting.\n");
1538 FinishExternalScreen();
1539 } /* static VOID StartLegacy() */
1541 // Start a device on a non-Mac using the EFI_LEGACY_BIOS_PROTOCOL
1542 #ifdef __MAKEWITH_TIANO
1543 static VOID
StartLegacyUEFI(IN LEGACY_ENTRY
*Entry
)
1545 BeginExternalScreen(TRUE
, L
"Booting Legacy OS (UEFI mode)");
1547 BdsLibConnectDevicePath (Entry
->BdsOption
->DevicePath
);
1548 BdsLibDoLegacyBoot(Entry
->BdsOption
);
1550 // If we get here, it means that there was a failure....
1551 Print(L
"Failure booting legacy (BIOS) OS.");
1553 FinishExternalScreen();
1554 } // static VOID StartLegacyUEFI()
1555 #endif // __MAKEWITH_TIANO
1557 static LEGACY_ENTRY
* AddLegacyEntry(IN CHAR16
*LoaderTitle
, IN REFIT_VOLUME
*Volume
)
1559 LEGACY_ENTRY
*Entry
, *SubEntry
;
1560 REFIT_MENU_SCREEN
*SubScreen
;
1562 CHAR16 ShortcutLetter
= 0;
1564 if (LoaderTitle
== NULL
) {
1565 if (Volume
->OSName
!= NULL
) {
1566 LoaderTitle
= Volume
->OSName
;
1567 if (LoaderTitle
[0] == 'W' || LoaderTitle
[0] == 'L')
1568 ShortcutLetter
= LoaderTitle
[0];
1570 LoaderTitle
= L
"Legacy OS";
1572 if (Volume
->VolName
!= NULL
)
1573 VolDesc
= Volume
->VolName
;
1575 VolDesc
= (Volume
->DiskKind
== DISK_KIND_OPTICAL
) ? L
"CD" : L
"HD";
1577 // prepare the menu entry
1578 Entry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1579 Entry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1580 SPrint(Entry
->me
.Title
, 255, L
"Boot %s from %s", LoaderTitle
, VolDesc
);
1581 Entry
->me
.Tag
= TAG_LEGACY
;
1583 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1584 Entry
->me
.Image
= LoadOSIcon(Volume
->OSIconName
, L
"legacy", FALSE
);
1585 Entry
->me
.BadgeImage
= Volume
->VolBadgeImage
;
1586 Entry
->Volume
= Volume
;
1587 Entry
->LoadOptions
= (Volume
->DiskKind
== DISK_KIND_OPTICAL
) ? L
"CD" :
1588 ((Volume
->DiskKind
== DISK_KIND_EXTERNAL
) ? L
"USB" : L
"HD");
1589 Entry
->Enabled
= TRUE
;
1591 // create the submenu
1592 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
1593 SubScreen
->Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1594 SPrint(SubScreen
->Title
, 255, L
"Boot Options for %s on %s", LoaderTitle
, VolDesc
);
1595 SubScreen
->TitleImage
= Entry
->me
.Image
;
1596 SubScreen
->Hint1
= StrDuplicate(SUBSCREEN_HINT1
);
1597 if (GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_EDITOR
) {
1598 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2_NO_EDITOR
);
1600 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2
);
1604 SubEntry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1605 SubEntry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1606 SPrint(SubEntry
->me
.Title
, 255, L
"Boot %s", LoaderTitle
);
1607 SubEntry
->me
.Tag
= TAG_LEGACY
;
1608 SubEntry
->Volume
= Entry
->Volume
;
1609 SubEntry
->LoadOptions
= Entry
->LoadOptions
;
1610 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
1612 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
1613 Entry
->me
.SubScreen
= SubScreen
;
1614 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1616 } /* static LEGACY_ENTRY * AddLegacyEntry() */
1619 #ifdef __MAKEWITH_GNUEFI
1620 static VOID
ScanLegacyUEFI(IN UINTN DiskType
){}
1622 // default volume badge icon based on disk kind
1623 static EG_IMAGE
* GetDiskBadge(IN UINTN DiskType
) {
1624 EG_IMAGE
* Badge
= NULL
;
1628 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_INTERNAL
);
1631 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_EXTERNAL
);
1634 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_OPTICAL
);
1638 } // static EG_IMAGE * GetDiskBadge()
1641 Create a rEFInd boot option from a Legacy BIOS protocol option.
1643 static LEGACY_ENTRY
* AddLegacyEntryUEFI(BDS_COMMON_OPTION
*BdsOption
, IN UINT16 DiskType
)
1645 LEGACY_ENTRY
*Entry
, *SubEntry
;
1646 REFIT_MENU_SCREEN
*SubScreen
;
1647 CHAR16 ShortcutLetter
= 0;
1648 CHAR16
*LegacyDescription
= BdsOption
->Description
;
1650 // prepare the menu entry
1651 Entry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1652 Entry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1653 SPrint(Entry
->me
.Title
, 255, L
"Boot legacy target %s", LegacyDescription
);
1654 Entry
->me
.Tag
= TAG_LEGACY_UEFI
;
1656 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1657 Entry
->me
.Image
= LoadOSIcon(L
"legacy", L
"legacy", TRUE
);
1658 Entry
->LoadOptions
= (DiskType
== BBS_CDROM
) ? L
"CD" :
1659 ((DiskType
== BBS_USB
) ? L
"USB" : L
"HD");
1660 Entry
->me
.BadgeImage
= GetDiskBadge(DiskType
);
1661 Entry
->BdsOption
= BdsOption
;
1662 Entry
->Enabled
= TRUE
;
1664 // create the submenu
1665 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
1666 SubScreen
->Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1667 SPrint(SubScreen
->Title
, 255, L
"No boot options for legacy target");
1668 SubScreen
->TitleImage
= Entry
->me
.Image
;
1669 SubScreen
->Hint1
= StrDuplicate(SUBSCREEN_HINT1
);
1670 if (GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_EDITOR
) {
1671 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2_NO_EDITOR
);
1673 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2
);
1677 SubEntry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1678 SubEntry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1679 SPrint(SubEntry
->me
.Title
, 255, L
"Boot %s", LegacyDescription
);
1680 SubEntry
->me
.Tag
= TAG_LEGACY_UEFI
;
1681 Entry
->BdsOption
= BdsOption
;
1682 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
1684 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
1685 Entry
->me
.SubScreen
= SubScreen
;
1686 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1688 } /* static LEGACY_ENTRY * AddLegacyEntryUEFI() */
1691 Scan for legacy BIOS targets on machines that implement EFI_LEGACY_BIOS_PROTOCOL.
1692 In testing, protocol has not been implemented on Macs but has been
1693 implemented on several Dell PCs and an ASUS motherboard.
1694 Restricts output to disks of the specified DiskType.
1696 static VOID
ScanLegacyUEFI(IN UINTN DiskType
)
1699 EFI_LEGACY_BIOS_PROTOCOL
*LegacyBios
;
1700 UINT16
*BootOrder
= NULL
;
1702 CHAR16 BootOption
[10];
1703 UINTN BootOrderSize
= 0;
1705 BDS_COMMON_OPTION
*BdsOption
;
1706 LIST_ENTRY TempList
;
1707 BBS_BBS_DEVICE_PATH
* BbsDevicePath
= NULL
;
1709 InitializeListHead (&TempList
);
1710 ZeroMem (Buffer
, sizeof (Buffer
));
1712 // If LegacyBios protocol is not implemented on this platform, then
1713 //we do not support this type of legacy boot on this machine.
1714 Status
= gBS
->LocateProtocol(&gEfiLegacyBootProtocolGuid
, NULL
, (VOID
**) &LegacyBios
);
1715 if (EFI_ERROR (Status
))
1718 // Grab the boot order
1719 BootOrder
= BdsLibGetVariableAndSize(L
"BootOrder", &gEfiGlobalVariableGuid
, &BootOrderSize
);
1720 if (BootOrder
== NULL
) {
1725 while (Index
< BootOrderSize
/ sizeof (UINT16
))
1727 // Grab each boot option variable from the boot order, and convert
1728 // the variable into a BDS boot option
1729 UnicodeSPrint (BootOption
, sizeof (BootOption
), L
"Boot%04x", BootOrder
[Index
]);
1730 BdsOption
= BdsLibVariableToOption (&TempList
, BootOption
);
1732 if (BdsOption
!= NULL
) {
1733 BbsDevicePath
= (BBS_BBS_DEVICE_PATH
*)BdsOption
->DevicePath
;
1735 // Only add the entry if it is of a requested type (e.g. USB, HD)
1737 // Two checks necessary because some systems return EFI boot loaders
1738 // with a DeviceType value that would inappropriately include them
1739 // as legacy loaders....
1740 if ((BbsDevicePath
->DeviceType
== DiskType
) && (BdsOption
->DevicePath
->Type
== DEVICE_TYPE_BIOS
)) {
1741 AddLegacyEntryUEFI(BdsOption
, BbsDevicePath
->DeviceType
);
1746 } /* static VOID ScanLegacyUEFI() */
1747 #endif // __MAKEWITH_GNUEFI
1749 static VOID
ScanLegacyVolume(REFIT_VOLUME
*Volume
, UINTN VolumeIndex
) {
1751 BOOLEAN ShowVolume
, HideIfOthersFound
;
1754 HideIfOthersFound
= FALSE
;
1755 if (Volume
->IsAppleLegacy
) {
1757 HideIfOthersFound
= TRUE
;
1758 } else if (Volume
->HasBootCode
) {
1760 if (Volume
->BlockIO
== Volume
->WholeDiskBlockIO
&&
1761 Volume
->BlockIOOffset
== 0 &&
1762 Volume
->OSName
== NULL
)
1763 // this is a whole disk (MBR) entry; hide if we have entries for partitions
1764 HideIfOthersFound
= TRUE
;
1766 if (HideIfOthersFound
) {
1767 // check for other bootable entries on the same disk
1768 for (VolumeIndex2
= 0; VolumeIndex2
< VolumesCount
; VolumeIndex2
++) {
1769 if (VolumeIndex2
!= VolumeIndex
&& Volumes
[VolumeIndex2
]->HasBootCode
&&
1770 Volumes
[VolumeIndex2
]->WholeDiskBlockIO
== Volume
->WholeDiskBlockIO
)
1776 AddLegacyEntry(NULL
, Volume
);
1777 } // static VOID ScanLegacyVolume()
1779 // Scan attached optical discs for legacy (BIOS) boot code
1780 // and add anything found to the list....
1781 static VOID
ScanLegacyDisc(VOID
)
1784 REFIT_VOLUME
*Volume
;
1786 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
) {
1787 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1788 Volume
= Volumes
[VolumeIndex
];
1789 if (Volume
->DiskKind
== DISK_KIND_OPTICAL
)
1790 ScanLegacyVolume(Volume
, VolumeIndex
);
1792 } else if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
1793 ScanLegacyUEFI(BBS_CDROM
);
1795 } /* static VOID ScanLegacyDisc() */
1797 // Scan internal hard disks for legacy (BIOS) boot code
1798 // and add anything found to the list....
1799 static VOID
ScanLegacyInternal(VOID
)
1802 REFIT_VOLUME
*Volume
;
1804 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
) {
1805 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1806 Volume
= Volumes
[VolumeIndex
];
1807 if (Volume
->DiskKind
== DISK_KIND_INTERNAL
)
1808 ScanLegacyVolume(Volume
, VolumeIndex
);
1810 } else if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
1811 ScanLegacyUEFI(BBS_HARDDISK
);
1813 } /* static VOID ScanLegacyInternal() */
1815 // Scan external disks for legacy (BIOS) boot code
1816 // and add anything found to the list....
1817 static VOID
ScanLegacyExternal(VOID
)
1820 REFIT_VOLUME
*Volume
;
1822 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
) {
1823 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1824 Volume
= Volumes
[VolumeIndex
];
1825 if (Volume
->DiskKind
== DISK_KIND_EXTERNAL
)
1826 ScanLegacyVolume(Volume
, VolumeIndex
);
1828 } else if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
1829 ScanLegacyUEFI(BBS_USB
);
1831 } /* static VOID ScanLegacyExternal() */
1834 // pre-boot tool functions
1837 static VOID
StartTool(IN LOADER_ENTRY
*Entry
)
1839 BeginExternalScreen(Entry
->UseGraphicsMode
, Entry
->me
.Title
+ 6); // assumes "Start <title>" as assigned below
1840 StartEFIImage(Entry
->DevicePath
, Entry
->LoadOptions
, Basename(Entry
->LoaderPath
),
1841 Basename(Entry
->LoaderPath
), Entry
->OSType
, NULL
, TRUE
);
1842 FinishExternalScreen();
1843 } /* static VOID StartTool() */
1845 static LOADER_ENTRY
* AddToolEntry(EFI_HANDLE DeviceHandle
, IN CHAR16
*LoaderPath
, IN CHAR16
*LoaderTitle
, IN EG_IMAGE
*Image
,
1846 IN CHAR16 ShortcutLetter
, IN BOOLEAN UseGraphicsMode
)
1848 LOADER_ENTRY
*Entry
;
1849 CHAR16
*TitleStr
= NULL
;
1851 Entry
= AllocateZeroPool(sizeof(LOADER_ENTRY
));
1853 MergeStrings(&TitleStr
, L
"Start ", 0);
1854 MergeStrings(&TitleStr
, LoaderTitle
, 0);
1855 Entry
->me
.Title
= TitleStr
;
1856 Entry
->me
.Tag
= TAG_TOOL
;
1858 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1859 Entry
->me
.Image
= Image
;
1860 Entry
->LoaderPath
= (LoaderPath
) ? StrDuplicate(LoaderPath
) : NULL
;
1861 Entry
->DevicePath
= FileDevicePath(DeviceHandle
, Entry
->LoaderPath
);
1862 Entry
->UseGraphicsMode
= UseGraphicsMode
;
1864 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1866 } /* static LOADER_ENTRY * AddToolEntry() */
1869 // pre-boot driver functions
1872 static UINTN
ScanDriverDir(IN CHAR16
*Path
)
1875 REFIT_DIR_ITER DirIter
;
1877 EFI_FILE_INFO
*DirEntry
;
1878 CHAR16 FileName
[256];
1880 CleanUpPathNameSlashes(Path
);
1881 // look through contents of the directory
1882 DirIterOpen(SelfRootDir
, Path
, &DirIter
);
1883 while (DirIterNext(&DirIter
, 2, LOADER_MATCH_PATTERNS
, &DirEntry
)) {
1884 if (DirEntry
->FileName
[0] == '.')
1885 continue; // skip this
1887 SPrint(FileName
, 255, L
"%s\\%s", Path
, DirEntry
->FileName
);
1889 Status
= StartEFIImage(FileDevicePath(SelfLoadedImage
->DeviceHandle
, FileName
),
1890 L
"", DirEntry
->FileName
, DirEntry
->FileName
, 0, NULL
, FALSE
);
1892 Status
= DirIterClose(&DirIter
);
1893 if (Status
!= EFI_NOT_FOUND
) {
1894 SPrint(FileName
, 255, L
"while scanning the %s directory", Path
);
1895 CheckError(Status
, FileName
);
1900 #ifdef __MAKEWITH_GNUEFI
1901 static EFI_STATUS
ConnectAllDriversToAllControllers(VOID
)
1904 UINTN AllHandleCount
;
1905 EFI_HANDLE
*AllHandleBuffer
;
1908 EFI_HANDLE
*HandleBuffer
;
1914 Status
= LibLocateHandle(AllHandles
,
1919 if (EFI_ERROR(Status
))
1922 for (Index
= 0; Index
< AllHandleCount
; Index
++) {
1924 // Scan the handle database
1926 Status
= LibScanHandleDatabase(NULL
,
1928 AllHandleBuffer
[Index
],
1933 if (EFI_ERROR (Status
))
1937 if (HandleType
[Index
] & EFI_HANDLE_TYPE_DRIVER_BINDING_HANDLE
)
1939 if (HandleType
[Index
] & EFI_HANDLE_TYPE_IMAGE_HANDLE
)
1944 for (HandleIndex
= 0; HandleIndex
< HandleCount
; HandleIndex
++) {
1945 if (HandleType
[HandleIndex
] & EFI_HANDLE_TYPE_PARENT_HANDLE
)
1950 if (HandleType
[Index
] & EFI_HANDLE_TYPE_DEVICE_HANDLE
) {
1951 Status
= refit_call4_wrapper(BS
->ConnectController
,
1952 AllHandleBuffer
[Index
],
1960 MyFreePool (HandleBuffer
);
1961 MyFreePool (HandleType
);
1965 MyFreePool (AllHandleBuffer
);
1967 } /* EFI_STATUS ConnectAllDriversToAllControllers() */
1969 static EFI_STATUS
ConnectAllDriversToAllControllers(VOID
) {
1970 BdsLibConnectAllDriversToAllControllers();
1975 // Load all EFI drivers from rEFInd's "drivers" subdirectory and from the
1976 // directories specified by the user in the "scan_driver_dirs" configuration
1978 static VOID
LoadDrivers(VOID
)
1980 CHAR16
*Directory
, *SelfDirectory
;
1981 UINTN i
= 0, Length
, NumFound
= 0;
1983 // load drivers from the subdirectories of rEFInd's home directory specified
1984 // in the DRIVER_DIRS constant.
1985 while ((Directory
= FindCommaDelimited(DRIVER_DIRS
, i
++)) != NULL
) {
1986 SelfDirectory
= SelfDirPath
? StrDuplicate(SelfDirPath
) : NULL
;
1987 CleanUpPathNameSlashes(SelfDirectory
);
1988 MergeStrings(&SelfDirectory
, Directory
, L
'\\');
1989 NumFound
+= ScanDriverDir(SelfDirectory
);
1990 MyFreePool(Directory
);
1991 MyFreePool(SelfDirectory
);
1994 // Scan additional user-specified driver directories....
1996 while ((Directory
= FindCommaDelimited(GlobalConfig
.DriverDirs
, i
++)) != NULL
) {
1997 CleanUpPathNameSlashes(Directory
);
1998 Length
= StrLen(Directory
);
2000 NumFound
+= ScanDriverDir(Directory
);
2002 MyFreePool(Directory
);
2005 // connect all devices
2007 ConnectAllDriversToAllControllers();
2008 } /* static VOID LoadDrivers() */
2010 // Determine what (if any) type of legacy (BIOS) boot support is available
2011 static VOID
FindLegacyBootType(VOID
) {
2012 #ifdef __MAKEWITH_TIANO
2014 EFI_LEGACY_BIOS_PROTOCOL
*LegacyBios
;
2017 GlobalConfig
.LegacyType
= LEGACY_TYPE_NONE
;
2019 // UEFI-style legacy BIOS support is available only with the TianoCore EDK2
2020 // build environment, and then only with some EFI implementations....
2021 #ifdef __MAKEWITH_TIANO
2022 Status
= gBS
->LocateProtocol (&gEfiLegacyBootProtocolGuid
, NULL
, (VOID
**) &LegacyBios
);
2023 if (!EFI_ERROR (Status
))
2024 GlobalConfig
.LegacyType
= LEGACY_TYPE_UEFI
;
2027 // Macs have their own system. If the firmware vendor code contains the
2028 // string "Apple", assume it's available. Note that this overrides the
2029 // UEFI type, and might yield false positives if the vendor string
2030 // contains "Apple" as part of something bigger, so this isn't 100%
2032 if (StriSubCmp(L
"Apple", ST
->FirmwareVendor
))
2033 GlobalConfig
.LegacyType
= LEGACY_TYPE_MAC
;
2034 } // static VOID FindLegacyBootType
2036 // Warn the user if legacy OS scans are enabled but the firmware or this
2037 // application can't support them....
2038 static VOID
WarnIfLegacyProblems() {
2039 BOOLEAN found
= FALSE
;
2042 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_NONE
) {
2044 if (GlobalConfig
.ScanFor
[i
] == 'h' || GlobalConfig
.ScanFor
[i
] == 'b' || GlobalConfig
.ScanFor
[i
] == 'c')
2047 } while ((i
< NUM_SCAN_OPTIONS
) && (!found
));
2049 Print(L
"NOTE: refind.conf's 'scanfor' line specifies scanning for one or more legacy\n");
2050 Print(L
"(BIOS) boot options; however, this is not possible because ");
2051 #ifdef __MAKEWITH_TIANO
2052 Print(L
"your computer lacks\n");
2053 Print(L
"the necessary Compatibility Support Module (CSM) support.\n");
2055 Print(L
"this program was\n");
2056 Print(L
"compiled without the necessary support. Please visit\n");
2057 Print(L
"http://www.rodsbooks.com/refind/getting.html and download and install a rEFInd\n");
2058 Print(L
"binary built with the TianoCore EDK2 to enable legacy boot support.\n");
2062 } // if no legacy support
2063 } // static VOID WarnIfLegacyProblems()
2065 // Locates boot loaders. NOTE: This assumes that GlobalConfig.LegacyType is set correctly.
2066 static VOID
ScanForBootloaders(VOID
) {
2071 // scan for loaders and tools, add them to the menu
2072 for (i
= 0; i
< NUM_SCAN_OPTIONS
; i
++) {
2073 switch(GlobalConfig
.ScanFor
[i
]) {
2078 ScanLegacyInternal();
2081 ScanLegacyExternal();
2084 ScanUserConfigured(CONFIG_FILE_NAME
);
2098 // assign shortcut keys
2099 for (i
= 0; i
< MainMenu
.EntryCount
&& MainMenu
.Entries
[i
]->Row
== 0 && i
< 9; i
++)
2100 MainMenu
.Entries
[i
]->ShortcutDigit
= (CHAR16
)('1' + i
);
2102 // wait for user ACK when there were errors
2103 FinishTextScreen(FALSE
);
2104 } // static VOID ScanForBootloaders()
2106 // Add the second-row tags containing built-in and external tools (EFI shell,
2108 static VOID
ScanForTools(VOID
) {
2109 CHAR16
*FileName
= NULL
, *MokLocations
, *MokName
, *PathName
, Description
[256];
2110 REFIT_MENU_ENTRY
*TempMenuEntry
;
2111 UINTN i
, j
, k
, VolumeIndex
;
2115 MokLocations
= StrDuplicate(MOK_LOCATIONS
);
2116 if (MokLocations
!= NULL
)
2117 MergeStrings(&MokLocations
, SelfDirPath
, L
',');
2119 for (i
= 0; i
< NUM_TOOLS
; i
++) {
2120 switch(GlobalConfig
.ShowTools
[i
]) {
2121 // NOTE: Be sure that FileName is NULL at the end of each case.
2123 TempMenuEntry
= CopyMenuEntry(&MenuEntryShutdown
);
2124 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_SHUTDOWN
);
2125 AddMenuEntry(&MainMenu
, TempMenuEntry
);
2128 TempMenuEntry
= CopyMenuEntry(&MenuEntryReset
);
2129 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_RESET
);
2130 AddMenuEntry(&MainMenu
, TempMenuEntry
);
2133 TempMenuEntry
= CopyMenuEntry(&MenuEntryAbout
);
2134 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT
);
2135 AddMenuEntry(&MainMenu
, TempMenuEntry
);
2138 TempMenuEntry
= CopyMenuEntry(&MenuEntryExit
);
2139 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_EXIT
);
2140 AddMenuEntry(&MainMenu
, TempMenuEntry
);
2143 if (EfivarGetRaw(&GlobalGuid
, L
"OsIndicationsSupported", &b
, &j
) == EFI_SUCCESS
) {
2145 if (osind
& EFI_OS_INDICATIONS_BOOT_TO_FW_UI
) {
2146 TempMenuEntry
= CopyMenuEntry(&MenuEntryFirmware
);
2147 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_FIRMWARE
);
2148 AddMenuEntry(&MainMenu
, TempMenuEntry
);
2154 while ((FileName
= FindCommaDelimited(SHELL_NAMES
, j
++)) != NULL
) {
2155 if (FileExists(SelfRootDir
, FileName
)) {
2156 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, L
"EFI Shell", BuiltinIcon(BUILTIN_ICON_TOOL_SHELL
),
2159 MyFreePool(FileName
);
2164 while ((FileName
= FindCommaDelimited(GPTSYNC_NAMES
, j
++)) != NULL
) {
2165 if (FileExists(SelfRootDir
, FileName
)) {
2166 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, L
"Hybrid MBR tool", BuiltinIcon(BUILTIN_ICON_TOOL_PART
),
2169 MyFreePool(FileName
);
2173 case TAG_APPLE_RECOVERY
:
2174 FileName
= StrDuplicate(L
"\\com.apple.recovery.boot\\boot.efi");
2175 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
2176 if ((Volumes
[VolumeIndex
]->RootDir
!= NULL
) && (FileExists(Volumes
[VolumeIndex
]->RootDir
, FileName
))) {
2177 SPrint(Description
, 255, L
"Apple Recovery on %s", Volumes
[VolumeIndex
]->VolName
);
2178 AddToolEntry(Volumes
[VolumeIndex
]->DeviceHandle
, FileName
, Description
,
2179 BuiltinIcon(BUILTIN_ICON_TOOL_APPLE_RESCUE
), 'R', TRUE
);
2182 MyFreePool(FileName
);
2187 while ((FileName
= FindCommaDelimited(MokLocations
, j
++)) != NULL
) {
2189 while ((MokName
= FindCommaDelimited(MOK_NAMES
, k
++)) != NULL
) {
2190 PathName
= StrDuplicate(FileName
);
2191 MergeStrings(&PathName
, MokName
, (StriCmp(PathName
, L
"\\") == 0) ? 0 : L
'\\');
2192 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
2193 if ((Volumes
[VolumeIndex
]->RootDir
!= NULL
) && (FileExists(Volumes
[VolumeIndex
]->RootDir
, PathName
))) {
2194 SPrint(Description
, 255, L
"MOK utility at %s on %s", PathName
, Volumes
[VolumeIndex
]->VolName
);
2195 AddToolEntry(Volumes
[VolumeIndex
]->DeviceHandle
, PathName
, Description
,
2196 BuiltinIcon(BUILTIN_ICON_TOOL_MOK_TOOL
), 'S', FALSE
);
2199 MyFreePool(PathName
);
2200 MyFreePool(MokName
);
2201 } // while MOK_NAMES
2202 MyFreePool(FileName
);
2203 } // while MokLocations
2208 } // static VOID ScanForTools
2210 // Rescan for boot loaders
2211 VOID
RescanAll(VOID
) {
2218 egDisplayMessage(L
"Scanning for new boot loaders; please wait....", &BGColor
);
2219 FreeList((VOID
***) &(MainMenu
.Entries
), &MainMenu
.EntryCount
);
2220 MainMenu
.Entries
= NULL
;
2221 MainMenu
.EntryCount
= 0;
2222 ReadConfig(CONFIG_FILE_NAME
);
2223 ConnectAllDriversToAllControllers();
2225 ScanForBootloaders();
2228 } // VOID RescanAll()
2230 #ifdef __MAKEWITH_TIANO
2232 // Minimal initialization function
2233 static VOID
InitializeLib(IN EFI_HANDLE ImageHandle
, IN EFI_SYSTEM_TABLE
*SystemTable
) {
2235 // gImageHandle = ImageHandle;
2236 gBS
= SystemTable
->BootServices
;
2237 // gRS = SystemTable->RuntimeServices;
2238 gRT
= SystemTable
->RuntimeServices
; // Some BDS functions need gRT to be set
2239 EfiGetSystemConfigurationTable (&gEfiDxeServicesTableGuid
, (VOID
**) &gDS
);
2241 InitializeConsoleSim();
2246 // Set up our own Secure Boot extensions....
2247 // Returns TRUE on success, FALSE otherwise
2248 static BOOLEAN
SecureBootSetup(VOID
) {
2250 BOOLEAN Success
= FALSE
;
2252 if (secure_mode() && ShimLoaded()) {
2253 Status
= security_policy_install();
2254 if (Status
== EFI_SUCCESS
) {
2257 Print(L
"Failed to install MOK Secure Boot extensions");
2261 } // VOID SecureBootSetup()
2263 // Remove our own Secure Boot extensions....
2264 // Returns TRUE on success, FALSE otherwise
2265 static BOOLEAN
SecureBootUninstall(VOID
) {
2267 BOOLEAN Success
= TRUE
;
2269 if (secure_mode()) {
2270 Status
= security_policy_uninstall();
2271 if (Status
!= EFI_SUCCESS
) {
2273 BeginTextScreen(L
"Secure Boot Policy Failure");
2274 Print(L
"Failed to uninstall MOK Secure Boot extensions; forcing a reboot.");
2276 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);
2280 } // VOID SecureBootUninstall
2287 efi_main (EFI_HANDLE ImageHandle
, EFI_SYSTEM_TABLE
*SystemTable
)
2290 BOOLEAN MainLoopRunning
= TRUE
;
2291 BOOLEAN MokProtocol
;
2292 REFIT_MENU_ENTRY
*ChosenEntry
;
2294 CHAR16
*Selection
= NULL
;
2298 InitializeLib(ImageHandle
, SystemTable
);
2299 Status
= InitRefitLib(ImageHandle
);
2300 if (EFI_ERROR(Status
))
2303 // read configuration
2304 CopyMem(GlobalConfig
.ScanFor
, "ieom ", NUM_SCAN_OPTIONS
);
2305 FindLegacyBootType();
2306 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
)
2307 CopyMem(GlobalConfig
.ScanFor
, "ihebocm ", NUM_SCAN_OPTIONS
);
2309 ReadConfig(CONFIG_FILE_NAME
);
2312 WarnIfLegacyProblems();
2313 MainMenu
.TimeoutSeconds
= GlobalConfig
.Timeout
;
2315 // disable EFI watchdog timer
2316 refit_call4_wrapper(BS
->SetWatchdogTimer
, 0x0000, 0x0000, 0x0000, NULL
);
2318 // further bootstrap (now with config available)
2319 MokProtocol
= SecureBootSetup();
2321 ScanForBootloaders();
2325 if (GlobalConfig
.ScanDelay
> 0) {
2330 egDisplayMessage(L
"Pausing before disk scan; please wait....", &BGColor
);
2331 for (i
= 0; i
< GlobalConfig
.ScanDelay
; i
++)
2332 refit_call1_wrapper(BS
->Stall
, 1000000);
2336 if (GlobalConfig
.DefaultSelection
)
2337 Selection
= StrDuplicate(GlobalConfig
.DefaultSelection
);
2339 while (MainLoopRunning
) {
2340 MenuExit
= RunMainMenu(&MainMenu
, Selection
, &ChosenEntry
);
2342 // The Escape key triggers a re-scan operation....
2343 if (MenuExit
== MENU_EXIT_ESCAPE
) {
2348 switch (ChosenEntry
->Tag
) {
2350 case TAG_REBOOT
: // Reboot
2352 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);
2353 MainLoopRunning
= FALSE
; // just in case we get this far
2356 case TAG_SHUTDOWN
: // Shut Down
2358 refit_call4_wrapper(RT
->ResetSystem
, EfiResetShutdown
, EFI_SUCCESS
, 0, NULL
);
2359 MainLoopRunning
= FALSE
; // just in case we get this far
2362 case TAG_ABOUT
: // About rEFInd
2366 case TAG_LOADER
: // Boot OS via .EFI loader
2367 StartLoader((LOADER_ENTRY
*)ChosenEntry
);
2370 case TAG_LEGACY
: // Boot legacy OS
2371 StartLegacy((LEGACY_ENTRY
*)ChosenEntry
);
2374 #ifdef __MAKEWITH_TIANO
2375 case TAG_LEGACY_UEFI
: // Boot a legacy OS on a non-Mac
2376 StartLegacyUEFI((LEGACY_ENTRY
*)ChosenEntry
);
2380 case TAG_TOOL
: // Start a EFI tool
2381 StartTool((LOADER_ENTRY
*)ChosenEntry
);
2384 case TAG_EXIT
: // Terminate rEFInd
2385 if ((MokProtocol
) && !SecureBootUninstall()) {
2386 MainLoopRunning
= FALSE
; // just in case we get this far
2388 BeginTextScreen(L
" ");
2393 case TAG_FIRMWARE
: // Reboot into firmware's user interface
2394 RebootIntoFirmware();
2398 MyFreePool(Selection
);
2399 Selection
= (ChosenEntry
->Title
) ? StrDuplicate(ChosenEntry
->Title
) : NULL
;
2402 // If we end up here, things have gone wrong. Try to reboot, and if that
2403 // fails, go into an endless loop.
2404 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);