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"
112 static REFIT_MENU_ENTRY MenuEntryAbout
= { L
"About rEFInd", TAG_ABOUT
, 1, 0, 'A', NULL
, NULL
, NULL
};
113 static REFIT_MENU_ENTRY MenuEntryReset
= { L
"Reboot Computer", TAG_REBOOT
, 1, 0, 'R', NULL
, NULL
, NULL
};
114 static REFIT_MENU_ENTRY MenuEntryShutdown
= { L
"Shut Down Computer", TAG_SHUTDOWN
, 1, 0, 'U', NULL
, NULL
, NULL
};
115 static REFIT_MENU_ENTRY MenuEntryReturn
= { L
"Return to Main Menu", TAG_RETURN
, 1, 0, 0, NULL
, NULL
, NULL
};
116 static REFIT_MENU_ENTRY MenuEntryExit
= { L
"Exit rEFInd", TAG_EXIT
, 1, 0, 0, NULL
, NULL
, NULL
};
117 static REFIT_MENU_ENTRY MenuEntryFirmware
= { L
"Reboot to Computer Setup Utility", TAG_FIRMWARE
, 1, 0, 0, NULL
, NULL
, NULL
};
119 static REFIT_MENU_SCREEN MainMenu
= { L
"Main Menu", NULL
, 0, NULL
, 0, NULL
, 0, L
"Automatic boot",
120 L
"Use arrow keys to move cursor; Enter to boot;",
121 L
"Insert or F2 for more options; Esc to refresh" };
122 static REFIT_MENU_SCREEN AboutMenu
= { L
"About", NULL
, 0, NULL
, 0, NULL
, 0, NULL
, L
"Press Enter to return to main menu", L
"" };
124 REFIT_CONFIG GlobalConfig
= { FALSE
, FALSE
, 0, 0, 0, DONT_CHANGE_TEXT_MODE
, 20, 0, 0, GRAPHICS_FOR_OSX
, LEGACY_TYPE_MAC
, 0, 0,
125 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
126 { TAG_SHELL
, TAG_APPLE_RECOVERY
, TAG_MOK_TOOL
, TAG_ABOUT
, TAG_SHUTDOWN
, TAG_REBOOT
, TAG_FIRMWARE
,
130 EFI_GUID GlobalGuid
= EFI_GLOBAL_VARIABLE
;
132 // Structure used to hold boot loader filenames and time stamps in
133 // a linked list; used to sort entries within a directory.
137 struct LOADER_LIST
*NextEntry
;
144 static VOID
AboutrEFInd(VOID
)
146 CHAR16
*TempStr
; // Note: Don't deallocate; moved to menu structure
148 if (AboutMenu
.EntryCount
== 0) {
149 AboutMenu
.TitleImage
= BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT
);
150 AddMenuInfoLine(&AboutMenu
, L
"rEFInd Version 0.7.0");
151 AddMenuInfoLine(&AboutMenu
, L
"");
152 AddMenuInfoLine(&AboutMenu
, L
"Copyright (c) 2006-2010 Christoph Pfisterer");
153 AddMenuInfoLine(&AboutMenu
, L
"Copyright (c) 2012-2013 Roderick W. Smith");
154 AddMenuInfoLine(&AboutMenu
, L
"Portions Copyright (c) Intel Corporation and others");
155 AddMenuInfoLine(&AboutMenu
, L
"Distributed under the terms of the GNU GPLv3 license");
156 AddMenuInfoLine(&AboutMenu
, L
"");
157 AddMenuInfoLine(&AboutMenu
, L
"Running on:");
158 TempStr
= AllocateZeroPool(256 * sizeof(CHAR16
));
159 SPrint(TempStr
, 255, L
" EFI Revision %d.%02d", ST
->Hdr
.Revision
>> 16, ST
->Hdr
.Revision
& ((1 << 16) - 1));
160 AddMenuInfoLine(&AboutMenu
, TempStr
);
162 AddMenuInfoLine(&AboutMenu
, L
" Platform: x86 (32 bit)");
163 #elif defined(EFIX64)
164 TempStr
= AllocateZeroPool(256 * sizeof(CHAR16
));
165 SPrint(TempStr
, 255, L
" Platform: x86_64 (64 bit); Secure Boot %s", secure_mode() ? L
"active" : L
"inactive");
166 AddMenuInfoLine(&AboutMenu
, TempStr
);
168 AddMenuInfoLine(&AboutMenu
, L
" Platform: unknown");
170 TempStr
= AllocateZeroPool(256 * sizeof(CHAR16
));
171 SPrint(TempStr
, 255, L
" Firmware: %s %d.%02d",
172 ST
->FirmwareVendor
, ST
->FirmwareRevision
>> 16, ST
->FirmwareRevision
& ((1 << 16) - 1));
173 AddMenuInfoLine(&AboutMenu
, TempStr
);
174 TempStr
= AllocateZeroPool(256 * sizeof(CHAR16
));
175 SPrint(TempStr
, 255, L
" Screen Output: %s", egScreenDescription());
176 AddMenuInfoLine(&AboutMenu
, TempStr
);
177 AddMenuInfoLine(&AboutMenu
, L
"");
178 #if defined(__MAKEWITH_GNUEFI)
179 AddMenuInfoLine(&AboutMenu
, L
"Built with GNU-EFI");
181 AddMenuInfoLine(&AboutMenu
, L
"Built with TianoCore EDK2");
183 AddMenuInfoLine(&AboutMenu
, L
"");
184 AddMenuInfoLine(&AboutMenu
, L
"For more information, see the rEFInd Web site:");
185 AddMenuInfoLine(&AboutMenu
, L
"http://www.rodsbooks.com/refind/");
186 AddMenuEntry(&AboutMenu
, &MenuEntryReturn
);
189 RunMenu(&AboutMenu
, NULL
);
190 } /* VOID AboutrEFInd() */
192 static VOID
WarnSecureBootError(CHAR16
*Name
, BOOLEAN Verbose
) {
194 Name
= L
"the loader";
196 refit_call2_wrapper(ST
->ConOut
->SetAttribute
, ST
->ConOut
, ATTR_ERROR
);
197 Print(L
"Secure Boot validation failure loading %s!\n", Name
);
198 refit_call2_wrapper(ST
->ConOut
->SetAttribute
, ST
->ConOut
, ATTR_BASIC
);
199 if (Verbose
&& secure_mode()) {
200 Print(L
"\nThis computer is configured with Secure Boot active, but\n%s has failed validation.\n", Name
);
201 Print(L
"\nYou can:\n * Launch another boot loader\n");
202 Print(L
" * Disable Secure Boot in your firmware\n");
203 Print(L
" * Sign %s with a machine owner key (MOK)\n", Name
);
204 Print(L
" * Use a MOK utility (often present on the second row) to add a MOK with which\n");
205 Print(L
" %s has already been signed.\n", Name
);
206 Print(L
" * Use a MOK utility to register %s (\"enroll its hash\") without\n", Name
);
207 Print(L
" signing it.\n");
208 Print(L
"\nSee http://www.rodsbooks.com/refind/secureboot.html for more information\n");
211 } // VOID WarnSecureBootError()
213 // Launch an EFI binary.
214 static EFI_STATUS
StartEFIImageList(IN EFI_DEVICE_PATH
**DevicePaths
,
215 IN CHAR16
*LoadOptions
, IN CHAR16
*LoadOptionsPrefix
,
216 IN CHAR16
*ImageTitle
, IN CHAR8 OSType
,
217 OUT UINTN
*ErrorInStep
,
220 EFI_STATUS Status
, ReturnStatus
;
221 EFI_HANDLE ChildImageHandle
;
222 EFI_LOADED_IMAGE
*ChildLoadedImage
= NULL
;
223 UINTN DevicePathIndex
;
224 CHAR16 ErrorInfo
[256];
225 CHAR16
*FullLoadOptions
= NULL
;
227 if (ErrorInStep
!= NULL
)
231 if (LoadOptions
!= NULL
) {
232 if (LoadOptionsPrefix
!= NULL
) {
233 MergeStrings(&FullLoadOptions
, LoadOptions
, L
' ');
235 MergeStrings(&FullLoadOptions
, L
" ", 0);
236 // NOTE: That last space is also added by the EFI shell and seems to be significant
237 // when passing options to Apple's boot.efi...
240 MergeStrings(&FullLoadOptions
, LoadOptions
, 0);
242 } else { // LoadOptions == NULL
243 // NOTE: We provide a non-null string when no options are specified for safety;
244 // some systems (at least DUET) can hang when launching some programs (such as
245 // an EFI shell) without this.
246 FullLoadOptions
= StrDuplicate(L
" ");
249 Print(L
"Starting %s\nUsing load options '%s'\n", ImageTitle
, FullLoadOptions
);
251 // load the image into memory (and execute it, in the case of a shim/MOK image).
252 ReturnStatus
= Status
= EFI_NOT_FOUND
; // in case the list is empty
253 for (DevicePathIndex
= 0; DevicePaths
[DevicePathIndex
] != NULL
; DevicePathIndex
++) {
254 // NOTE: Below commented-out line could be more efficient if file were read ahead of
255 // time and passed as a pre-loaded image to LoadImage(), but it doesn't work on my
256 // 32-bit Mac Mini or my 64-bit Intel box when launching a Linux kernel; the
257 // kernel returns a "Failed to handle fs_proto" error message.
258 // TODO: Track down the cause of this error and fix it, if possible.
259 // ReturnStatus = Status = refit_call6_wrapper(BS->LoadImage, FALSE, SelfImageHandle, DevicePaths[DevicePathIndex],
260 // ImageData, ImageSize, &ChildImageHandle);
261 ReturnStatus
= Status
= refit_call6_wrapper(BS
->LoadImage
, FALSE
, SelfImageHandle
, DevicePaths
[DevicePathIndex
],
262 NULL
, 0, &ChildImageHandle
);
263 if (ReturnStatus
!= EFI_NOT_FOUND
) {
267 if ((Status
== EFI_ACCESS_DENIED
) || (Status
== EFI_SECURITY_VIOLATION
)) {
268 WarnSecureBootError(ImageTitle
, Verbose
);
271 SPrint(ErrorInfo
, 255, L
"while loading %s", ImageTitle
);
272 if (CheckError(Status
, ErrorInfo
)) {
273 if (ErrorInStep
!= NULL
)
278 ReturnStatus
= Status
= refit_call3_wrapper(BS
->HandleProtocol
, ChildImageHandle
, &LoadedImageProtocol
,
279 (VOID
**) &ChildLoadedImage
);
280 if (CheckError(Status
, L
"while getting a LoadedImageProtocol handle")) {
281 if (ErrorInStep
!= NULL
)
285 ChildLoadedImage
->LoadOptions
= (VOID
*)FullLoadOptions
;
286 ChildLoadedImage
->LoadOptionsSize
= ((UINT32
)StrLen(FullLoadOptions
) + 1) * sizeof(CHAR16
);
287 // turn control over to the image
288 // TODO: (optionally) re-enable the EFI watchdog timer!
290 // close open file handles
292 ReturnStatus
= Status
= refit_call3_wrapper(BS
->StartImage
, ChildImageHandle
, NULL
, NULL
);
294 // control returns here when the child image calls Exit()
295 SPrint(ErrorInfo
, 255, L
"returned from %s", ImageTitle
);
296 if (CheckError(Status
, ErrorInfo
)) {
297 if (ErrorInStep
!= NULL
)
301 // re-open file handles
305 // unload the image, we don't care if it works or not...
306 Status
= refit_call1_wrapper(BS
->UnloadImage
, ChildImageHandle
);
309 MyFreePool(FullLoadOptions
);
311 } /* static EFI_STATUS StartEFIImageList() */
313 static EFI_STATUS
StartEFIImage(IN EFI_DEVICE_PATH
*DevicePath
,
314 IN CHAR16
*LoadOptions
, IN CHAR16
*LoadOptionsPrefix
,
315 IN CHAR16
*ImageTitle
, IN CHAR8 OSType
,
316 OUT UINTN
*ErrorInStep
,
319 EFI_DEVICE_PATH
*DevicePaths
[2];
321 DevicePaths
[0] = DevicePath
;
322 DevicePaths
[1] = NULL
;
323 return StartEFIImageList(DevicePaths
, LoadOptions
, LoadOptionsPrefix
, ImageTitle
, OSType
, ErrorInStep
, Verbose
);
324 } /* static EFI_STATUS StartEFIImage() */
326 // From gummiboot: Retrieve a raw EFI variable.
327 // Returns EFI status
328 static EFI_STATUS
EfivarGetRaw(EFI_GUID
*vendor
, CHAR16
*name
, CHAR8
**buffer
, UINTN
*size
) {
333 l
= sizeof(CHAR16
*) * EFI_MAXIMUM_VARIABLE_SIZE
;
334 buf
= AllocatePool(l
);
336 return EFI_OUT_OF_RESOURCES
;
338 err
= refit_call5_wrapper(RT
->GetVariable
, name
, vendor
, NULL
, &l
, buf
);
339 if (EFI_ERROR(err
) == EFI_SUCCESS
) {
346 } // EFI_STATUS EfivarGetRaw()
348 // From gummiboot: Set an EFI variable
349 static EFI_STATUS
EfivarSetRaw(EFI_GUID
*vendor
, CHAR16
*name
, CHAR8
*buf
, UINTN size
, BOOLEAN persistent
) {
352 flags
= EFI_VARIABLE_BOOTSERVICE_ACCESS
|EFI_VARIABLE_RUNTIME_ACCESS
;
354 flags
|= EFI_VARIABLE_NON_VOLATILE
;
356 return refit_call5_wrapper(RT
->SetVariable
, name
, vendor
, flags
, size
, buf
);
357 } // EFI_STATUS EfivarSetRaw()
359 // From gummiboot: Reboot the computer into its built-in user interface
360 static EFI_STATUS
RebootIntoFirmware(VOID
) {
366 osind
= EFI_OS_INDICATIONS_BOOT_TO_FW_UI
;
368 err
= EfivarGetRaw(&GlobalGuid
, L
"OsIndications", &b
, &size
);
369 if (err
== EFI_SUCCESS
)
373 err
= EfivarSetRaw(&GlobalGuid
, L
"OsIndications", (CHAR8
*)&osind
, sizeof(UINT64
), TRUE
);
374 if (err
!= EFI_SUCCESS
)
377 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);
378 Print(L
"Error calling ResetSystem: %r", err
);
380 // uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
386 // EFI OS loader functions
389 static VOID
StartLoader(LOADER_ENTRY
*Entry
)
391 UINTN ErrorInStep
= 0;
393 BeginExternalScreen(Entry
->UseGraphicsMode
, L
"Booting OS");
394 StartEFIImage(Entry
->DevicePath
, Entry
->LoadOptions
, Basename(Entry
->LoaderPath
),
395 Basename(Entry
->LoaderPath
), Entry
->OSType
, &ErrorInStep
, !Entry
->UseGraphicsMode
);
396 FinishExternalScreen();
399 // Locate an initrd or initramfs file that matches the kernel specified by LoaderPath.
400 // The matching file has a name that begins with "init" and includes the same version
401 // number string as is found in LoaderPath -- but not a longer version number string.
402 // For instance, if LoaderPath is \EFI\kernels\bzImage-3.3.0.efi, and if \EFI\kernels
403 // has a file called initramfs-3.3.0.img, this function will return the string
404 // '\EFI\kernels\initramfs-3.3.0.img'. If the directory ALSO contains the file
405 // initramfs-3.3.0-rc7.img or initramfs-13.3.0.img, those files will NOT match;
406 // however, initmine-3.3.0.img might match. (FindInitrd() returns the first match it
407 // finds). Thus, care should be taken to avoid placing duplicate matching files in
408 // the kernel's directory.
409 // If no matching init file can be found, returns NULL.
410 static CHAR16
* FindInitrd(IN CHAR16
*LoaderPath
, IN REFIT_VOLUME
*Volume
) {
411 CHAR16
*InitrdName
= NULL
, *FileName
, *KernelVersion
, *InitrdVersion
, *Path
;
412 REFIT_DIR_ITER DirIter
;
413 EFI_FILE_INFO
*DirEntry
;
415 FileName
= Basename(LoaderPath
);
416 KernelVersion
= FindNumbers(FileName
);
417 Path
= FindPath(LoaderPath
);
419 // Add trailing backslash for root directory; necessary on some systems, but must
420 // NOT be added to all directories, since on other systems, a trailing backslash on
421 // anything but the root directory causes them to flake out!
422 if (StrLen(Path
) == 0) {
423 MergeStrings(&Path
, L
"\\", 0);
425 DirIterOpen(Volume
->RootDir
, Path
, &DirIter
);
426 // Now add a trailing backslash if it was NOT added earlier, for consistency in
427 // building the InitrdName later....
428 if ((StrLen(Path
) > 0) && (Path
[StrLen(Path
) - 1] != L
'\\'))
429 MergeStrings(&Path
, L
"\\", 0);
430 while ((DirIterNext(&DirIter
, 2, L
"init*", &DirEntry
)) && (InitrdName
== NULL
)) {
431 InitrdVersion
= FindNumbers(DirEntry
->FileName
);
432 if (KernelVersion
!= NULL
) {
433 if (StriCmp(InitrdVersion
, KernelVersion
) == 0) {
434 MergeStrings(&InitrdName
, Path
, 0);
435 MergeStrings(&InitrdName
, DirEntry
->FileName
, 0);
438 if (InitrdVersion
== NULL
) {
439 MergeStrings(&InitrdName
, Path
, 0);
440 MergeStrings(&InitrdName
, DirEntry
->FileName
, 0);
443 MyFreePool(InitrdVersion
);
445 DirIterClose(&DirIter
);
447 // Note: Don't FreePool(FileName), since Basename returns a pointer WITHIN the string it's passed.
448 MyFreePool(KernelVersion
);
451 } // static CHAR16 * FindInitrd()
453 LOADER_ENTRY
* AddPreparedLoaderEntry(LOADER_ENTRY
*Entry
) {
454 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
457 } // LOADER_ENTRY * AddPreparedLoaderEntry()
459 // Creates a copy of a menu screen.
460 // Returns a pointer to the copy of the menu screen.
461 static REFIT_MENU_SCREEN
* CopyMenuScreen(REFIT_MENU_SCREEN
*Entry
) {
462 REFIT_MENU_SCREEN
*NewEntry
;
465 NewEntry
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
466 if ((Entry
!= NULL
) && (NewEntry
!= NULL
)) {
467 CopyMem(NewEntry
, Entry
, sizeof(REFIT_MENU_SCREEN
));
468 NewEntry
->Title
= (Entry
->Title
) ? StrDuplicate(Entry
->Title
) : NULL
;
469 NewEntry
->TimeoutText
= (Entry
->TimeoutText
) ? StrDuplicate(Entry
->TimeoutText
) : NULL
;
470 if (Entry
->TitleImage
!= NULL
) {
471 NewEntry
->TitleImage
= AllocatePool(sizeof(EG_IMAGE
));
472 if (NewEntry
->TitleImage
!= NULL
)
473 CopyMem(NewEntry
->TitleImage
, Entry
->TitleImage
, sizeof(EG_IMAGE
));
475 NewEntry
->InfoLines
= (CHAR16
**) AllocateZeroPool(Entry
->InfoLineCount
* (sizeof(CHAR16
*)));
476 for (i
= 0; i
< Entry
->InfoLineCount
&& NewEntry
->InfoLines
; i
++) {
477 NewEntry
->InfoLines
[i
] = (Entry
->InfoLines
[i
]) ? StrDuplicate(Entry
->InfoLines
[i
]) : NULL
;
479 NewEntry
->Entries
= (REFIT_MENU_ENTRY
**) AllocateZeroPool(Entry
->EntryCount
* (sizeof (REFIT_MENU_ENTRY
*)));
480 for (i
= 0; i
< Entry
->EntryCount
&& NewEntry
->Entries
; i
++) {
481 AddMenuEntry(NewEntry
, Entry
->Entries
[i
]);
483 NewEntry
->Hint1
= (Entry
->Hint1
) ? StrDuplicate(Entry
->Hint1
) : NULL
;
484 NewEntry
->Hint2
= (Entry
->Hint2
) ? StrDuplicate(Entry
->Hint2
) : NULL
;
487 } // static REFIT_MENU_SCREEN* CopyMenuScreen()
489 // Creates a copy of a menu entry. Intended to enable moving a stack-based
490 // menu entry (such as the ones for the "reboot" and "exit" functions) to
491 // to the heap. This enables easier deletion of the whole set of menu
492 // entries when re-scanning.
493 // Returns a pointer to the copy of the menu entry.
494 static REFIT_MENU_ENTRY
* CopyMenuEntry(REFIT_MENU_ENTRY
*Entry
) {
495 REFIT_MENU_ENTRY
*NewEntry
;
497 NewEntry
= AllocateZeroPool(sizeof(REFIT_MENU_ENTRY
));
498 if ((Entry
!= NULL
) && (NewEntry
!= NULL
)) {
499 CopyMem(NewEntry
, Entry
, sizeof(REFIT_MENU_ENTRY
));
500 NewEntry
->Title
= (Entry
->Title
) ? StrDuplicate(Entry
->Title
) : NULL
;
501 if (Entry
->BadgeImage
!= NULL
) {
502 NewEntry
->BadgeImage
= AllocatePool(sizeof(EG_IMAGE
));
503 if (NewEntry
->BadgeImage
!= NULL
)
504 CopyMem(NewEntry
->BadgeImage
, Entry
->BadgeImage
, sizeof(EG_IMAGE
));
506 if (Entry
->Image
!= NULL
) {
507 NewEntry
->Image
= AllocatePool(sizeof(EG_IMAGE
));
508 if (NewEntry
->Image
!= NULL
)
509 CopyMem(NewEntry
->Image
, Entry
->Image
, sizeof(EG_IMAGE
));
511 if (Entry
->SubScreen
!= NULL
) {
512 NewEntry
->SubScreen
= CopyMenuScreen(Entry
->SubScreen
);
516 } // REFIT_MENU_ENTRY* CopyMenuEntry()
518 // Creates a new LOADER_ENTRY data structure and populates it with
519 // default values from the specified Entry, or NULL values if Entry
520 // is unspecified (NULL).
521 // Returns a pointer to the new data structure, or NULL if it
522 // couldn't be allocated
523 LOADER_ENTRY
*InitializeLoaderEntry(IN LOADER_ENTRY
*Entry
) {
524 LOADER_ENTRY
*NewEntry
= NULL
;
526 NewEntry
= AllocateZeroPool(sizeof(LOADER_ENTRY
));
527 if (NewEntry
!= NULL
) {
528 NewEntry
->me
.Title
= NULL
;
529 NewEntry
->me
.Tag
= TAG_LOADER
;
530 NewEntry
->Enabled
= TRUE
;
531 NewEntry
->UseGraphicsMode
= FALSE
;
532 NewEntry
->OSType
= 0;
534 NewEntry
->LoaderPath
= (Entry
->LoaderPath
) ? StrDuplicate(Entry
->LoaderPath
) : NULL
;
535 NewEntry
->VolName
= (Entry
->VolName
) ? StrDuplicate(Entry
->VolName
) : NULL
;
536 NewEntry
->DevicePath
= Entry
->DevicePath
;
537 NewEntry
->UseGraphicsMode
= Entry
->UseGraphicsMode
;
538 NewEntry
->LoadOptions
= (Entry
->LoadOptions
) ? StrDuplicate(Entry
->LoadOptions
) : NULL
;
539 NewEntry
->InitrdPath
= (Entry
->InitrdPath
) ? StrDuplicate(Entry
->InitrdPath
) : NULL
;
543 } // LOADER_ENTRY *InitializeLoaderEntry()
545 // Adds InitrdPath to Options, but only if Options doesn't already include an
546 // initrd= line. Done to enable overriding the default initrd selection in a
547 // refind_linux.conf file's options list.
548 // Returns a pointer to a new string. The calling function is responsible for
549 // freeing its memory.
550 static CHAR16
*AddInitrdToOptions(CHAR16
*Options
, CHAR16
*InitrdPath
) {
551 CHAR16
*NewOptions
= NULL
;
554 NewOptions
= StrDuplicate(Options
);
555 if ((InitrdPath
!= NULL
) && !StriSubCmp(L
"initrd=", Options
)) {
556 MergeStrings(&NewOptions
, L
"initrd=", L
' ');
557 MergeStrings(&NewOptions
, InitrdPath
, 0);
560 } // CHAR16 *AddInitrdToOptions()
562 // Prepare a REFIT_MENU_SCREEN data structure for a subscreen entry. This sets up
563 // the default entry that launches the boot loader using the same options as the
564 // main Entry does. Subsequent options can be added by the calling function.
565 // If a subscreen already exists in the Entry that's passed to this function,
566 // it's left unchanged and a pointer to it is returned.
567 // Returns a pointer to the new subscreen data structure, or NULL if there
568 // were problems allocating memory.
569 REFIT_MENU_SCREEN
*InitializeSubScreen(IN LOADER_ENTRY
*Entry
) {
570 CHAR16
*FileName
, *MainOptions
= NULL
;
571 REFIT_MENU_SCREEN
*SubScreen
= NULL
;
572 LOADER_ENTRY
*SubEntry
;
574 FileName
= Basename(Entry
->LoaderPath
);
575 if (Entry
->me
.SubScreen
== NULL
) { // No subscreen yet; initialize default entry....
576 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
577 if (SubScreen
!= NULL
) {
578 SubScreen
->Title
= AllocateZeroPool(sizeof(CHAR16
) * 256);
579 SPrint(SubScreen
->Title
, 255, L
"Boot Options for %s on %s",
580 (Entry
->Title
!= NULL
) ? Entry
->Title
: FileName
, Entry
->VolName
);
581 SubScreen
->TitleImage
= Entry
->me
.Image
;
583 SubEntry
= InitializeLoaderEntry(Entry
);
584 if (SubEntry
!= NULL
) {
585 SubEntry
->me
.Title
= StrDuplicate(L
"Boot using default options");
586 MainOptions
= SubEntry
->LoadOptions
;
587 SubEntry
->LoadOptions
= AddInitrdToOptions(MainOptions
, SubEntry
->InitrdPath
);
588 MyFreePool(MainOptions
);
589 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
590 } // if (SubEntry != NULL)
591 SubScreen
->Hint1
= StrDuplicate(SUBSCREEN_HINT1
);
592 if (GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_EDITOR
) {
593 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2_NO_EDITOR
);
595 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2
);
597 } // if (SubScreen != NULL)
598 } else { // existing subscreen; less initialization, and just add new entry later....
599 SubScreen
= Entry
->me
.SubScreen
;
602 } // REFIT_MENU_SCREEN *InitializeSubScreen()
604 VOID
GenerateSubScreen(LOADER_ENTRY
*Entry
, IN REFIT_VOLUME
*Volume
) {
605 REFIT_MENU_SCREEN
*SubScreen
;
606 LOADER_ENTRY
*SubEntry
;
608 CHAR16 DiagsFileName
[256];
613 // create the submenu
614 if (StrLen(Entry
->Title
) == 0) {
615 MyFreePool(Entry
->Title
);
618 SubScreen
= InitializeSubScreen(Entry
);
620 // loader-specific submenu entries
621 if (Entry
->OSType
== 'M') { // entries for Mac OS X
623 SubEntry
= InitializeLoaderEntry(Entry
);
624 if (SubEntry
!= NULL
) {
625 SubEntry
->me
.Title
= L
"Boot Mac OS X with a 64-bit kernel";
626 SubEntry
->LoadOptions
= L
"arch=x86_64";
627 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
628 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
631 SubEntry
= InitializeLoaderEntry(Entry
);
632 if (SubEntry
!= NULL
) {
633 SubEntry
->me
.Title
= L
"Boot Mac OS X with a 32-bit kernel";
634 SubEntry
->LoadOptions
= L
"arch=i386";
635 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
636 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
640 if (!(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_SINGLEUSER
)) {
641 SubEntry
= InitializeLoaderEntry(Entry
);
642 if (SubEntry
!= NULL
) {
643 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode";
644 SubEntry
->UseGraphicsMode
= FALSE
;
645 SubEntry
->LoadOptions
= L
"-v";
646 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
650 SubEntry
= InitializeLoaderEntry(Entry
);
651 if (SubEntry
!= NULL
) {
652 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode (64-bit)";
653 SubEntry
->UseGraphicsMode
= FALSE
;
654 SubEntry
->LoadOptions
= L
"-v arch=x86_64";
655 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
658 SubEntry
= InitializeLoaderEntry(Entry
);
659 if (SubEntry
!= NULL
) {
660 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode (32-bit)";
661 SubEntry
->UseGraphicsMode
= FALSE
;
662 SubEntry
->LoadOptions
= L
"-v arch=i386";
663 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
667 SubEntry
= InitializeLoaderEntry(Entry
);
668 if (SubEntry
!= NULL
) {
669 SubEntry
->me
.Title
= L
"Boot Mac OS X in single user mode";
670 SubEntry
->UseGraphicsMode
= FALSE
;
671 SubEntry
->LoadOptions
= L
"-v -s";
672 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
674 } // single-user mode allowed
676 if (!(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_SAFEMODE
)) {
677 SubEntry
= InitializeLoaderEntry(Entry
);
678 if (SubEntry
!= NULL
) {
679 SubEntry
->me
.Title
= L
"Boot Mac OS X in safe mode";
680 SubEntry
->UseGraphicsMode
= FALSE
;
681 SubEntry
->LoadOptions
= L
"-v -x";
682 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
684 } // safe mode allowed
686 // check for Apple hardware diagnostics
687 StrCpy(DiagsFileName
, L
"System\\Library\\CoreServices\\.diagnostics\\diags.efi");
688 if (FileExists(Volume
->RootDir
, DiagsFileName
) && !(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_HWTEST
)) {
689 SubEntry
= InitializeLoaderEntry(Entry
);
690 if (SubEntry
!= NULL
) {
691 SubEntry
->me
.Title
= L
"Run Apple Hardware Test";
692 MyFreePool(SubEntry
->LoaderPath
);
693 SubEntry
->LoaderPath
= StrDuplicate(DiagsFileName
);
694 SubEntry
->DevicePath
= FileDevicePath(Volume
->DeviceHandle
, SubEntry
->LoaderPath
);
695 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
696 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
698 } // if diagnostics entry found
700 } else if (Entry
->OSType
== 'L') { // entries for Linux kernels with EFI stub loaders
701 File
= ReadLinuxOptionsFile(Entry
->LoaderPath
, Volume
);
703 InitrdName
= FindInitrd(Entry
->LoaderPath
, Volume
);
704 TokenCount
= ReadTokenLine(File
, &TokenList
);
705 // first entry requires special processing, since it was initially set
706 // up with a default title but correct options by InitializeSubScreen(),
708 if ((SubScreen
->Entries
!= NULL
) && (SubScreen
->Entries
[0] != NULL
)) {
709 MyFreePool(SubScreen
->Entries
[0]->Title
);
710 SubScreen
->Entries
[0]->Title
= TokenList
[0] ? StrDuplicate(TokenList
[0]) : StrDuplicate(L
"Boot Linux");
712 FreeTokenLine(&TokenList
, &TokenCount
);
713 while ((TokenCount
= ReadTokenLine(File
, &TokenList
)) > 1) {
714 SubEntry
= InitializeLoaderEntry(Entry
);
715 SubEntry
->me
.Title
= TokenList
[0] ? StrDuplicate(TokenList
[0]) : StrDuplicate(L
"Boot Linux");
716 MyFreePool(SubEntry
->LoadOptions
);
717 SubEntry
->LoadOptions
= AddInitrdToOptions(TokenList
[1], InitrdName
);
718 FreeTokenLine(&TokenList
, &TokenCount
);
719 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_LINUX
;
720 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
722 MyFreePool(InitrdName
);
726 } else if (Entry
->OSType
== 'E') { // entries for ELILO
727 SubEntry
= InitializeLoaderEntry(Entry
);
728 if (SubEntry
!= NULL
) {
729 SubEntry
->me
.Title
= L
"Run ELILO in interactive mode";
730 SubEntry
->LoadOptions
= L
"-p";
731 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
732 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
735 SubEntry
= InitializeLoaderEntry(Entry
);
736 if (SubEntry
!= NULL
) {
737 SubEntry
->me
.Title
= L
"Boot Linux for a 17\" iMac or a 15\" MacBook Pro (*)";
738 SubEntry
->UseGraphicsMode
= TRUE
;
739 SubEntry
->LoadOptions
= L
"-d 0 i17";
740 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
741 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
744 SubEntry
= InitializeLoaderEntry(Entry
);
745 if (SubEntry
!= NULL
) {
746 SubEntry
->me
.Title
= L
"Boot Linux for a 20\" iMac (*)";
747 SubEntry
->UseGraphicsMode
= TRUE
;
748 SubEntry
->LoadOptions
= L
"-d 0 i20";
749 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
750 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
753 SubEntry
= InitializeLoaderEntry(Entry
);
754 if (SubEntry
!= NULL
) {
755 SubEntry
->me
.Title
= L
"Boot Linux for a Mac Mini (*)";
756 SubEntry
->UseGraphicsMode
= TRUE
;
757 SubEntry
->LoadOptions
= L
"-d 0 mini";
758 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
759 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
762 AddMenuInfoLine(SubScreen
, L
"NOTE: This is an example. Entries");
763 AddMenuInfoLine(SubScreen
, L
"marked with (*) may not work.");
765 } else if (Entry
->OSType
== 'X') { // entries for xom.efi
766 // by default, skip the built-in selection and boot from hard disk only
767 Entry
->LoadOptions
= L
"-s -h";
769 SubEntry
= InitializeLoaderEntry(Entry
);
770 if (SubEntry
!= NULL
) {
771 SubEntry
->me
.Title
= L
"Boot Windows from Hard Disk";
772 SubEntry
->LoadOptions
= L
"-s -h";
773 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
774 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
777 SubEntry
= InitializeLoaderEntry(Entry
);
778 if (SubEntry
!= NULL
) {
779 SubEntry
->me
.Title
= L
"Boot Windows from CD-ROM";
780 SubEntry
->LoadOptions
= L
"-s -c";
781 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
782 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
785 SubEntry
= InitializeLoaderEntry(Entry
);
786 if (SubEntry
!= NULL
) {
787 SubEntry
->me
.Title
= L
"Run XOM in text mode";
788 SubEntry
->UseGraphicsMode
= FALSE
;
789 SubEntry
->LoadOptions
= L
"-v";
790 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
791 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
793 } // entries for xom.efi
794 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
795 Entry
->me
.SubScreen
= SubScreen
;
796 } // VOID GenerateSubScreen()
798 // Returns options for a Linux kernel. Reads them from an options file in the
799 // kernel's directory; and if present, adds an initrd= option for an initial
800 // RAM disk file with the same version number as the kernel file.
801 static CHAR16
* GetMainLinuxOptions(IN CHAR16
* LoaderPath
, IN REFIT_VOLUME
*Volume
) {
802 CHAR16
*Options
= NULL
, *InitrdName
, *FullOptions
= NULL
;
804 Options
= GetFirstOptionsFromFile(LoaderPath
, Volume
);
805 InitrdName
= FindInitrd(LoaderPath
, Volume
);
806 FullOptions
= AddInitrdToOptions(Options
, InitrdName
);
809 MyFreePool(InitrdName
);
810 return (FullOptions
);
811 } // static CHAR16 * GetMainLinuxOptions()
813 // Try to guess the name of the Linux distribution & add that name to
815 static VOID
GuessLinuxDistribution(CHAR16
**OSIconName
, REFIT_VOLUME
*Volume
, CHAR16
*LoaderPath
) {
819 UINTN TokenCount
= 0;
821 // If on Linux root fs, /etc/os-release file probably has clues....
822 if (FileExists(Volume
->RootDir
, L
"etc\\os-release") &&
823 (ReadFile(Volume
->RootDir
, L
"etc\\os-release", &File
, &FileSize
) == EFI_SUCCESS
)) {
825 TokenCount
= ReadTokenLine(&File
, &TokenList
);
826 if ((TokenCount
> 1) && ((StriCmp(TokenList
[0], L
"ID") == 0) || (StriCmp(TokenList
[0], L
"NAME") == 0))) {
827 MergeStrings(OSIconName
, TokenList
[1], L
',');
829 FreeTokenLine(&TokenList
, &TokenCount
);
830 } while (TokenCount
> 0);
831 MyFreePool(File
.Buffer
);
834 // Search for clues in the kernel's filename....
835 if (StriSubCmp(L
".fc", LoaderPath
))
836 MergeStrings(OSIconName
, L
"fedora", L
',');
837 if (StriSubCmp(L
".el", LoaderPath
))
838 MergeStrings(OSIconName
, L
"redhat", L
',');
839 } // VOID GuessLinuxDistribution()
841 // Sets a few defaults for a loader entry -- mainly the icon, but also the OS type
842 // code and shortcut letter. For Linux EFI stub loaders, also sets kernel options
843 // that will (with luck) work fairly automatically.
844 VOID
SetLoaderDefaults(LOADER_ENTRY
*Entry
, CHAR16
*LoaderPath
, REFIT_VOLUME
*Volume
) {
845 CHAR16
*FileName
, *PathOnly
, *NoExtension
, *OSIconName
= NULL
, *Temp
, *SubString
;
846 CHAR16 ShortcutLetter
= 0;
849 FileName
= Basename(LoaderPath
);
850 PathOnly
= FindPath(LoaderPath
);
851 NoExtension
= StripEfiExtension(FileName
);
853 // locate a custom icon for the loader
854 // Anything found here takes precedence over the "hints" in the OSIconName variable
855 if (!Entry
->me
.Image
)
856 Entry
->me
.Image
= egLoadIconAnyType(Volume
->RootDir
, PathOnly
, NoExtension
, 128);
857 if (!Entry
->me
.Image
)
858 Entry
->me
.Image
= egCopyImage(Volume
->VolIconImage
);
860 // Begin creating icon "hints" by using last part of directory path leading
862 Temp
= FindLastDirName(LoaderPath
);
863 MergeStrings(&OSIconName
, Temp
, L
',');
866 if (OSIconName
!= NULL
) {
867 ShortcutLetter
= OSIconName
[0];
870 // Add every "word" in the volume label, delimited by spaces, dashes (-), or
871 // underscores (_), to the list of hints to be used in searching for OS
873 if ((Volume
->VolName
) && (StrLen(Volume
->VolName
) > 0)) {
874 Temp
= SubString
= StrDuplicate(Volume
->VolName
);
876 Length
= StrLen(Temp
);
877 for (i
= 0; i
< Length
; i
++) {
878 if ((Temp
[i
] == L
' ') || (Temp
[i
] == L
'_') || (Temp
[i
] == L
'-')) {
880 if (StrLen(SubString
) > 0)
881 MergeStrings(&OSIconName
, SubString
, L
',');
882 SubString
= Temp
+ i
+ 1;
885 MergeStrings(&OSIconName
, SubString
, L
',');
890 // detect specific loaders
891 if (StriSubCmp(L
"bzImage", LoaderPath
) || StriSubCmp(L
"vmlinuz", LoaderPath
)) {
892 GuessLinuxDistribution(&OSIconName
, Volume
, LoaderPath
);
893 MergeStrings(&OSIconName
, L
"linux", L
',');
895 if (ShortcutLetter
== 0)
896 ShortcutLetter
= 'L';
897 Entry
->LoadOptions
= GetMainLinuxOptions(LoaderPath
, Volume
);
898 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_LINUX
;
899 } else if (StriSubCmp(L
"refit", LoaderPath
)) {
900 MergeStrings(&OSIconName
, L
"refit", L
',');
902 ShortcutLetter
= 'R';
903 } else if (StriSubCmp(L
"refind", LoaderPath
)) {
904 MergeStrings(&OSIconName
, L
"refind", L
',');
906 ShortcutLetter
= 'R';
907 } else if (StriCmp(LoaderPath
, MACOSX_LOADER_PATH
) == 0) {
908 if (Volume
->VolIconImage
!= NULL
) { // custom icon file found
909 Entry
->me
.Image
= Volume
->VolIconImage
;
911 MergeStrings(&OSIconName
, L
"mac", L
',');
913 ShortcutLetter
= 'M';
914 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
915 } else if (StriCmp(FileName
, L
"diags.efi") == 0) {
916 MergeStrings(&OSIconName
, L
"hwtest", L
',');
917 } else if (StriCmp(FileName
, L
"e.efi") == 0 || StriCmp(FileName
, L
"elilo.efi") == 0 || StriSubCmp(L
"elilo", FileName
)) {
918 MergeStrings(&OSIconName
, L
"elilo,linux", L
',');
920 if (ShortcutLetter
== 0)
921 ShortcutLetter
= 'L';
922 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
923 } else if (StriSubCmp(L
"grub", FileName
)) {
925 ShortcutLetter
= 'G';
926 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_GRUB
;
927 } else if (StriCmp(FileName
, L
"cdboot.efi") == 0 ||
928 StriCmp(FileName
, L
"bootmgr.efi") == 0 ||
929 StriCmp(FileName
, L
"bootmgfw.efi") == 0) {
930 MergeStrings(&OSIconName
, L
"win", L
',');
932 ShortcutLetter
= 'W';
933 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
934 } else if (StriCmp(FileName
, L
"xom.efi") == 0) {
935 MergeStrings(&OSIconName
, L
"xom,win", L
',');
936 Entry
->UseGraphicsMode
= TRUE
;
938 ShortcutLetter
= 'W';
939 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
942 if ((ShortcutLetter
>= 'a') && (ShortcutLetter
<= 'z'))
943 ShortcutLetter
= ShortcutLetter
- 'a' + 'A'; // convert lowercase to uppercase
944 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
945 if (Entry
->me
.Image
== NULL
)
946 Entry
->me
.Image
= LoadOSIcon(OSIconName
, L
"unknown", FALSE
);
947 MyFreePool(PathOnly
);
948 } // VOID SetLoaderDefaults()
950 // Add a specified EFI boot loader to the list, using automatic settings
951 // for icons, options, etc.
952 LOADER_ENTRY
* AddLoaderEntry(IN CHAR16
*LoaderPath
, IN CHAR16
*LoaderTitle
, IN REFIT_VOLUME
*Volume
) {
955 CleanUpPathNameSlashes(LoaderPath
);
956 Entry
= InitializeLoaderEntry(NULL
);
958 Entry
->Title
= StrDuplicate((LoaderTitle
!= NULL
) ? LoaderTitle
: LoaderPath
);
959 Entry
->me
.Title
= AllocateZeroPool(sizeof(CHAR16
) * 256);
960 SPrint(Entry
->me
.Title
, 255, L
"Boot %s from %s", (LoaderTitle
!= NULL
) ? LoaderTitle
: LoaderPath
, Volume
->VolName
);
962 Entry
->me
.BadgeImage
= Volume
->VolBadgeImage
;
963 if ((LoaderPath
!= NULL
) && (LoaderPath
[0] != L
'\\')) {
964 Entry
->LoaderPath
= StrDuplicate(L
"\\");
966 Entry
->LoaderPath
= NULL
;
968 MergeStrings(&(Entry
->LoaderPath
), LoaderPath
, 0);
969 Entry
->VolName
= Volume
->VolName
;
970 Entry
->DevicePath
= FileDevicePath(Volume
->DeviceHandle
, Entry
->LoaderPath
);
971 SetLoaderDefaults(Entry
, LoaderPath
, Volume
);
972 GenerateSubScreen(Entry
, Volume
);
973 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
977 } // LOADER_ENTRY * AddLoaderEntry()
979 // Returns -1 if (Time1 < Time2), +1 if (Time1 > Time2), or 0 if
980 // (Time1 == Time2). Precision is only to the nearest second; since
981 // this is used for sorting boot loader entries, differences smaller
982 // than this are likely to be meaningless (and unlikely!).
983 INTN
TimeComp(IN EFI_TIME
*Time1
, IN EFI_TIME
*Time2
) {
984 INT64 Time1InSeconds
, Time2InSeconds
;
986 // Following values are overestimates; I'm assuming 31 days in every month.
987 // This is fine for the purpose of this function, which is limited
988 Time1InSeconds
= Time1
->Second
+ (Time1
->Minute
* 60) + (Time1
->Hour
* 3600) + (Time1
->Day
* 86400) +
989 (Time1
->Month
* 2678400) + ((Time1
->Year
- 1998) * 32140800);
990 Time2InSeconds
= Time2
->Second
+ (Time2
->Minute
* 60) + (Time2
->Hour
* 3600) + (Time2
->Day
* 86400) +
991 (Time2
->Month
* 2678400) + ((Time2
->Year
- 1998) * 32140800);
992 if (Time1InSeconds
< Time2InSeconds
)
994 else if (Time1InSeconds
> Time2InSeconds
)
1000 // Adds a loader list element, keeping it sorted by date. Returns the new
1001 // first element (the one with the most recent date).
1002 static struct LOADER_LIST
* AddLoaderListEntry(struct LOADER_LIST
*LoaderList
, struct LOADER_LIST
*NewEntry
) {
1003 struct LOADER_LIST
*LatestEntry
, *CurrentEntry
, *PrevEntry
= NULL
;
1005 LatestEntry
= CurrentEntry
= LoaderList
;
1006 if (LoaderList
== NULL
) {
1007 LatestEntry
= NewEntry
;
1009 while ((CurrentEntry
!= NULL
) && (TimeComp(&(NewEntry
->TimeStamp
), &(CurrentEntry
->TimeStamp
)) < 0)) {
1010 PrevEntry
= CurrentEntry
;
1011 CurrentEntry
= CurrentEntry
->NextEntry
;
1013 NewEntry
->NextEntry
= CurrentEntry
;
1014 if (PrevEntry
== NULL
) {
1015 LatestEntry
= NewEntry
;
1017 PrevEntry
->NextEntry
= NewEntry
;
1020 return (LatestEntry
);
1021 } // static VOID AddLoaderListEntry()
1023 // Delete the LOADER_LIST linked list
1024 static VOID
CleanUpLoaderList(struct LOADER_LIST
*LoaderList
) {
1025 struct LOADER_LIST
*Temp
;
1027 while (LoaderList
!= NULL
) {
1029 LoaderList
= LoaderList
->NextEntry
;
1030 MyFreePool(Temp
->FileName
);
1033 } // static VOID CleanUpLoaderList()
1035 // Returns FALSE if the specified file/volume matches the GlobalConfig.DontScanDirs
1036 // or GlobalConfig.DontScanVolumes specification, or if Path points to a volume
1037 // other than the one specified by Volume, or if the specified path is SelfDir.
1038 // Returns TRUE if none of these conditions is met -- that is, if the path is
1039 // eligible for scanning.
1040 static BOOLEAN
ShouldScan(REFIT_VOLUME
*Volume
, CHAR16
*Path
) {
1041 CHAR16
*VolName
= NULL
, *DontScanDir
;
1042 UINTN i
= 0, VolNum
;
1043 BOOLEAN ScanIt
= TRUE
;
1045 if (IsIn(Volume
->VolName
, GlobalConfig
.DontScanVolumes
))
1048 if ((StriCmp(Path
, SelfDirPath
) == 0) && (Volume
->DeviceHandle
== SelfVolume
->DeviceHandle
))
1051 while ((DontScanDir
= FindCommaDelimited(GlobalConfig
.DontScanDirs
, i
++)) && ScanIt
) {
1052 SplitVolumeAndFilename(&DontScanDir
, &VolName
);
1053 CleanUpPathNameSlashes(DontScanDir
);
1054 if (VolName
!= NULL
) {
1055 if ((StriCmp(VolName
, Volume
->VolName
) == 0) && (StriCmp(DontScanDir
, Path
) == 0))
1057 if ((StrLen(VolName
) > 2) && (VolName
[0] == L
'f') && (VolName
[1] == L
's') && (VolName
[2] >= L
'0') && (VolName
[2] <= L
'9')) {
1058 VolNum
= Atoi(VolName
+ 2);
1059 if ((VolNum
== Volume
->VolNumber
) && (StriCmp(DontScanDir
, Path
) == 0))
1063 if (StriCmp(DontScanDir
, Path
) == 0)
1066 MyFreePool(DontScanDir
);
1070 } // BOOLEAN ShouldScan()
1072 // Returns TRUE if the file is byte-for-byte identical with the fallback file
1073 // on the volume AND if the file is not itself the fallback file; returns
1074 // FALSE if the file is not identical to the fallback file OR if the file
1075 // IS the fallback file. Intended for use in excluding the fallback boot
1076 // loader when it's a duplicate of another boot loader.
1077 static BOOLEAN
DuplicatesFallback(IN REFIT_VOLUME
*Volume
, IN CHAR16
*FileName
) {
1078 CHAR8
*FileContents
, *FallbackContents
;
1079 EFI_FILE_HANDLE FileHandle
, FallbackHandle
;
1080 EFI_FILE_INFO
*FileInfo
, *FallbackInfo
;
1081 UINTN FileSize
= 0, FallbackSize
= 0;
1083 BOOLEAN AreIdentical
= FALSE
;
1085 if (!FileExists(Volume
->RootDir
, FileName
) || !FileExists(Volume
->RootDir
, FALLBACK_FULLNAME
))
1088 CleanUpPathNameSlashes(FileName
);
1090 if (StriCmp(FileName
, FALLBACK_FULLNAME
) == 0)
1091 return FALSE
; // identical filenames, so not a duplicate....
1093 Status
= refit_call5_wrapper(Volume
->RootDir
->Open
, Volume
->RootDir
, &FileHandle
, FileName
, EFI_FILE_MODE_READ
, 0);
1094 if (Status
== EFI_SUCCESS
) {
1095 FileInfo
= LibFileInfo(FileHandle
);
1096 FileSize
= FileInfo
->FileSize
;
1101 Status
= refit_call5_wrapper(Volume
->RootDir
->Open
, Volume
->RootDir
, &FallbackHandle
, FALLBACK_FULLNAME
, EFI_FILE_MODE_READ
, 0);
1102 if (Status
== EFI_SUCCESS
) {
1103 FallbackInfo
= LibFileInfo(FallbackHandle
);
1104 FallbackSize
= FallbackInfo
->FileSize
;
1106 refit_call1_wrapper(FileHandle
->Close
, FileHandle
);
1110 if (FallbackSize
!= FileSize
) { // not same size, so can't be identical
1111 AreIdentical
= FALSE
;
1112 } else { // could be identical; do full check....
1113 FileContents
= AllocatePool(FileSize
);
1114 FallbackContents
= AllocatePool(FallbackSize
);
1115 if (FileContents
&& FallbackContents
) {
1116 Status
= refit_call3_wrapper(FileHandle
->Read
, FileHandle
, &FileSize
, FileContents
);
1117 if (Status
== EFI_SUCCESS
) {
1118 Status
= refit_call3_wrapper(FallbackHandle
->Read
, FallbackHandle
, &FallbackSize
, FallbackContents
);
1120 if (Status
== EFI_SUCCESS
) {
1121 AreIdentical
= (CompareMem(FileContents
, FallbackContents
, FileSize
) == 0);
1124 MyFreePool(FileContents
);
1125 MyFreePool(FallbackContents
);
1128 // BUG ALERT: Some systems (e.g., DUET, some Macs with large displays) crash if the
1129 // following two calls are reversed. Go figure....
1130 refit_call1_wrapper(FileHandle
->Close
, FallbackHandle
);
1131 refit_call1_wrapper(FileHandle
->Close
, FileHandle
);
1132 return AreIdentical
;
1133 } // BOOLEAN DuplicatesFallback()
1135 // Returns FALSE if two measures of file size are identical for a single file,
1136 // TRUE if not or if the file can't be opened and the other measure is non-0.
1137 // Despite the function's name, this isn't really a direct test of symbolic
1138 // link status, since EFI doesn't officially support symlinks. It does seem
1139 // to be a reliable indicator, though. (OTOH, some disk errors might cause a
1140 // file to fail to open, which would return a false positive -- but as I use
1141 // this function to exclude symbolic links from the list of boot loaders,
1142 // that would be fine, since such boot loaders wouldn't work.)
1143 static BOOLEAN
IsSymbolicLink(REFIT_VOLUME
*Volume
, CHAR16
*Path
, EFI_FILE_INFO
*DirEntry
) {
1144 EFI_FILE_HANDLE FileHandle
;
1145 EFI_FILE_INFO
*FileInfo
= NULL
;
1147 UINTN FileSize2
= 0;
1150 FileName
= StrDuplicate(Path
);
1151 MergeStrings(&FileName
, DirEntry
->FileName
, L
'\\');
1152 CleanUpPathNameSlashes(FileName
);
1154 Status
= refit_call5_wrapper(Volume
->RootDir
->Open
, Volume
->RootDir
, &FileHandle
, FileName
, EFI_FILE_MODE_READ
, 0);
1155 if (Status
== EFI_SUCCESS
) {
1156 FileInfo
= LibFileInfo(FileHandle
);
1157 if (FileInfo
!= NULL
)
1158 FileSize2
= FileInfo
->FileSize
;
1161 MyFreePool(FileName
);
1162 MyFreePool(FileInfo
);
1164 return (DirEntry
->FileSize
!= FileSize2
);
1165 } // BOOLEAN IsSymbolicLink()
1167 // Returns TRUE if this file is a valid EFI loader file, and is proper ARCH
1168 static BOOLEAN
IsValidLoader(REFIT_VOLUME
*Volume
, CHAR16
*FileName
) {
1169 BOOLEAN IsValid
= TRUE
;
1170 #if defined (EFIX64) | defined (EFI32)
1172 EFI_FILE_HANDLE FileHandle
;
1174 UINTN Size
= sizeof(Header
);
1176 Status
= refit_call5_wrapper(Volume
->RootDir
->Open
, Volume
->RootDir
, &FileHandle
, FileName
, EFI_FILE_MODE_READ
, 0);
1177 if (EFI_ERROR(Status
))
1180 Status
= refit_call3_wrapper(FileHandle
->Read
, FileHandle
, &Size
, Header
);
1181 refit_call1_wrapper(FileHandle
->Close
, FileHandle
);
1183 IsValid
= !EFI_ERROR(Status
) &&
1184 Size
== sizeof(Header
) &&
1185 ((Header
[0] == 'M' && Header
[1] == 'Z' &&
1186 (Size
= *(UINT32
*)&Header
[0x3c]) < 0x180 &&
1187 Header
[Size
] == 'P' && Header
[Size
+1] == 'E' &&
1188 Header
[Size
+2] == 0 && Header
[Size
+3] == 0 &&
1189 *(UINT16
*)&Header
[Size
+4] == EFI_STUB_ARCH
) ||
1190 (*(UINT32
*)&Header
== FAT_ARCH
));
1193 } // BOOLEAN IsValidLoader()
1195 // Scan an individual directory for EFI boot loader files and, if found,
1196 // add them to the list. Exception: Ignores FALLBACK_FULLNAME, which is picked
1197 // up in ScanEfiFiles(). Sorts the entries within the loader directory so that
1198 // the most recent one appears first in the list.
1199 // Returns TRUE if a duplicate for FALLBACK_FILENAME was found, FALSE if not.
1200 static BOOLEAN
ScanLoaderDir(IN REFIT_VOLUME
*Volume
, IN CHAR16
*Path
, IN CHAR16
*Pattern
)
1203 REFIT_DIR_ITER DirIter
;
1204 EFI_FILE_INFO
*DirEntry
;
1205 CHAR16 FileName
[256], *Extension
;
1206 struct LOADER_LIST
*LoaderList
= NULL
, *NewLoader
;
1207 BOOLEAN FoundFallbackDuplicate
= FALSE
;
1209 if ((!SelfDirPath
|| !Path
|| ((StriCmp(Path
, SelfDirPath
) == 0) && (Volume
->DeviceHandle
!= SelfVolume
->DeviceHandle
)) ||
1210 (StriCmp(Path
, SelfDirPath
) != 0)) &&
1211 (ShouldScan(Volume
, Path
))) {
1212 // look through contents of the directory
1213 DirIterOpen(Volume
->RootDir
, Path
, &DirIter
);
1214 while (DirIterNext(&DirIter
, 2, Pattern
, &DirEntry
)) {
1215 Extension
= FindExtension(DirEntry
->FileName
);
1216 if (DirEntry
->FileName
[0] == '.' ||
1217 StriCmp(Extension
, L
".icns") == 0 ||
1218 StriCmp(Extension
, L
".png") == 0 ||
1219 (StriCmp(DirEntry
->FileName
, FALLBACK_BASENAME
) == 0 && (StriCmp(Path
, L
"EFI\\BOOT") == 0)) ||
1220 StriSubCmp(L
"shell", DirEntry
->FileName
) ||
1221 IsSymbolicLink(Volume
, Path
, DirEntry
) || /* is symbolic link */
1222 IsIn(DirEntry
->FileName
, GlobalConfig
.DontScanFiles
))
1223 continue; // skip this
1226 SPrint(FileName
, 255, L
"\\%s\\%s", Path
, DirEntry
->FileName
);
1228 SPrint(FileName
, 255, L
"\\%s", DirEntry
->FileName
);
1229 CleanUpPathNameSlashes(FileName
);
1231 if(!IsValidLoader(Volume
, FileName
))
1234 NewLoader
= AllocateZeroPool(sizeof(struct LOADER_LIST
));
1235 if (NewLoader
!= NULL
) {
1236 NewLoader
->FileName
= StrDuplicate(FileName
);
1237 NewLoader
->TimeStamp
= DirEntry
->ModificationTime
;
1238 LoaderList
= AddLoaderListEntry(LoaderList
, NewLoader
);
1239 if (DuplicatesFallback(Volume
, FileName
))
1240 FoundFallbackDuplicate
= TRUE
;
1242 MyFreePool(Extension
);
1245 NewLoader
= LoaderList
;
1246 while (NewLoader
!= NULL
) {
1247 AddLoaderEntry(NewLoader
->FileName
, NULL
, Volume
);
1248 NewLoader
= NewLoader
->NextEntry
;
1251 CleanUpLoaderList(LoaderList
);
1252 Status
= DirIterClose(&DirIter
);
1253 // NOTE: EFI_INVALID_PARAMETER really is an error that should be reported;
1254 // but I've gotten reports from users who are getting this error occasionally
1255 // and I can't find anything wrong or reproduce the problem, so I'm putting
1256 // it down to buggy EFI implementations and ignoring that particular error....
1257 if ((Status
!= EFI_NOT_FOUND
) && (Status
!= EFI_INVALID_PARAMETER
)) {
1259 SPrint(FileName
, 255, L
"while scanning the %s directory", Path
);
1261 StrCpy(FileName
, L
"while scanning the root directory");
1262 CheckError(Status
, FileName
);
1263 } // if (Status != EFI_NOT_FOUND)
1264 } // if not scanning a blacklisted directory
1266 return FoundFallbackDuplicate
;
1267 } /* static VOID ScanLoaderDir() */
1269 static VOID
ScanEfiFiles(REFIT_VOLUME
*Volume
) {
1271 REFIT_DIR_ITER EfiDirIter
;
1272 EFI_FILE_INFO
*EfiDirEntry
;
1273 CHAR16 FileName
[256], *Directory
, *MatchPatterns
, *VolName
= NULL
, *SelfPath
;
1275 BOOLEAN ScanFallbackLoader
= TRUE
;
1277 MatchPatterns
= StrDuplicate(LOADER_MATCH_PATTERNS
);
1278 if (GlobalConfig
.ScanAllLinux
)
1279 MergeStrings(&MatchPatterns
, LINUX_MATCH_PATTERNS
, L
',');
1281 if ((Volume
->RootDir
!= NULL
) && (Volume
->VolName
!= NULL
)) {
1282 // check for Mac OS X boot loader
1283 if (ShouldScan(Volume
, L
"System\\Library\\CoreServices")) {
1284 StrCpy(FileName
, MACOSX_LOADER_PATH
);
1285 if (FileExists(Volume
->RootDir
, FileName
) && !IsIn(L
"boot.efi", GlobalConfig
.DontScanFiles
)) {
1286 AddLoaderEntry(FileName
, L
"Mac OS X", Volume
);
1287 if (DuplicatesFallback(Volume
, FileName
))
1288 ScanFallbackLoader
= FALSE
;
1292 StrCpy(FileName
, L
"System\\Library\\CoreServices\\xom.efi");
1293 if (FileExists(Volume
->RootDir
, FileName
) && !IsIn(L
"boot.efi", GlobalConfig
.DontScanFiles
)) {
1294 AddLoaderEntry(FileName
, L
"Windows XP (XoM)", Volume
);
1295 if (DuplicatesFallback(Volume
, FileName
))
1296 ScanFallbackLoader
= FALSE
;
1298 } // if should scan Mac directory
1300 // check for Microsoft boot loader/menu
1301 StrCpy(FileName
, L
"EFI\\Microsoft\\Boot\\bootmgfw.efi");
1302 if (FileExists(Volume
->RootDir
, FileName
) && ShouldScan(Volume
, L
"EFI\\Microsoft\\Boot") &&
1303 !IsIn(L
"bootmgfw.efi", GlobalConfig
.DontScanFiles
)) {
1304 AddLoaderEntry(FileName
, L
"Microsoft EFI boot", Volume
);
1305 if (DuplicatesFallback(Volume
, FileName
))
1306 ScanFallbackLoader
= FALSE
;
1309 // scan the root directory for EFI executables
1310 if (ScanLoaderDir(Volume
, L
"\\", MatchPatterns
))
1311 ScanFallbackLoader
= FALSE
;
1313 // scan subdirectories of the EFI directory (as per the standard)
1314 DirIterOpen(Volume
->RootDir
, L
"EFI", &EfiDirIter
);
1315 while (DirIterNext(&EfiDirIter
, 1, NULL
, &EfiDirEntry
)) {
1316 if (StriCmp(EfiDirEntry
->FileName
, L
"tools") == 0 || EfiDirEntry
->FileName
[0] == '.')
1317 continue; // skip this, doesn't contain boot loaders or is scanned later
1318 SPrint(FileName
, 255, L
"EFI\\%s", EfiDirEntry
->FileName
);
1319 if (ScanLoaderDir(Volume
, FileName
, MatchPatterns
))
1320 ScanFallbackLoader
= FALSE
;
1322 Status
= DirIterClose(&EfiDirIter
);
1323 if (Status
!= EFI_NOT_FOUND
)
1324 CheckError(Status
, L
"while scanning the EFI directory");
1326 // Scan user-specified (or additional default) directories....
1328 while ((Directory
= FindCommaDelimited(GlobalConfig
.AlsoScan
, i
++)) != NULL
) {
1329 SplitVolumeAndFilename(&Directory
, &VolName
);
1330 CleanUpPathNameSlashes(Directory
);
1331 Length
= StrLen(Directory
);
1332 if ((Length
> 0) && ScanLoaderDir(Volume
, Directory
, MatchPatterns
))
1333 ScanFallbackLoader
= FALSE
;
1334 MyFreePool(Directory
);
1335 MyFreePool(VolName
);
1338 // Don't scan the fallback loader if it's on the same volume and a duplicate of rEFInd itself....
1339 SelfPath
= DevicePathToStr(SelfLoadedImage
->FilePath
);
1340 CleanUpPathNameSlashes(SelfPath
);
1341 if ((Volume
->DeviceHandle
== SelfLoadedImage
->DeviceHandle
) && DuplicatesFallback(Volume
, SelfPath
))
1342 ScanFallbackLoader
= FALSE
;
1344 // If not a duplicate & if it exists & if it's not us, create an entry
1345 // for the fallback boot loader
1346 if (ScanFallbackLoader
&& FileExists(Volume
->RootDir
, FALLBACK_FULLNAME
) && ShouldScan(Volume
, L
"EFI\\BOOT"))
1347 AddLoaderEntry(FALLBACK_FULLNAME
, L
"Fallback boot loader", Volume
);
1349 } // static VOID ScanEfiFiles()
1351 // Scan internal disks for valid EFI boot loaders....
1352 static VOID
ScanInternal(VOID
) {
1355 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1356 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_INTERNAL
) {
1357 ScanEfiFiles(Volumes
[VolumeIndex
]);
1360 } // static VOID ScanInternal()
1362 // Scan external disks for valid EFI boot loaders....
1363 static VOID
ScanExternal(VOID
) {
1366 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1367 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_EXTERNAL
) {
1368 ScanEfiFiles(Volumes
[VolumeIndex
]);
1371 } // static VOID ScanExternal()
1373 // Scan internal disks for valid EFI boot loaders....
1374 static VOID
ScanOptical(VOID
) {
1377 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1378 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_OPTICAL
) {
1379 ScanEfiFiles(Volumes
[VolumeIndex
]);
1382 } // static VOID ScanOptical()
1385 // legacy boot functions
1388 static EFI_STATUS
ActivateMbrPartition(IN EFI_BLOCK_IO
*BlockIO
, IN UINTN PartitionIndex
)
1391 UINT8 SectorBuffer
[512];
1392 MBR_PARTITION_INFO
*MbrTable
, *EMbrTable
;
1393 UINT32 ExtBase
, ExtCurrent
, NextExtCurrent
;
1394 UINTN LogicalPartitionIndex
= 4;
1396 BOOLEAN HaveBootCode
;
1399 Status
= refit_call5_wrapper(BlockIO
->ReadBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, 0, 512, SectorBuffer
);
1400 if (EFI_ERROR(Status
))
1402 if (*((UINT16
*)(SectorBuffer
+ 510)) != 0xaa55)
1403 return EFI_NOT_FOUND
; // safety measure #1
1405 // add boot code if necessary
1406 HaveBootCode
= FALSE
;
1407 for (i
= 0; i
< MBR_BOOTCODE_SIZE
; i
++) {
1408 if (SectorBuffer
[i
] != 0) {
1409 HaveBootCode
= TRUE
;
1413 if (!HaveBootCode
) {
1414 // no boot code found in the MBR, add the syslinux MBR code
1415 SetMem(SectorBuffer
, MBR_BOOTCODE_SIZE
, 0);
1416 CopyMem(SectorBuffer
, syslinux_mbr
, SYSLINUX_MBR_SIZE
);
1419 // set the partition active
1420 MbrTable
= (MBR_PARTITION_INFO
*)(SectorBuffer
+ 446);
1422 for (i
= 0; i
< 4; i
++) {
1423 if (MbrTable
[i
].Flags
!= 0x00 && MbrTable
[i
].Flags
!= 0x80)
1424 return EFI_NOT_FOUND
; // safety measure #2
1425 if (i
== PartitionIndex
)
1426 MbrTable
[i
].Flags
= 0x80;
1427 else if (PartitionIndex
>= 4 && IS_EXTENDED_PART_TYPE(MbrTable
[i
].Type
)) {
1428 MbrTable
[i
].Flags
= 0x80;
1429 ExtBase
= MbrTable
[i
].StartLBA
;
1431 MbrTable
[i
].Flags
= 0x00;
1435 Status
= refit_call5_wrapper(BlockIO
->WriteBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, 0, 512, SectorBuffer
);
1436 if (EFI_ERROR(Status
))
1439 if (PartitionIndex
>= 4) {
1440 // we have to activate a logical partition, so walk the EMBR chain
1442 // NOTE: ExtBase was set above while looking at the MBR table
1443 for (ExtCurrent
= ExtBase
; ExtCurrent
; ExtCurrent
= NextExtCurrent
) {
1444 // read current EMBR
1445 Status
= refit_call5_wrapper(BlockIO
->ReadBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, ExtCurrent
, 512, SectorBuffer
);
1446 if (EFI_ERROR(Status
))
1448 if (*((UINT16
*)(SectorBuffer
+ 510)) != 0xaa55)
1449 return EFI_NOT_FOUND
; // safety measure #3
1451 // scan EMBR, set appropriate partition active
1452 EMbrTable
= (MBR_PARTITION_INFO
*)(SectorBuffer
+ 446);
1454 for (i
= 0; i
< 4; i
++) {
1455 if (EMbrTable
[i
].Flags
!= 0x00 && EMbrTable
[i
].Flags
!= 0x80)
1456 return EFI_NOT_FOUND
; // safety measure #4
1457 if (EMbrTable
[i
].StartLBA
== 0 || EMbrTable
[i
].Size
== 0)
1459 if (IS_EXTENDED_PART_TYPE(EMbrTable
[i
].Type
)) {
1460 // link to next EMBR
1461 NextExtCurrent
= ExtBase
+ EMbrTable
[i
].StartLBA
;
1462 EMbrTable
[i
].Flags
= (PartitionIndex
>= LogicalPartitionIndex
) ? 0x80 : 0x00;
1465 // logical partition
1466 EMbrTable
[i
].Flags
= (PartitionIndex
== LogicalPartitionIndex
) ? 0x80 : 0x00;
1467 LogicalPartitionIndex
++;
1471 // write current EMBR
1472 Status
= refit_call5_wrapper(BlockIO
->WriteBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, ExtCurrent
, 512, SectorBuffer
);
1473 if (EFI_ERROR(Status
))
1476 if (PartitionIndex
< LogicalPartitionIndex
)
1477 break; // stop the loop, no need to touch further EMBRs
1483 } /* static EFI_STATUS ActivateMbrPartition() */
1485 // early 2006 Core Duo / Core Solo models
1486 static UINT8 LegacyLoaderDevicePath1Data
[] = {
1487 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1488 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1489 0xFF, 0xFF, 0xF9, 0xFF, 0x00, 0x00, 0x00, 0x00,
1490 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1491 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1492 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1494 // mid-2006 Mac Pro (and probably other Core 2 models)
1495 static UINT8 LegacyLoaderDevicePath2Data
[] = {
1496 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1497 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1498 0xFF, 0xFF, 0xF7, 0xFF, 0x00, 0x00, 0x00, 0x00,
1499 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1500 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1501 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1503 // mid-2007 MBP ("Santa Rosa" based models)
1504 static UINT8 LegacyLoaderDevicePath3Data
[] = {
1505 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1506 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1507 0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
1508 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1509 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1510 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1513 static UINT8 LegacyLoaderDevicePath4Data
[] = {
1514 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1515 0x00, 0x00, 0xC0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1516 0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
1517 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1518 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1519 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1521 // late-2008 MB/MBP (NVidia chipset)
1522 static UINT8 LegacyLoaderDevicePath5Data
[] = {
1523 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1524 0x00, 0x40, 0xCB, 0xFF, 0x00, 0x00, 0x00, 0x00,
1525 0xFF, 0xBF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
1526 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1527 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1528 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1531 static EFI_DEVICE_PATH
*LegacyLoaderList
[] = {
1532 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath1Data
,
1533 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath2Data
,
1534 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath3Data
,
1535 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath4Data
,
1536 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath5Data
,
1540 #define MAX_DISCOVERED_PATHS (16)
1542 static VOID
StartLegacy(IN LEGACY_ENTRY
*Entry
)
1545 EG_IMAGE
*BootLogoImage
;
1546 UINTN ErrorInStep
= 0;
1547 EFI_DEVICE_PATH
*DiscoveredPathList
[MAX_DISCOVERED_PATHS
];
1549 BeginExternalScreen(TRUE
, L
"Booting Legacy OS (Mac mode)");
1551 BootLogoImage
= LoadOSIcon(Entry
->Volume
->OSIconName
, L
"legacy", TRUE
);
1552 if (BootLogoImage
!= NULL
)
1553 BltImageAlpha(BootLogoImage
,
1554 (UGAWidth
- BootLogoImage
->Width
) >> 1,
1555 (UGAHeight
- BootLogoImage
->Height
) >> 1,
1556 &StdBackgroundPixel
);
1558 if (Entry
->Volume
->IsMbrPartition
) {
1559 ActivateMbrPartition(Entry
->Volume
->WholeDiskBlockIO
, Entry
->Volume
->MbrPartitionIndex
);
1562 ExtractLegacyLoaderPaths(DiscoveredPathList
, MAX_DISCOVERED_PATHS
, LegacyLoaderList
);
1564 Status
= StartEFIImageList(DiscoveredPathList
, Entry
->LoadOptions
, NULL
, L
"legacy loader", 0, &ErrorInStep
, TRUE
);
1565 if (Status
== EFI_NOT_FOUND
) {
1566 if (ErrorInStep
== 1) {
1567 Print(L
"\nPlease make sure that you have the latest firmware update installed.\n");
1568 } else if (ErrorInStep
== 3) {
1569 Print(L
"\nThe firmware refused to boot from the selected volume. Note that external\n"
1570 L
"hard drives are not well-supported by Apple's firmware for legacy OS booting.\n");
1573 FinishExternalScreen();
1574 } /* static VOID StartLegacy() */
1576 // Start a device on a non-Mac using the EFI_LEGACY_BIOS_PROTOCOL
1577 #ifdef __MAKEWITH_TIANO
1578 static VOID
StartLegacyUEFI(IN LEGACY_ENTRY
*Entry
)
1580 BeginExternalScreen(TRUE
, L
"Booting Legacy OS (UEFI mode)");
1582 BdsLibConnectDevicePath (Entry
->BdsOption
->DevicePath
);
1583 BdsLibDoLegacyBoot(Entry
->BdsOption
);
1585 // If we get here, it means that there was a failure....
1586 Print(L
"Failure booting legacy (BIOS) OS.");
1588 FinishExternalScreen();
1589 } // static VOID StartLegacyUEFI()
1590 #endif // __MAKEWITH_TIANO
1592 static LEGACY_ENTRY
* AddLegacyEntry(IN CHAR16
*LoaderTitle
, IN REFIT_VOLUME
*Volume
)
1594 LEGACY_ENTRY
*Entry
, *SubEntry
;
1595 REFIT_MENU_SCREEN
*SubScreen
;
1597 CHAR16 ShortcutLetter
= 0;
1599 if (LoaderTitle
== NULL
) {
1600 if (Volume
->OSName
!= NULL
) {
1601 LoaderTitle
= Volume
->OSName
;
1602 if (LoaderTitle
[0] == 'W' || LoaderTitle
[0] == 'L')
1603 ShortcutLetter
= LoaderTitle
[0];
1605 LoaderTitle
= L
"Legacy OS";
1607 if (Volume
->VolName
!= NULL
)
1608 VolDesc
= Volume
->VolName
;
1610 VolDesc
= (Volume
->DiskKind
== DISK_KIND_OPTICAL
) ? L
"CD" : L
"HD";
1612 // prepare the menu entry
1613 Entry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1614 Entry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1615 SPrint(Entry
->me
.Title
, 255, L
"Boot %s from %s", LoaderTitle
, VolDesc
);
1616 Entry
->me
.Tag
= TAG_LEGACY
;
1618 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1619 Entry
->me
.Image
= LoadOSIcon(Volume
->OSIconName
, L
"legacy", FALSE
);
1620 Entry
->me
.BadgeImage
= Volume
->VolBadgeImage
;
1621 Entry
->Volume
= Volume
;
1622 Entry
->LoadOptions
= (Volume
->DiskKind
== DISK_KIND_OPTICAL
) ? L
"CD" :
1623 ((Volume
->DiskKind
== DISK_KIND_EXTERNAL
) ? L
"USB" : L
"HD");
1624 Entry
->Enabled
= TRUE
;
1626 // create the submenu
1627 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
1628 SubScreen
->Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1629 SPrint(SubScreen
->Title
, 255, L
"Boot Options for %s on %s", LoaderTitle
, VolDesc
);
1630 SubScreen
->TitleImage
= Entry
->me
.Image
;
1631 SubScreen
->Hint1
= StrDuplicate(SUBSCREEN_HINT1
);
1632 if (GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_EDITOR
) {
1633 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2_NO_EDITOR
);
1635 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2
);
1639 SubEntry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1640 SubEntry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1641 SPrint(SubEntry
->me
.Title
, 255, L
"Boot %s", LoaderTitle
);
1642 SubEntry
->me
.Tag
= TAG_LEGACY
;
1643 SubEntry
->Volume
= Entry
->Volume
;
1644 SubEntry
->LoadOptions
= Entry
->LoadOptions
;
1645 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
1647 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
1648 Entry
->me
.SubScreen
= SubScreen
;
1649 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1651 } /* static LEGACY_ENTRY * AddLegacyEntry() */
1654 #ifdef __MAKEWITH_GNUEFI
1655 static VOID
ScanLegacyUEFI(IN UINTN DiskType
){}
1657 // default volume badge icon based on disk kind
1658 static EG_IMAGE
* GetDiskBadge(IN UINTN DiskType
) {
1659 EG_IMAGE
* Badge
= NULL
;
1663 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_INTERNAL
);
1666 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_EXTERNAL
);
1669 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_OPTICAL
);
1673 } // static EG_IMAGE * GetDiskBadge()
1676 Create a rEFInd boot option from a Legacy BIOS protocol option.
1678 static LEGACY_ENTRY
* AddLegacyEntryUEFI(BDS_COMMON_OPTION
*BdsOption
, IN UINT16 DiskType
)
1680 LEGACY_ENTRY
*Entry
, *SubEntry
;
1681 REFIT_MENU_SCREEN
*SubScreen
;
1682 CHAR16 ShortcutLetter
= 0;
1683 CHAR16
*LegacyDescription
= BdsOption
->Description
;
1685 // prepare the menu entry
1686 Entry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1687 Entry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1688 SPrint(Entry
->me
.Title
, 255, L
"Boot legacy target %s", LegacyDescription
);
1689 Entry
->me
.Tag
= TAG_LEGACY_UEFI
;
1691 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1692 Entry
->me
.Image
= LoadOSIcon(L
"legacy", L
"legacy", TRUE
);
1693 Entry
->LoadOptions
= (DiskType
== BBS_CDROM
) ? L
"CD" :
1694 ((DiskType
== BBS_USB
) ? L
"USB" : L
"HD");
1695 Entry
->me
.BadgeImage
= GetDiskBadge(DiskType
);
1696 Entry
->BdsOption
= BdsOption
;
1697 Entry
->Enabled
= TRUE
;
1699 // create the submenu
1700 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
1701 SubScreen
->Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1702 SPrint(SubScreen
->Title
, 255, L
"No boot options for legacy target");
1703 SubScreen
->TitleImage
= Entry
->me
.Image
;
1704 SubScreen
->Hint1
= StrDuplicate(SUBSCREEN_HINT1
);
1705 if (GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_EDITOR
) {
1706 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2_NO_EDITOR
);
1708 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2
);
1712 SubEntry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1713 SubEntry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1714 SPrint(SubEntry
->me
.Title
, 255, L
"Boot %s", LegacyDescription
);
1715 SubEntry
->me
.Tag
= TAG_LEGACY_UEFI
;
1716 Entry
->BdsOption
= BdsOption
;
1717 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
1719 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
1720 Entry
->me
.SubScreen
= SubScreen
;
1721 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1723 } /* static LEGACY_ENTRY * AddLegacyEntryUEFI() */
1726 Scan for legacy BIOS targets on machines that implement EFI_LEGACY_BIOS_PROTOCOL.
1727 In testing, protocol has not been implemented on Macs but has been
1728 implemented on several Dell PCs and an ASUS motherboard.
1729 Restricts output to disks of the specified DiskType.
1731 static VOID
ScanLegacyUEFI(IN UINTN DiskType
)
1734 EFI_LEGACY_BIOS_PROTOCOL
*LegacyBios
;
1735 UINT16
*BootOrder
= NULL
;
1737 CHAR16 BootOption
[10];
1738 UINTN BootOrderSize
= 0;
1740 BDS_COMMON_OPTION
*BdsOption
;
1741 LIST_ENTRY TempList
;
1742 BBS_BBS_DEVICE_PATH
* BbsDevicePath
= NULL
;
1744 InitializeListHead (&TempList
);
1745 ZeroMem (Buffer
, sizeof (Buffer
));
1747 // If LegacyBios protocol is not implemented on this platform, then
1748 //we do not support this type of legacy boot on this machine.
1749 Status
= gBS
->LocateProtocol(&gEfiLegacyBootProtocolGuid
, NULL
, (VOID
**) &LegacyBios
);
1750 if (EFI_ERROR (Status
))
1753 // Grab the boot order
1754 BootOrder
= BdsLibGetVariableAndSize(L
"BootOrder", &gEfiGlobalVariableGuid
, &BootOrderSize
);
1755 if (BootOrder
== NULL
) {
1760 while (Index
< BootOrderSize
/ sizeof (UINT16
))
1762 // Grab each boot option variable from the boot order, and convert
1763 // the variable into a BDS boot option
1764 UnicodeSPrint (BootOption
, sizeof (BootOption
), L
"Boot%04x", BootOrder
[Index
]);
1765 BdsOption
= BdsLibVariableToOption (&TempList
, BootOption
);
1767 if (BdsOption
!= NULL
) {
1768 BbsDevicePath
= (BBS_BBS_DEVICE_PATH
*)BdsOption
->DevicePath
;
1770 // Only add the entry if it is of a requested type (e.g. USB, HD)
1772 // Two checks necessary because some systems return EFI boot loaders
1773 // with a DeviceType value that would inappropriately include them
1774 // as legacy loaders....
1775 if ((BbsDevicePath
->DeviceType
== DiskType
) && (BdsOption
->DevicePath
->Type
== DEVICE_TYPE_BIOS
)) {
1776 AddLegacyEntryUEFI(BdsOption
, BbsDevicePath
->DeviceType
);
1781 } /* static VOID ScanLegacyUEFI() */
1782 #endif // __MAKEWITH_GNUEFI
1784 static VOID
ScanLegacyVolume(REFIT_VOLUME
*Volume
, UINTN VolumeIndex
) {
1786 BOOLEAN ShowVolume
, HideIfOthersFound
;
1789 HideIfOthersFound
= FALSE
;
1790 if (Volume
->IsAppleLegacy
) {
1792 HideIfOthersFound
= TRUE
;
1793 } else if (Volume
->HasBootCode
) {
1795 if (Volume
->BlockIO
== Volume
->WholeDiskBlockIO
&&
1796 Volume
->BlockIOOffset
== 0 &&
1797 Volume
->OSName
== NULL
)
1798 // this is a whole disk (MBR) entry; hide if we have entries for partitions
1799 HideIfOthersFound
= TRUE
;
1801 if (HideIfOthersFound
) {
1802 // check for other bootable entries on the same disk
1803 for (VolumeIndex2
= 0; VolumeIndex2
< VolumesCount
; VolumeIndex2
++) {
1804 if (VolumeIndex2
!= VolumeIndex
&& Volumes
[VolumeIndex2
]->HasBootCode
&&
1805 Volumes
[VolumeIndex2
]->WholeDiskBlockIO
== Volume
->WholeDiskBlockIO
)
1811 AddLegacyEntry(NULL
, Volume
);
1812 } // static VOID ScanLegacyVolume()
1814 // Scan attached optical discs for legacy (BIOS) boot code
1815 // and add anything found to the list....
1816 static VOID
ScanLegacyDisc(VOID
)
1819 REFIT_VOLUME
*Volume
;
1821 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
) {
1822 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1823 Volume
= Volumes
[VolumeIndex
];
1824 if (Volume
->DiskKind
== DISK_KIND_OPTICAL
)
1825 ScanLegacyVolume(Volume
, VolumeIndex
);
1827 } else if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
1828 ScanLegacyUEFI(BBS_CDROM
);
1830 } /* static VOID ScanLegacyDisc() */
1832 // Scan internal hard disks for legacy (BIOS) boot code
1833 // and add anything found to the list....
1834 static VOID
ScanLegacyInternal(VOID
)
1837 REFIT_VOLUME
*Volume
;
1839 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
) {
1840 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1841 Volume
= Volumes
[VolumeIndex
];
1842 if (Volume
->DiskKind
== DISK_KIND_INTERNAL
)
1843 ScanLegacyVolume(Volume
, VolumeIndex
);
1845 } else if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
1846 ScanLegacyUEFI(BBS_HARDDISK
);
1848 } /* static VOID ScanLegacyInternal() */
1850 // Scan external disks for legacy (BIOS) boot code
1851 // and add anything found to the list....
1852 static VOID
ScanLegacyExternal(VOID
)
1855 REFIT_VOLUME
*Volume
;
1857 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
) {
1858 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1859 Volume
= Volumes
[VolumeIndex
];
1860 if (Volume
->DiskKind
== DISK_KIND_EXTERNAL
)
1861 ScanLegacyVolume(Volume
, VolumeIndex
);
1863 } else if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
1864 ScanLegacyUEFI(BBS_USB
);
1866 } /* static VOID ScanLegacyExternal() */
1869 // pre-boot tool functions
1872 static VOID
StartTool(IN LOADER_ENTRY
*Entry
)
1874 BeginExternalScreen(Entry
->UseGraphicsMode
, Entry
->me
.Title
+ 6); // assumes "Start <title>" as assigned below
1875 StartEFIImage(Entry
->DevicePath
, Entry
->LoadOptions
, Basename(Entry
->LoaderPath
),
1876 Basename(Entry
->LoaderPath
), Entry
->OSType
, NULL
, TRUE
);
1877 FinishExternalScreen();
1878 } /* static VOID StartTool() */
1880 static LOADER_ENTRY
* AddToolEntry(EFI_HANDLE DeviceHandle
, IN CHAR16
*LoaderPath
, IN CHAR16
*LoaderTitle
, IN EG_IMAGE
*Image
,
1881 IN CHAR16 ShortcutLetter
, IN BOOLEAN UseGraphicsMode
)
1883 LOADER_ENTRY
*Entry
;
1884 CHAR16
*TitleStr
= NULL
;
1886 Entry
= AllocateZeroPool(sizeof(LOADER_ENTRY
));
1888 MergeStrings(&TitleStr
, L
"Start ", 0);
1889 MergeStrings(&TitleStr
, LoaderTitle
, 0);
1890 Entry
->me
.Title
= TitleStr
;
1891 Entry
->me
.Tag
= TAG_TOOL
;
1893 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1894 Entry
->me
.Image
= Image
;
1895 Entry
->LoaderPath
= (LoaderPath
) ? StrDuplicate(LoaderPath
) : NULL
;
1896 Entry
->DevicePath
= FileDevicePath(DeviceHandle
, Entry
->LoaderPath
);
1897 Entry
->UseGraphicsMode
= UseGraphicsMode
;
1899 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1901 } /* static LOADER_ENTRY * AddToolEntry() */
1904 // pre-boot driver functions
1907 static UINTN
ScanDriverDir(IN CHAR16
*Path
)
1910 REFIT_DIR_ITER DirIter
;
1912 EFI_FILE_INFO
*DirEntry
;
1913 CHAR16 FileName
[256];
1915 CleanUpPathNameSlashes(Path
);
1916 // look through contents of the directory
1917 DirIterOpen(SelfRootDir
, Path
, &DirIter
);
1918 while (DirIterNext(&DirIter
, 2, LOADER_MATCH_PATTERNS
, &DirEntry
)) {
1919 if (DirEntry
->FileName
[0] == '.')
1920 continue; // skip this
1922 SPrint(FileName
, 255, L
"%s\\%s", Path
, DirEntry
->FileName
);
1924 Status
= StartEFIImage(FileDevicePath(SelfLoadedImage
->DeviceHandle
, FileName
),
1925 L
"", DirEntry
->FileName
, DirEntry
->FileName
, 0, NULL
, FALSE
);
1927 Status
= DirIterClose(&DirIter
);
1928 if (Status
!= EFI_NOT_FOUND
) {
1929 SPrint(FileName
, 255, L
"while scanning the %s directory", Path
);
1930 CheckError(Status
, FileName
);
1935 #ifdef __MAKEWITH_GNUEFI
1936 static EFI_STATUS
ConnectAllDriversToAllControllers(VOID
)
1939 UINTN AllHandleCount
;
1940 EFI_HANDLE
*AllHandleBuffer
;
1943 EFI_HANDLE
*HandleBuffer
;
1949 Status
= LibLocateHandle(AllHandles
,
1954 if (EFI_ERROR(Status
))
1957 for (Index
= 0; Index
< AllHandleCount
; Index
++) {
1959 // Scan the handle database
1961 Status
= LibScanHandleDatabase(NULL
,
1963 AllHandleBuffer
[Index
],
1968 if (EFI_ERROR (Status
))
1972 if (HandleType
[Index
] & EFI_HANDLE_TYPE_DRIVER_BINDING_HANDLE
)
1974 if (HandleType
[Index
] & EFI_HANDLE_TYPE_IMAGE_HANDLE
)
1979 for (HandleIndex
= 0; HandleIndex
< HandleCount
; HandleIndex
++) {
1980 if (HandleType
[HandleIndex
] & EFI_HANDLE_TYPE_PARENT_HANDLE
)
1985 if (HandleType
[Index
] & EFI_HANDLE_TYPE_DEVICE_HANDLE
) {
1986 Status
= refit_call4_wrapper(BS
->ConnectController
,
1987 AllHandleBuffer
[Index
],
1995 MyFreePool (HandleBuffer
);
1996 MyFreePool (HandleType
);
2000 MyFreePool (AllHandleBuffer
);
2002 } /* EFI_STATUS ConnectAllDriversToAllControllers() */
2004 static EFI_STATUS
ConnectAllDriversToAllControllers(VOID
) {
2005 BdsLibConnectAllDriversToAllControllers();
2010 // Load all EFI drivers from rEFInd's "drivers" subdirectory and from the
2011 // directories specified by the user in the "scan_driver_dirs" configuration
2013 static VOID
LoadDrivers(VOID
)
2015 CHAR16
*Directory
, *SelfDirectory
;
2016 UINTN i
= 0, Length
, NumFound
= 0;
2018 // load drivers from the subdirectories of rEFInd's home directory specified
2019 // in the DRIVER_DIRS constant.
2020 while ((Directory
= FindCommaDelimited(DRIVER_DIRS
, i
++)) != NULL
) {
2021 SelfDirectory
= SelfDirPath
? StrDuplicate(SelfDirPath
) : NULL
;
2022 CleanUpPathNameSlashes(SelfDirectory
);
2023 MergeStrings(&SelfDirectory
, Directory
, L
'\\');
2024 NumFound
+= ScanDriverDir(SelfDirectory
);
2025 MyFreePool(Directory
);
2026 MyFreePool(SelfDirectory
);
2029 // Scan additional user-specified driver directories....
2031 while ((Directory
= FindCommaDelimited(GlobalConfig
.DriverDirs
, i
++)) != NULL
) {
2032 CleanUpPathNameSlashes(Directory
);
2033 Length
= StrLen(Directory
);
2035 NumFound
+= ScanDriverDir(Directory
);
2037 MyFreePool(Directory
);
2040 // connect all devices
2042 ConnectAllDriversToAllControllers();
2043 } /* static VOID LoadDrivers() */
2045 // Determine what (if any) type of legacy (BIOS) boot support is available
2046 static VOID
FindLegacyBootType(VOID
) {
2047 #ifdef __MAKEWITH_TIANO
2049 EFI_LEGACY_BIOS_PROTOCOL
*LegacyBios
;
2052 GlobalConfig
.LegacyType
= LEGACY_TYPE_NONE
;
2054 // UEFI-style legacy BIOS support is available only with the TianoCore EDK2
2055 // build environment, and then only with some EFI implementations....
2056 #ifdef __MAKEWITH_TIANO
2057 Status
= gBS
->LocateProtocol (&gEfiLegacyBootProtocolGuid
, NULL
, (VOID
**) &LegacyBios
);
2058 if (!EFI_ERROR (Status
))
2059 GlobalConfig
.LegacyType
= LEGACY_TYPE_UEFI
;
2062 // Macs have their own system. If the firmware vendor code contains the
2063 // string "Apple", assume it's available. Note that this overrides the
2064 // UEFI type, and might yield false positives if the vendor string
2065 // contains "Apple" as part of something bigger, so this isn't 100%
2067 if (StriSubCmp(L
"Apple", ST
->FirmwareVendor
))
2068 GlobalConfig
.LegacyType
= LEGACY_TYPE_MAC
;
2069 } // static VOID FindLegacyBootType
2071 // Warn the user if legacy OS scans are enabled but the firmware or this
2072 // application can't support them....
2073 static VOID
WarnIfLegacyProblems() {
2074 BOOLEAN found
= FALSE
;
2077 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_NONE
) {
2079 if (GlobalConfig
.ScanFor
[i
] == 'h' || GlobalConfig
.ScanFor
[i
] == 'b' || GlobalConfig
.ScanFor
[i
] == 'c')
2082 } while ((i
< NUM_SCAN_OPTIONS
) && (!found
));
2084 Print(L
"NOTE: refind.conf's 'scanfor' line specifies scanning for one or more legacy\n");
2085 Print(L
"(BIOS) boot options; however, this is not possible because ");
2086 #ifdef __MAKEWITH_TIANO
2087 Print(L
"your computer lacks\n");
2088 Print(L
"the necessary Compatibility Support Module (CSM) support.\n");
2090 Print(L
"this program was\n");
2091 Print(L
"compiled without the necessary support. Please visit\n");
2092 Print(L
"http://www.rodsbooks.com/refind/getting.html and download and install a rEFInd\n");
2093 Print(L
"binary built with the TianoCore EDK2 to enable legacy boot support.\n");
2097 } // if no legacy support
2098 } // static VOID WarnIfLegacyProblems()
2100 // Locates boot loaders. NOTE: This assumes that GlobalConfig.LegacyType is set correctly.
2101 static VOID
ScanForBootloaders(VOID
) {
2106 // scan for loaders and tools, add them to the menu
2107 for (i
= 0; i
< NUM_SCAN_OPTIONS
; i
++) {
2108 switch(GlobalConfig
.ScanFor
[i
]) {
2113 ScanLegacyInternal();
2116 ScanLegacyExternal();
2119 ScanUserConfigured(CONFIG_FILE_NAME
);
2133 // assign shortcut keys
2134 for (i
= 0; i
< MainMenu
.EntryCount
&& MainMenu
.Entries
[i
]->Row
== 0 && i
< 9; i
++)
2135 MainMenu
.Entries
[i
]->ShortcutDigit
= (CHAR16
)('1' + i
);
2137 // wait for user ACK when there were errors
2138 FinishTextScreen(FALSE
);
2139 } // static VOID ScanForBootloaders()
2141 // Add the second-row tags containing built-in and external tools (EFI shell,
2143 static VOID
ScanForTools(VOID
) {
2144 CHAR16
*FileName
= NULL
, *MokLocations
, *MokName
, *PathName
, Description
[256];
2145 REFIT_MENU_ENTRY
*TempMenuEntry
;
2146 UINTN i
, j
, k
, VolumeIndex
;
2150 MokLocations
= StrDuplicate(MOK_LOCATIONS
);
2151 if (MokLocations
!= NULL
)
2152 MergeStrings(&MokLocations
, SelfDirPath
, L
',');
2154 for (i
= 0; i
< NUM_TOOLS
; i
++) {
2155 switch(GlobalConfig
.ShowTools
[i
]) {
2156 // NOTE: Be sure that FileName is NULL at the end of each case.
2158 TempMenuEntry
= CopyMenuEntry(&MenuEntryShutdown
);
2159 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_SHUTDOWN
);
2160 AddMenuEntry(&MainMenu
, TempMenuEntry
);
2163 TempMenuEntry
= CopyMenuEntry(&MenuEntryReset
);
2164 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_RESET
);
2165 AddMenuEntry(&MainMenu
, TempMenuEntry
);
2168 TempMenuEntry
= CopyMenuEntry(&MenuEntryAbout
);
2169 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT
);
2170 AddMenuEntry(&MainMenu
, TempMenuEntry
);
2173 TempMenuEntry
= CopyMenuEntry(&MenuEntryExit
);
2174 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_EXIT
);
2175 AddMenuEntry(&MainMenu
, TempMenuEntry
);
2178 if (EfivarGetRaw(&GlobalGuid
, L
"OsIndicationsSupported", &b
, &j
) == EFI_SUCCESS
) {
2180 if (osind
& EFI_OS_INDICATIONS_BOOT_TO_FW_UI
) {
2181 TempMenuEntry
= CopyMenuEntry(&MenuEntryFirmware
);
2182 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_FIRMWARE
);
2183 AddMenuEntry(&MainMenu
, TempMenuEntry
);
2189 while ((FileName
= FindCommaDelimited(SHELL_NAMES
, j
++)) != NULL
) {
2190 if (FileExists(SelfRootDir
, FileName
)) {
2191 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, L
"EFI Shell", BuiltinIcon(BUILTIN_ICON_TOOL_SHELL
),
2194 MyFreePool(FileName
);
2199 while ((FileName
= FindCommaDelimited(GPTSYNC_NAMES
, j
++)) != NULL
) {
2200 if (FileExists(SelfRootDir
, FileName
)) {
2201 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, L
"Hybrid MBR tool", BuiltinIcon(BUILTIN_ICON_TOOL_PART
),
2204 MyFreePool(FileName
);
2208 case TAG_APPLE_RECOVERY
:
2209 FileName
= StrDuplicate(L
"\\com.apple.recovery.boot\\boot.efi");
2210 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
2211 if ((Volumes
[VolumeIndex
]->RootDir
!= NULL
) && (FileExists(Volumes
[VolumeIndex
]->RootDir
, FileName
))) {
2212 SPrint(Description
, 255, L
"Apple Recovery on %s", Volumes
[VolumeIndex
]->VolName
);
2213 AddToolEntry(Volumes
[VolumeIndex
]->DeviceHandle
, FileName
, Description
,
2214 BuiltinIcon(BUILTIN_ICON_TOOL_APPLE_RESCUE
), 'R', TRUE
);
2217 MyFreePool(FileName
);
2222 while ((FileName
= FindCommaDelimited(MokLocations
, j
++)) != NULL
) {
2224 while ((MokName
= FindCommaDelimited(MOK_NAMES
, k
++)) != NULL
) {
2225 PathName
= StrDuplicate(FileName
);
2226 MergeStrings(&PathName
, MokName
, (StriCmp(PathName
, L
"\\") == 0) ? 0 : L
'\\');
2227 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
2228 if ((Volumes
[VolumeIndex
]->RootDir
!= NULL
) && (FileExists(Volumes
[VolumeIndex
]->RootDir
, PathName
))) {
2229 SPrint(Description
, 255, L
"MOK utility at %s on %s", PathName
, Volumes
[VolumeIndex
]->VolName
);
2230 AddToolEntry(Volumes
[VolumeIndex
]->DeviceHandle
, PathName
, Description
,
2231 BuiltinIcon(BUILTIN_ICON_TOOL_MOK_TOOL
), 'S', FALSE
);
2234 MyFreePool(PathName
);
2235 MyFreePool(MokName
);
2236 } // while MOK_NAMES
2237 MyFreePool(FileName
);
2238 } // while MokLocations
2243 } // static VOID ScanForTools
2245 // Rescan for boot loaders
2246 VOID
RescanAll(VOID
) {
2253 egDisplayMessage(L
"Scanning for new boot loaders; please wait....", &BGColor
);
2254 FreeList((VOID
***) &(MainMenu
.Entries
), &MainMenu
.EntryCount
);
2255 MainMenu
.Entries
= NULL
;
2256 MainMenu
.EntryCount
= 0;
2257 ReadConfig(CONFIG_FILE_NAME
);
2258 ConnectAllDriversToAllControllers();
2260 ScanForBootloaders();
2263 } // VOID RescanAll()
2265 #ifdef __MAKEWITH_TIANO
2267 // Minimal initialization function
2268 static VOID
InitializeLib(IN EFI_HANDLE ImageHandle
, IN EFI_SYSTEM_TABLE
*SystemTable
) {
2270 // gImageHandle = ImageHandle;
2271 gBS
= SystemTable
->BootServices
;
2272 // gRS = SystemTable->RuntimeServices;
2273 gRT
= SystemTable
->RuntimeServices
; // Some BDS functions need gRT to be set
2274 EfiGetSystemConfigurationTable (&gEfiDxeServicesTableGuid
, (VOID
**) &gDS
);
2276 InitializeConsoleSim();
2281 // Set up our own Secure Boot extensions....
2282 // Returns TRUE on success, FALSE otherwise
2283 static BOOLEAN
SecureBootSetup(VOID
) {
2285 BOOLEAN Success
= FALSE
;
2287 if (secure_mode() && ShimLoaded()) {
2288 Status
= security_policy_install();
2289 if (Status
== EFI_SUCCESS
) {
2292 Print(L
"Failed to install MOK Secure Boot extensions");
2296 } // VOID SecureBootSetup()
2298 // Remove our own Secure Boot extensions....
2299 // Returns TRUE on success, FALSE otherwise
2300 static BOOLEAN
SecureBootUninstall(VOID
) {
2302 BOOLEAN Success
= TRUE
;
2304 if (secure_mode()) {
2305 Status
= security_policy_uninstall();
2306 if (Status
!= EFI_SUCCESS
) {
2308 BeginTextScreen(L
"Secure Boot Policy Failure");
2309 Print(L
"Failed to uninstall MOK Secure Boot extensions; forcing a reboot.");
2311 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);
2315 } // VOID SecureBootUninstall
2322 efi_main (EFI_HANDLE ImageHandle
, EFI_SYSTEM_TABLE
*SystemTable
)
2325 BOOLEAN MainLoopRunning
= TRUE
;
2326 BOOLEAN MokProtocol
;
2327 REFIT_MENU_ENTRY
*ChosenEntry
;
2329 CHAR16
*Selection
= NULL
;
2333 InitializeLib(ImageHandle
, SystemTable
);
2334 Status
= InitRefitLib(ImageHandle
);
2335 if (EFI_ERROR(Status
))
2338 // read configuration
2339 CopyMem(GlobalConfig
.ScanFor
, "ieom ", NUM_SCAN_OPTIONS
);
2340 FindLegacyBootType();
2341 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
)
2342 CopyMem(GlobalConfig
.ScanFor
, "ihebocm ", NUM_SCAN_OPTIONS
);
2344 ReadConfig(CONFIG_FILE_NAME
);
2347 WarnIfLegacyProblems();
2348 MainMenu
.TimeoutSeconds
= GlobalConfig
.Timeout
;
2350 // disable EFI watchdog timer
2351 refit_call4_wrapper(BS
->SetWatchdogTimer
, 0x0000, 0x0000, 0x0000, NULL
);
2353 // further bootstrap (now with config available)
2354 MokProtocol
= SecureBootSetup();
2356 ScanForBootloaders();
2360 if (GlobalConfig
.ScanDelay
> 0) {
2365 egDisplayMessage(L
"Pausing before disk scan; please wait....", &BGColor
);
2366 for (i
= 0; i
< GlobalConfig
.ScanDelay
; i
++)
2367 refit_call1_wrapper(BS
->Stall
, 1000000);
2371 if (GlobalConfig
.DefaultSelection
)
2372 Selection
= StrDuplicate(GlobalConfig
.DefaultSelection
);
2374 while (MainLoopRunning
) {
2375 MenuExit
= RunMainMenu(&MainMenu
, Selection
, &ChosenEntry
);
2377 // The Escape key triggers a re-scan operation....
2378 if (MenuExit
== MENU_EXIT_ESCAPE
) {
2383 switch (ChosenEntry
->Tag
) {
2385 case TAG_REBOOT
: // Reboot
2387 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);
2388 MainLoopRunning
= FALSE
; // just in case we get this far
2391 case TAG_SHUTDOWN
: // Shut Down
2393 refit_call4_wrapper(RT
->ResetSystem
, EfiResetShutdown
, EFI_SUCCESS
, 0, NULL
);
2394 MainLoopRunning
= FALSE
; // just in case we get this far
2397 case TAG_ABOUT
: // About rEFInd
2401 case TAG_LOADER
: // Boot OS via .EFI loader
2402 StartLoader((LOADER_ENTRY
*)ChosenEntry
);
2405 case TAG_LEGACY
: // Boot legacy OS
2406 StartLegacy((LEGACY_ENTRY
*)ChosenEntry
);
2409 #ifdef __MAKEWITH_TIANO
2410 case TAG_LEGACY_UEFI
: // Boot a legacy OS on a non-Mac
2411 StartLegacyUEFI((LEGACY_ENTRY
*)ChosenEntry
);
2415 case TAG_TOOL
: // Start a EFI tool
2416 StartTool((LOADER_ENTRY
*)ChosenEntry
);
2419 case TAG_EXIT
: // Terminate rEFInd
2420 if ((MokProtocol
) && !SecureBootUninstall()) {
2421 MainLoopRunning
= FALSE
; // just in case we get this far
2423 BeginTextScreen(L
" ");
2428 case TAG_FIRMWARE
: // Reboot into firmware's user interface
2429 RebootIntoFirmware();
2433 MyFreePool(Selection
);
2434 Selection
= (ChosenEntry
->Title
) ? StrDuplicate(ChosenEntry
->Title
) : NULL
;
2437 // If we end up here, things have gone wrong. Try to reboot, and if that
2438 // fails, go into an endless loop.
2439 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);