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 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 "../include/Handle.h"
53 #include "../include/refit_call_wrapper.h"
54 #include "driver_support.h"
55 #include "../include/syslinux_mbr.h"
57 #ifdef __MAKEWITH_TIANO
58 #include "../EfiLib/BdsHelper.h"
59 #endif // __MAKEWITH_TIANO
64 #define MACOSX_LOADER_PATH L"\\System\\Library\\CoreServices\\boot.efi"
66 #define SHELL_NAMES L"\\EFI\\tools\\shell.efi,\\shellx64.efi"
67 #define DRIVER_DIRS L"drivers,drivers_x64"
69 #define SHELL_NAMES L"\\EFI\\tools\\shell.efi,\\shellia32.efi"
70 #define DRIVER_DIRS L"drivers,drivers_ia32"
72 #define SHELL_NAMES L"\\EFI\\tools\\shell.efi"
73 #define DRIVER_DIRS L"drivers"
76 #define MOK_NAMES L"\\EFI\\tools\\MokManager.efi,\\EFI\\redhat\\MokManager.efi"
78 // Filename patterns that identify EFI boot loaders. Note that a single case (either L"*.efi" or
79 // L"*.EFI") is fine for most systems; but Gigabyte's buggy Hybrid EFI does a case-sensitive
80 // comparison when it should do a case-insensitive comparison, so I'm doubling this up. It does
81 // no harm on other computers, AFAIK. In theory, every case variation should be done for
82 // completeness, but that's ridiculous....
83 #define LOADER_MATCH_PATTERNS L"*.efi,*.EFI"
85 // Patterns that identify Linux kernels. Added to the loader match pattern when the
86 // scan_all_linux_kernels option is set in the configuration file. Causes kernels WITHOUT
87 // a ".efi" extension to be found when scanning for boot loaders.
88 #define LINUX_MATCH_PATTERNS L"vmlinuz*,bzImage*"
90 static REFIT_MENU_ENTRY MenuEntryAbout
= { L
"About rEFInd", TAG_ABOUT
, 1, 0, 'A', NULL
, NULL
, NULL
};
91 static REFIT_MENU_ENTRY MenuEntryReset
= { L
"Reboot Computer", TAG_REBOOT
, 1, 0, 'R', NULL
, NULL
, NULL
};
92 static REFIT_MENU_ENTRY MenuEntryShutdown
= { L
"Shut Down Computer", TAG_SHUTDOWN
, 1, 0, 'U', NULL
, NULL
, NULL
};
93 static REFIT_MENU_ENTRY MenuEntryReturn
= { L
"Return to Main Menu", TAG_RETURN
, 0, 0, 0, NULL
, NULL
, NULL
};
94 static REFIT_MENU_ENTRY MenuEntryExit
= { L
"Exit rEFInd", TAG_EXIT
, 1, 0, 0, NULL
, NULL
, NULL
};
96 static REFIT_MENU_SCREEN MainMenu
= { L
"Main Menu", NULL
, 0, NULL
, 0, NULL
, 0, L
"Automatic boot" };
97 static REFIT_MENU_SCREEN AboutMenu
= { L
"About", NULL
, 0, NULL
, 0, NULL
, 0, NULL
};
99 REFIT_CONFIG GlobalConfig
= { FALSE
, FALSE
, 0, 0, 20, 0, 0, GRAPHICS_FOR_OSX
, LEGACY_TYPE_MAC
, 0,
100 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
101 {TAG_SHELL
, TAG_APPLE_RECOVERY
, TAG_MOK_TOOL
, TAG_ABOUT
, TAG_SHUTDOWN
, TAG_REBOOT
, 0, 0, 0, 0, 0 }};
103 // Structure used to hold boot loader filenames and time stamps in
104 // a linked list; used to sort entries within a directory.
108 struct LOADER_LIST
*NextEntry
;
115 static VOID
AboutrEFInd(VOID
)
117 CHAR16
*TempStr
; // Note: Don't deallocate; moved to menu structure
119 if (AboutMenu
.EntryCount
== 0) {
120 AboutMenu
.TitleImage
= BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT
);
121 AddMenuInfoLine(&AboutMenu
, L
"rEFInd Version 0.4.7.7");
122 AddMenuInfoLine(&AboutMenu
, L
"");
123 AddMenuInfoLine(&AboutMenu
, L
"Copyright (c) 2006-2010 Christoph Pfisterer");
124 AddMenuInfoLine(&AboutMenu
, L
"Copyright (c) 2012 Roderick W. Smith");
125 AddMenuInfoLine(&AboutMenu
, L
"Portions Copyright (c) Intel Corporation and others");
126 AddMenuInfoLine(&AboutMenu
, L
"Distributed under the terms of the GNU GPLv3 license");
127 AddMenuInfoLine(&AboutMenu
, L
"");
128 AddMenuInfoLine(&AboutMenu
, L
"Running on:");
129 TempStr
= AllocateZeroPool(256 * sizeof(CHAR16
));
130 SPrint(TempStr
, 255, L
" EFI Revision %d.%02d", ST
->Hdr
.Revision
>> 16, ST
->Hdr
.Revision
& ((1 << 16) - 1));
131 AddMenuInfoLine(&AboutMenu
, TempStr
);
133 AddMenuInfoLine(&AboutMenu
, L
" Platform: x86 (32 bit)");
134 #elif defined(EFIX64)
135 AddMenuInfoLine(&AboutMenu
, L
" Platform: x86_64 (64 bit)");
137 AddMenuInfoLine(&AboutMenu
, L
" Platform: unknown");
139 TempStr
= AllocateZeroPool(256 * sizeof(CHAR16
));
140 SPrint(TempStr
, 255, L
" Firmware: %s %d.%02d",
141 ST
->FirmwareVendor
, ST
->FirmwareRevision
>> 16, ST
->FirmwareRevision
& ((1 << 16) - 1));
142 AddMenuInfoLine(&AboutMenu
, TempStr
);
143 TempStr
= AllocateZeroPool(256 * sizeof(CHAR16
));
144 SPrint(TempStr
, 255, L
" Screen Output: %s", egScreenDescription());
145 AddMenuInfoLine(&AboutMenu
, TempStr
);
146 AddMenuInfoLine(&AboutMenu
, L
"");
147 #if defined(__MAKEWITH_GNUEFI)
148 AddMenuInfoLine(&AboutMenu
, L
"Built with GNU-EFI");
150 AddMenuInfoLine(&AboutMenu
, L
"Built with TianoCore EDK2");
152 AddMenuInfoLine(&AboutMenu
, L
"");
153 AddMenuInfoLine(&AboutMenu
, L
"For more information, see the rEFInd Web site:");
154 AddMenuInfoLine(&AboutMenu
, L
"http://www.rodsbooks.com/refind/");
155 AddMenuEntry(&AboutMenu
, &MenuEntryReturn
);
158 RunMenu(&AboutMenu
, NULL
);
159 } /* VOID AboutrEFInd() */
161 static EFI_STATUS
StartEFIImageList(IN EFI_DEVICE_PATH
**DevicePaths
,
162 IN CHAR16
*LoadOptions
, IN CHAR16
*LoadOptionsPrefix
,
163 IN CHAR16
*ImageTitle
, IN CHAR8 OSType
,
164 OUT UINTN
*ErrorInStep
,
167 EFI_STATUS Status
, ReturnStatus
;
168 EFI_HANDLE ChildImageHandle
;
169 EFI_LOADED_IMAGE
*ChildLoadedImage
= NULL
;
171 VOID
*ImageData
= NULL
;
173 REFIT_VOLUME
*DeviceVolume
= NULL
;
174 UINTN DevicePathIndex
;
175 CHAR16 ErrorInfo
[256];
176 CHAR16
*FullLoadOptions
= NULL
;
177 CHAR16
*loader
= NULL
;
178 BOOLEAN UseMok
= FALSE
;
180 if (ErrorInStep
!= NULL
)
184 if (LoadOptions
!= NULL
) {
185 if (LoadOptionsPrefix
!= NULL
) {
186 MergeStrings(&FullLoadOptions
, LoadOptionsPrefix
, 0);
187 MergeStrings(&FullLoadOptions
, LoadOptions
, L
' ');
189 MergeStrings(&FullLoadOptions
, L
" ", 0);
190 // NOTE: That last space is also added by the EFI shell and seems to be significant
191 // when passing options to Apple's boot.efi...
194 MergeStrings(&FullLoadOptions
, LoadOptions
, 0);
196 // NOTE: We also include the terminating null in the length for safety.
197 } // if (LoadOptions != NULL)
199 Print(L
"Starting %s\nUsing load options '%s'\n", ImageTitle
, FullLoadOptions
);
201 // load the image into memory (and execute it, in the case of a MOK image).
202 ReturnStatus
= Status
= EFI_NOT_FOUND
; // in case the list is empty
203 for (DevicePathIndex
= 0; DevicePaths
[DevicePathIndex
] != NULL
; DevicePathIndex
++) {
204 ReturnStatus
= Status
= refit_call6_wrapper(BS
->LoadImage
, FALSE
, SelfImageHandle
, DevicePaths
[DevicePathIndex
],
205 NULL
, 0, &ChildImageHandle
);
206 // ReturnStatus = Status = refit_call6_wrapper(BS->LoadImage, FALSE, SelfImageHandle, DevicePaths[DevicePathIndex],
207 // ImageData, ImageSize, &ChildImageHandle);
208 // TODO: Commented-out version above is more efficient if the below FindVolumeAndFilename()
209 // and ReadFile() calls (and surrounding logic) are moved earlier; however, this causes
210 // some computers, including my 32-bit Mac Mini and 64-bit Intel machine, to fail when
211 // launching a Linux kernel, with a "Failed to handle fs_proto" error message from the
212 // kernel. Find out what's causing this and fix it.
213 if (ReturnStatus
== EFI_ACCESS_DENIED
) {
214 // TODO: I originally had the next few lines a
215 FindVolumeAndFilename(DevicePaths
[DevicePathIndex
], &DeviceVolume
, &loader
);
216 if (DeviceVolume
!= NULL
) {
217 Status
= ReadFile(DeviceVolume
->RootDir
, loader
, &File
, &ImageSize
);
218 ImageData
= File
.Buffer
;
220 Status
= EFI_NOT_FOUND
;
221 Print(L
"Error: device volume not found!\n");
223 ReturnStatus
= Status
= start_image(SelfImageHandle
, loader
, ImageData
, ImageSize
, FullLoadOptions
, DeviceVolume
);
224 if (ReturnStatus
== EFI_SUCCESS
) {
228 if (ReturnStatus
!= EFI_NOT_FOUND
) {
232 SPrint(ErrorInfo
, 255, L
"while loading %s", ImageTitle
);
233 if (CheckError(Status
, ErrorInfo
)) {
234 if (ErrorInStep
!= NULL
)
240 ReturnStatus
= Status
= refit_call3_wrapper(BS
->HandleProtocol
, ChildImageHandle
, &LoadedImageProtocol
,
241 (VOID
**) &ChildLoadedImage
);
242 if (CheckError(Status
, L
"while getting a LoadedImageProtocol handle")) {
243 if (ErrorInStep
!= NULL
)
250 ChildLoadedImage
->LoadOptions
= (VOID
*)FullLoadOptions
;
251 ChildLoadedImage
->LoadOptionsSize
= ((UINT32
)StrLen(FullLoadOptions
) + 1) * sizeof(CHAR16
);
252 // turn control over to the image
253 // TODO: (optionally) re-enable the EFI watchdog timer!
255 // close open file handles
257 ReturnStatus
= Status
= refit_call3_wrapper(BS
->StartImage
, ChildImageHandle
, NULL
, NULL
);
258 // control returns here when the child image calls Exit()
259 SPrint(ErrorInfo
, 255, L
"returned from %s", ImageTitle
);
260 if (CheckError(Status
, ErrorInfo
)) {
261 if (ErrorInStep
!= NULL
)
265 // re-open file handles
270 // unload the image, we don't care if it works or not...
272 Status
= refit_call1_wrapper(BS
->UnloadImage
, ChildImageHandle
);
274 MyFreePool(FullLoadOptions
);
276 } /* static EFI_STATUS StartEFIImageList() */
278 static EFI_STATUS
StartEFIImage(IN EFI_DEVICE_PATH
*DevicePath
,
279 IN CHAR16
*LoadOptions
, IN CHAR16
*LoadOptionsPrefix
,
280 IN CHAR16
*ImageTitle
, IN CHAR8 OSType
,
281 OUT UINTN
*ErrorInStep
,
284 EFI_DEVICE_PATH
*DevicePaths
[2];
286 DevicePaths
[0] = DevicePath
;
287 DevicePaths
[1] = NULL
;
288 return StartEFIImageList(DevicePaths
, LoadOptions
, LoadOptionsPrefix
, ImageTitle
, OSType
, ErrorInStep
, Verbose
);
289 } /* static EFI_STATUS StartEFIImage() */
292 // EFI OS loader functions
295 static VOID
StartLoader(IN LOADER_ENTRY
*Entry
)
297 UINTN ErrorInStep
= 0;
299 BeginExternalScreen(Entry
->UseGraphicsMode
, L
"Booting OS");
300 StartEFIImage(Entry
->DevicePath
, Entry
->LoadOptions
, Basename(Entry
->LoaderPath
),
301 Basename(Entry
->LoaderPath
), Entry
->OSType
, &ErrorInStep
, !Entry
->UseGraphicsMode
);
302 FinishExternalScreen();
305 // Locate an initrd or initramfs file that matches the kernel specified by LoaderPath.
306 // The matching file has a name that begins with "init" and includes the same version
307 // number string as is found in LoaderPath -- but not a longer version number string.
308 // For instance, if LoaderPath is \EFI\kernels\bzImage-3.3.0.efi, and if \EFI\kernels
309 // has a file called initramfs-3.3.0.img, this function will return the string
310 // '\EFI\kernels\initramfs-3.3.0.img'. If the directory ALSO contains the file
311 // initramfs-3.3.0-rc7.img or initramfs-13.3.0.img, those files will NOT match;
312 // however, initmine-3.3.0.img might match. (FindInitrd() returns the first match it
313 // finds). Thus, care should be taken to avoid placing duplicate matching files in
314 // the kernel's directory.
315 // If no matching init file can be found, returns NULL.
316 static CHAR16
* FindInitrd(IN CHAR16
*LoaderPath
, IN REFIT_VOLUME
*Volume
) {
317 CHAR16
*InitrdName
= NULL
, *FileName
, *KernelVersion
, *InitrdVersion
, *Path
;
318 REFIT_DIR_ITER DirIter
;
319 EFI_FILE_INFO
*DirEntry
;
321 FileName
= Basename(LoaderPath
);
322 KernelVersion
= FindNumbers(FileName
);
323 Path
= FindPath(LoaderPath
);
325 // Add trailing backslash for root directory; necessary on some systems, but must
326 // NOT be added to all directories, since on other systems, a trailing backslash on
327 // anything but the root directory causes them to flake out!
328 if (StrLen(Path
) == 0) {
329 MergeStrings(&Path
, L
"\\", 0);
331 DirIterOpen(Volume
->RootDir
, Path
, &DirIter
);
332 // Now add a trailing backslash if it was NOT added earlier, for consistency in
333 // building the InitrdName later....
334 if ((StrLen(Path
) > 0) && (Path
[StrLen(Path
) - 1] != L
'\\'))
335 MergeStrings(&Path
, L
"\\", 0);
336 while ((DirIterNext(&DirIter
, 2, L
"init*", &DirEntry
)) && (InitrdName
== NULL
)) {
337 InitrdVersion
= FindNumbers(DirEntry
->FileName
);
338 if (KernelVersion
!= NULL
) {
339 if (StriCmp(InitrdVersion
, KernelVersion
) == 0) {
340 MergeStrings(&InitrdName
, Path
, 0);
341 MergeStrings(&InitrdName
, DirEntry
->FileName
, 0);
344 if (InitrdVersion
== NULL
) {
345 MergeStrings(&InitrdName
, Path
, 0);
346 MergeStrings(&InitrdName
, DirEntry
->FileName
, 0);
349 MyFreePool(InitrdVersion
);
351 DirIterClose(&DirIter
);
353 // Note: Don't FreePool(FileName), since Basename returns a pointer WITHIN the string it's passed.
354 MyFreePool(KernelVersion
);
357 } // static CHAR16 * FindInitrd()
359 LOADER_ENTRY
* AddPreparedLoaderEntry(LOADER_ENTRY
*Entry
) {
360 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
363 } // LOADER_ENTRY * AddPreparedLoaderEntry()
365 // Creates a copy of a menu screen.
366 // Returns a pointer to the copy of the menu screen.
367 static REFIT_MENU_SCREEN
* CopyMenuScreen(REFIT_MENU_SCREEN
*Entry
) {
368 REFIT_MENU_SCREEN
*NewEntry
;
371 NewEntry
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
372 if ((Entry
!= NULL
) && (NewEntry
!= NULL
)) {
373 CopyMem(NewEntry
, Entry
, sizeof(REFIT_MENU_SCREEN
));
374 NewEntry
->Title
= (Entry
->Title
) ? StrDuplicate(Entry
->Title
) : NULL
;
375 NewEntry
->TimeoutText
= (Entry
->TimeoutText
) ? StrDuplicate(Entry
->TimeoutText
) : NULL
;
376 if (Entry
->TitleImage
!= NULL
) {
377 NewEntry
->TitleImage
= AllocatePool(sizeof(EG_IMAGE
));
378 if (NewEntry
->TitleImage
!= NULL
)
379 CopyMem(NewEntry
->TitleImage
, Entry
->TitleImage
, sizeof(EG_IMAGE
));
381 NewEntry
->InfoLines
= (CHAR16
**) AllocateZeroPool(Entry
->InfoLineCount
* (sizeof(CHAR16
*)));
382 for (i
= 0; i
< Entry
->InfoLineCount
&& NewEntry
->InfoLines
; i
++) {
383 NewEntry
->InfoLines
[i
] = (Entry
->InfoLines
[i
]) ? StrDuplicate(Entry
->InfoLines
[i
]) : NULL
;
385 NewEntry
->Entries
= (REFIT_MENU_ENTRY
**) AllocateZeroPool(Entry
->EntryCount
* (sizeof (REFIT_MENU_ENTRY
*)));
386 for (i
= 0; i
< Entry
->EntryCount
&& NewEntry
->Entries
; i
++) {
387 AddMenuEntry(NewEntry
, Entry
->Entries
[i
]);
391 } // static REFIT_MENU_SCREEN* CopyMenuScreen()
393 // Creates a copy of a menu entry. Intended to enable moving a stack-based
394 // menu entry (such as the ones for the "reboot" and "exit" functions) to
395 // to the heap. This enables easier deletion of the whole set of menu
396 // entries when re-scanning.
397 // Returns a pointer to the copy of the menu entry.
398 static REFIT_MENU_ENTRY
* CopyMenuEntry(REFIT_MENU_ENTRY
*Entry
) {
399 REFIT_MENU_ENTRY
*NewEntry
;
401 NewEntry
= AllocateZeroPool(sizeof(REFIT_MENU_ENTRY
));
402 if ((Entry
!= NULL
) && (NewEntry
!= NULL
)) {
403 CopyMem(NewEntry
, Entry
, sizeof(REFIT_MENU_ENTRY
));
404 NewEntry
->Title
= (Entry
->Title
) ? StrDuplicate(Entry
->Title
) : NULL
;
405 if (Entry
->BadgeImage
!= NULL
) {
406 NewEntry
->BadgeImage
= AllocatePool(sizeof(EG_IMAGE
));
407 if (NewEntry
->BadgeImage
!= NULL
)
408 CopyMem(NewEntry
->BadgeImage
, Entry
->BadgeImage
, sizeof(EG_IMAGE
));
410 if (Entry
->Image
!= NULL
) {
411 NewEntry
->Image
= AllocatePool(sizeof(EG_IMAGE
));
412 if (NewEntry
->Image
!= NULL
)
413 CopyMem(NewEntry
->Image
, Entry
->Image
, sizeof(EG_IMAGE
));
415 if (Entry
->SubScreen
!= NULL
) {
416 NewEntry
->SubScreen
= CopyMenuScreen(Entry
->SubScreen
);
420 } // REFIT_MENU_ENTRY* CopyMenuEntry()
422 // Creates a new LOADER_ENTRY data structure and populates it with
423 // default values from the specified Entry, or NULL values if Entry
424 // is unspecified (NULL).
425 // Returns a pointer to the new data structure, or NULL if it
426 // couldn't be allocated
427 LOADER_ENTRY
*InitializeLoaderEntry(IN LOADER_ENTRY
*Entry
) {
428 LOADER_ENTRY
*NewEntry
= NULL
;
430 NewEntry
= AllocateZeroPool(sizeof(LOADER_ENTRY
));
431 if (NewEntry
!= NULL
) {
432 NewEntry
->me
.Title
= NULL
;
433 NewEntry
->me
.Tag
= TAG_LOADER
;
434 NewEntry
->Enabled
= TRUE
;
435 NewEntry
->UseGraphicsMode
= FALSE
;
436 NewEntry
->OSType
= 0;
438 NewEntry
->LoaderPath
= (Entry
->LoaderPath
) ? StrDuplicate(Entry
->LoaderPath
) : NULL
;
439 NewEntry
->VolName
= (Entry
->VolName
) ? StrDuplicate(Entry
->VolName
) : NULL
;
440 NewEntry
->DevicePath
= Entry
->DevicePath
;
441 NewEntry
->UseGraphicsMode
= Entry
->UseGraphicsMode
;
442 NewEntry
->LoadOptions
= (Entry
->LoadOptions
) ? StrDuplicate(Entry
->LoadOptions
) : NULL
;
443 NewEntry
->InitrdPath
= (Entry
->InitrdPath
) ? StrDuplicate(Entry
->InitrdPath
) : NULL
;
447 } // LOADER_ENTRY *InitializeLoaderEntry()
449 // Prepare a REFIT_MENU_SCREEN data structure for a subscreen entry. This sets up
450 // the default entry that launches the boot loader using the same options as the
451 // main Entry does. Subsequent options can be added by the calling function.
452 // If a subscreen already exists in the Entry that's passed to this function,
453 // it's left unchanged and a pointer to it is returned.
454 // Returns a pointer to the new subscreen data structure, or NULL if there
455 // were problems allocating memory.
456 REFIT_MENU_SCREEN
*InitializeSubScreen(IN LOADER_ENTRY
*Entry
) {
457 CHAR16
*FileName
, *Temp
= NULL
;
458 REFIT_MENU_SCREEN
*SubScreen
= NULL
;
459 LOADER_ENTRY
*SubEntry
;
461 FileName
= Basename(Entry
->LoaderPath
);
462 if (Entry
->me
.SubScreen
== NULL
) { // No subscreen yet; initialize default entry....
463 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
464 if (SubScreen
!= NULL
) {
465 SubScreen
->Title
= AllocateZeroPool(sizeof(CHAR16
) * 256);
466 SPrint(SubScreen
->Title
, 255, L
"Boot Options for %s on %s",
467 (Entry
->Title
!= NULL
) ? Entry
->Title
: FileName
, Entry
->VolName
);
468 SubScreen
->TitleImage
= Entry
->me
.Image
;
470 SubEntry
= InitializeLoaderEntry(Entry
);
471 if (SubEntry
!= NULL
) {
472 SubEntry
->me
.Title
= L
"Boot using default options";
473 if ((SubEntry
->InitrdPath
!= NULL
) && (StrLen(SubEntry
->InitrdPath
) > 0) && (!StriSubCmp(L
"initrd", SubEntry
->LoadOptions
))) {
474 MergeStrings(&Temp
, L
"initrd=", 0);
475 MergeStrings(&Temp
, SubEntry
->InitrdPath
, 0);
476 MergeStrings(&SubEntry
->LoadOptions
, Temp
, L
' ');
479 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
480 } // if (SubEntry != NULL)
481 } // if (SubScreen != NULL)
482 } else { // existing subscreen; less initialization, and just add new entry later....
483 SubScreen
= Entry
->me
.SubScreen
;
486 } // REFIT_MENU_SCREEN *InitializeSubScreen()
488 VOID
GenerateSubScreen(LOADER_ENTRY
*Entry
, IN REFIT_VOLUME
*Volume
) {
489 REFIT_MENU_SCREEN
*SubScreen
;
490 LOADER_ENTRY
*SubEntry
;
491 CHAR16
*InitrdOption
= NULL
, *Temp
;
492 CHAR16 DiagsFileName
[256];
497 // create the submenu
498 if (StrLen(Entry
->Title
) == 0) {
499 MyFreePool(Entry
->Title
);
502 SubScreen
= InitializeSubScreen(Entry
);
504 // loader-specific submenu entries
505 if (Entry
->OSType
== 'M') { // entries for Mac OS X
507 SubEntry
= InitializeLoaderEntry(Entry
);
508 if (SubEntry
!= NULL
) {
509 SubEntry
->me
.Title
= L
"Boot Mac OS X with a 64-bit kernel";
510 SubEntry
->LoadOptions
= L
"arch=x86_64";
511 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
512 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
515 SubEntry
= InitializeLoaderEntry(Entry
);
516 if (SubEntry
!= NULL
) {
517 SubEntry
->me
.Title
= L
"Boot Mac OS X with a 32-bit kernel";
518 SubEntry
->LoadOptions
= L
"arch=i386";
519 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
520 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
524 if (!(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_SINGLEUSER
)) {
525 SubEntry
= InitializeLoaderEntry(Entry
);
526 if (SubEntry
!= NULL
) {
527 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode";
528 SubEntry
->UseGraphicsMode
= FALSE
;
529 SubEntry
->LoadOptions
= L
"-v";
530 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
534 SubEntry
= InitializeLoaderEntry(Entry
);
535 if (SubEntry
!= NULL
) {
536 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode (64-bit)";
537 SubEntry
->UseGraphicsMode
= FALSE
;
538 SubEntry
->LoadOptions
= L
"-v arch=x86_64";
539 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
542 SubEntry
= InitializeLoaderEntry(Entry
);
543 if (SubEntry
!= NULL
) {
544 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode (32-bit)";
545 SubEntry
->UseGraphicsMode
= FALSE
;
546 SubEntry
->LoadOptions
= L
"-v arch=i386";
547 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
551 SubEntry
= InitializeLoaderEntry(Entry
);
552 if (SubEntry
!= NULL
) {
553 SubEntry
->me
.Title
= L
"Boot Mac OS X in single user mode";
554 SubEntry
->UseGraphicsMode
= FALSE
;
555 SubEntry
->LoadOptions
= L
"-v -s";
556 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
560 // check for Apple hardware diagnostics
561 StrCpy(DiagsFileName
, L
"\\System\\Library\\CoreServices\\.diagnostics\\diags.efi");
562 if (FileExists(Volume
->RootDir
, DiagsFileName
) && !(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_HWTEST
)) {
563 SubEntry
= InitializeLoaderEntry(Entry
);
564 if (SubEntry
!= NULL
) {
565 SubEntry
->me
.Title
= L
"Run Apple Hardware Test";
566 MyFreePool(SubEntry
->LoaderPath
);
567 SubEntry
->LoaderPath
= StrDuplicate(DiagsFileName
);
568 SubEntry
->DevicePath
= FileDevicePath(Volume
->DeviceHandle
, SubEntry
->LoaderPath
);
569 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
570 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
572 } // if diagnostics entry found
574 } else if (Entry
->OSType
== 'L') { // entries for Linux kernels with EFI stub loaders
575 File
= ReadLinuxOptionsFile(Entry
->LoaderPath
, Volume
);
577 if ((Temp
= FindInitrd(Entry
->LoaderPath
, Volume
)) != NULL
) {
578 MergeStrings(&InitrdOption
, L
"initrd=", 0);
579 MergeStrings(&InitrdOption
, Temp
, 0);
581 TokenCount
= ReadTokenLine(File
, &TokenList
); // read and discard first entry, since it's
582 FreeTokenLine(&TokenList
, &TokenCount
); // set up by InitializeSubScreen(), earlier....
583 while ((TokenCount
= ReadTokenLine(File
, &TokenList
)) > 1) {
584 SubEntry
= InitializeLoaderEntry(Entry
);
585 SubEntry
->me
.Title
= StrDuplicate(TokenList
[0]);
586 MyFreePool(SubEntry
->LoadOptions
);
587 SubEntry
->LoadOptions
= StrDuplicate(TokenList
[1]);
588 MergeStrings(&SubEntry
->LoadOptions
, InitrdOption
, L
' ');
589 FreeTokenLine(&TokenList
, &TokenCount
);
590 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_LINUX
;
591 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
593 MyFreePool(InitrdOption
);
596 } // if Linux options file exists
598 } else if (Entry
->OSType
== 'E') { // entries for ELILO
599 SubEntry
= InitializeLoaderEntry(Entry
);
600 if (SubEntry
!= NULL
) {
601 SubEntry
->me
.Title
= L
"Run ELILO in interactive mode";
602 SubEntry
->LoadOptions
= L
"-p";
603 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
604 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
607 SubEntry
= InitializeLoaderEntry(Entry
);
608 if (SubEntry
!= NULL
) {
609 SubEntry
->me
.Title
= L
"Boot Linux for a 17\" iMac or a 15\" MacBook Pro (*)";
610 SubEntry
->UseGraphicsMode
= TRUE
;
611 SubEntry
->LoadOptions
= L
"-d 0 i17";
612 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
613 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
616 SubEntry
= InitializeLoaderEntry(Entry
);
617 if (SubEntry
!= NULL
) {
618 SubEntry
->me
.Title
= L
"Boot Linux for a 20\" iMac (*)";
619 SubEntry
->UseGraphicsMode
= TRUE
;
620 SubEntry
->LoadOptions
= L
"-d 0 i20";
621 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
622 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
625 SubEntry
= InitializeLoaderEntry(Entry
);
626 if (SubEntry
!= NULL
) {
627 SubEntry
->me
.Title
= L
"Boot Linux for a Mac Mini (*)";
628 SubEntry
->UseGraphicsMode
= TRUE
;
629 SubEntry
->LoadOptions
= L
"-d 0 mini";
630 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
631 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
634 AddMenuInfoLine(SubScreen
, L
"NOTE: This is an example. Entries");
635 AddMenuInfoLine(SubScreen
, L
"marked with (*) may not work.");
637 } else if (Entry
->OSType
== 'X') { // entries for xom.efi
638 // by default, skip the built-in selection and boot from hard disk only
639 Entry
->LoadOptions
= L
"-s -h";
641 SubEntry
= InitializeLoaderEntry(Entry
);
642 if (SubEntry
!= NULL
) {
643 SubEntry
->me
.Title
= L
"Boot Windows from Hard Disk";
644 SubEntry
->LoadOptions
= L
"-s -h";
645 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
646 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
649 SubEntry
= InitializeLoaderEntry(Entry
);
650 if (SubEntry
!= NULL
) {
651 SubEntry
->me
.Title
= L
"Boot Windows from CD-ROM";
652 SubEntry
->LoadOptions
= L
"-s -c";
653 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
654 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
657 SubEntry
= InitializeLoaderEntry(Entry
);
658 if (SubEntry
!= NULL
) {
659 SubEntry
->me
.Title
= L
"Run XOM in text mode";
660 SubEntry
->UseGraphicsMode
= FALSE
;
661 SubEntry
->LoadOptions
= L
"-v";
662 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
663 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
665 } // entries for xom.efi
666 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
667 Entry
->me
.SubScreen
= SubScreen
;
668 } // VOID GenerateSubScreen()
670 // Returns options for a Linux kernel. Reads them from an options file in the
671 // kernel's directory; and if present, adds an initrd= option for an initial
672 // RAM disk file with the same version number as the kernel file.
673 static CHAR16
* GetMainLinuxOptions(IN CHAR16
* LoaderPath
, IN REFIT_VOLUME
*Volume
) {
674 CHAR16
*Options
= NULL
, *InitrdName
, *InitrdOption
= NULL
;
676 Options
= GetFirstOptionsFromFile(LoaderPath
, Volume
);
677 InitrdName
= FindInitrd(LoaderPath
, Volume
);
678 if (InitrdName
!= NULL
) {
679 MergeStrings(&InitrdOption
, L
"initrd=", 0);
680 MergeStrings(&InitrdOption
, InitrdName
, 0);
682 MergeStrings(&Options
, InitrdOption
, ' ');
683 MyFreePool(InitrdOption
);
684 MyFreePool(InitrdName
);
686 } // static CHAR16 * GetMainLinuxOptions()
688 // Sets a few defaults for a loader entry -- mainly the icon, but also the OS type
689 // code and shortcut letter. For Linux EFI stub loaders, also sets kernel options
690 // that will (with luck) work fairly automatically.
691 VOID
SetLoaderDefaults(LOADER_ENTRY
*Entry
, CHAR16
*LoaderPath
, IN REFIT_VOLUME
*Volume
) {
692 CHAR16 IconFileName
[256];
693 CHAR16
*FileName
, *PathOnly
, *OSIconName
= NULL
, *Temp
;
694 CHAR16 ShortcutLetter
= 0;
696 FileName
= Basename(LoaderPath
);
697 PathOnly
= FindPath(LoaderPath
);
699 // locate a custom icon for the loader
700 StrCpy(IconFileName
, LoaderPath
);
701 ReplaceEfiExtension(IconFileName
, L
".icns");
702 if (FileExists(Volume
->RootDir
, IconFileName
)) {
703 Entry
->me
.Image
= LoadIcns(Volume
->RootDir
, IconFileName
, 128);
704 } else if ((StrLen(PathOnly
) == 0) && (Volume
->VolIconImage
!= NULL
)) {
705 Entry
->me
.Image
= Volume
->VolIconImage
;
706 } // icon matched to loader or volume
708 Temp
= FindLastDirName(LoaderPath
);
709 MergeStrings(&OSIconName
, Temp
, L
',');
711 if (OSIconName
!= NULL
) {
712 ShortcutLetter
= OSIconName
[0];
715 // detect specific loaders
716 if (StriSubCmp(L
"bzImage", LoaderPath
) || StriSubCmp(L
"vmlinuz", LoaderPath
)) {
717 MergeStrings(&OSIconName
, L
"linux", L
',');
719 if (ShortcutLetter
== 0)
720 ShortcutLetter
= 'L';
721 Entry
->LoadOptions
= GetMainLinuxOptions(LoaderPath
, Volume
);
722 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_LINUX
;
723 } else if (StriSubCmp(L
"refit", LoaderPath
)) {
724 MergeStrings(&OSIconName
, L
"refit", L
',');
726 ShortcutLetter
= 'R';
727 } else if (StriCmp(LoaderPath
, MACOSX_LOADER_PATH
) == 0) {
728 if (Volume
->VolIconImage
!= NULL
) { // custom icon file found
729 Entry
->me
.Image
= Volume
->VolIconImage
;
731 MergeStrings(&OSIconName
, L
"mac", L
',');
733 ShortcutLetter
= 'M';
734 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
735 } else if (StriCmp(FileName
, L
"diags.efi") == 0) {
736 MergeStrings(&OSIconName
, L
"hwtest", L
',');
737 } else if (StriCmp(FileName
, L
"e.efi") == 0 || StriCmp(FileName
, L
"elilo.efi") == 0) {
738 MergeStrings(&OSIconName
, L
"elilo,linux", L
',');
740 if (ShortcutLetter
== 0)
741 ShortcutLetter
= 'L';
742 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
743 } else if (StriSubCmp(L
"grub", FileName
)) {
745 ShortcutLetter
= 'G';
746 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_GRUB
;
747 } else if (StriCmp(FileName
, L
"cdboot.efi") == 0 ||
748 StriCmp(FileName
, L
"bootmgr.efi") == 0 ||
749 StriCmp(FileName
, L
"Bootmgfw.efi") == 0) {
750 MergeStrings(&OSIconName
, L
"win", L
',');
752 ShortcutLetter
= 'W';
753 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
754 } else if (StriCmp(FileName
, L
"xom.efi") == 0) {
755 MergeStrings(&OSIconName
, L
"xom,win", L
',');
756 Entry
->UseGraphicsMode
= TRUE
;
758 ShortcutLetter
= 'W';
759 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
762 if ((ShortcutLetter
>= 'a') && (ShortcutLetter
<= 'z'))
763 ShortcutLetter
= ShortcutLetter
- 'a' + 'A'; // convert lowercase to uppercase
764 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
765 if (Entry
->me
.Image
== NULL
)
766 Entry
->me
.Image
= LoadOSIcon(OSIconName
, L
"unknown", FALSE
);
767 MyFreePool(PathOnly
);
768 } // VOID SetLoaderDefaults()
770 // Add a specified EFI boot loader to the list, using automatic settings
771 // for icons, options, etc.
772 LOADER_ENTRY
* AddLoaderEntry(IN CHAR16
*LoaderPath
, IN CHAR16
*LoaderTitle
, IN REFIT_VOLUME
*Volume
) {
775 CleanUpPathNameSlashes(LoaderPath
);
776 Entry
= InitializeLoaderEntry(NULL
);
778 Entry
->Title
= StrDuplicate((LoaderTitle
!= NULL
) ? LoaderTitle
: LoaderPath
);
779 Entry
->me
.Title
= AllocateZeroPool(sizeof(CHAR16
) * 256);
780 SPrint(Entry
->me
.Title
, 255, L
"Boot %s from %s", (LoaderTitle
!= NULL
) ? LoaderTitle
: LoaderPath
, Volume
->VolName
);
782 Entry
->me
.BadgeImage
= Volume
->VolBadgeImage
;
783 if ((LoaderPath
!= NULL
) && (LoaderPath
[0] != L
'\\')) {
784 Entry
->LoaderPath
= StrDuplicate(L
"\\");
786 Entry
->LoaderPath
= NULL
;
788 MergeStrings(&(Entry
->LoaderPath
), LoaderPath
, 0);
789 Entry
->VolName
= Volume
->VolName
;
790 Entry
->DevicePath
= FileDevicePath(Volume
->DeviceHandle
, Entry
->LoaderPath
);
791 SetLoaderDefaults(Entry
, LoaderPath
, Volume
);
792 GenerateSubScreen(Entry
, Volume
);
793 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
797 } // LOADER_ENTRY * AddLoaderEntry()
799 // Returns -1 if (Time1 < Time2), +1 if (Time1 > Time2), or 0 if
800 // (Time1 == Time2). Precision is only to the nearest second; since
801 // this is used for sorting boot loader entries, differences smaller
802 // than this are likely to be meaningless (and unlikely!).
803 INTN
TimeComp(EFI_TIME
*Time1
, EFI_TIME
*Time2
) {
804 INT64 Time1InSeconds
, Time2InSeconds
;
806 // Following values are overestimates; I'm assuming 31 days in every month.
807 // This is fine for the purpose of this function, which has a limited
809 Time1InSeconds
= Time1
->Second
+ (Time1
->Minute
* 60) + (Time1
->Hour
* 3600) + (Time1
->Day
* 86400) +
810 (Time1
->Month
* 2678400) + ((Time1
->Year
- 1998) * 32140800);
811 Time2InSeconds
= Time2
->Second
+ (Time2
->Minute
* 60) + (Time2
->Hour
* 3600) + (Time2
->Day
* 86400) +
812 (Time2
->Month
* 2678400) + ((Time2
->Year
- 1998) * 32140800);
813 if (Time1InSeconds
< Time2InSeconds
)
815 else if (Time1InSeconds
> Time2InSeconds
)
821 // Adds a loader list element, keeping it sorted by date. Returns the new
822 // first element (the one with the most recent date).
823 static struct LOADER_LIST
* AddLoaderListEntry(struct LOADER_LIST
*LoaderList
, struct LOADER_LIST
*NewEntry
) {
824 struct LOADER_LIST
*LatestEntry
, *CurrentEntry
, *PrevEntry
= NULL
;
826 LatestEntry
= CurrentEntry
= LoaderList
;
827 if (LoaderList
== NULL
) {
828 LatestEntry
= NewEntry
;
830 while ((CurrentEntry
!= NULL
) && (TimeComp(&(NewEntry
->TimeStamp
), &(CurrentEntry
->TimeStamp
)) < 0)) {
831 PrevEntry
= CurrentEntry
;
832 CurrentEntry
= CurrentEntry
->NextEntry
;
834 NewEntry
->NextEntry
= CurrentEntry
;
835 if (PrevEntry
== NULL
) {
836 LatestEntry
= NewEntry
;
838 PrevEntry
->NextEntry
= NewEntry
;
841 return (LatestEntry
);
842 } // static VOID AddLoaderListEntry()
844 // Delete the LOADER_LIST linked list
845 static VOID
CleanUpLoaderList(struct LOADER_LIST
*LoaderList
) {
846 struct LOADER_LIST
*Temp
;
848 while (LoaderList
!= NULL
) {
850 LoaderList
= LoaderList
->NextEntry
;
851 MyFreePool(Temp
->FileName
);
854 } // static VOID CleanUpLoaderList()
856 // Scan an individual directory for EFI boot loader files and, if found,
857 // add them to the list. Sorts the entries within the loader directory
858 // so that the most recent one appears first in the list.
859 static VOID
ScanLoaderDir(IN REFIT_VOLUME
*Volume
, IN CHAR16
*Path
, IN CHAR16
*Pattern
)
862 REFIT_DIR_ITER DirIter
;
863 EFI_FILE_INFO
*DirEntry
;
864 CHAR16 FileName
[256], *Extension
;
865 struct LOADER_LIST
*LoaderList
= NULL
, *NewLoader
;
867 if ((!SelfDirPath
|| !Path
|| ((StriCmp(Path
, SelfDirPath
) == 0) && Volume
->DeviceHandle
!= SelfVolume
->DeviceHandle
) ||
868 (StriCmp(Path
, SelfDirPath
) != 0)) && (!IsIn(Path
, GlobalConfig
.DontScan
))) {
869 // look through contents of the directory
870 DirIterOpen(Volume
->RootDir
, Path
, &DirIter
);
871 while (DirIterNext(&DirIter
, 2, Pattern
, &DirEntry
)) {
872 Extension
= FindExtension(DirEntry
->FileName
);
873 if (DirEntry
->FileName
[0] == '.' ||
874 StriCmp(DirEntry
->FileName
, L
"TextMode.efi") == 0 ||
875 StriCmp(DirEntry
->FileName
, L
"ebounce.efi") == 0 ||
876 StriCmp(DirEntry
->FileName
, L
"GraphicsConsole.efi") == 0 ||
877 StriCmp(Extension
, L
".icns") == 0 ||
878 StriSubCmp(L
"shell", DirEntry
->FileName
))
879 continue; // skip this
882 SPrint(FileName
, 255, L
"\\%s\\%s", Path
, DirEntry
->FileName
);
884 SPrint(FileName
, 255, L
"\\%s", DirEntry
->FileName
);
885 CleanUpPathNameSlashes(FileName
);
886 NewLoader
= AllocateZeroPool(sizeof(struct LOADER_LIST
));
887 if (NewLoader
!= NULL
) {
888 NewLoader
->FileName
= StrDuplicate(FileName
);
889 NewLoader
->TimeStamp
= DirEntry
->ModificationTime
;
890 LoaderList
= AddLoaderListEntry(LoaderList
, NewLoader
);
892 MyFreePool(Extension
);
894 NewLoader
= LoaderList
;
895 while (NewLoader
!= NULL
) {
896 AddLoaderEntry(NewLoader
->FileName
, NULL
, Volume
);
897 NewLoader
= NewLoader
->NextEntry
;
899 CleanUpLoaderList(LoaderList
);
900 Status
= DirIterClose(&DirIter
);
901 if (Status
!= EFI_NOT_FOUND
) {
903 SPrint(FileName
, 255, L
"while scanning the %s directory", Path
);
905 StrCpy(FileName
, L
"while scanning the root directory");
906 CheckError(Status
, FileName
);
907 } // if (Status != EFI_NOT_FOUND)
908 } // if not scanning our own directory
909 } /* static VOID ScanLoaderDir() */
911 static VOID
ScanEfiFiles(REFIT_VOLUME
*Volume
) {
913 REFIT_DIR_ITER EfiDirIter
;
914 EFI_FILE_INFO
*EfiDirEntry
;
915 CHAR16 FileName
[256], *Directory
, *MatchPatterns
;
918 MatchPatterns
= StrDuplicate(LOADER_MATCH_PATTERNS
);
919 if (GlobalConfig
.ScanAllLinux
)
920 MergeStrings(&MatchPatterns
, LINUX_MATCH_PATTERNS
, L
',');
922 if ((Volume
->RootDir
!= NULL
) && (Volume
->VolName
!= NULL
)) {
923 // check for Mac OS X boot loader
924 if (!IsIn(L
"\\System\\Library\\CoreServices", GlobalConfig
.DontScan
)) {
925 StrCpy(FileName
, MACOSX_LOADER_PATH
);
926 if (FileExists(Volume
->RootDir
, FileName
)) {
927 AddLoaderEntry(FileName
, L
"Mac OS X", Volume
);
931 StrCpy(FileName
, L
"\\System\\Library\\CoreServices\\xom.efi");
932 if (FileExists(Volume
->RootDir
, FileName
)) {
933 AddLoaderEntry(FileName
, L
"Windows XP (XoM)", Volume
);
935 } // if Mac directory not in GlobalConfig.DontScan list
937 // check for Microsoft boot loader/menu
938 StrCpy(FileName
, L
"\\EFI\\Microsoft\\Boot\\Bootmgfw.efi");
939 if (FileExists(Volume
->RootDir
, FileName
) && !IsIn(L
"\\EFI\\Microsoft\\Boot", GlobalConfig
.DontScan
)) {
940 AddLoaderEntry(FileName
, L
"Microsoft EFI boot", Volume
);
943 // scan the root directory for EFI executables
944 ScanLoaderDir(Volume
, L
"\\", MatchPatterns
);
946 // scan subdirectories of the EFI directory (as per the standard)
947 DirIterOpen(Volume
->RootDir
, L
"EFI", &EfiDirIter
);
948 while (DirIterNext(&EfiDirIter
, 1, NULL
, &EfiDirEntry
)) {
949 if (StriCmp(EfiDirEntry
->FileName
, L
"tools") == 0 || EfiDirEntry
->FileName
[0] == '.')
950 continue; // skip this, doesn't contain boot loaders
951 SPrint(FileName
, 255, L
"\\EFI\\%s", EfiDirEntry
->FileName
);
952 ScanLoaderDir(Volume
, FileName
, MatchPatterns
);
954 Status
= DirIterClose(&EfiDirIter
);
955 if (Status
!= EFI_NOT_FOUND
)
956 CheckError(Status
, L
"while scanning the EFI directory");
958 // Scan user-specified (or additional default) directories....
960 while ((Directory
= FindCommaDelimited(GlobalConfig
.AlsoScan
, i
++)) != NULL
) {
961 CleanUpPathNameSlashes(Directory
);
962 Length
= StrLen(Directory
);
964 ScanLoaderDir(Volume
, Directory
, MatchPatterns
);
965 MyFreePool(Directory
);
968 } // static VOID ScanEfiFiles()
970 // Scan internal disks for valid EFI boot loaders....
971 static VOID
ScanInternal(VOID
) {
974 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
975 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_INTERNAL
) {
976 ScanEfiFiles(Volumes
[VolumeIndex
]);
979 } // static VOID ScanInternal()
981 // Scan external disks for valid EFI boot loaders....
982 static VOID
ScanExternal(VOID
) {
985 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
986 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_EXTERNAL
) {
987 ScanEfiFiles(Volumes
[VolumeIndex
]);
990 } // static VOID ScanExternal()
992 // Scan internal disks for valid EFI boot loaders....
993 static VOID
ScanOptical(VOID
) {
996 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
997 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_OPTICAL
) {
998 ScanEfiFiles(Volumes
[VolumeIndex
]);
1001 } // static VOID ScanOptical()
1004 // legacy boot functions
1007 static EFI_STATUS
ActivateMbrPartition(IN EFI_BLOCK_IO
*BlockIO
, IN UINTN PartitionIndex
)
1010 UINT8 SectorBuffer
[512];
1011 MBR_PARTITION_INFO
*MbrTable
, *EMbrTable
;
1012 UINT32 ExtBase
, ExtCurrent
, NextExtCurrent
;
1013 UINTN LogicalPartitionIndex
= 4;
1015 BOOLEAN HaveBootCode
;
1018 Status
= refit_call5_wrapper(BlockIO
->ReadBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, 0, 512, SectorBuffer
);
1019 if (EFI_ERROR(Status
))
1021 if (*((UINT16
*)(SectorBuffer
+ 510)) != 0xaa55)
1022 return EFI_NOT_FOUND
; // safety measure #1
1024 // add boot code if necessary
1025 HaveBootCode
= FALSE
;
1026 for (i
= 0; i
< MBR_BOOTCODE_SIZE
; i
++) {
1027 if (SectorBuffer
[i
] != 0) {
1028 HaveBootCode
= TRUE
;
1032 if (!HaveBootCode
) {
1033 // no boot code found in the MBR, add the syslinux MBR code
1034 SetMem(SectorBuffer
, MBR_BOOTCODE_SIZE
, 0);
1035 CopyMem(SectorBuffer
, syslinux_mbr
, SYSLINUX_MBR_SIZE
);
1038 // set the partition active
1039 MbrTable
= (MBR_PARTITION_INFO
*)(SectorBuffer
+ 446);
1041 for (i
= 0; i
< 4; i
++) {
1042 if (MbrTable
[i
].Flags
!= 0x00 && MbrTable
[i
].Flags
!= 0x80)
1043 return EFI_NOT_FOUND
; // safety measure #2
1044 if (i
== PartitionIndex
)
1045 MbrTable
[i
].Flags
= 0x80;
1046 else if (PartitionIndex
>= 4 && IS_EXTENDED_PART_TYPE(MbrTable
[i
].Type
)) {
1047 MbrTable
[i
].Flags
= 0x80;
1048 ExtBase
= MbrTable
[i
].StartLBA
;
1050 MbrTable
[i
].Flags
= 0x00;
1054 Status
= refit_call5_wrapper(BlockIO
->WriteBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, 0, 512, SectorBuffer
);
1055 if (EFI_ERROR(Status
))
1058 if (PartitionIndex
>= 4) {
1059 // we have to activate a logical partition, so walk the EMBR chain
1061 // NOTE: ExtBase was set above while looking at the MBR table
1062 for (ExtCurrent
= ExtBase
; ExtCurrent
; ExtCurrent
= NextExtCurrent
) {
1063 // read current EMBR
1064 Status
= refit_call5_wrapper(BlockIO
->ReadBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, ExtCurrent
, 512, SectorBuffer
);
1065 if (EFI_ERROR(Status
))
1067 if (*((UINT16
*)(SectorBuffer
+ 510)) != 0xaa55)
1068 return EFI_NOT_FOUND
; // safety measure #3
1070 // scan EMBR, set appropriate partition active
1071 EMbrTable
= (MBR_PARTITION_INFO
*)(SectorBuffer
+ 446);
1073 for (i
= 0; i
< 4; i
++) {
1074 if (EMbrTable
[i
].Flags
!= 0x00 && EMbrTable
[i
].Flags
!= 0x80)
1075 return EFI_NOT_FOUND
; // safety measure #4
1076 if (EMbrTable
[i
].StartLBA
== 0 || EMbrTable
[i
].Size
== 0)
1078 if (IS_EXTENDED_PART_TYPE(EMbrTable
[i
].Type
)) {
1079 // link to next EMBR
1080 NextExtCurrent
= ExtBase
+ EMbrTable
[i
].StartLBA
;
1081 EMbrTable
[i
].Flags
= (PartitionIndex
>= LogicalPartitionIndex
) ? 0x80 : 0x00;
1084 // logical partition
1085 EMbrTable
[i
].Flags
= (PartitionIndex
== LogicalPartitionIndex
) ? 0x80 : 0x00;
1086 LogicalPartitionIndex
++;
1090 // write current EMBR
1091 Status
= refit_call5_wrapper(BlockIO
->WriteBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, ExtCurrent
, 512, SectorBuffer
);
1092 if (EFI_ERROR(Status
))
1095 if (PartitionIndex
< LogicalPartitionIndex
)
1096 break; // stop the loop, no need to touch further EMBRs
1102 } /* static EFI_STATUS ActivateMbrPartition() */
1104 // early 2006 Core Duo / Core Solo models
1105 static UINT8 LegacyLoaderDevicePath1Data
[] = {
1106 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1107 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1108 0xFF, 0xFF, 0xF9, 0xFF, 0x00, 0x00, 0x00, 0x00,
1109 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1110 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1111 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1113 // mid-2006 Mac Pro (and probably other Core 2 models)
1114 static UINT8 LegacyLoaderDevicePath2Data
[] = {
1115 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1116 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1117 0xFF, 0xFF, 0xF7, 0xFF, 0x00, 0x00, 0x00, 0x00,
1118 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1119 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1120 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1122 // mid-2007 MBP ("Santa Rosa" based models)
1123 static UINT8 LegacyLoaderDevicePath3Data
[] = {
1124 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1125 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1126 0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
1127 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1128 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1129 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1132 static UINT8 LegacyLoaderDevicePath4Data
[] = {
1133 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1134 0x00, 0x00, 0xC0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1135 0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
1136 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1137 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1138 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1140 // late-2008 MB/MBP (NVidia chipset)
1141 static UINT8 LegacyLoaderDevicePath5Data
[] = {
1142 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1143 0x00, 0x40, 0xCB, 0xFF, 0x00, 0x00, 0x00, 0x00,
1144 0xFF, 0xBF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
1145 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1146 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1147 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1150 static EFI_DEVICE_PATH
*LegacyLoaderList
[] = {
1151 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath1Data
,
1152 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath2Data
,
1153 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath3Data
,
1154 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath4Data
,
1155 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath5Data
,
1159 #define MAX_DISCOVERED_PATHS (16)
1161 static VOID
StartLegacy(IN LEGACY_ENTRY
*Entry
)
1164 EG_IMAGE
*BootLogoImage
;
1165 UINTN ErrorInStep
= 0;
1166 EFI_DEVICE_PATH
*DiscoveredPathList
[MAX_DISCOVERED_PATHS
];
1168 BeginExternalScreen(TRUE
, L
"Booting Legacy OS (Mac mode)");
1170 BootLogoImage
= LoadOSIcon(Entry
->Volume
->OSIconName
, L
"legacy", TRUE
);
1171 if (BootLogoImage
!= NULL
)
1172 BltImageAlpha(BootLogoImage
,
1173 (UGAWidth
- BootLogoImage
->Width
) >> 1,
1174 (UGAHeight
- BootLogoImage
->Height
) >> 1,
1175 &StdBackgroundPixel
);
1177 if (Entry
->Volume
->IsMbrPartition
) {
1178 ActivateMbrPartition(Entry
->Volume
->WholeDiskBlockIO
, Entry
->Volume
->MbrPartitionIndex
);
1181 ExtractLegacyLoaderPaths(DiscoveredPathList
, MAX_DISCOVERED_PATHS
, LegacyLoaderList
);
1183 Status
= StartEFIImageList(DiscoveredPathList
, Entry
->LoadOptions
, NULL
, L
"legacy loader", 0, &ErrorInStep
, TRUE
);
1184 if (Status
== EFI_NOT_FOUND
) {
1185 if (ErrorInStep
== 1) {
1186 Print(L
"\nPlease make sure that you have the latest firmware update installed.\n");
1187 } else if (ErrorInStep
== 3) {
1188 Print(L
"\nThe firmware refused to boot from the selected volume. Note that external\n"
1189 L
"hard drives are not well-supported by Apple's firmware for legacy OS booting.\n");
1192 FinishExternalScreen();
1193 } /* static VOID StartLegacy() */
1195 // Start a device on a non-Mac using the EFI_LEGACY_BIOS_PROTOCOL
1196 #ifdef __MAKEWITH_TIANO
1197 static VOID
StartLegacyUEFI(IN LEGACY_ENTRY
*Entry
)
1199 BeginExternalScreen(TRUE
, L
"Booting Legacy OS (UEFI mode)");
1201 BdsLibConnectDevicePath (Entry
->BdsOption
->DevicePath
);
1202 BdsLibDoLegacyBoot(Entry
->BdsOption
);
1204 // If we get here, it means that there was a failure....
1205 Print(L
"Failure booting legacy (BIOS) OS.");
1207 FinishExternalScreen();
1208 } // static VOID StartLegacyUEFI()
1209 #endif // __MAKEWITH_TIANO
1211 static LEGACY_ENTRY
* AddLegacyEntry(IN CHAR16
*LoaderTitle
, IN REFIT_VOLUME
*Volume
)
1213 LEGACY_ENTRY
*Entry
, *SubEntry
;
1214 REFIT_MENU_SCREEN
*SubScreen
;
1216 CHAR16 ShortcutLetter
= 0;
1218 if (LoaderTitle
== NULL
) {
1219 if (Volume
->OSName
!= NULL
) {
1220 LoaderTitle
= Volume
->OSName
;
1221 if (LoaderTitle
[0] == 'W' || LoaderTitle
[0] == 'L')
1222 ShortcutLetter
= LoaderTitle
[0];
1224 LoaderTitle
= L
"Legacy OS";
1226 if (Volume
->VolName
!= NULL
)
1227 VolDesc
= Volume
->VolName
;
1229 VolDesc
= (Volume
->DiskKind
== DISK_KIND_OPTICAL
) ? L
"CD" : L
"HD";
1231 // prepare the menu entry
1232 Entry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1233 Entry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1234 SPrint(Entry
->me
.Title
, 255, L
"Boot %s from %s", LoaderTitle
, VolDesc
);
1235 Entry
->me
.Tag
= TAG_LEGACY
;
1237 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1238 Entry
->me
.Image
= LoadOSIcon(Volume
->OSIconName
, L
"legacy", FALSE
);
1239 Entry
->me
.BadgeImage
= Volume
->VolBadgeImage
;
1240 Entry
->Volume
= Volume
;
1241 Entry
->LoadOptions
= (Volume
->DiskKind
== DISK_KIND_OPTICAL
) ? L
"CD" :
1242 ((Volume
->DiskKind
== DISK_KIND_EXTERNAL
) ? L
"USB" : L
"HD");
1243 Entry
->Enabled
= TRUE
;
1245 // create the submenu
1246 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
1247 SubScreen
->Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1248 SPrint(SubScreen
->Title
, 255, L
"Boot Options for %s on %s", LoaderTitle
, VolDesc
);
1249 SubScreen
->TitleImage
= Entry
->me
.Image
;
1252 SubEntry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1253 SubEntry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1254 SPrint(SubEntry
->me
.Title
, 255, L
"Boot %s", LoaderTitle
);
1255 SubEntry
->me
.Tag
= TAG_LEGACY
;
1256 SubEntry
->Volume
= Entry
->Volume
;
1257 SubEntry
->LoadOptions
= Entry
->LoadOptions
;
1258 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
1260 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
1261 Entry
->me
.SubScreen
= SubScreen
;
1262 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1264 } /* static LEGACY_ENTRY * AddLegacyEntry() */
1267 #ifdef __MAKEWITH_TIANO
1268 // default volume badge icon based on disk kind
1269 static EG_IMAGE
* GetDiskBadge(IN UINTN DiskType
) {
1270 EG_IMAGE
* Badge
= NULL
;
1274 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_INTERNAL
);
1277 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_EXTERNAL
);
1280 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_OPTICAL
);
1284 } // static EG_IMAGE * GetDiskBadge()
1287 Create a rEFInd boot option from a Legacy BIOS protocol option.
1289 static LEGACY_ENTRY
* AddLegacyEntryUEFI(BDS_COMMON_OPTION
*BdsOption
, IN UINT16 DiskType
)
1291 LEGACY_ENTRY
*Entry
, *SubEntry
;
1292 REFIT_MENU_SCREEN
*SubScreen
;
1293 CHAR16 ShortcutLetter
= 0;
1294 CHAR16
*LegacyDescription
= BdsOption
->Description
;
1296 // ScanVolume(Volume);
1298 // prepare the menu entry
1299 Entry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1300 Entry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1301 SPrint(Entry
->me
.Title
, 255, L
"Boot legacy target %s", LegacyDescription
);
1302 Entry
->me
.Tag
= TAG_LEGACY_UEFI
;
1304 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1305 Entry
->me
.Image
= LoadOSIcon(L
"legacy", L
"legacy", TRUE
);
1306 Entry
->LoadOptions
= (DiskType
== BBS_CDROM
) ? L
"CD" :
1307 ((DiskType
== BBS_USB
) ? L
"USB" : L
"HD");
1308 Entry
->me
.BadgeImage
= GetDiskBadge(DiskType
);
1309 // Entry->me.BadgeImage = Volume->VolBadgeImage;
1310 Entry
->BdsOption
= BdsOption
;
1311 Entry
->Enabled
= TRUE
;
1313 // create the submenu
1314 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
1315 SubScreen
->Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1316 SPrint(SubScreen
->Title
, 255, L
"No boot options for legacy target");
1317 SubScreen
->TitleImage
= Entry
->me
.Image
;
1320 SubEntry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1321 SubEntry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1322 SPrint(SubEntry
->me
.Title
, 255, L
"Boot %s", LegacyDescription
);
1323 SubEntry
->me
.Tag
= TAG_LEGACY_UEFI
;
1324 Entry
->BdsOption
= BdsOption
;
1325 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
1327 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
1328 Entry
->me
.SubScreen
= SubScreen
;
1329 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1331 } /* static LEGACY_ENTRY * AddLegacyEntryUEFI() */
1334 Scan for legacy BIOS targets on machines that implement EFI_LEGACY_BIOS_PROTOCOL.
1335 In testing, protocol has not been implemented on Macs but has been
1336 implemented on several Dell PCs and an ASUS motherboard.
1337 Restricts output to disks of the specified DiskType.
1339 static VOID
ScanLegacyUEFI(IN UINTN DiskType
)
1342 EFI_LEGACY_BIOS_PROTOCOL
*LegacyBios
;
1343 UINT16
*BootOrder
= NULL
;
1345 CHAR16 BootOption
[10];
1346 UINTN BootOrderSize
= 0;
1348 BDS_COMMON_OPTION
*BdsOption
;
1349 LIST_ENTRY TempList
;
1350 BBS_BBS_DEVICE_PATH
* BbsDevicePath
= NULL
;
1351 // REFIT_VOLUME Volume;
1353 InitializeListHead (&TempList
);
1354 ZeroMem (Buffer
, sizeof (Buffer
));
1356 // If LegacyBios protocol is not implemented on this platform, then
1357 //we do not support this type of legacy boot on this machine.
1358 Status
= gBS
->LocateProtocol(&gEfiLegacyBootProtocolGuid
, NULL
, (VOID
**) &LegacyBios
);
1359 if (EFI_ERROR (Status
))
1362 // Grab the boot order
1363 BootOrder
= BdsLibGetVariableAndSize(L
"BootOrder", &gEfiGlobalVariableGuid
, &BootOrderSize
);
1364 if (BootOrder
== NULL
) {
1369 while (Index
< BootOrderSize
/ sizeof (UINT16
))
1371 // Grab each boot option variable from the boot order, and convert
1372 // the variable into a BDS boot option
1373 UnicodeSPrint (BootOption
, sizeof (BootOption
), L
"Boot%04x", BootOrder
[Index
]);
1374 BdsOption
= BdsLibVariableToOption (&TempList
, BootOption
);
1376 if (BdsOption
!= NULL
) {
1377 BbsDevicePath
= (BBS_BBS_DEVICE_PATH
*)BdsOption
->DevicePath
;
1379 // Only add the entry if it is of a requested type (e.g. USB, HD)
1381 // Two checks necessary because some systems return EFI boot loaders
1382 // with a DeviceType value that would inappropriately include them
1383 // as legacy loaders....
1384 if ((BbsDevicePath
->DeviceType
== DiskType
) && (BdsOption
->DevicePath
->Type
== DEVICE_TYPE_BIOS
)) {
1385 AddLegacyEntryUEFI(BdsOption
, BbsDevicePath
->DeviceType
);
1390 } /* static VOID ScanLegacyUEFI() */
1392 static VOID
ScanLegacyUEFI(IN UINTN DiskType
){}
1393 #endif // __MAKEWITH_TIANO
1395 static VOID
ScanLegacyVolume(REFIT_VOLUME
*Volume
, UINTN VolumeIndex
) {
1397 BOOLEAN ShowVolume
, HideIfOthersFound
;
1400 HideIfOthersFound
= FALSE
;
1401 if (Volume
->IsAppleLegacy
) {
1403 HideIfOthersFound
= TRUE
;
1404 } else if (Volume
->HasBootCode
) {
1406 if (Volume
->BlockIO
== Volume
->WholeDiskBlockIO
&&
1407 Volume
->BlockIOOffset
== 0 &&
1408 Volume
->OSName
== NULL
)
1409 // this is a whole disk (MBR) entry; hide if we have entries for partitions
1410 HideIfOthersFound
= TRUE
;
1412 if (HideIfOthersFound
) {
1413 // check for other bootable entries on the same disk
1414 for (VolumeIndex2
= 0; VolumeIndex2
< VolumesCount
; VolumeIndex2
++) {
1415 if (VolumeIndex2
!= VolumeIndex
&& Volumes
[VolumeIndex2
]->HasBootCode
&&
1416 Volumes
[VolumeIndex2
]->WholeDiskBlockIO
== Volume
->WholeDiskBlockIO
)
1422 AddLegacyEntry(NULL
, Volume
);
1423 } // static VOID ScanLegacyVolume()
1425 // Scan attached optical discs for legacy (BIOS) boot code
1426 // and add anything found to the list....
1427 static VOID
ScanLegacyDisc(VOID
)
1430 REFIT_VOLUME
*Volume
;
1432 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
) {
1433 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1434 Volume
= Volumes
[VolumeIndex
];
1435 if (Volume
->DiskKind
== DISK_KIND_OPTICAL
)
1436 ScanLegacyVolume(Volume
, VolumeIndex
);
1438 } else if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
1439 ScanLegacyUEFI(BBS_CDROM
);
1441 } /* static VOID ScanLegacyDisc() */
1443 // Scan internal hard disks for legacy (BIOS) boot code
1444 // and add anything found to the list....
1445 static VOID
ScanLegacyInternal(VOID
)
1448 REFIT_VOLUME
*Volume
;
1450 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
) {
1451 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1452 Volume
= Volumes
[VolumeIndex
];
1453 if (Volume
->DiskKind
== DISK_KIND_INTERNAL
)
1454 ScanLegacyVolume(Volume
, VolumeIndex
);
1456 } else if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
1457 ScanLegacyUEFI(BBS_HARDDISK
);
1459 } /* static VOID ScanLegacyInternal() */
1461 // Scan external disks for legacy (BIOS) boot code
1462 // and add anything found to the list....
1463 static VOID
ScanLegacyExternal(VOID
)
1466 REFIT_VOLUME
*Volume
;
1468 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
) {
1469 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1470 Volume
= Volumes
[VolumeIndex
];
1471 if (Volume
->DiskKind
== DISK_KIND_EXTERNAL
)
1472 ScanLegacyVolume(Volume
, VolumeIndex
);
1474 } else if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
1475 ScanLegacyUEFI(BBS_USB
);
1477 } /* static VOID ScanLegacyExternal() */
1480 // pre-boot tool functions
1483 static VOID
StartTool(IN LOADER_ENTRY
*Entry
)
1485 BeginExternalScreen(Entry
->UseGraphicsMode
, Entry
->me
.Title
+ 6); // assumes "Start <title>" as assigned below
1486 StartEFIImage(Entry
->DevicePath
, Entry
->LoadOptions
, Basename(Entry
->LoaderPath
),
1487 Basename(Entry
->LoaderPath
), Entry
->OSType
, NULL
, TRUE
);
1488 FinishExternalScreen();
1489 } /* static VOID StartTool() */
1491 static LOADER_ENTRY
* AddToolEntry(EFI_HANDLE DeviceHandle
, IN CHAR16
*LoaderPath
, IN CHAR16
*LoaderTitle
, IN EG_IMAGE
*Image
,
1492 IN CHAR16 ShortcutLetter
, IN BOOLEAN UseGraphicsMode
)
1494 LOADER_ENTRY
*Entry
;
1495 CHAR16
*TitleStr
= NULL
;
1497 Entry
= AllocateZeroPool(sizeof(LOADER_ENTRY
));
1499 MergeStrings(&TitleStr
, L
"Start ", 0);
1500 MergeStrings(&TitleStr
, LoaderTitle
, 0);
1501 Entry
->me
.Title
= TitleStr
;
1502 Entry
->me
.Tag
= TAG_TOOL
;
1504 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1505 Entry
->me
.Image
= Image
;
1506 Entry
->LoaderPath
= (LoaderPath
) ? StrDuplicate(LoaderPath
) : NULL
;
1507 Entry
->DevicePath
= FileDevicePath(DeviceHandle
, Entry
->LoaderPath
);
1508 Entry
->UseGraphicsMode
= UseGraphicsMode
;
1510 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1512 } /* static LOADER_ENTRY * AddToolEntry() */
1515 // pre-boot driver functions
1518 static UINTN
ScanDriverDir(IN CHAR16
*Path
)
1521 REFIT_DIR_ITER DirIter
;
1523 EFI_FILE_INFO
*DirEntry
;
1524 CHAR16 FileName
[256];
1526 CleanUpPathNameSlashes(Path
);
1527 // look through contents of the directory
1528 DirIterOpen(SelfRootDir
, Path
, &DirIter
);
1529 while (DirIterNext(&DirIter
, 2, LOADER_MATCH_PATTERNS
, &DirEntry
)) {
1530 if (DirEntry
->FileName
[0] == '.')
1531 continue; // skip this
1533 SPrint(FileName
, 255, L
"%s\\%s", Path
, DirEntry
->FileName
);
1535 Status
= StartEFIImage(FileDevicePath(SelfLoadedImage
->DeviceHandle
, FileName
),
1536 L
"", DirEntry
->FileName
, DirEntry
->FileName
, 0, NULL
, FALSE
);
1538 Status
= DirIterClose(&DirIter
);
1539 if (Status
!= EFI_NOT_FOUND
) {
1540 SPrint(FileName
, 255, L
"while scanning the %s directory", Path
);
1541 CheckError(Status
, FileName
);
1546 #ifdef __MAKEWITH_GNUEFI
1547 static EFI_STATUS
ConnectAllDriversToAllControllers(VOID
)
1550 UINTN AllHandleCount
;
1551 EFI_HANDLE
*AllHandleBuffer
;
1554 EFI_HANDLE
*HandleBuffer
;
1560 Status
= LibLocateHandle(AllHandles
,
1565 if (EFI_ERROR(Status
))
1568 for (Index
= 0; Index
< AllHandleCount
; Index
++) {
1570 // Scan the handle database
1572 Status
= LibScanHandleDatabase(NULL
,
1574 AllHandleBuffer
[Index
],
1579 if (EFI_ERROR (Status
))
1583 if (HandleType
[Index
] & EFI_HANDLE_TYPE_DRIVER_BINDING_HANDLE
)
1585 if (HandleType
[Index
] & EFI_HANDLE_TYPE_IMAGE_HANDLE
)
1590 for (HandleIndex
= 0; HandleIndex
< HandleCount
; HandleIndex
++) {
1591 if (HandleType
[HandleIndex
] & EFI_HANDLE_TYPE_PARENT_HANDLE
)
1596 if (HandleType
[Index
] & EFI_HANDLE_TYPE_DEVICE_HANDLE
) {
1597 Status
= refit_call4_wrapper(BS
->ConnectController
,
1598 AllHandleBuffer
[Index
],
1606 MyFreePool (HandleBuffer
);
1607 MyFreePool (HandleType
);
1611 MyFreePool (AllHandleBuffer
);
1613 } /* EFI_STATUS ConnectAllDriversToAllControllers() */
1615 static EFI_STATUS
ConnectAllDriversToAllControllers(VOID
) {
1616 BdsLibConnectAllDriversToAllControllers();
1621 // Load all EFI drivers from rEFInd's "drivers" subdirectory and from the
1622 // directories specified by the user in the "scan_driver_dirs" configuration
1624 static VOID
LoadDrivers(VOID
)
1626 CHAR16
*Directory
, *SelfDirectory
;
1627 UINTN i
= 0, Length
, NumFound
= 0;
1629 // load drivers from the subdirectories of rEFInd's home directory specified
1630 // in the DRIVER_DIRS constant.
1631 while ((Directory
= FindCommaDelimited(DRIVER_DIRS
, i
++)) != NULL
) {
1632 SelfDirectory
= SelfDirPath
? StrDuplicate(SelfDirPath
) : NULL
;
1633 CleanUpPathNameSlashes(SelfDirectory
);
1634 MergeStrings(&SelfDirectory
, Directory
, L
'\\');
1635 NumFound
+= ScanDriverDir(SelfDirectory
);
1636 MyFreePool(Directory
);
1637 MyFreePool(SelfDirectory
);
1640 // Scan additional user-specified driver directories....
1642 while ((Directory
= FindCommaDelimited(GlobalConfig
.DriverDirs
, i
++)) != NULL
) {
1643 CleanUpPathNameSlashes(Directory
);
1644 Length
= StrLen(Directory
);
1646 NumFound
+= ScanDriverDir(Directory
);
1648 MyFreePool(Directory
);
1651 // connect all devices
1653 ConnectAllDriversToAllControllers();
1654 } /* static VOID LoadDrivers() */
1656 // Determine what (if any) type of legacy (BIOS) boot support is available
1657 static VOID
FindLegacyBootType(VOID
) {
1658 #ifdef __MAKEWITH_TIANO
1660 EFI_LEGACY_BIOS_PROTOCOL
*LegacyBios
;
1663 GlobalConfig
.LegacyType
= LEGACY_TYPE_NONE
;
1665 // UEFI-style legacy BIOS support is available only with the TianoCore EDK2
1666 // build environment, and then only with some implementations....
1667 #ifdef __MAKEWITH_TIANO
1668 Status
= gBS
->LocateProtocol (&gEfiLegacyBootProtocolGuid
, NULL
, (VOID
**) &LegacyBios
);
1669 if (!EFI_ERROR (Status
))
1670 GlobalConfig
.LegacyType
= LEGACY_TYPE_UEFI
;
1673 // Macs have their own system. If the firmware vendor code contains the
1674 // string "Apple", assume it's available. Note that this overrides the
1675 // UEFI type, and might yield false positives if the vendor string
1676 // contains "Apple" as part of something bigger, so this isn't 100%
1678 if (StriSubCmp(L
"Apple", ST
->FirmwareVendor
))
1679 GlobalConfig
.LegacyType
= LEGACY_TYPE_MAC
;
1680 } // static VOID FindLegacyBootType
1682 // Warn the user if legacy OS scans are enabled but the firmware or this
1683 // application can't support them....
1684 static VOID
WarnIfLegacyProblems() {
1685 BOOLEAN found
= FALSE
;
1688 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_NONE
) {
1690 if (GlobalConfig
.ScanFor
[i
] == 'h' || GlobalConfig
.ScanFor
[i
] == 'b' || GlobalConfig
.ScanFor
[i
] == 'c')
1693 } while ((i
< NUM_SCAN_OPTIONS
) && (!found
));
1695 Print(L
"NOTE: refind.conf's 'scanfor' line specifies scanning for one or more legacy\n");
1696 Print(L
"(BIOS) boot options; however, this is not possible because ");
1697 #ifdef __MAKEWITH_TIANO
1698 Print(L
"your computer lacks\n");
1699 Print(L
"the necessary Compatibility Support Module (CSM) support.\n");
1701 Print(L
"this program was\n");
1702 Print(L
"compiled without the necessary support. Please visit\n");
1703 Print(L
"http://www.rodsbooks.com/refind/getting.html and download and install a rEFInd\n");
1704 Print(L
"binary built with the TianoCore EDK2 to enable legacy boot support.\n");
1708 } // if no legacy support
1709 } // static VOID WarnIfLegacyProblems()
1711 // Locates boot loaders. NOTE: This assumes that GlobalConfig.LegacyType is set correctly.
1712 static VOID
ScanForBootloaders(VOID
) {
1717 // scan for loaders and tools, add them to the menu
1718 for (i
= 0; i
< NUM_SCAN_OPTIONS
; i
++) {
1719 switch(GlobalConfig
.ScanFor
[i
]) {
1724 ScanLegacyInternal();
1727 ScanLegacyExternal();
1730 ScanUserConfigured();
1744 // assign shortcut keys
1745 for (i
= 0; i
< MainMenu
.EntryCount
&& MainMenu
.Entries
[i
]->Row
== 0 && i
< 9; i
++)
1746 MainMenu
.Entries
[i
]->ShortcutDigit
= (CHAR16
)('1' + i
);
1748 // wait for user ACK when there were errors
1749 FinishTextScreen(FALSE
);
1750 } // static VOID ScanForBootloaders()
1752 // Add the second-row tags containing built-in and external tools (EFI shell,
1754 static VOID
ScanForTools(VOID
) {
1755 CHAR16
*FileName
= NULL
, Description
[256];
1756 REFIT_MENU_ENTRY
*TempMenuEntry
;
1757 UINTN i
, j
, VolumeIndex
;
1759 for (i
= 0; i
< NUM_TOOLS
; i
++) {
1760 switch(GlobalConfig
.ShowTools
[i
]) {
1762 TempMenuEntry
= CopyMenuEntry(&MenuEntryShutdown
);
1763 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_SHUTDOWN
);
1764 AddMenuEntry(&MainMenu
, TempMenuEntry
);
1767 TempMenuEntry
= CopyMenuEntry(&MenuEntryReset
);
1768 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_RESET
);
1769 AddMenuEntry(&MainMenu
, TempMenuEntry
);
1772 TempMenuEntry
= CopyMenuEntry(&MenuEntryAbout
);
1773 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT
);
1774 AddMenuEntry(&MainMenu
, TempMenuEntry
);
1777 TempMenuEntry
= CopyMenuEntry(&MenuEntryExit
);
1778 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_EXIT
);
1779 AddMenuEntry(&MainMenu
, TempMenuEntry
);
1783 MyFreePool(FileName
);
1784 while ((FileName
= FindCommaDelimited(SHELL_NAMES
, j
++)) != NULL
) {
1785 if (FileExists(SelfRootDir
, FileName
)) {
1786 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, L
"EFI Shell", BuiltinIcon(BUILTIN_ICON_TOOL_SHELL
),
1792 MyFreePool(FileName
);
1794 MergeStrings(&FileName
, L
"\\efi\\tools\\gptsync.efi", 0);
1795 if (FileExists(SelfRootDir
, FileName
)) {
1796 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, L
"Make Hybrid MBR", BuiltinIcon(BUILTIN_ICON_TOOL_PART
), 'P', FALSE
);
1799 case TAG_APPLE_RECOVERY
:
1800 MyFreePool(FileName
);
1802 MergeStrings(&FileName
, L
"\\com.apple.recovery.boot\\boot.efi", 0);
1803 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1804 if ((Volumes
[VolumeIndex
]->RootDir
!= NULL
) && (FileExists(Volumes
[VolumeIndex
]->RootDir
, FileName
))) {
1805 SPrint(Description
, 255, L
"Apple Recovery on %s", Volumes
[VolumeIndex
]->VolName
);
1806 AddToolEntry(Volumes
[VolumeIndex
]->DeviceHandle
, FileName
, Description
,
1807 BuiltinIcon(BUILTIN_ICON_TOOL_APPLE_RESCUE
), 'R', TRUE
);
1813 MyFreePool(FileName
);
1814 while ((FileName
= FindCommaDelimited(MOK_NAMES
, j
++)) != NULL
) {
1815 if (FileExists(SelfRootDir
, FileName
)) {
1816 SPrint(Description
, 255, L
"MOK Key Manager at %s", FileName
);
1817 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, Description
,
1818 BuiltinIcon(BUILTIN_ICON_TOOL_MOK_TOOL
), 'S', FALSE
);
1821 if (FileExists(SelfDir
, L
"MokManager.efi")) {
1822 MyFreePool(FileName
);
1823 FileName
= StrDuplicate(SelfDirPath
);
1824 MergeStrings(&FileName
, L
"\\MokManager.efi", 0);
1825 SPrint(Description
, 255, L
"MOK Key Manager at %s", FileName
);
1826 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, Description
,
1827 BuiltinIcon(BUILTIN_ICON_TOOL_MOK_TOOL
), 'S', FALSE
);
1831 MyFreePool(FileName
);
1834 } // static VOID ScanForTools
1836 // Rescan for boot loaders
1837 VOID
RescanAll(VOID
) {
1844 egDisplayMessage(L
"Scanning for new boot loaders; please wait....", &BGColor
);
1845 FreeList((VOID
***) &(MainMenu
.Entries
), &MainMenu
.EntryCount
);
1846 MainMenu
.Entries
= NULL
;
1847 MainMenu
.EntryCount
= 0;
1849 ConnectAllDriversToAllControllers();
1851 ScanForBootloaders();
1854 } // VOID RescanAll()
1856 #ifndef __MAKEWITH_GNUEFI
1858 // Minimal initialization function
1859 static VOID
InitializeLib(IN EFI_HANDLE ImageHandle
, IN EFI_SYSTEM_TABLE
*SystemTable
) {
1861 // gImageHandle = ImageHandle;
1862 gBS
= SystemTable
->BootServices
;
1863 // gRS = SystemTable->RuntimeServices;
1864 gRT
= SystemTable
->RuntimeServices
; // Some BDS functions need gRT to be set
1865 EfiGetSystemConfigurationTable (&gEfiDxeServicesTableGuid
, (VOID
**) &gDS
);
1867 InitializeConsoleSim();
1877 efi_main (IN EFI_HANDLE ImageHandle
, IN EFI_SYSTEM_TABLE
*SystemTable
)
1880 BOOLEAN MainLoopRunning
= TRUE
;
1881 REFIT_MENU_ENTRY
*ChosenEntry
;
1887 InitializeLib(ImageHandle
, SystemTable
);
1889 Status
= InitRefitLib(ImageHandle
);
1890 if (EFI_ERROR(Status
))
1893 // read configuration
1894 CopyMem(GlobalConfig
.ScanFor
, "ieom ", NUM_SCAN_OPTIONS
);
1895 FindLegacyBootType();
1896 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
)
1897 CopyMem(GlobalConfig
.ScanFor
, "ihebocm ", NUM_SCAN_OPTIONS
);
1899 WarnIfLegacyProblems();
1900 MainMenu
.TimeoutSeconds
= GlobalConfig
.Timeout
;
1902 // disable EFI watchdog timer
1903 refit_call4_wrapper(BS
->SetWatchdogTimer
, 0x0000, 0x0000, 0x0000, NULL
);
1905 // further bootstrap (now with config available)
1909 ScanForBootloaders();
1912 if (GlobalConfig
.ScanDelay
> 0) {
1917 egDisplayMessage(L
"Pausing before disk scan; please wait....", &BGColor
);
1918 for (i
= 0; i
< GlobalConfig
.ScanDelay
; i
++)
1919 refit_call1_wrapper(BS
->Stall
, 1000000);
1923 Selection
= StrDuplicate(GlobalConfig
.DefaultSelection
);
1924 while (MainLoopRunning
) {
1925 MenuExit
= RunMainMenu(&MainMenu
, Selection
, &ChosenEntry
);
1927 // The Escape key triggers a re-scan operation....
1928 if (MenuExit
== MENU_EXIT_ESCAPE
) {
1933 switch (ChosenEntry
->Tag
) {
1935 case TAG_REBOOT
: // Reboot
1937 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);
1938 MainLoopRunning
= FALSE
; // just in case we get this far
1941 case TAG_SHUTDOWN
: // Shut Down
1943 refit_call4_wrapper(RT
->ResetSystem
, EfiResetShutdown
, EFI_SUCCESS
, 0, NULL
);
1944 MainLoopRunning
= FALSE
; // just in case we get this far
1947 case TAG_ABOUT
: // About rEFInd
1951 case TAG_LOADER
: // Boot OS via .EFI loader
1952 StartLoader((LOADER_ENTRY
*)ChosenEntry
);
1955 case TAG_LEGACY
: // Boot legacy OS
1956 StartLegacy((LEGACY_ENTRY
*)ChosenEntry
);
1959 #ifdef __MAKEWITH_TIANO
1960 case TAG_LEGACY_UEFI
: // Boot a legacy OS on a non-Mac
1961 StartLegacyUEFI((LEGACY_ENTRY
*)ChosenEntry
);
1963 #endif // __MAKEWITH_TIANO
1965 case TAG_TOOL
: // Start a EFI tool
1966 StartTool((LOADER_ENTRY
*)ChosenEntry
);
1969 case TAG_EXIT
: // Terminate rEFInd
1970 BeginTextScreen(L
" ");
1975 MyFreePool(Selection
);
1976 Selection
= (ChosenEntry
->Title
) ? StrDuplicate(ChosenEntry
->Title
) : NULL
;
1979 // If we end up here, things have gone wrong. Try to reboot, and if that
1980 // fails, go into an endless loop.
1981 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);