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,\\EFI\\ubuntu\\MokManager.efi,\\EFI\\suse\\MokManager"
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
, 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
.DontScanDirs
))) {
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(Extension
, L
".icns") == 0 ||
875 StriSubCmp(L
"shell", DirEntry
->FileName
) ||
876 IsIn(DirEntry
->FileName
, GlobalConfig
.DontScanFiles
))
877 continue; // skip this
880 SPrint(FileName
, 255, L
"\\%s\\%s", Path
, DirEntry
->FileName
);
882 SPrint(FileName
, 255, L
"\\%s", DirEntry
->FileName
);
883 CleanUpPathNameSlashes(FileName
);
884 NewLoader
= AllocateZeroPool(sizeof(struct LOADER_LIST
));
885 if (NewLoader
!= NULL
) {
886 NewLoader
->FileName
= StrDuplicate(FileName
);
887 NewLoader
->TimeStamp
= DirEntry
->ModificationTime
;
888 LoaderList
= AddLoaderListEntry(LoaderList
, NewLoader
);
890 MyFreePool(Extension
);
892 NewLoader
= LoaderList
;
893 while (NewLoader
!= NULL
) {
894 AddLoaderEntry(NewLoader
->FileName
, NULL
, Volume
);
895 NewLoader
= NewLoader
->NextEntry
;
897 CleanUpLoaderList(LoaderList
);
898 Status
= DirIterClose(&DirIter
);
899 if (Status
!= EFI_NOT_FOUND
) {
901 SPrint(FileName
, 255, L
"while scanning the %s directory", Path
);
903 StrCpy(FileName
, L
"while scanning the root directory");
904 CheckError(Status
, FileName
);
905 } // if (Status != EFI_NOT_FOUND)
906 } // if not scanning our own directory
907 } /* static VOID ScanLoaderDir() */
909 static VOID
ScanEfiFiles(REFIT_VOLUME
*Volume
) {
911 REFIT_DIR_ITER EfiDirIter
;
912 EFI_FILE_INFO
*EfiDirEntry
;
913 CHAR16 FileName
[256], *Directory
, *MatchPatterns
;
916 MatchPatterns
= StrDuplicate(LOADER_MATCH_PATTERNS
);
917 if (GlobalConfig
.ScanAllLinux
)
918 MergeStrings(&MatchPatterns
, LINUX_MATCH_PATTERNS
, L
',');
920 if ((Volume
->RootDir
!= NULL
) && (Volume
->VolName
!= NULL
)) {
921 // check for Mac OS X boot loader
922 if (!IsIn(L
"\\System\\Library\\CoreServices", GlobalConfig
.DontScanDirs
)) {
923 StrCpy(FileName
, MACOSX_LOADER_PATH
);
924 if (FileExists(Volume
->RootDir
, FileName
) && !IsIn(L
"boot.efi", GlobalConfig
.DontScanFiles
)) {
925 AddLoaderEntry(FileName
, L
"Mac OS X", Volume
);
929 StrCpy(FileName
, L
"\\System\\Library\\CoreServices\\xom.efi");
930 if (FileExists(Volume
->RootDir
, FileName
) && !IsIn(L
"boot.efi", GlobalConfig
.DontScanFiles
)) {
931 AddLoaderEntry(FileName
, L
"Windows XP (XoM)", Volume
);
933 } // if Mac directory not in GlobalConfig.DontScanDirs list
935 // check for Microsoft boot loader/menu
936 StrCpy(FileName
, L
"\\EFI\\Microsoft\\Boot\\Bootmgfw.efi");
937 if (FileExists(Volume
->RootDir
, FileName
) && !IsIn(L
"\\EFI\\Microsoft\\Boot", GlobalConfig
.DontScanDirs
) &&
938 !IsIn(L
"bootmgfw.efi", GlobalConfig
.DontScanFiles
)) {
939 AddLoaderEntry(FileName
, L
"Microsoft EFI boot", Volume
);
942 // scan the root directory for EFI executables
943 ScanLoaderDir(Volume
, L
"\\", MatchPatterns
);
945 // scan subdirectories of the EFI directory (as per the standard)
946 DirIterOpen(Volume
->RootDir
, L
"EFI", &EfiDirIter
);
947 while (DirIterNext(&EfiDirIter
, 1, NULL
, &EfiDirEntry
)) {
948 if (StriCmp(EfiDirEntry
->FileName
, L
"tools") == 0 || EfiDirEntry
->FileName
[0] == '.')
949 continue; // skip this, doesn't contain boot loaders
950 SPrint(FileName
, 255, L
"\\EFI\\%s", EfiDirEntry
->FileName
);
951 ScanLoaderDir(Volume
, FileName
, MatchPatterns
);
953 Status
= DirIterClose(&EfiDirIter
);
954 if (Status
!= EFI_NOT_FOUND
)
955 CheckError(Status
, L
"while scanning the EFI directory");
957 // Scan user-specified (or additional default) directories....
959 while ((Directory
= FindCommaDelimited(GlobalConfig
.AlsoScan
, i
++)) != NULL
) {
960 CleanUpPathNameSlashes(Directory
);
961 Length
= StrLen(Directory
);
963 ScanLoaderDir(Volume
, Directory
, MatchPatterns
);
964 MyFreePool(Directory
);
967 } // static VOID ScanEfiFiles()
969 // Scan internal disks for valid EFI boot loaders....
970 static VOID
ScanInternal(VOID
) {
973 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
974 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_INTERNAL
) {
975 ScanEfiFiles(Volumes
[VolumeIndex
]);
978 } // static VOID ScanInternal()
980 // Scan external disks for valid EFI boot loaders....
981 static VOID
ScanExternal(VOID
) {
984 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
985 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_EXTERNAL
) {
986 ScanEfiFiles(Volumes
[VolumeIndex
]);
989 } // static VOID ScanExternal()
991 // Scan internal disks for valid EFI boot loaders....
992 static VOID
ScanOptical(VOID
) {
995 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
996 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_OPTICAL
) {
997 ScanEfiFiles(Volumes
[VolumeIndex
]);
1000 } // static VOID ScanOptical()
1003 // legacy boot functions
1006 static EFI_STATUS
ActivateMbrPartition(IN EFI_BLOCK_IO
*BlockIO
, IN UINTN PartitionIndex
)
1009 UINT8 SectorBuffer
[512];
1010 MBR_PARTITION_INFO
*MbrTable
, *EMbrTable
;
1011 UINT32 ExtBase
, ExtCurrent
, NextExtCurrent
;
1012 UINTN LogicalPartitionIndex
= 4;
1014 BOOLEAN HaveBootCode
;
1017 Status
= refit_call5_wrapper(BlockIO
->ReadBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, 0, 512, SectorBuffer
);
1018 if (EFI_ERROR(Status
))
1020 if (*((UINT16
*)(SectorBuffer
+ 510)) != 0xaa55)
1021 return EFI_NOT_FOUND
; // safety measure #1
1023 // add boot code if necessary
1024 HaveBootCode
= FALSE
;
1025 for (i
= 0; i
< MBR_BOOTCODE_SIZE
; i
++) {
1026 if (SectorBuffer
[i
] != 0) {
1027 HaveBootCode
= TRUE
;
1031 if (!HaveBootCode
) {
1032 // no boot code found in the MBR, add the syslinux MBR code
1033 SetMem(SectorBuffer
, MBR_BOOTCODE_SIZE
, 0);
1034 CopyMem(SectorBuffer
, syslinux_mbr
, SYSLINUX_MBR_SIZE
);
1037 // set the partition active
1038 MbrTable
= (MBR_PARTITION_INFO
*)(SectorBuffer
+ 446);
1040 for (i
= 0; i
< 4; i
++) {
1041 if (MbrTable
[i
].Flags
!= 0x00 && MbrTable
[i
].Flags
!= 0x80)
1042 return EFI_NOT_FOUND
; // safety measure #2
1043 if (i
== PartitionIndex
)
1044 MbrTable
[i
].Flags
= 0x80;
1045 else if (PartitionIndex
>= 4 && IS_EXTENDED_PART_TYPE(MbrTable
[i
].Type
)) {
1046 MbrTable
[i
].Flags
= 0x80;
1047 ExtBase
= MbrTable
[i
].StartLBA
;
1049 MbrTable
[i
].Flags
= 0x00;
1053 Status
= refit_call5_wrapper(BlockIO
->WriteBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, 0, 512, SectorBuffer
);
1054 if (EFI_ERROR(Status
))
1057 if (PartitionIndex
>= 4) {
1058 // we have to activate a logical partition, so walk the EMBR chain
1060 // NOTE: ExtBase was set above while looking at the MBR table
1061 for (ExtCurrent
= ExtBase
; ExtCurrent
; ExtCurrent
= NextExtCurrent
) {
1062 // read current EMBR
1063 Status
= refit_call5_wrapper(BlockIO
->ReadBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, ExtCurrent
, 512, SectorBuffer
);
1064 if (EFI_ERROR(Status
))
1066 if (*((UINT16
*)(SectorBuffer
+ 510)) != 0xaa55)
1067 return EFI_NOT_FOUND
; // safety measure #3
1069 // scan EMBR, set appropriate partition active
1070 EMbrTable
= (MBR_PARTITION_INFO
*)(SectorBuffer
+ 446);
1072 for (i
= 0; i
< 4; i
++) {
1073 if (EMbrTable
[i
].Flags
!= 0x00 && EMbrTable
[i
].Flags
!= 0x80)
1074 return EFI_NOT_FOUND
; // safety measure #4
1075 if (EMbrTable
[i
].StartLBA
== 0 || EMbrTable
[i
].Size
== 0)
1077 if (IS_EXTENDED_PART_TYPE(EMbrTable
[i
].Type
)) {
1078 // link to next EMBR
1079 NextExtCurrent
= ExtBase
+ EMbrTable
[i
].StartLBA
;
1080 EMbrTable
[i
].Flags
= (PartitionIndex
>= LogicalPartitionIndex
) ? 0x80 : 0x00;
1083 // logical partition
1084 EMbrTable
[i
].Flags
= (PartitionIndex
== LogicalPartitionIndex
) ? 0x80 : 0x00;
1085 LogicalPartitionIndex
++;
1089 // write current EMBR
1090 Status
= refit_call5_wrapper(BlockIO
->WriteBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, ExtCurrent
, 512, SectorBuffer
);
1091 if (EFI_ERROR(Status
))
1094 if (PartitionIndex
< LogicalPartitionIndex
)
1095 break; // stop the loop, no need to touch further EMBRs
1101 } /* static EFI_STATUS ActivateMbrPartition() */
1103 // early 2006 Core Duo / Core Solo models
1104 static UINT8 LegacyLoaderDevicePath1Data
[] = {
1105 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1106 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1107 0xFF, 0xFF, 0xF9, 0xFF, 0x00, 0x00, 0x00, 0x00,
1108 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1109 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1110 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1112 // mid-2006 Mac Pro (and probably other Core 2 models)
1113 static UINT8 LegacyLoaderDevicePath2Data
[] = {
1114 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1115 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1116 0xFF, 0xFF, 0xF7, 0xFF, 0x00, 0x00, 0x00, 0x00,
1117 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1118 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1119 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1121 // mid-2007 MBP ("Santa Rosa" based models)
1122 static UINT8 LegacyLoaderDevicePath3Data
[] = {
1123 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1124 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1125 0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
1126 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1127 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1128 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1131 static UINT8 LegacyLoaderDevicePath4Data
[] = {
1132 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1133 0x00, 0x00, 0xC0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1134 0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
1135 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1136 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1137 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1139 // late-2008 MB/MBP (NVidia chipset)
1140 static UINT8 LegacyLoaderDevicePath5Data
[] = {
1141 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1142 0x00, 0x40, 0xCB, 0xFF, 0x00, 0x00, 0x00, 0x00,
1143 0xFF, 0xBF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
1144 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1145 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1146 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1149 static EFI_DEVICE_PATH
*LegacyLoaderList
[] = {
1150 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath1Data
,
1151 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath2Data
,
1152 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath3Data
,
1153 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath4Data
,
1154 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath5Data
,
1158 #define MAX_DISCOVERED_PATHS (16)
1160 static VOID
StartLegacy(IN LEGACY_ENTRY
*Entry
)
1163 EG_IMAGE
*BootLogoImage
;
1164 UINTN ErrorInStep
= 0;
1165 EFI_DEVICE_PATH
*DiscoveredPathList
[MAX_DISCOVERED_PATHS
];
1167 BeginExternalScreen(TRUE
, L
"Booting Legacy OS (Mac mode)");
1169 BootLogoImage
= LoadOSIcon(Entry
->Volume
->OSIconName
, L
"legacy", TRUE
);
1170 if (BootLogoImage
!= NULL
)
1171 BltImageAlpha(BootLogoImage
,
1172 (UGAWidth
- BootLogoImage
->Width
) >> 1,
1173 (UGAHeight
- BootLogoImage
->Height
) >> 1,
1174 &StdBackgroundPixel
);
1176 if (Entry
->Volume
->IsMbrPartition
) {
1177 ActivateMbrPartition(Entry
->Volume
->WholeDiskBlockIO
, Entry
->Volume
->MbrPartitionIndex
);
1180 ExtractLegacyLoaderPaths(DiscoveredPathList
, MAX_DISCOVERED_PATHS
, LegacyLoaderList
);
1182 Status
= StartEFIImageList(DiscoveredPathList
, Entry
->LoadOptions
, NULL
, L
"legacy loader", 0, &ErrorInStep
, TRUE
);
1183 if (Status
== EFI_NOT_FOUND
) {
1184 if (ErrorInStep
== 1) {
1185 Print(L
"\nPlease make sure that you have the latest firmware update installed.\n");
1186 } else if (ErrorInStep
== 3) {
1187 Print(L
"\nThe firmware refused to boot from the selected volume. Note that external\n"
1188 L
"hard drives are not well-supported by Apple's firmware for legacy OS booting.\n");
1191 FinishExternalScreen();
1192 } /* static VOID StartLegacy() */
1194 // Start a device on a non-Mac using the EFI_LEGACY_BIOS_PROTOCOL
1195 #ifdef __MAKEWITH_TIANO
1196 static VOID
StartLegacyUEFI(IN LEGACY_ENTRY
*Entry
)
1198 BeginExternalScreen(TRUE
, L
"Booting Legacy OS (UEFI mode)");
1200 BdsLibConnectDevicePath (Entry
->BdsOption
->DevicePath
);
1201 BdsLibDoLegacyBoot(Entry
->BdsOption
);
1203 // If we get here, it means that there was a failure....
1204 Print(L
"Failure booting legacy (BIOS) OS.");
1206 FinishExternalScreen();
1207 } // static VOID StartLegacyUEFI()
1208 #endif // __MAKEWITH_TIANO
1210 static LEGACY_ENTRY
* AddLegacyEntry(IN CHAR16
*LoaderTitle
, IN REFIT_VOLUME
*Volume
)
1212 LEGACY_ENTRY
*Entry
, *SubEntry
;
1213 REFIT_MENU_SCREEN
*SubScreen
;
1215 CHAR16 ShortcutLetter
= 0;
1217 if (LoaderTitle
== NULL
) {
1218 if (Volume
->OSName
!= NULL
) {
1219 LoaderTitle
= Volume
->OSName
;
1220 if (LoaderTitle
[0] == 'W' || LoaderTitle
[0] == 'L')
1221 ShortcutLetter
= LoaderTitle
[0];
1223 LoaderTitle
= L
"Legacy OS";
1225 if (Volume
->VolName
!= NULL
)
1226 VolDesc
= Volume
->VolName
;
1228 VolDesc
= (Volume
->DiskKind
== DISK_KIND_OPTICAL
) ? L
"CD" : L
"HD";
1230 // prepare the menu entry
1231 Entry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1232 Entry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1233 SPrint(Entry
->me
.Title
, 255, L
"Boot %s from %s", LoaderTitle
, VolDesc
);
1234 Entry
->me
.Tag
= TAG_LEGACY
;
1236 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1237 Entry
->me
.Image
= LoadOSIcon(Volume
->OSIconName
, L
"legacy", FALSE
);
1238 Entry
->me
.BadgeImage
= Volume
->VolBadgeImage
;
1239 Entry
->Volume
= Volume
;
1240 Entry
->LoadOptions
= (Volume
->DiskKind
== DISK_KIND_OPTICAL
) ? L
"CD" :
1241 ((Volume
->DiskKind
== DISK_KIND_EXTERNAL
) ? L
"USB" : L
"HD");
1242 Entry
->Enabled
= TRUE
;
1244 // create the submenu
1245 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
1246 SubScreen
->Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1247 SPrint(SubScreen
->Title
, 255, L
"Boot Options for %s on %s", LoaderTitle
, VolDesc
);
1248 SubScreen
->TitleImage
= Entry
->me
.Image
;
1251 SubEntry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1252 SubEntry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1253 SPrint(SubEntry
->me
.Title
, 255, L
"Boot %s", LoaderTitle
);
1254 SubEntry
->me
.Tag
= TAG_LEGACY
;
1255 SubEntry
->Volume
= Entry
->Volume
;
1256 SubEntry
->LoadOptions
= Entry
->LoadOptions
;
1257 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
1259 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
1260 Entry
->me
.SubScreen
= SubScreen
;
1261 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1263 } /* static LEGACY_ENTRY * AddLegacyEntry() */
1266 #ifdef __MAKEWITH_TIANO
1267 // default volume badge icon based on disk kind
1268 static EG_IMAGE
* GetDiskBadge(IN UINTN DiskType
) {
1269 EG_IMAGE
* Badge
= NULL
;
1273 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_INTERNAL
);
1276 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_EXTERNAL
);
1279 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_OPTICAL
);
1283 } // static EG_IMAGE * GetDiskBadge()
1286 Create a rEFInd boot option from a Legacy BIOS protocol option.
1288 static LEGACY_ENTRY
* AddLegacyEntryUEFI(BDS_COMMON_OPTION
*BdsOption
, IN UINT16 DiskType
)
1290 LEGACY_ENTRY
*Entry
, *SubEntry
;
1291 REFIT_MENU_SCREEN
*SubScreen
;
1292 CHAR16 ShortcutLetter
= 0;
1293 CHAR16
*LegacyDescription
= BdsOption
->Description
;
1295 // ScanVolume(Volume);
1297 // prepare the menu entry
1298 Entry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1299 Entry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1300 SPrint(Entry
->me
.Title
, 255, L
"Boot legacy target %s", LegacyDescription
);
1301 Entry
->me
.Tag
= TAG_LEGACY_UEFI
;
1303 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1304 Entry
->me
.Image
= LoadOSIcon(L
"legacy", L
"legacy", TRUE
);
1305 Entry
->LoadOptions
= (DiskType
== BBS_CDROM
) ? L
"CD" :
1306 ((DiskType
== BBS_USB
) ? L
"USB" : L
"HD");
1307 Entry
->me
.BadgeImage
= GetDiskBadge(DiskType
);
1308 // Entry->me.BadgeImage = Volume->VolBadgeImage;
1309 Entry
->BdsOption
= BdsOption
;
1310 Entry
->Enabled
= TRUE
;
1312 // create the submenu
1313 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
1314 SubScreen
->Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1315 SPrint(SubScreen
->Title
, 255, L
"No boot options for legacy target");
1316 SubScreen
->TitleImage
= Entry
->me
.Image
;
1319 SubEntry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1320 SubEntry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1321 SPrint(SubEntry
->me
.Title
, 255, L
"Boot %s", LegacyDescription
);
1322 SubEntry
->me
.Tag
= TAG_LEGACY_UEFI
;
1323 Entry
->BdsOption
= BdsOption
;
1324 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
1326 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
1327 Entry
->me
.SubScreen
= SubScreen
;
1328 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1330 } /* static LEGACY_ENTRY * AddLegacyEntryUEFI() */
1333 Scan for legacy BIOS targets on machines that implement EFI_LEGACY_BIOS_PROTOCOL.
1334 In testing, protocol has not been implemented on Macs but has been
1335 implemented on several Dell PCs and an ASUS motherboard.
1336 Restricts output to disks of the specified DiskType.
1338 static VOID
ScanLegacyUEFI(IN UINTN DiskType
)
1341 EFI_LEGACY_BIOS_PROTOCOL
*LegacyBios
;
1342 UINT16
*BootOrder
= NULL
;
1344 CHAR16 BootOption
[10];
1345 UINTN BootOrderSize
= 0;
1347 BDS_COMMON_OPTION
*BdsOption
;
1348 LIST_ENTRY TempList
;
1349 BBS_BBS_DEVICE_PATH
* BbsDevicePath
= NULL
;
1350 // REFIT_VOLUME Volume;
1352 InitializeListHead (&TempList
);
1353 ZeroMem (Buffer
, sizeof (Buffer
));
1355 // If LegacyBios protocol is not implemented on this platform, then
1356 //we do not support this type of legacy boot on this machine.
1357 Status
= gBS
->LocateProtocol(&gEfiLegacyBootProtocolGuid
, NULL
, (VOID
**) &LegacyBios
);
1358 if (EFI_ERROR (Status
))
1361 // Grab the boot order
1362 BootOrder
= BdsLibGetVariableAndSize(L
"BootOrder", &gEfiGlobalVariableGuid
, &BootOrderSize
);
1363 if (BootOrder
== NULL
) {
1368 while (Index
< BootOrderSize
/ sizeof (UINT16
))
1370 // Grab each boot option variable from the boot order, and convert
1371 // the variable into a BDS boot option
1372 UnicodeSPrint (BootOption
, sizeof (BootOption
), L
"Boot%04x", BootOrder
[Index
]);
1373 BdsOption
= BdsLibVariableToOption (&TempList
, BootOption
);
1375 if (BdsOption
!= NULL
) {
1376 BbsDevicePath
= (BBS_BBS_DEVICE_PATH
*)BdsOption
->DevicePath
;
1378 // Only add the entry if it is of a requested type (e.g. USB, HD)
1380 // Two checks necessary because some systems return EFI boot loaders
1381 // with a DeviceType value that would inappropriately include them
1382 // as legacy loaders....
1383 if ((BbsDevicePath
->DeviceType
== DiskType
) && (BdsOption
->DevicePath
->Type
== DEVICE_TYPE_BIOS
)) {
1384 AddLegacyEntryUEFI(BdsOption
, BbsDevicePath
->DeviceType
);
1389 } /* static VOID ScanLegacyUEFI() */
1391 static VOID
ScanLegacyUEFI(IN UINTN DiskType
){}
1392 #endif // __MAKEWITH_TIANO
1394 static VOID
ScanLegacyVolume(REFIT_VOLUME
*Volume
, UINTN VolumeIndex
) {
1396 BOOLEAN ShowVolume
, HideIfOthersFound
;
1399 HideIfOthersFound
= FALSE
;
1400 if (Volume
->IsAppleLegacy
) {
1402 HideIfOthersFound
= TRUE
;
1403 } else if (Volume
->HasBootCode
) {
1405 if (Volume
->BlockIO
== Volume
->WholeDiskBlockIO
&&
1406 Volume
->BlockIOOffset
== 0 &&
1407 Volume
->OSName
== NULL
)
1408 // this is a whole disk (MBR) entry; hide if we have entries for partitions
1409 HideIfOthersFound
= TRUE
;
1411 if (HideIfOthersFound
) {
1412 // check for other bootable entries on the same disk
1413 for (VolumeIndex2
= 0; VolumeIndex2
< VolumesCount
; VolumeIndex2
++) {
1414 if (VolumeIndex2
!= VolumeIndex
&& Volumes
[VolumeIndex2
]->HasBootCode
&&
1415 Volumes
[VolumeIndex2
]->WholeDiskBlockIO
== Volume
->WholeDiskBlockIO
)
1421 AddLegacyEntry(NULL
, Volume
);
1422 } // static VOID ScanLegacyVolume()
1424 // Scan attached optical discs for legacy (BIOS) boot code
1425 // and add anything found to the list....
1426 static VOID
ScanLegacyDisc(VOID
)
1429 REFIT_VOLUME
*Volume
;
1431 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
) {
1432 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1433 Volume
= Volumes
[VolumeIndex
];
1434 if (Volume
->DiskKind
== DISK_KIND_OPTICAL
)
1435 ScanLegacyVolume(Volume
, VolumeIndex
);
1437 } else if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
1438 ScanLegacyUEFI(BBS_CDROM
);
1440 } /* static VOID ScanLegacyDisc() */
1442 // Scan internal hard disks for legacy (BIOS) boot code
1443 // and add anything found to the list....
1444 static VOID
ScanLegacyInternal(VOID
)
1447 REFIT_VOLUME
*Volume
;
1449 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
) {
1450 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1451 Volume
= Volumes
[VolumeIndex
];
1452 if (Volume
->DiskKind
== DISK_KIND_INTERNAL
)
1453 ScanLegacyVolume(Volume
, VolumeIndex
);
1455 } else if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
1456 ScanLegacyUEFI(BBS_HARDDISK
);
1458 } /* static VOID ScanLegacyInternal() */
1460 // Scan external disks for legacy (BIOS) boot code
1461 // and add anything found to the list....
1462 static VOID
ScanLegacyExternal(VOID
)
1465 REFIT_VOLUME
*Volume
;
1467 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
) {
1468 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1469 Volume
= Volumes
[VolumeIndex
];
1470 if (Volume
->DiskKind
== DISK_KIND_EXTERNAL
)
1471 ScanLegacyVolume(Volume
, VolumeIndex
);
1473 } else if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
1474 ScanLegacyUEFI(BBS_USB
);
1476 } /* static VOID ScanLegacyExternal() */
1479 // pre-boot tool functions
1482 static VOID
StartTool(IN LOADER_ENTRY
*Entry
)
1484 BeginExternalScreen(Entry
->UseGraphicsMode
, Entry
->me
.Title
+ 6); // assumes "Start <title>" as assigned below
1485 StartEFIImage(Entry
->DevicePath
, Entry
->LoadOptions
, Basename(Entry
->LoaderPath
),
1486 Basename(Entry
->LoaderPath
), Entry
->OSType
, NULL
, TRUE
);
1487 FinishExternalScreen();
1488 } /* static VOID StartTool() */
1490 static LOADER_ENTRY
* AddToolEntry(EFI_HANDLE DeviceHandle
, IN CHAR16
*LoaderPath
, IN CHAR16
*LoaderTitle
, IN EG_IMAGE
*Image
,
1491 IN CHAR16 ShortcutLetter
, IN BOOLEAN UseGraphicsMode
)
1493 LOADER_ENTRY
*Entry
;
1494 CHAR16
*TitleStr
= NULL
;
1496 Entry
= AllocateZeroPool(sizeof(LOADER_ENTRY
));
1498 MergeStrings(&TitleStr
, L
"Start ", 0);
1499 MergeStrings(&TitleStr
, LoaderTitle
, 0);
1500 Entry
->me
.Title
= TitleStr
;
1501 Entry
->me
.Tag
= TAG_TOOL
;
1503 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1504 Entry
->me
.Image
= Image
;
1505 Entry
->LoaderPath
= (LoaderPath
) ? StrDuplicate(LoaderPath
) : NULL
;
1506 Entry
->DevicePath
= FileDevicePath(DeviceHandle
, Entry
->LoaderPath
);
1507 Entry
->UseGraphicsMode
= UseGraphicsMode
;
1509 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1511 } /* static LOADER_ENTRY * AddToolEntry() */
1514 // pre-boot driver functions
1517 static UINTN
ScanDriverDir(IN CHAR16
*Path
)
1520 REFIT_DIR_ITER DirIter
;
1522 EFI_FILE_INFO
*DirEntry
;
1523 CHAR16 FileName
[256];
1525 CleanUpPathNameSlashes(Path
);
1526 // look through contents of the directory
1527 DirIterOpen(SelfRootDir
, Path
, &DirIter
);
1528 while (DirIterNext(&DirIter
, 2, LOADER_MATCH_PATTERNS
, &DirEntry
)) {
1529 if (DirEntry
->FileName
[0] == '.')
1530 continue; // skip this
1532 SPrint(FileName
, 255, L
"%s\\%s", Path
, DirEntry
->FileName
);
1534 Status
= StartEFIImage(FileDevicePath(SelfLoadedImage
->DeviceHandle
, FileName
),
1535 L
"", DirEntry
->FileName
, DirEntry
->FileName
, 0, NULL
, FALSE
);
1537 Status
= DirIterClose(&DirIter
);
1538 if (Status
!= EFI_NOT_FOUND
) {
1539 SPrint(FileName
, 255, L
"while scanning the %s directory", Path
);
1540 CheckError(Status
, FileName
);
1545 #ifdef __MAKEWITH_GNUEFI
1546 static EFI_STATUS
ConnectAllDriversToAllControllers(VOID
)
1549 UINTN AllHandleCount
;
1550 EFI_HANDLE
*AllHandleBuffer
;
1553 EFI_HANDLE
*HandleBuffer
;
1559 Status
= LibLocateHandle(AllHandles
,
1564 if (EFI_ERROR(Status
))
1567 for (Index
= 0; Index
< AllHandleCount
; Index
++) {
1569 // Scan the handle database
1571 Status
= LibScanHandleDatabase(NULL
,
1573 AllHandleBuffer
[Index
],
1578 if (EFI_ERROR (Status
))
1582 if (HandleType
[Index
] & EFI_HANDLE_TYPE_DRIVER_BINDING_HANDLE
)
1584 if (HandleType
[Index
] & EFI_HANDLE_TYPE_IMAGE_HANDLE
)
1589 for (HandleIndex
= 0; HandleIndex
< HandleCount
; HandleIndex
++) {
1590 if (HandleType
[HandleIndex
] & EFI_HANDLE_TYPE_PARENT_HANDLE
)
1595 if (HandleType
[Index
] & EFI_HANDLE_TYPE_DEVICE_HANDLE
) {
1596 Status
= refit_call4_wrapper(BS
->ConnectController
,
1597 AllHandleBuffer
[Index
],
1605 MyFreePool (HandleBuffer
);
1606 MyFreePool (HandleType
);
1610 MyFreePool (AllHandleBuffer
);
1612 } /* EFI_STATUS ConnectAllDriversToAllControllers() */
1614 static EFI_STATUS
ConnectAllDriversToAllControllers(VOID
) {
1615 BdsLibConnectAllDriversToAllControllers();
1620 // Load all EFI drivers from rEFInd's "drivers" subdirectory and from the
1621 // directories specified by the user in the "scan_driver_dirs" configuration
1623 static VOID
LoadDrivers(VOID
)
1625 CHAR16
*Directory
, *SelfDirectory
;
1626 UINTN i
= 0, Length
, NumFound
= 0;
1628 // load drivers from the subdirectories of rEFInd's home directory specified
1629 // in the DRIVER_DIRS constant.
1630 while ((Directory
= FindCommaDelimited(DRIVER_DIRS
, i
++)) != NULL
) {
1631 SelfDirectory
= SelfDirPath
? StrDuplicate(SelfDirPath
) : NULL
;
1632 CleanUpPathNameSlashes(SelfDirectory
);
1633 MergeStrings(&SelfDirectory
, Directory
, L
'\\');
1634 NumFound
+= ScanDriverDir(SelfDirectory
);
1635 MyFreePool(Directory
);
1636 MyFreePool(SelfDirectory
);
1639 // Scan additional user-specified driver directories....
1641 while ((Directory
= FindCommaDelimited(GlobalConfig
.DriverDirs
, i
++)) != NULL
) {
1642 CleanUpPathNameSlashes(Directory
);
1643 Length
= StrLen(Directory
);
1645 NumFound
+= ScanDriverDir(Directory
);
1647 MyFreePool(Directory
);
1650 // connect all devices
1652 ConnectAllDriversToAllControllers();
1653 } /* static VOID LoadDrivers() */
1655 // Determine what (if any) type of legacy (BIOS) boot support is available
1656 static VOID
FindLegacyBootType(VOID
) {
1657 #ifdef __MAKEWITH_TIANO
1659 EFI_LEGACY_BIOS_PROTOCOL
*LegacyBios
;
1662 GlobalConfig
.LegacyType
= LEGACY_TYPE_NONE
;
1664 // UEFI-style legacy BIOS support is available only with the TianoCore EDK2
1665 // build environment, and then only with some implementations....
1666 #ifdef __MAKEWITH_TIANO
1667 Status
= gBS
->LocateProtocol (&gEfiLegacyBootProtocolGuid
, NULL
, (VOID
**) &LegacyBios
);
1668 if (!EFI_ERROR (Status
))
1669 GlobalConfig
.LegacyType
= LEGACY_TYPE_UEFI
;
1672 // Macs have their own system. If the firmware vendor code contains the
1673 // string "Apple", assume it's available. Note that this overrides the
1674 // UEFI type, and might yield false positives if the vendor string
1675 // contains "Apple" as part of something bigger, so this isn't 100%
1677 if (StriSubCmp(L
"Apple", ST
->FirmwareVendor
))
1678 GlobalConfig
.LegacyType
= LEGACY_TYPE_MAC
;
1679 } // static VOID FindLegacyBootType
1681 // Warn the user if legacy OS scans are enabled but the firmware or this
1682 // application can't support them....
1683 static VOID
WarnIfLegacyProblems() {
1684 BOOLEAN found
= FALSE
;
1687 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_NONE
) {
1689 if (GlobalConfig
.ScanFor
[i
] == 'h' || GlobalConfig
.ScanFor
[i
] == 'b' || GlobalConfig
.ScanFor
[i
] == 'c')
1692 } while ((i
< NUM_SCAN_OPTIONS
) && (!found
));
1694 Print(L
"NOTE: refind.conf's 'scanfor' line specifies scanning for one or more legacy\n");
1695 Print(L
"(BIOS) boot options; however, this is not possible because ");
1696 #ifdef __MAKEWITH_TIANO
1697 Print(L
"your computer lacks\n");
1698 Print(L
"the necessary Compatibility Support Module (CSM) support.\n");
1700 Print(L
"this program was\n");
1701 Print(L
"compiled without the necessary support. Please visit\n");
1702 Print(L
"http://www.rodsbooks.com/refind/getting.html and download and install a rEFInd\n");
1703 Print(L
"binary built with the TianoCore EDK2 to enable legacy boot support.\n");
1707 } // if no legacy support
1708 } // static VOID WarnIfLegacyProblems()
1710 // Locates boot loaders. NOTE: This assumes that GlobalConfig.LegacyType is set correctly.
1711 static VOID
ScanForBootloaders(VOID
) {
1716 // scan for loaders and tools, add them to the menu
1717 for (i
= 0; i
< NUM_SCAN_OPTIONS
; i
++) {
1718 switch(GlobalConfig
.ScanFor
[i
]) {
1723 ScanLegacyInternal();
1726 ScanLegacyExternal();
1729 ScanUserConfigured();
1743 // assign shortcut keys
1744 for (i
= 0; i
< MainMenu
.EntryCount
&& MainMenu
.Entries
[i
]->Row
== 0 && i
< 9; i
++)
1745 MainMenu
.Entries
[i
]->ShortcutDigit
= (CHAR16
)('1' + i
);
1747 // wait for user ACK when there were errors
1748 FinishTextScreen(FALSE
);
1749 } // static VOID ScanForBootloaders()
1751 // Add the second-row tags containing built-in and external tools (EFI shell,
1753 static VOID
ScanForTools(VOID
) {
1754 CHAR16
*FileName
= NULL
, Description
[256];
1755 REFIT_MENU_ENTRY
*TempMenuEntry
;
1756 UINTN i
, j
, VolumeIndex
;
1758 for (i
= 0; i
< NUM_TOOLS
; i
++) {
1759 switch(GlobalConfig
.ShowTools
[i
]) {
1761 TempMenuEntry
= CopyMenuEntry(&MenuEntryShutdown
);
1762 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_SHUTDOWN
);
1763 AddMenuEntry(&MainMenu
, TempMenuEntry
);
1766 TempMenuEntry
= CopyMenuEntry(&MenuEntryReset
);
1767 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_RESET
);
1768 AddMenuEntry(&MainMenu
, TempMenuEntry
);
1771 TempMenuEntry
= CopyMenuEntry(&MenuEntryAbout
);
1772 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT
);
1773 AddMenuEntry(&MainMenu
, TempMenuEntry
);
1776 TempMenuEntry
= CopyMenuEntry(&MenuEntryExit
);
1777 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_EXIT
);
1778 AddMenuEntry(&MainMenu
, TempMenuEntry
);
1782 MyFreePool(FileName
);
1783 while ((FileName
= FindCommaDelimited(SHELL_NAMES
, j
++)) != NULL
) {
1784 if (FileExists(SelfRootDir
, FileName
)) {
1785 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, L
"EFI Shell", BuiltinIcon(BUILTIN_ICON_TOOL_SHELL
),
1791 MyFreePool(FileName
);
1793 MergeStrings(&FileName
, L
"\\efi\\tools\\gptsync.efi", 0);
1794 if (FileExists(SelfRootDir
, FileName
)) {
1795 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, L
"Make Hybrid MBR", BuiltinIcon(BUILTIN_ICON_TOOL_PART
), 'P', FALSE
);
1798 case TAG_APPLE_RECOVERY
:
1799 MyFreePool(FileName
);
1801 MergeStrings(&FileName
, L
"\\com.apple.recovery.boot\\boot.efi", 0);
1802 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1803 if ((Volumes
[VolumeIndex
]->RootDir
!= NULL
) && (FileExists(Volumes
[VolumeIndex
]->RootDir
, FileName
))) {
1804 SPrint(Description
, 255, L
"Apple Recovery on %s", Volumes
[VolumeIndex
]->VolName
);
1805 AddToolEntry(Volumes
[VolumeIndex
]->DeviceHandle
, FileName
, Description
,
1806 BuiltinIcon(BUILTIN_ICON_TOOL_APPLE_RESCUE
), 'R', TRUE
);
1812 MyFreePool(FileName
);
1813 while ((FileName
= FindCommaDelimited(MOK_NAMES
, j
++)) != NULL
) {
1814 if (FileExists(SelfRootDir
, FileName
)) {
1815 SPrint(Description
, 255, L
"MOK Key Manager at %s", FileName
);
1816 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, Description
,
1817 BuiltinIcon(BUILTIN_ICON_TOOL_MOK_TOOL
), 'S', FALSE
);
1820 if (FileExists(SelfDir
, L
"MokManager.efi")) {
1821 MyFreePool(FileName
);
1822 FileName
= StrDuplicate(SelfDirPath
);
1823 MergeStrings(&FileName
, L
"\\MokManager.efi", 0);
1824 SPrint(Description
, 255, L
"MOK Key Manager at %s", FileName
);
1825 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, Description
,
1826 BuiltinIcon(BUILTIN_ICON_TOOL_MOK_TOOL
), 'S', FALSE
);
1830 MyFreePool(FileName
);
1833 } // static VOID ScanForTools
1835 // Rescan for boot loaders
1836 VOID
RescanAll(VOID
) {
1843 egDisplayMessage(L
"Scanning for new boot loaders; please wait....", &BGColor
);
1844 FreeList((VOID
***) &(MainMenu
.Entries
), &MainMenu
.EntryCount
);
1845 MainMenu
.Entries
= NULL
;
1846 MainMenu
.EntryCount
= 0;
1848 ConnectAllDriversToAllControllers();
1850 ScanForBootloaders();
1853 } // VOID RescanAll()
1855 #ifndef __MAKEWITH_GNUEFI
1857 // Minimal initialization function
1858 static VOID
InitializeLib(IN EFI_HANDLE ImageHandle
, IN EFI_SYSTEM_TABLE
*SystemTable
) {
1860 // gImageHandle = ImageHandle;
1861 gBS
= SystemTable
->BootServices
;
1862 // gRS = SystemTable->RuntimeServices;
1863 gRT
= SystemTable
->RuntimeServices
; // Some BDS functions need gRT to be set
1864 EfiGetSystemConfigurationTable (&gEfiDxeServicesTableGuid
, (VOID
**) &gDS
);
1866 InitializeConsoleSim();
1876 efi_main (IN EFI_HANDLE ImageHandle
, IN EFI_SYSTEM_TABLE
*SystemTable
)
1879 BOOLEAN MainLoopRunning
= TRUE
;
1880 REFIT_MENU_ENTRY
*ChosenEntry
;
1886 InitializeLib(ImageHandle
, SystemTable
);
1888 Status
= InitRefitLib(ImageHandle
);
1889 if (EFI_ERROR(Status
))
1892 // read configuration
1893 CopyMem(GlobalConfig
.ScanFor
, "ieom ", NUM_SCAN_OPTIONS
);
1894 FindLegacyBootType();
1895 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
)
1896 CopyMem(GlobalConfig
.ScanFor
, "ihebocm ", NUM_SCAN_OPTIONS
);
1898 WarnIfLegacyProblems();
1899 MainMenu
.TimeoutSeconds
= GlobalConfig
.Timeout
;
1901 // disable EFI watchdog timer
1902 refit_call4_wrapper(BS
->SetWatchdogTimer
, 0x0000, 0x0000, 0x0000, NULL
);
1904 // further bootstrap (now with config available)
1908 ScanForBootloaders();
1911 if (GlobalConfig
.ScanDelay
> 0) {
1916 egDisplayMessage(L
"Pausing before disk scan; please wait....", &BGColor
);
1917 for (i
= 0; i
< GlobalConfig
.ScanDelay
; i
++)
1918 refit_call1_wrapper(BS
->Stall
, 1000000);
1922 Selection
= StrDuplicate(GlobalConfig
.DefaultSelection
);
1923 while (MainLoopRunning
) {
1924 MenuExit
= RunMainMenu(&MainMenu
, Selection
, &ChosenEntry
);
1926 // The Escape key triggers a re-scan operation....
1927 if (MenuExit
== MENU_EXIT_ESCAPE
) {
1932 switch (ChosenEntry
->Tag
) {
1934 case TAG_REBOOT
: // Reboot
1936 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);
1937 MainLoopRunning
= FALSE
; // just in case we get this far
1940 case TAG_SHUTDOWN
: // Shut Down
1942 refit_call4_wrapper(RT
->ResetSystem
, EfiResetShutdown
, EFI_SUCCESS
, 0, NULL
);
1943 MainLoopRunning
= FALSE
; // just in case we get this far
1946 case TAG_ABOUT
: // About rEFInd
1950 case TAG_LOADER
: // Boot OS via .EFI loader
1951 StartLoader((LOADER_ENTRY
*)ChosenEntry
);
1954 case TAG_LEGACY
: // Boot legacy OS
1955 StartLegacy((LEGACY_ENTRY
*)ChosenEntry
);
1958 #ifdef __MAKEWITH_TIANO
1959 case TAG_LEGACY_UEFI
: // Boot a legacy OS on a non-Mac
1960 StartLegacyUEFI((LEGACY_ENTRY
*)ChosenEntry
);
1962 #endif // __MAKEWITH_TIANO
1964 case TAG_TOOL
: // Start a EFI tool
1965 StartTool((LOADER_ENTRY
*)ChosenEntry
);
1968 case TAG_EXIT
: // Terminate rEFInd
1969 BeginTextScreen(L
" ");
1974 MyFreePool(Selection
);
1975 Selection
= (ChosenEntry
->Title
) ? StrDuplicate(ChosenEntry
->Title
) : NULL
;
1978 // If we end up here, things have gone wrong. Try to reboot, and if that
1979 // fails, go into an endless loop.
1980 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);