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, 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.5.1.5");
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 TempStr
= AllocateZeroPool(256 * sizeof(CHAR16
));
136 SPrint(TempStr
, 255, L
" Platform: x86_64 (64 bit); Secure Boot %s", secure_mode() ? L
"active" : L
"inactive");
137 AddMenuInfoLine(&AboutMenu
, TempStr
);
139 AddMenuInfoLine(&AboutMenu
, L
" Platform: unknown");
141 TempStr
= AllocateZeroPool(256 * sizeof(CHAR16
));
142 SPrint(TempStr
, 255, L
" Firmware: %s %d.%02d",
143 ST
->FirmwareVendor
, ST
->FirmwareRevision
>> 16, ST
->FirmwareRevision
& ((1 << 16) - 1));
144 AddMenuInfoLine(&AboutMenu
, TempStr
);
145 TempStr
= AllocateZeroPool(256 * sizeof(CHAR16
));
146 SPrint(TempStr
, 255, L
" Screen Output: %s", egScreenDescription());
147 AddMenuInfoLine(&AboutMenu
, TempStr
);
148 AddMenuInfoLine(&AboutMenu
, L
"");
149 #if defined(__MAKEWITH_GNUEFI)
150 AddMenuInfoLine(&AboutMenu
, L
"Built with GNU-EFI");
152 AddMenuInfoLine(&AboutMenu
, L
"Built with TianoCore EDK2");
154 AddMenuInfoLine(&AboutMenu
, L
"");
155 AddMenuInfoLine(&AboutMenu
, L
"For more information, see the rEFInd Web site:");
156 AddMenuInfoLine(&AboutMenu
, L
"http://www.rodsbooks.com/refind/");
157 AddMenuEntry(&AboutMenu
, &MenuEntryReturn
);
160 RunMenu(&AboutMenu
, NULL
);
161 } /* VOID AboutrEFInd() */
163 // Launch an EFI binary.
164 static EFI_STATUS
StartEFIImageList(IN EFI_DEVICE_PATH
**DevicePaths
,
165 IN CHAR16
*LoadOptions
, IN CHAR16
*LoadOptionsPrefix
,
166 IN CHAR16
*ImageTitle
, IN CHAR8 OSType
,
167 OUT UINTN
*ErrorInStep
,
170 EFI_STATUS Status
, ReturnStatus
;
171 EFI_HANDLE ChildImageHandle
;
172 EFI_LOADED_IMAGE
*ChildLoadedImage
= NULL
;
174 VOID
*ImageData
= NULL
;
176 REFIT_VOLUME
*DeviceVolume
= NULL
;
177 UINTN DevicePathIndex
;
178 CHAR16 ErrorInfo
[256];
179 CHAR16
*FullLoadOptions
= NULL
;
180 CHAR16
*loader
= NULL
;
181 BOOLEAN UseMok
= FALSE
;
183 if (ErrorInStep
!= NULL
)
187 if (LoadOptions
!= NULL
) {
188 if (LoadOptionsPrefix
!= NULL
) {
189 MergeStrings(&FullLoadOptions
, LoadOptionsPrefix
, 0);
190 MergeStrings(&FullLoadOptions
, LoadOptions
, L
' ');
192 MergeStrings(&FullLoadOptions
, L
" ", 0);
193 // NOTE: That last space is also added by the EFI shell and seems to be significant
194 // when passing options to Apple's boot.efi...
197 MergeStrings(&FullLoadOptions
, LoadOptions
, 0);
199 } else { // LoadOptions == NULL
200 // NOTE: We provide a non-null string when no options are specified for safety;
201 // some systems (at least DUET) can hang when launching some programs (such as
202 // an EFI shell) without this.
203 FullLoadOptions
= StrDuplicate(L
" ");
206 Print(L
"Starting %s\nUsing load options '%s'\n", ImageTitle
, FullLoadOptions
);
208 // load the image into memory (and execute it, in the case of a shim/MOK image).
209 ReturnStatus
= Status
= EFI_NOT_FOUND
; // in case the list is empty
210 for (DevicePathIndex
= 0; DevicePaths
[DevicePathIndex
] != NULL
; DevicePathIndex
++) {
211 // NOTE: Below commented-out line could be more efficient if the ReadFile() and
212 // FindVolumeAndFilename() calls were moved earlier, but it doesn't work on my
213 // 32-bit Mac Mini or my 64-bit Intel box when launching a Linux kernel; the
214 // kernel returns a "Failed to handle fs_proto" error message.
215 // TODO: Track down the cause of this error and fix it, if possible.
216 // ReturnStatus = Status = refit_call6_wrapper(BS->LoadImage, FALSE, SelfImageHandle, DevicePaths[DevicePathIndex],
217 // ImageData, ImageSize, &ChildImageHandle);
218 ReturnStatus
= Status
= refit_call6_wrapper(BS
->LoadImage
, FALSE
, SelfImageHandle
, DevicePaths
[DevicePathIndex
],
219 NULL
, 0, &ChildImageHandle
);
220 if ((Status
== EFI_ACCESS_DENIED
) && (ShimLoaded())) {
221 FindVolumeAndFilename(DevicePaths
[DevicePathIndex
], &DeviceVolume
, &loader
);
222 if (DeviceVolume
!= NULL
) {
223 Status
= ReadFile(DeviceVolume
->RootDir
, loader
, &File
, &ImageSize
);
224 ImageData
= File
.Buffer
;
226 Status
= EFI_NOT_FOUND
;
227 Print(L
"Error: device volume not found!\n");
229 if (Status
!= EFI_NOT_FOUND
) {
230 ReturnStatus
= Status
= start_image(SelfImageHandle
, loader
, ImageData
, ImageSize
, FullLoadOptions
,
231 DeviceVolume
, FileDevicePath(DeviceVolume
->DeviceHandle
, loader
));
232 // ReturnStatus = Status = start_image(SelfImageHandle, loader, ImageData, ImageSize, FullLoadOptions,
233 // DeviceVolume, DevicePaths[DevicePathIndex]);
235 if (ReturnStatus
== EFI_SUCCESS
) {
238 } // if (UEFI SB failed; use shim)
239 if (ReturnStatus
!= EFI_NOT_FOUND
) {
243 SPrint(ErrorInfo
, 255, L
"while loading %s", ImageTitle
);
244 if (CheckError(Status
, ErrorInfo
)) {
245 if (ErrorInStep
!= NULL
)
251 ReturnStatus
= Status
= refit_call3_wrapper(BS
->HandleProtocol
, ChildImageHandle
, &LoadedImageProtocol
,
252 (VOID
**) &ChildLoadedImage
);
253 if (CheckError(Status
, L
"while getting a LoadedImageProtocol handle")) {
254 if (ErrorInStep
!= NULL
)
258 ChildLoadedImage
->LoadOptions
= (VOID
*)FullLoadOptions
;
259 ChildLoadedImage
->LoadOptionsSize
= ((UINT32
)StrLen(FullLoadOptions
) + 1) * sizeof(CHAR16
);
260 // turn control over to the image
261 // TODO: (optionally) re-enable the EFI watchdog timer!
263 // close open file handles
265 ReturnStatus
= Status
= refit_call3_wrapper(BS
->StartImage
, ChildImageHandle
, NULL
, NULL
);
266 // control returns here when the child image calls Exit()
267 SPrint(ErrorInfo
, 255, L
"returned from %s", ImageTitle
);
268 if (CheckError(Status
, ErrorInfo
)) {
269 if (ErrorInStep
!= NULL
)
273 // re-open file handles
278 // unload the image, we don't care if it works or not...
280 Status
= refit_call1_wrapper(BS
->UnloadImage
, ChildImageHandle
);
283 MyFreePool(FullLoadOptions
);
285 } /* static EFI_STATUS StartEFIImageList() */
287 static EFI_STATUS
StartEFIImage(IN EFI_DEVICE_PATH
*DevicePath
,
288 IN CHAR16
*LoadOptions
, IN CHAR16
*LoadOptionsPrefix
,
289 IN CHAR16
*ImageTitle
, IN CHAR8 OSType
,
290 OUT UINTN
*ErrorInStep
,
293 EFI_DEVICE_PATH
*DevicePaths
[2];
295 DevicePaths
[0] = DevicePath
;
296 DevicePaths
[1] = NULL
;
297 return StartEFIImageList(DevicePaths
, LoadOptions
, LoadOptionsPrefix
, ImageTitle
, OSType
, ErrorInStep
, Verbose
);
298 } /* static EFI_STATUS StartEFIImage() */
301 // EFI OS loader functions
304 static VOID
StartLoader(IN LOADER_ENTRY
*Entry
)
306 UINTN ErrorInStep
= 0;
308 BeginExternalScreen(Entry
->UseGraphicsMode
, L
"Booting OS");
309 StartEFIImage(Entry
->DevicePath
, Entry
->LoadOptions
, Basename(Entry
->LoaderPath
),
310 Basename(Entry
->LoaderPath
), Entry
->OSType
, &ErrorInStep
, !Entry
->UseGraphicsMode
);
311 FinishExternalScreen();
314 // Locate an initrd or initramfs file that matches the kernel specified by LoaderPath.
315 // The matching file has a name that begins with "init" and includes the same version
316 // number string as is found in LoaderPath -- but not a longer version number string.
317 // For instance, if LoaderPath is \EFI\kernels\bzImage-3.3.0.efi, and if \EFI\kernels
318 // has a file called initramfs-3.3.0.img, this function will return the string
319 // '\EFI\kernels\initramfs-3.3.0.img'. If the directory ALSO contains the file
320 // initramfs-3.3.0-rc7.img or initramfs-13.3.0.img, those files will NOT match;
321 // however, initmine-3.3.0.img might match. (FindInitrd() returns the first match it
322 // finds). Thus, care should be taken to avoid placing duplicate matching files in
323 // the kernel's directory.
324 // If no matching init file can be found, returns NULL.
325 static CHAR16
* FindInitrd(IN CHAR16
*LoaderPath
, IN REFIT_VOLUME
*Volume
) {
326 CHAR16
*InitrdName
= NULL
, *FileName
, *KernelVersion
, *InitrdVersion
, *Path
;
327 REFIT_DIR_ITER DirIter
;
328 EFI_FILE_INFO
*DirEntry
;
330 FileName
= Basename(LoaderPath
);
331 KernelVersion
= FindNumbers(FileName
);
332 Path
= FindPath(LoaderPath
);
334 // Add trailing backslash for root directory; necessary on some systems, but must
335 // NOT be added to all directories, since on other systems, a trailing backslash on
336 // anything but the root directory causes them to flake out!
337 if (StrLen(Path
) == 0) {
338 MergeStrings(&Path
, L
"\\", 0);
340 DirIterOpen(Volume
->RootDir
, Path
, &DirIter
);
341 // Now add a trailing backslash if it was NOT added earlier, for consistency in
342 // building the InitrdName later....
343 if ((StrLen(Path
) > 0) && (Path
[StrLen(Path
) - 1] != L
'\\'))
344 MergeStrings(&Path
, L
"\\", 0);
345 while ((DirIterNext(&DirIter
, 2, L
"init*", &DirEntry
)) && (InitrdName
== NULL
)) {
346 InitrdVersion
= FindNumbers(DirEntry
->FileName
);
347 if (KernelVersion
!= NULL
) {
348 if (StriCmp(InitrdVersion
, KernelVersion
) == 0) {
349 MergeStrings(&InitrdName
, Path
, 0);
350 MergeStrings(&InitrdName
, DirEntry
->FileName
, 0);
353 if (InitrdVersion
== NULL
) {
354 MergeStrings(&InitrdName
, Path
, 0);
355 MergeStrings(&InitrdName
, DirEntry
->FileName
, 0);
358 MyFreePool(InitrdVersion
);
360 DirIterClose(&DirIter
);
362 // Note: Don't FreePool(FileName), since Basename returns a pointer WITHIN the string it's passed.
363 MyFreePool(KernelVersion
);
366 } // static CHAR16 * FindInitrd()
368 LOADER_ENTRY
* AddPreparedLoaderEntry(LOADER_ENTRY
*Entry
) {
369 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
372 } // LOADER_ENTRY * AddPreparedLoaderEntry()
374 // Creates a copy of a menu screen.
375 // Returns a pointer to the copy of the menu screen.
376 static REFIT_MENU_SCREEN
* CopyMenuScreen(REFIT_MENU_SCREEN
*Entry
) {
377 REFIT_MENU_SCREEN
*NewEntry
;
380 NewEntry
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
381 if ((Entry
!= NULL
) && (NewEntry
!= NULL
)) {
382 CopyMem(NewEntry
, Entry
, sizeof(REFIT_MENU_SCREEN
));
383 NewEntry
->Title
= (Entry
->Title
) ? StrDuplicate(Entry
->Title
) : NULL
;
384 NewEntry
->TimeoutText
= (Entry
->TimeoutText
) ? StrDuplicate(Entry
->TimeoutText
) : NULL
;
385 if (Entry
->TitleImage
!= NULL
) {
386 NewEntry
->TitleImage
= AllocatePool(sizeof(EG_IMAGE
));
387 if (NewEntry
->TitleImage
!= NULL
)
388 CopyMem(NewEntry
->TitleImage
, Entry
->TitleImage
, sizeof(EG_IMAGE
));
390 NewEntry
->InfoLines
= (CHAR16
**) AllocateZeroPool(Entry
->InfoLineCount
* (sizeof(CHAR16
*)));
391 for (i
= 0; i
< Entry
->InfoLineCount
&& NewEntry
->InfoLines
; i
++) {
392 NewEntry
->InfoLines
[i
] = (Entry
->InfoLines
[i
]) ? StrDuplicate(Entry
->InfoLines
[i
]) : NULL
;
394 NewEntry
->Entries
= (REFIT_MENU_ENTRY
**) AllocateZeroPool(Entry
->EntryCount
* (sizeof (REFIT_MENU_ENTRY
*)));
395 for (i
= 0; i
< Entry
->EntryCount
&& NewEntry
->Entries
; i
++) {
396 AddMenuEntry(NewEntry
, Entry
->Entries
[i
]);
400 } // static REFIT_MENU_SCREEN* CopyMenuScreen()
402 // Creates a copy of a menu entry. Intended to enable moving a stack-based
403 // menu entry (such as the ones for the "reboot" and "exit" functions) to
404 // to the heap. This enables easier deletion of the whole set of menu
405 // entries when re-scanning.
406 // Returns a pointer to the copy of the menu entry.
407 static REFIT_MENU_ENTRY
* CopyMenuEntry(REFIT_MENU_ENTRY
*Entry
) {
408 REFIT_MENU_ENTRY
*NewEntry
;
410 NewEntry
= AllocateZeroPool(sizeof(REFIT_MENU_ENTRY
));
411 if ((Entry
!= NULL
) && (NewEntry
!= NULL
)) {
412 CopyMem(NewEntry
, Entry
, sizeof(REFIT_MENU_ENTRY
));
413 NewEntry
->Title
= (Entry
->Title
) ? StrDuplicate(Entry
->Title
) : NULL
;
414 if (Entry
->BadgeImage
!= NULL
) {
415 NewEntry
->BadgeImage
= AllocatePool(sizeof(EG_IMAGE
));
416 if (NewEntry
->BadgeImage
!= NULL
)
417 CopyMem(NewEntry
->BadgeImage
, Entry
->BadgeImage
, sizeof(EG_IMAGE
));
419 if (Entry
->Image
!= NULL
) {
420 NewEntry
->Image
= AllocatePool(sizeof(EG_IMAGE
));
421 if (NewEntry
->Image
!= NULL
)
422 CopyMem(NewEntry
->Image
, Entry
->Image
, sizeof(EG_IMAGE
));
424 if (Entry
->SubScreen
!= NULL
) {
425 NewEntry
->SubScreen
= CopyMenuScreen(Entry
->SubScreen
);
429 } // REFIT_MENU_ENTRY* CopyMenuEntry()
431 // Creates a new LOADER_ENTRY data structure and populates it with
432 // default values from the specified Entry, or NULL values if Entry
433 // is unspecified (NULL).
434 // Returns a pointer to the new data structure, or NULL if it
435 // couldn't be allocated
436 LOADER_ENTRY
*InitializeLoaderEntry(IN LOADER_ENTRY
*Entry
) {
437 LOADER_ENTRY
*NewEntry
= NULL
;
439 NewEntry
= AllocateZeroPool(sizeof(LOADER_ENTRY
));
440 if (NewEntry
!= NULL
) {
441 NewEntry
->me
.Title
= NULL
;
442 NewEntry
->me
.Tag
= TAG_LOADER
;
443 NewEntry
->Enabled
= TRUE
;
444 NewEntry
->UseGraphicsMode
= FALSE
;
445 NewEntry
->OSType
= 0;
447 NewEntry
->LoaderPath
= (Entry
->LoaderPath
) ? StrDuplicate(Entry
->LoaderPath
) : NULL
;
448 NewEntry
->VolName
= (Entry
->VolName
) ? StrDuplicate(Entry
->VolName
) : NULL
;
449 NewEntry
->DevicePath
= Entry
->DevicePath
;
450 NewEntry
->UseGraphicsMode
= Entry
->UseGraphicsMode
;
451 NewEntry
->LoadOptions
= (Entry
->LoadOptions
) ? StrDuplicate(Entry
->LoadOptions
) : NULL
;
452 NewEntry
->InitrdPath
= (Entry
->InitrdPath
) ? StrDuplicate(Entry
->InitrdPath
) : NULL
;
456 } // LOADER_ENTRY *InitializeLoaderEntry()
458 // Prepare a REFIT_MENU_SCREEN data structure for a subscreen entry. This sets up
459 // the default entry that launches the boot loader using the same options as the
460 // main Entry does. Subsequent options can be added by the calling function.
461 // If a subscreen already exists in the Entry that's passed to this function,
462 // it's left unchanged and a pointer to it is returned.
463 // Returns a pointer to the new subscreen data structure, or NULL if there
464 // were problems allocating memory.
465 REFIT_MENU_SCREEN
*InitializeSubScreen(IN LOADER_ENTRY
*Entry
) {
466 CHAR16
*FileName
, *Temp
= NULL
;
467 REFIT_MENU_SCREEN
*SubScreen
= NULL
;
468 LOADER_ENTRY
*SubEntry
;
470 FileName
= Basename(Entry
->LoaderPath
);
471 if (Entry
->me
.SubScreen
== NULL
) { // No subscreen yet; initialize default entry....
472 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
473 if (SubScreen
!= NULL
) {
474 SubScreen
->Title
= AllocateZeroPool(sizeof(CHAR16
) * 256);
475 SPrint(SubScreen
->Title
, 255, L
"Boot Options for %s on %s",
476 (Entry
->Title
!= NULL
) ? Entry
->Title
: FileName
, Entry
->VolName
);
477 SubScreen
->TitleImage
= Entry
->me
.Image
;
479 SubEntry
= InitializeLoaderEntry(Entry
);
480 if (SubEntry
!= NULL
) {
481 SubEntry
->me
.Title
= L
"Boot using default options";
482 if ((SubEntry
->InitrdPath
!= NULL
) && (StrLen(SubEntry
->InitrdPath
) > 0) && (!StriSubCmp(L
"initrd", SubEntry
->LoadOptions
))) {
483 MergeStrings(&Temp
, L
"initrd=", 0);
484 MergeStrings(&Temp
, SubEntry
->InitrdPath
, 0);
485 MergeStrings(&SubEntry
->LoadOptions
, Temp
, L
' ');
488 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
489 } // if (SubEntry != NULL)
490 } // if (SubScreen != NULL)
491 } else { // existing subscreen; less initialization, and just add new entry later....
492 SubScreen
= Entry
->me
.SubScreen
;
495 } // REFIT_MENU_SCREEN *InitializeSubScreen()
497 VOID
GenerateSubScreen(LOADER_ENTRY
*Entry
, IN REFIT_VOLUME
*Volume
) {
498 REFIT_MENU_SCREEN
*SubScreen
;
499 LOADER_ENTRY
*SubEntry
;
500 CHAR16
*InitrdOption
= NULL
, *Temp
;
501 CHAR16 DiagsFileName
[256];
506 // create the submenu
507 if (StrLen(Entry
->Title
) == 0) {
508 MyFreePool(Entry
->Title
);
511 SubScreen
= InitializeSubScreen(Entry
);
513 // loader-specific submenu entries
514 if (Entry
->OSType
== 'M') { // entries for Mac OS X
516 SubEntry
= InitializeLoaderEntry(Entry
);
517 if (SubEntry
!= NULL
) {
518 SubEntry
->me
.Title
= L
"Boot Mac OS X with a 64-bit kernel";
519 SubEntry
->LoadOptions
= L
"arch=x86_64";
520 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
521 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
524 SubEntry
= InitializeLoaderEntry(Entry
);
525 if (SubEntry
!= NULL
) {
526 SubEntry
->me
.Title
= L
"Boot Mac OS X with a 32-bit kernel";
527 SubEntry
->LoadOptions
= L
"arch=i386";
528 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
529 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
533 if (!(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_SINGLEUSER
)) {
534 SubEntry
= InitializeLoaderEntry(Entry
);
535 if (SubEntry
!= NULL
) {
536 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode";
537 SubEntry
->UseGraphicsMode
= FALSE
;
538 SubEntry
->LoadOptions
= L
"-v";
539 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
543 SubEntry
= InitializeLoaderEntry(Entry
);
544 if (SubEntry
!= NULL
) {
545 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode (64-bit)";
546 SubEntry
->UseGraphicsMode
= FALSE
;
547 SubEntry
->LoadOptions
= L
"-v arch=x86_64";
548 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
551 SubEntry
= InitializeLoaderEntry(Entry
);
552 if (SubEntry
!= NULL
) {
553 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode (32-bit)";
554 SubEntry
->UseGraphicsMode
= FALSE
;
555 SubEntry
->LoadOptions
= L
"-v arch=i386";
556 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
560 SubEntry
= InitializeLoaderEntry(Entry
);
561 if (SubEntry
!= NULL
) {
562 SubEntry
->me
.Title
= L
"Boot Mac OS X in single user mode";
563 SubEntry
->UseGraphicsMode
= FALSE
;
564 SubEntry
->LoadOptions
= L
"-v -s";
565 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
569 // check for Apple hardware diagnostics
570 StrCpy(DiagsFileName
, L
"\\System\\Library\\CoreServices\\.diagnostics\\diags.efi");
571 if (FileExists(Volume
->RootDir
, DiagsFileName
) && !(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_HWTEST
)) {
572 SubEntry
= InitializeLoaderEntry(Entry
);
573 if (SubEntry
!= NULL
) {
574 SubEntry
->me
.Title
= L
"Run Apple Hardware Test";
575 MyFreePool(SubEntry
->LoaderPath
);
576 SubEntry
->LoaderPath
= StrDuplicate(DiagsFileName
);
577 SubEntry
->DevicePath
= FileDevicePath(Volume
->DeviceHandle
, SubEntry
->LoaderPath
);
578 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
579 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
581 } // if diagnostics entry found
583 } else if (Entry
->OSType
== 'L') { // entries for Linux kernels with EFI stub loaders
584 File
= ReadLinuxOptionsFile(Entry
->LoaderPath
, Volume
);
586 if ((Temp
= FindInitrd(Entry
->LoaderPath
, Volume
)) != NULL
) {
587 MergeStrings(&InitrdOption
, L
"initrd=", 0);
588 MergeStrings(&InitrdOption
, Temp
, 0);
590 TokenCount
= ReadTokenLine(File
, &TokenList
); // read and discard first entry, since it's
591 FreeTokenLine(&TokenList
, &TokenCount
); // set up by InitializeSubScreen(), earlier....
592 while ((TokenCount
= ReadTokenLine(File
, &TokenList
)) > 1) {
593 SubEntry
= InitializeLoaderEntry(Entry
);
594 SubEntry
->me
.Title
= StrDuplicate(TokenList
[0]);
595 MyFreePool(SubEntry
->LoadOptions
);
596 SubEntry
->LoadOptions
= StrDuplicate(TokenList
[1]);
597 MergeStrings(&SubEntry
->LoadOptions
, InitrdOption
, L
' ');
598 FreeTokenLine(&TokenList
, &TokenCount
);
599 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_LINUX
;
600 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
602 MyFreePool(InitrdOption
);
605 } // if Linux options file exists
607 } else if (Entry
->OSType
== 'E') { // entries for ELILO
608 SubEntry
= InitializeLoaderEntry(Entry
);
609 if (SubEntry
!= NULL
) {
610 SubEntry
->me
.Title
= L
"Run ELILO in interactive mode";
611 SubEntry
->LoadOptions
= L
"-p";
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 17\" iMac or a 15\" MacBook Pro (*)";
619 SubEntry
->UseGraphicsMode
= TRUE
;
620 SubEntry
->LoadOptions
= L
"-d 0 i17";
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 20\" iMac (*)";
628 SubEntry
->UseGraphicsMode
= TRUE
;
629 SubEntry
->LoadOptions
= L
"-d 0 i20";
630 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
631 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
634 SubEntry
= InitializeLoaderEntry(Entry
);
635 if (SubEntry
!= NULL
) {
636 SubEntry
->me
.Title
= L
"Boot Linux for a Mac Mini (*)";
637 SubEntry
->UseGraphicsMode
= TRUE
;
638 SubEntry
->LoadOptions
= L
"-d 0 mini";
639 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
640 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
643 AddMenuInfoLine(SubScreen
, L
"NOTE: This is an example. Entries");
644 AddMenuInfoLine(SubScreen
, L
"marked with (*) may not work.");
646 } else if (Entry
->OSType
== 'X') { // entries for xom.efi
647 // by default, skip the built-in selection and boot from hard disk only
648 Entry
->LoadOptions
= L
"-s -h";
650 SubEntry
= InitializeLoaderEntry(Entry
);
651 if (SubEntry
!= NULL
) {
652 SubEntry
->me
.Title
= L
"Boot Windows from Hard Disk";
653 SubEntry
->LoadOptions
= L
"-s -h";
654 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
655 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
658 SubEntry
= InitializeLoaderEntry(Entry
);
659 if (SubEntry
!= NULL
) {
660 SubEntry
->me
.Title
= L
"Boot Windows from CD-ROM";
661 SubEntry
->LoadOptions
= L
"-s -c";
662 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
663 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
666 SubEntry
= InitializeLoaderEntry(Entry
);
667 if (SubEntry
!= NULL
) {
668 SubEntry
->me
.Title
= L
"Run XOM in text mode";
669 SubEntry
->UseGraphicsMode
= FALSE
;
670 SubEntry
->LoadOptions
= L
"-v";
671 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
672 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
674 } // entries for xom.efi
675 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
676 Entry
->me
.SubScreen
= SubScreen
;
677 } // VOID GenerateSubScreen()
679 // Returns options for a Linux kernel. Reads them from an options file in the
680 // kernel's directory; and if present, adds an initrd= option for an initial
681 // RAM disk file with the same version number as the kernel file.
682 static CHAR16
* GetMainLinuxOptions(IN CHAR16
* LoaderPath
, IN REFIT_VOLUME
*Volume
) {
683 CHAR16
*Options
= NULL
, *InitrdName
, *InitrdOption
= NULL
;
685 Options
= GetFirstOptionsFromFile(LoaderPath
, Volume
);
686 InitrdName
= FindInitrd(LoaderPath
, Volume
);
687 if (InitrdName
!= NULL
) {
688 MergeStrings(&InitrdOption
, L
"initrd=", 0);
689 MergeStrings(&InitrdOption
, InitrdName
, 0);
691 MergeStrings(&Options
, InitrdOption
, ' ');
692 MyFreePool(InitrdOption
);
693 MyFreePool(InitrdName
);
695 } // static CHAR16 * GetMainLinuxOptions()
697 // Sets a few defaults for a loader entry -- mainly the icon, but also the OS type
698 // code and shortcut letter. For Linux EFI stub loaders, also sets kernel options
699 // that will (with luck) work fairly automatically.
700 VOID
SetLoaderDefaults(LOADER_ENTRY
*Entry
, CHAR16
*LoaderPath
, IN REFIT_VOLUME
*Volume
) {
701 CHAR16 IconFileName
[256];
702 CHAR16
*FileName
, *PathOnly
, *OSIconName
= NULL
, *Temp
;
703 CHAR16 ShortcutLetter
= 0;
705 FileName
= Basename(LoaderPath
);
706 PathOnly
= FindPath(LoaderPath
);
708 // locate a custom icon for the loader
709 StrCpy(IconFileName
, LoaderPath
);
710 ReplaceEfiExtension(IconFileName
, L
".icns");
711 if (FileExists(Volume
->RootDir
, IconFileName
)) {
712 Entry
->me
.Image
= LoadIcns(Volume
->RootDir
, IconFileName
, 128);
713 } else if ((StrLen(PathOnly
) == 0) && (Volume
->VolIconImage
!= NULL
)) {
714 Entry
->me
.Image
= Volume
->VolIconImage
;
715 } // icon matched to loader or volume
717 Temp
= FindLastDirName(LoaderPath
);
718 MergeStrings(&OSIconName
, Temp
, L
',');
721 if (OSIconName
!= NULL
) {
722 ShortcutLetter
= OSIconName
[0];
725 // detect specific loaders
726 if (StriSubCmp(L
"bzImage", LoaderPath
) || StriSubCmp(L
"vmlinuz", LoaderPath
)) {
727 MergeStrings(&OSIconName
, L
"linux", L
',');
729 if (ShortcutLetter
== 0)
730 ShortcutLetter
= 'L';
731 Entry
->LoadOptions
= GetMainLinuxOptions(LoaderPath
, Volume
);
732 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_LINUX
;
733 } else if (StriSubCmp(L
"refit", LoaderPath
)) {
734 MergeStrings(&OSIconName
, L
"refit", L
',');
736 ShortcutLetter
= 'R';
737 } else if (StriCmp(LoaderPath
, MACOSX_LOADER_PATH
) == 0) {
738 if (Volume
->VolIconImage
!= NULL
) { // custom icon file found
739 Entry
->me
.Image
= Volume
->VolIconImage
;
741 MergeStrings(&OSIconName
, L
"mac", L
',');
743 ShortcutLetter
= 'M';
744 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
745 } else if (StriCmp(FileName
, L
"diags.efi") == 0) {
746 MergeStrings(&OSIconName
, L
"hwtest", L
',');
747 } else if (StriCmp(FileName
, L
"e.efi") == 0 || StriCmp(FileName
, L
"elilo.efi") == 0 || StriSubCmp(L
"elilo", FileName
)) {
748 MergeStrings(&OSIconName
, L
"elilo,linux", L
',');
750 // if (secure_mode()) { // hack to enable ELILO to boot in secure mode
751 // Temp = StrDuplicate(L"-C ");
752 // MergeStrings(&Temp, PathOnly, 0);
753 // MergeStrings(&Temp, L"elilo.conf", L'\\');
754 // Entry->LoadOptions = Temp;
756 if (ShortcutLetter
== 0)
757 ShortcutLetter
= 'L';
758 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
759 } else if (StriSubCmp(L
"grub", FileName
)) {
761 ShortcutLetter
= 'G';
762 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_GRUB
;
763 } else if (StriCmp(FileName
, L
"cdboot.efi") == 0 ||
764 StriCmp(FileName
, L
"bootmgr.efi") == 0 ||
765 StriCmp(FileName
, L
"Bootmgfw.efi") == 0) {
766 MergeStrings(&OSIconName
, L
"win", L
',');
768 ShortcutLetter
= 'W';
769 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
770 } else if (StriCmp(FileName
, L
"xom.efi") == 0) {
771 MergeStrings(&OSIconName
, L
"xom,win", L
',');
772 Entry
->UseGraphicsMode
= TRUE
;
774 ShortcutLetter
= 'W';
775 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
778 if ((ShortcutLetter
>= 'a') && (ShortcutLetter
<= 'z'))
779 ShortcutLetter
= ShortcutLetter
- 'a' + 'A'; // convert lowercase to uppercase
780 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
781 if (Entry
->me
.Image
== NULL
)
782 Entry
->me
.Image
= LoadOSIcon(OSIconName
, L
"unknown", FALSE
);
783 MyFreePool(PathOnly
);
784 } // VOID SetLoaderDefaults()
786 // Add a specified EFI boot loader to the list, using automatic settings
787 // for icons, options, etc.
788 LOADER_ENTRY
* AddLoaderEntry(IN CHAR16
*LoaderPath
, IN CHAR16
*LoaderTitle
, IN REFIT_VOLUME
*Volume
) {
791 CleanUpPathNameSlashes(LoaderPath
);
792 Entry
= InitializeLoaderEntry(NULL
);
794 Entry
->Title
= StrDuplicate((LoaderTitle
!= NULL
) ? LoaderTitle
: LoaderPath
);
795 Entry
->me
.Title
= AllocateZeroPool(sizeof(CHAR16
) * 256);
796 SPrint(Entry
->me
.Title
, 255, L
"Boot %s from %s", (LoaderTitle
!= NULL
) ? LoaderTitle
: LoaderPath
, Volume
->VolName
);
798 Entry
->me
.BadgeImage
= Volume
->VolBadgeImage
;
799 if ((LoaderPath
!= NULL
) && (LoaderPath
[0] != L
'\\')) {
800 Entry
->LoaderPath
= StrDuplicate(L
"\\");
802 Entry
->LoaderPath
= NULL
;
804 MergeStrings(&(Entry
->LoaderPath
), LoaderPath
, 0);
805 Entry
->VolName
= Volume
->VolName
;
806 Entry
->DevicePath
= FileDevicePath(Volume
->DeviceHandle
, Entry
->LoaderPath
);
807 SetLoaderDefaults(Entry
, LoaderPath
, Volume
);
808 GenerateSubScreen(Entry
, Volume
);
809 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
813 } // LOADER_ENTRY * AddLoaderEntry()
815 // Returns -1 if (Time1 < Time2), +1 if (Time1 > Time2), or 0 if
816 // (Time1 == Time2). Precision is only to the nearest second; since
817 // this is used for sorting boot loader entries, differences smaller
818 // than this are likely to be meaningless (and unlikely!).
819 INTN
TimeComp(EFI_TIME
*Time1
, EFI_TIME
*Time2
) {
820 INT64 Time1InSeconds
, Time2InSeconds
;
822 // Following values are overestimates; I'm assuming 31 days in every month.
823 // This is fine for the purpose of this function, which has a limited
825 Time1InSeconds
= Time1
->Second
+ (Time1
->Minute
* 60) + (Time1
->Hour
* 3600) + (Time1
->Day
* 86400) +
826 (Time1
->Month
* 2678400) + ((Time1
->Year
- 1998) * 32140800);
827 Time2InSeconds
= Time2
->Second
+ (Time2
->Minute
* 60) + (Time2
->Hour
* 3600) + (Time2
->Day
* 86400) +
828 (Time2
->Month
* 2678400) + ((Time2
->Year
- 1998) * 32140800);
829 if (Time1InSeconds
< Time2InSeconds
)
831 else if (Time1InSeconds
> Time2InSeconds
)
837 // Adds a loader list element, keeping it sorted by date. Returns the new
838 // first element (the one with the most recent date).
839 static struct LOADER_LIST
* AddLoaderListEntry(struct LOADER_LIST
*LoaderList
, struct LOADER_LIST
*NewEntry
) {
840 struct LOADER_LIST
*LatestEntry
, *CurrentEntry
, *PrevEntry
= NULL
;
842 LatestEntry
= CurrentEntry
= LoaderList
;
843 if (LoaderList
== NULL
) {
844 LatestEntry
= NewEntry
;
846 while ((CurrentEntry
!= NULL
) && (TimeComp(&(NewEntry
->TimeStamp
), &(CurrentEntry
->TimeStamp
)) < 0)) {
847 PrevEntry
= CurrentEntry
;
848 CurrentEntry
= CurrentEntry
->NextEntry
;
850 NewEntry
->NextEntry
= CurrentEntry
;
851 if (PrevEntry
== NULL
) {
852 LatestEntry
= NewEntry
;
854 PrevEntry
->NextEntry
= NewEntry
;
857 return (LatestEntry
);
858 } // static VOID AddLoaderListEntry()
860 // Delete the LOADER_LIST linked list
861 static VOID
CleanUpLoaderList(struct LOADER_LIST
*LoaderList
) {
862 struct LOADER_LIST
*Temp
;
864 while (LoaderList
!= NULL
) {
866 LoaderList
= LoaderList
->NextEntry
;
867 MyFreePool(Temp
->FileName
);
870 } // static VOID CleanUpLoaderList()
872 // Scan an individual directory for EFI boot loader files and, if found,
873 // add them to the list. Sorts the entries within the loader directory
874 // so that the most recent one appears first in the list.
875 static VOID
ScanLoaderDir(IN REFIT_VOLUME
*Volume
, IN CHAR16
*Path
, IN CHAR16
*Pattern
)
878 REFIT_DIR_ITER DirIter
;
879 EFI_FILE_INFO
*DirEntry
;
880 CHAR16 FileName
[256], *Extension
;
881 struct LOADER_LIST
*LoaderList
= NULL
, *NewLoader
;
883 if ((!SelfDirPath
|| !Path
|| ((StriCmp(Path
, SelfDirPath
) == 0) && Volume
->DeviceHandle
!= SelfVolume
->DeviceHandle
) ||
884 (StriCmp(Path
, SelfDirPath
) != 0)) && (!IsIn(Path
, GlobalConfig
.DontScanDirs
))) {
885 // look through contents of the directory
886 DirIterOpen(Volume
->RootDir
, Path
, &DirIter
);
887 while (DirIterNext(&DirIter
, 2, Pattern
, &DirEntry
)) {
888 Extension
= FindExtension(DirEntry
->FileName
);
889 if (DirEntry
->FileName
[0] == '.' ||
890 StriCmp(Extension
, L
".icns") == 0 ||
891 StriSubCmp(L
"shell", DirEntry
->FileName
) ||
892 IsIn(DirEntry
->FileName
, GlobalConfig
.DontScanFiles
))
893 continue; // skip this
896 SPrint(FileName
, 255, L
"\\%s\\%s", Path
, DirEntry
->FileName
);
898 SPrint(FileName
, 255, L
"\\%s", DirEntry
->FileName
);
899 CleanUpPathNameSlashes(FileName
);
900 NewLoader
= AllocateZeroPool(sizeof(struct LOADER_LIST
));
901 if (NewLoader
!= NULL
) {
902 NewLoader
->FileName
= StrDuplicate(FileName
);
903 NewLoader
->TimeStamp
= DirEntry
->ModificationTime
;
904 LoaderList
= AddLoaderListEntry(LoaderList
, NewLoader
);
906 MyFreePool(Extension
);
908 NewLoader
= LoaderList
;
909 while (NewLoader
!= NULL
) {
910 AddLoaderEntry(NewLoader
->FileName
, NULL
, Volume
);
911 NewLoader
= NewLoader
->NextEntry
;
913 CleanUpLoaderList(LoaderList
);
914 Status
= DirIterClose(&DirIter
);
915 if (Status
!= EFI_NOT_FOUND
) {
917 SPrint(FileName
, 255, L
"while scanning the %s directory", Path
);
919 StrCpy(FileName
, L
"while scanning the root directory");
920 CheckError(Status
, FileName
);
921 } // if (Status != EFI_NOT_FOUND)
922 } // if not scanning our own directory
923 } /* static VOID ScanLoaderDir() */
925 static VOID
ScanEfiFiles(REFIT_VOLUME
*Volume
) {
927 REFIT_DIR_ITER EfiDirIter
;
928 EFI_FILE_INFO
*EfiDirEntry
;
929 CHAR16 FileName
[256], *Directory
, *MatchPatterns
;
932 MatchPatterns
= StrDuplicate(LOADER_MATCH_PATTERNS
);
933 if (GlobalConfig
.ScanAllLinux
)
934 MergeStrings(&MatchPatterns
, LINUX_MATCH_PATTERNS
, L
',');
936 if ((Volume
->RootDir
!= NULL
) && (Volume
->VolName
!= NULL
)) {
937 // check for Mac OS X boot loader
938 if (!IsIn(L
"\\System\\Library\\CoreServices", GlobalConfig
.DontScanDirs
)) {
939 StrCpy(FileName
, MACOSX_LOADER_PATH
);
940 if (FileExists(Volume
->RootDir
, FileName
) && !IsIn(L
"boot.efi", GlobalConfig
.DontScanFiles
)) {
941 AddLoaderEntry(FileName
, L
"Mac OS X", Volume
);
945 StrCpy(FileName
, L
"\\System\\Library\\CoreServices\\xom.efi");
946 if (FileExists(Volume
->RootDir
, FileName
) && !IsIn(L
"boot.efi", GlobalConfig
.DontScanFiles
)) {
947 AddLoaderEntry(FileName
, L
"Windows XP (XoM)", Volume
);
949 } // if Mac directory not in GlobalConfig.DontScanDirs list
951 // check for Microsoft boot loader/menu
952 StrCpy(FileName
, L
"\\EFI\\Microsoft\\Boot\\Bootmgfw.efi");
953 if (FileExists(Volume
->RootDir
, FileName
) && !IsIn(L
"\\EFI\\Microsoft\\Boot", GlobalConfig
.DontScanDirs
) &&
954 !IsIn(L
"bootmgfw.efi", GlobalConfig
.DontScanFiles
)) {
955 AddLoaderEntry(FileName
, L
"Microsoft EFI boot", Volume
);
958 // scan the root directory for EFI executables
959 ScanLoaderDir(Volume
, L
"\\", MatchPatterns
);
961 // scan subdirectories of the EFI directory (as per the standard)
962 DirIterOpen(Volume
->RootDir
, L
"EFI", &EfiDirIter
);
963 while (DirIterNext(&EfiDirIter
, 1, NULL
, &EfiDirEntry
)) {
964 if (StriCmp(EfiDirEntry
->FileName
, L
"tools") == 0 || EfiDirEntry
->FileName
[0] == '.')
965 continue; // skip this, doesn't contain boot loaders
966 SPrint(FileName
, 255, L
"\\EFI\\%s", EfiDirEntry
->FileName
);
967 ScanLoaderDir(Volume
, FileName
, MatchPatterns
);
969 Status
= DirIterClose(&EfiDirIter
);
970 if (Status
!= EFI_NOT_FOUND
)
971 CheckError(Status
, L
"while scanning the EFI directory");
973 // Scan user-specified (or additional default) directories....
975 while ((Directory
= FindCommaDelimited(GlobalConfig
.AlsoScan
, i
++)) != NULL
) {
976 CleanUpPathNameSlashes(Directory
);
977 Length
= StrLen(Directory
);
979 ScanLoaderDir(Volume
, Directory
, MatchPatterns
);
980 MyFreePool(Directory
);
983 } // static VOID ScanEfiFiles()
985 // Scan internal disks for valid EFI boot loaders....
986 static VOID
ScanInternal(VOID
) {
989 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
990 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_INTERNAL
) {
991 ScanEfiFiles(Volumes
[VolumeIndex
]);
994 } // static VOID ScanInternal()
996 // Scan external disks for valid EFI boot loaders....
997 static VOID
ScanExternal(VOID
) {
1000 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1001 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_EXTERNAL
) {
1002 ScanEfiFiles(Volumes
[VolumeIndex
]);
1005 } // static VOID ScanExternal()
1007 // Scan internal disks for valid EFI boot loaders....
1008 static VOID
ScanOptical(VOID
) {
1011 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1012 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_OPTICAL
) {
1013 ScanEfiFiles(Volumes
[VolumeIndex
]);
1016 } // static VOID ScanOptical()
1019 // legacy boot functions
1022 static EFI_STATUS
ActivateMbrPartition(IN EFI_BLOCK_IO
*BlockIO
, IN UINTN PartitionIndex
)
1025 UINT8 SectorBuffer
[512];
1026 MBR_PARTITION_INFO
*MbrTable
, *EMbrTable
;
1027 UINT32 ExtBase
, ExtCurrent
, NextExtCurrent
;
1028 UINTN LogicalPartitionIndex
= 4;
1030 BOOLEAN HaveBootCode
;
1033 Status
= refit_call5_wrapper(BlockIO
->ReadBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, 0, 512, SectorBuffer
);
1034 if (EFI_ERROR(Status
))
1036 if (*((UINT16
*)(SectorBuffer
+ 510)) != 0xaa55)
1037 return EFI_NOT_FOUND
; // safety measure #1
1039 // add boot code if necessary
1040 HaveBootCode
= FALSE
;
1041 for (i
= 0; i
< MBR_BOOTCODE_SIZE
; i
++) {
1042 if (SectorBuffer
[i
] != 0) {
1043 HaveBootCode
= TRUE
;
1047 if (!HaveBootCode
) {
1048 // no boot code found in the MBR, add the syslinux MBR code
1049 SetMem(SectorBuffer
, MBR_BOOTCODE_SIZE
, 0);
1050 CopyMem(SectorBuffer
, syslinux_mbr
, SYSLINUX_MBR_SIZE
);
1053 // set the partition active
1054 MbrTable
= (MBR_PARTITION_INFO
*)(SectorBuffer
+ 446);
1056 for (i
= 0; i
< 4; i
++) {
1057 if (MbrTable
[i
].Flags
!= 0x00 && MbrTable
[i
].Flags
!= 0x80)
1058 return EFI_NOT_FOUND
; // safety measure #2
1059 if (i
== PartitionIndex
)
1060 MbrTable
[i
].Flags
= 0x80;
1061 else if (PartitionIndex
>= 4 && IS_EXTENDED_PART_TYPE(MbrTable
[i
].Type
)) {
1062 MbrTable
[i
].Flags
= 0x80;
1063 ExtBase
= MbrTable
[i
].StartLBA
;
1065 MbrTable
[i
].Flags
= 0x00;
1069 Status
= refit_call5_wrapper(BlockIO
->WriteBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, 0, 512, SectorBuffer
);
1070 if (EFI_ERROR(Status
))
1073 if (PartitionIndex
>= 4) {
1074 // we have to activate a logical partition, so walk the EMBR chain
1076 // NOTE: ExtBase was set above while looking at the MBR table
1077 for (ExtCurrent
= ExtBase
; ExtCurrent
; ExtCurrent
= NextExtCurrent
) {
1078 // read current EMBR
1079 Status
= refit_call5_wrapper(BlockIO
->ReadBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, ExtCurrent
, 512, SectorBuffer
);
1080 if (EFI_ERROR(Status
))
1082 if (*((UINT16
*)(SectorBuffer
+ 510)) != 0xaa55)
1083 return EFI_NOT_FOUND
; // safety measure #3
1085 // scan EMBR, set appropriate partition active
1086 EMbrTable
= (MBR_PARTITION_INFO
*)(SectorBuffer
+ 446);
1088 for (i
= 0; i
< 4; i
++) {
1089 if (EMbrTable
[i
].Flags
!= 0x00 && EMbrTable
[i
].Flags
!= 0x80)
1090 return EFI_NOT_FOUND
; // safety measure #4
1091 if (EMbrTable
[i
].StartLBA
== 0 || EMbrTable
[i
].Size
== 0)
1093 if (IS_EXTENDED_PART_TYPE(EMbrTable
[i
].Type
)) {
1094 // link to next EMBR
1095 NextExtCurrent
= ExtBase
+ EMbrTable
[i
].StartLBA
;
1096 EMbrTable
[i
].Flags
= (PartitionIndex
>= LogicalPartitionIndex
) ? 0x80 : 0x00;
1099 // logical partition
1100 EMbrTable
[i
].Flags
= (PartitionIndex
== LogicalPartitionIndex
) ? 0x80 : 0x00;
1101 LogicalPartitionIndex
++;
1105 // write current EMBR
1106 Status
= refit_call5_wrapper(BlockIO
->WriteBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, ExtCurrent
, 512, SectorBuffer
);
1107 if (EFI_ERROR(Status
))
1110 if (PartitionIndex
< LogicalPartitionIndex
)
1111 break; // stop the loop, no need to touch further EMBRs
1117 } /* static EFI_STATUS ActivateMbrPartition() */
1119 // early 2006 Core Duo / Core Solo models
1120 static UINT8 LegacyLoaderDevicePath1Data
[] = {
1121 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1122 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1123 0xFF, 0xFF, 0xF9, 0xFF, 0x00, 0x00, 0x00, 0x00,
1124 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1125 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1126 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1128 // mid-2006 Mac Pro (and probably other Core 2 models)
1129 static UINT8 LegacyLoaderDevicePath2Data
[] = {
1130 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1131 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1132 0xFF, 0xFF, 0xF7, 0xFF, 0x00, 0x00, 0x00, 0x00,
1133 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1134 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1135 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1137 // mid-2007 MBP ("Santa Rosa" based models)
1138 static UINT8 LegacyLoaderDevicePath3Data
[] = {
1139 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1140 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1141 0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
1142 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1143 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1144 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1147 static UINT8 LegacyLoaderDevicePath4Data
[] = {
1148 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1149 0x00, 0x00, 0xC0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1150 0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
1151 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1152 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1153 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1155 // late-2008 MB/MBP (NVidia chipset)
1156 static UINT8 LegacyLoaderDevicePath5Data
[] = {
1157 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1158 0x00, 0x40, 0xCB, 0xFF, 0x00, 0x00, 0x00, 0x00,
1159 0xFF, 0xBF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
1160 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1161 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1162 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1165 static EFI_DEVICE_PATH
*LegacyLoaderList
[] = {
1166 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath1Data
,
1167 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath2Data
,
1168 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath3Data
,
1169 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath4Data
,
1170 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath5Data
,
1174 #define MAX_DISCOVERED_PATHS (16)
1176 static VOID
StartLegacy(IN LEGACY_ENTRY
*Entry
)
1179 EG_IMAGE
*BootLogoImage
;
1180 UINTN ErrorInStep
= 0;
1181 EFI_DEVICE_PATH
*DiscoveredPathList
[MAX_DISCOVERED_PATHS
];
1183 BeginExternalScreen(TRUE
, L
"Booting Legacy OS (Mac mode)");
1185 BootLogoImage
= LoadOSIcon(Entry
->Volume
->OSIconName
, L
"legacy", TRUE
);
1186 if (BootLogoImage
!= NULL
)
1187 BltImageAlpha(BootLogoImage
,
1188 (UGAWidth
- BootLogoImage
->Width
) >> 1,
1189 (UGAHeight
- BootLogoImage
->Height
) >> 1,
1190 &StdBackgroundPixel
);
1192 if (Entry
->Volume
->IsMbrPartition
) {
1193 ActivateMbrPartition(Entry
->Volume
->WholeDiskBlockIO
, Entry
->Volume
->MbrPartitionIndex
);
1196 ExtractLegacyLoaderPaths(DiscoveredPathList
, MAX_DISCOVERED_PATHS
, LegacyLoaderList
);
1198 Status
= StartEFIImageList(DiscoveredPathList
, Entry
->LoadOptions
, NULL
, L
"legacy loader", 0, &ErrorInStep
, TRUE
);
1199 if (Status
== EFI_NOT_FOUND
) {
1200 if (ErrorInStep
== 1) {
1201 Print(L
"\nPlease make sure that you have the latest firmware update installed.\n");
1202 } else if (ErrorInStep
== 3) {
1203 Print(L
"\nThe firmware refused to boot from the selected volume. Note that external\n"
1204 L
"hard drives are not well-supported by Apple's firmware for legacy OS booting.\n");
1207 FinishExternalScreen();
1208 } /* static VOID StartLegacy() */
1210 // Start a device on a non-Mac using the EFI_LEGACY_BIOS_PROTOCOL
1211 #ifdef __MAKEWITH_TIANO
1212 static VOID
StartLegacyUEFI(IN LEGACY_ENTRY
*Entry
)
1214 BeginExternalScreen(TRUE
, L
"Booting Legacy OS (UEFI mode)");
1216 BdsLibConnectDevicePath (Entry
->BdsOption
->DevicePath
);
1217 BdsLibDoLegacyBoot(Entry
->BdsOption
);
1219 // If we get here, it means that there was a failure....
1220 Print(L
"Failure booting legacy (BIOS) OS.");
1222 FinishExternalScreen();
1223 } // static VOID StartLegacyUEFI()
1224 #endif // __MAKEWITH_TIANO
1226 static LEGACY_ENTRY
* AddLegacyEntry(IN CHAR16
*LoaderTitle
, IN REFIT_VOLUME
*Volume
)
1228 LEGACY_ENTRY
*Entry
, *SubEntry
;
1229 REFIT_MENU_SCREEN
*SubScreen
;
1231 CHAR16 ShortcutLetter
= 0;
1233 if (LoaderTitle
== NULL
) {
1234 if (Volume
->OSName
!= NULL
) {
1235 LoaderTitle
= Volume
->OSName
;
1236 if (LoaderTitle
[0] == 'W' || LoaderTitle
[0] == 'L')
1237 ShortcutLetter
= LoaderTitle
[0];
1239 LoaderTitle
= L
"Legacy OS";
1241 if (Volume
->VolName
!= NULL
)
1242 VolDesc
= Volume
->VolName
;
1244 VolDesc
= (Volume
->DiskKind
== DISK_KIND_OPTICAL
) ? L
"CD" : L
"HD";
1246 // prepare the menu entry
1247 Entry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1248 Entry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1249 SPrint(Entry
->me
.Title
, 255, L
"Boot %s from %s", LoaderTitle
, VolDesc
);
1250 Entry
->me
.Tag
= TAG_LEGACY
;
1252 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1253 Entry
->me
.Image
= LoadOSIcon(Volume
->OSIconName
, L
"legacy", FALSE
);
1254 Entry
->me
.BadgeImage
= Volume
->VolBadgeImage
;
1255 Entry
->Volume
= Volume
;
1256 Entry
->LoadOptions
= (Volume
->DiskKind
== DISK_KIND_OPTICAL
) ? L
"CD" :
1257 ((Volume
->DiskKind
== DISK_KIND_EXTERNAL
) ? L
"USB" : L
"HD");
1258 Entry
->Enabled
= TRUE
;
1260 // create the submenu
1261 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
1262 SubScreen
->Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1263 SPrint(SubScreen
->Title
, 255, L
"Boot Options for %s on %s", LoaderTitle
, VolDesc
);
1264 SubScreen
->TitleImage
= Entry
->me
.Image
;
1267 SubEntry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1268 SubEntry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1269 SPrint(SubEntry
->me
.Title
, 255, L
"Boot %s", LoaderTitle
);
1270 SubEntry
->me
.Tag
= TAG_LEGACY
;
1271 SubEntry
->Volume
= Entry
->Volume
;
1272 SubEntry
->LoadOptions
= Entry
->LoadOptions
;
1273 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
1275 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
1276 Entry
->me
.SubScreen
= SubScreen
;
1277 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1279 } /* static LEGACY_ENTRY * AddLegacyEntry() */
1282 #ifdef __MAKEWITH_TIANO
1283 // default volume badge icon based on disk kind
1284 static EG_IMAGE
* GetDiskBadge(IN UINTN DiskType
) {
1285 EG_IMAGE
* Badge
= NULL
;
1289 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_INTERNAL
);
1292 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_EXTERNAL
);
1295 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_OPTICAL
);
1299 } // static EG_IMAGE * GetDiskBadge()
1302 Create a rEFInd boot option from a Legacy BIOS protocol option.
1304 static LEGACY_ENTRY
* AddLegacyEntryUEFI(BDS_COMMON_OPTION
*BdsOption
, IN UINT16 DiskType
)
1306 LEGACY_ENTRY
*Entry
, *SubEntry
;
1307 REFIT_MENU_SCREEN
*SubScreen
;
1308 CHAR16 ShortcutLetter
= 0;
1309 CHAR16
*LegacyDescription
= BdsOption
->Description
;
1311 // ScanVolume(Volume);
1313 // prepare the menu entry
1314 Entry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1315 Entry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1316 SPrint(Entry
->me
.Title
, 255, L
"Boot legacy target %s", LegacyDescription
);
1317 Entry
->me
.Tag
= TAG_LEGACY_UEFI
;
1319 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1320 Entry
->me
.Image
= LoadOSIcon(L
"legacy", L
"legacy", TRUE
);
1321 Entry
->LoadOptions
= (DiskType
== BBS_CDROM
) ? L
"CD" :
1322 ((DiskType
== BBS_USB
) ? L
"USB" : L
"HD");
1323 Entry
->me
.BadgeImage
= GetDiskBadge(DiskType
);
1324 // Entry->me.BadgeImage = Volume->VolBadgeImage;
1325 Entry
->BdsOption
= BdsOption
;
1326 Entry
->Enabled
= TRUE
;
1328 // create the submenu
1329 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
1330 SubScreen
->Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1331 SPrint(SubScreen
->Title
, 255, L
"No boot options for legacy target");
1332 SubScreen
->TitleImage
= Entry
->me
.Image
;
1335 SubEntry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1336 SubEntry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1337 SPrint(SubEntry
->me
.Title
, 255, L
"Boot %s", LegacyDescription
);
1338 SubEntry
->me
.Tag
= TAG_LEGACY_UEFI
;
1339 Entry
->BdsOption
= BdsOption
;
1340 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
1342 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
1343 Entry
->me
.SubScreen
= SubScreen
;
1344 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1346 } /* static LEGACY_ENTRY * AddLegacyEntryUEFI() */
1349 Scan for legacy BIOS targets on machines that implement EFI_LEGACY_BIOS_PROTOCOL.
1350 In testing, protocol has not been implemented on Macs but has been
1351 implemented on several Dell PCs and an ASUS motherboard.
1352 Restricts output to disks of the specified DiskType.
1354 static VOID
ScanLegacyUEFI(IN UINTN DiskType
)
1357 EFI_LEGACY_BIOS_PROTOCOL
*LegacyBios
;
1358 UINT16
*BootOrder
= NULL
;
1360 CHAR16 BootOption
[10];
1361 UINTN BootOrderSize
= 0;
1363 BDS_COMMON_OPTION
*BdsOption
;
1364 LIST_ENTRY TempList
;
1365 BBS_BBS_DEVICE_PATH
* BbsDevicePath
= NULL
;
1366 // REFIT_VOLUME Volume;
1368 InitializeListHead (&TempList
);
1369 ZeroMem (Buffer
, sizeof (Buffer
));
1371 // If LegacyBios protocol is not implemented on this platform, then
1372 //we do not support this type of legacy boot on this machine.
1373 Status
= gBS
->LocateProtocol(&gEfiLegacyBootProtocolGuid
, NULL
, (VOID
**) &LegacyBios
);
1374 if (EFI_ERROR (Status
))
1377 // Grab the boot order
1378 BootOrder
= BdsLibGetVariableAndSize(L
"BootOrder", &gEfiGlobalVariableGuid
, &BootOrderSize
);
1379 if (BootOrder
== NULL
) {
1384 while (Index
< BootOrderSize
/ sizeof (UINT16
))
1386 // Grab each boot option variable from the boot order, and convert
1387 // the variable into a BDS boot option
1388 UnicodeSPrint (BootOption
, sizeof (BootOption
), L
"Boot%04x", BootOrder
[Index
]);
1389 BdsOption
= BdsLibVariableToOption (&TempList
, BootOption
);
1391 if (BdsOption
!= NULL
) {
1392 BbsDevicePath
= (BBS_BBS_DEVICE_PATH
*)BdsOption
->DevicePath
;
1394 // Only add the entry if it is of a requested type (e.g. USB, HD)
1396 // Two checks necessary because some systems return EFI boot loaders
1397 // with a DeviceType value that would inappropriately include them
1398 // as legacy loaders....
1399 if ((BbsDevicePath
->DeviceType
== DiskType
) && (BdsOption
->DevicePath
->Type
== DEVICE_TYPE_BIOS
)) {
1400 AddLegacyEntryUEFI(BdsOption
, BbsDevicePath
->DeviceType
);
1405 } /* static VOID ScanLegacyUEFI() */
1407 static VOID
ScanLegacyUEFI(IN UINTN DiskType
){}
1408 #endif // __MAKEWITH_TIANO
1410 static VOID
ScanLegacyVolume(REFIT_VOLUME
*Volume
, UINTN VolumeIndex
) {
1412 BOOLEAN ShowVolume
, HideIfOthersFound
;
1415 HideIfOthersFound
= FALSE
;
1416 if (Volume
->IsAppleLegacy
) {
1418 HideIfOthersFound
= TRUE
;
1419 } else if (Volume
->HasBootCode
) {
1421 if (Volume
->BlockIO
== Volume
->WholeDiskBlockIO
&&
1422 Volume
->BlockIOOffset
== 0 &&
1423 Volume
->OSName
== NULL
)
1424 // this is a whole disk (MBR) entry; hide if we have entries for partitions
1425 HideIfOthersFound
= TRUE
;
1427 if (HideIfOthersFound
) {
1428 // check for other bootable entries on the same disk
1429 for (VolumeIndex2
= 0; VolumeIndex2
< VolumesCount
; VolumeIndex2
++) {
1430 if (VolumeIndex2
!= VolumeIndex
&& Volumes
[VolumeIndex2
]->HasBootCode
&&
1431 Volumes
[VolumeIndex2
]->WholeDiskBlockIO
== Volume
->WholeDiskBlockIO
)
1437 AddLegacyEntry(NULL
, Volume
);
1438 } // static VOID ScanLegacyVolume()
1440 // Scan attached optical discs for legacy (BIOS) boot code
1441 // and add anything found to the list....
1442 static VOID
ScanLegacyDisc(VOID
)
1445 REFIT_VOLUME
*Volume
;
1447 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
) {
1448 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1449 Volume
= Volumes
[VolumeIndex
];
1450 if (Volume
->DiskKind
== DISK_KIND_OPTICAL
)
1451 ScanLegacyVolume(Volume
, VolumeIndex
);
1453 } else if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
1454 ScanLegacyUEFI(BBS_CDROM
);
1456 } /* static VOID ScanLegacyDisc() */
1458 // Scan internal hard disks for legacy (BIOS) boot code
1459 // and add anything found to the list....
1460 static VOID
ScanLegacyInternal(VOID
)
1463 REFIT_VOLUME
*Volume
;
1465 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
) {
1466 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1467 Volume
= Volumes
[VolumeIndex
];
1468 if (Volume
->DiskKind
== DISK_KIND_INTERNAL
)
1469 ScanLegacyVolume(Volume
, VolumeIndex
);
1471 } else if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
1472 ScanLegacyUEFI(BBS_HARDDISK
);
1474 } /* static VOID ScanLegacyInternal() */
1476 // Scan external disks for legacy (BIOS) boot code
1477 // and add anything found to the list....
1478 static VOID
ScanLegacyExternal(VOID
)
1481 REFIT_VOLUME
*Volume
;
1483 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
) {
1484 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1485 Volume
= Volumes
[VolumeIndex
];
1486 if (Volume
->DiskKind
== DISK_KIND_EXTERNAL
)
1487 ScanLegacyVolume(Volume
, VolumeIndex
);
1489 } else if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
1490 ScanLegacyUEFI(BBS_USB
);
1492 } /* static VOID ScanLegacyExternal() */
1495 // pre-boot tool functions
1498 static VOID
StartTool(IN LOADER_ENTRY
*Entry
)
1500 BeginExternalScreen(Entry
->UseGraphicsMode
, Entry
->me
.Title
+ 6); // assumes "Start <title>" as assigned below
1501 StartEFIImage(Entry
->DevicePath
, Entry
->LoadOptions
, Basename(Entry
->LoaderPath
),
1502 Basename(Entry
->LoaderPath
), Entry
->OSType
, NULL
, TRUE
);
1503 FinishExternalScreen();
1504 } /* static VOID StartTool() */
1506 static LOADER_ENTRY
* AddToolEntry(EFI_HANDLE DeviceHandle
, IN CHAR16
*LoaderPath
, IN CHAR16
*LoaderTitle
, IN EG_IMAGE
*Image
,
1507 IN CHAR16 ShortcutLetter
, IN BOOLEAN UseGraphicsMode
)
1509 LOADER_ENTRY
*Entry
;
1510 CHAR16
*TitleStr
= NULL
;
1512 Entry
= AllocateZeroPool(sizeof(LOADER_ENTRY
));
1514 MergeStrings(&TitleStr
, L
"Start ", 0);
1515 MergeStrings(&TitleStr
, LoaderTitle
, 0);
1516 Entry
->me
.Title
= TitleStr
;
1517 Entry
->me
.Tag
= TAG_TOOL
;
1519 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1520 Entry
->me
.Image
= Image
;
1521 Entry
->LoaderPath
= (LoaderPath
) ? StrDuplicate(LoaderPath
) : NULL
;
1522 Entry
->DevicePath
= FileDevicePath(DeviceHandle
, Entry
->LoaderPath
);
1523 Entry
->UseGraphicsMode
= UseGraphicsMode
;
1525 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1527 } /* static LOADER_ENTRY * AddToolEntry() */
1530 // pre-boot driver functions
1533 static UINTN
ScanDriverDir(IN CHAR16
*Path
)
1536 REFIT_DIR_ITER DirIter
;
1538 EFI_FILE_INFO
*DirEntry
;
1539 CHAR16 FileName
[256];
1541 CleanUpPathNameSlashes(Path
);
1542 // look through contents of the directory
1543 DirIterOpen(SelfRootDir
, Path
, &DirIter
);
1544 while (DirIterNext(&DirIter
, 2, LOADER_MATCH_PATTERNS
, &DirEntry
)) {
1545 if (DirEntry
->FileName
[0] == '.')
1546 continue; // skip this
1548 SPrint(FileName
, 255, L
"%s\\%s", Path
, DirEntry
->FileName
);
1550 Status
= StartEFIImage(FileDevicePath(SelfLoadedImage
->DeviceHandle
, FileName
),
1551 L
"", DirEntry
->FileName
, DirEntry
->FileName
, 0, NULL
, FALSE
);
1553 Status
= DirIterClose(&DirIter
);
1554 if (Status
!= EFI_NOT_FOUND
) {
1555 SPrint(FileName
, 255, L
"while scanning the %s directory", Path
);
1556 CheckError(Status
, FileName
);
1561 #ifdef __MAKEWITH_GNUEFI
1562 static EFI_STATUS
ConnectAllDriversToAllControllers(VOID
)
1565 UINTN AllHandleCount
;
1566 EFI_HANDLE
*AllHandleBuffer
;
1569 EFI_HANDLE
*HandleBuffer
;
1575 Status
= LibLocateHandle(AllHandles
,
1580 if (EFI_ERROR(Status
))
1583 for (Index
= 0; Index
< AllHandleCount
; Index
++) {
1585 // Scan the handle database
1587 Status
= LibScanHandleDatabase(NULL
,
1589 AllHandleBuffer
[Index
],
1594 if (EFI_ERROR (Status
))
1598 if (HandleType
[Index
] & EFI_HANDLE_TYPE_DRIVER_BINDING_HANDLE
)
1600 if (HandleType
[Index
] & EFI_HANDLE_TYPE_IMAGE_HANDLE
)
1605 for (HandleIndex
= 0; HandleIndex
< HandleCount
; HandleIndex
++) {
1606 if (HandleType
[HandleIndex
] & EFI_HANDLE_TYPE_PARENT_HANDLE
)
1611 if (HandleType
[Index
] & EFI_HANDLE_TYPE_DEVICE_HANDLE
) {
1612 Status
= refit_call4_wrapper(BS
->ConnectController
,
1613 AllHandleBuffer
[Index
],
1621 MyFreePool (HandleBuffer
);
1622 MyFreePool (HandleType
);
1626 MyFreePool (AllHandleBuffer
);
1628 } /* EFI_STATUS ConnectAllDriversToAllControllers() */
1630 static EFI_STATUS
ConnectAllDriversToAllControllers(VOID
) {
1631 BdsLibConnectAllDriversToAllControllers();
1636 // Load all EFI drivers from rEFInd's "drivers" subdirectory and from the
1637 // directories specified by the user in the "scan_driver_dirs" configuration
1639 static VOID
LoadDrivers(VOID
)
1641 CHAR16
*Directory
, *SelfDirectory
;
1642 UINTN i
= 0, Length
, NumFound
= 0;
1644 // load drivers from the subdirectories of rEFInd's home directory specified
1645 // in the DRIVER_DIRS constant.
1646 while ((Directory
= FindCommaDelimited(DRIVER_DIRS
, i
++)) != NULL
) {
1647 SelfDirectory
= SelfDirPath
? StrDuplicate(SelfDirPath
) : NULL
;
1648 CleanUpPathNameSlashes(SelfDirectory
);
1649 MergeStrings(&SelfDirectory
, Directory
, L
'\\');
1650 NumFound
+= ScanDriverDir(SelfDirectory
);
1651 MyFreePool(Directory
);
1652 MyFreePool(SelfDirectory
);
1655 // Scan additional user-specified driver directories....
1657 while ((Directory
= FindCommaDelimited(GlobalConfig
.DriverDirs
, i
++)) != NULL
) {
1658 CleanUpPathNameSlashes(Directory
);
1659 Length
= StrLen(Directory
);
1661 NumFound
+= ScanDriverDir(Directory
);
1663 MyFreePool(Directory
);
1666 // connect all devices
1668 ConnectAllDriversToAllControllers();
1669 } /* static VOID LoadDrivers() */
1671 // Determine what (if any) type of legacy (BIOS) boot support is available
1672 static VOID
FindLegacyBootType(VOID
) {
1673 #ifdef __MAKEWITH_TIANO
1675 EFI_LEGACY_BIOS_PROTOCOL
*LegacyBios
;
1678 GlobalConfig
.LegacyType
= LEGACY_TYPE_NONE
;
1680 // UEFI-style legacy BIOS support is available only with the TianoCore EDK2
1681 // build environment, and then only with some implementations....
1682 #ifdef __MAKEWITH_TIANO
1683 Status
= gBS
->LocateProtocol (&gEfiLegacyBootProtocolGuid
, NULL
, (VOID
**) &LegacyBios
);
1684 if (!EFI_ERROR (Status
))
1685 GlobalConfig
.LegacyType
= LEGACY_TYPE_UEFI
;
1688 // Macs have their own system. If the firmware vendor code contains the
1689 // string "Apple", assume it's available. Note that this overrides the
1690 // UEFI type, and might yield false positives if the vendor string
1691 // contains "Apple" as part of something bigger, so this isn't 100%
1693 if (StriSubCmp(L
"Apple", ST
->FirmwareVendor
))
1694 GlobalConfig
.LegacyType
= LEGACY_TYPE_MAC
;
1695 } // static VOID FindLegacyBootType
1697 // Warn the user if legacy OS scans are enabled but the firmware or this
1698 // application can't support them....
1699 static VOID
WarnIfLegacyProblems() {
1700 BOOLEAN found
= FALSE
;
1703 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_NONE
) {
1705 if (GlobalConfig
.ScanFor
[i
] == 'h' || GlobalConfig
.ScanFor
[i
] == 'b' || GlobalConfig
.ScanFor
[i
] == 'c')
1708 } while ((i
< NUM_SCAN_OPTIONS
) && (!found
));
1710 Print(L
"NOTE: refind.conf's 'scanfor' line specifies scanning for one or more legacy\n");
1711 Print(L
"(BIOS) boot options; however, this is not possible because ");
1712 #ifdef __MAKEWITH_TIANO
1713 Print(L
"your computer lacks\n");
1714 Print(L
"the necessary Compatibility Support Module (CSM) support.\n");
1716 Print(L
"this program was\n");
1717 Print(L
"compiled without the necessary support. Please visit\n");
1718 Print(L
"http://www.rodsbooks.com/refind/getting.html and download and install a rEFInd\n");
1719 Print(L
"binary built with the TianoCore EDK2 to enable legacy boot support.\n");
1723 } // if no legacy support
1724 } // static VOID WarnIfLegacyProblems()
1726 // Locates boot loaders. NOTE: This assumes that GlobalConfig.LegacyType is set correctly.
1727 static VOID
ScanForBootloaders(VOID
) {
1732 // scan for loaders and tools, add them to the menu
1733 for (i
= 0; i
< NUM_SCAN_OPTIONS
; i
++) {
1734 switch(GlobalConfig
.ScanFor
[i
]) {
1739 ScanLegacyInternal();
1742 ScanLegacyExternal();
1745 ScanUserConfigured();
1759 // assign shortcut keys
1760 for (i
= 0; i
< MainMenu
.EntryCount
&& MainMenu
.Entries
[i
]->Row
== 0 && i
< 9; i
++)
1761 MainMenu
.Entries
[i
]->ShortcutDigit
= (CHAR16
)('1' + i
);
1763 // wait for user ACK when there were errors
1764 FinishTextScreen(FALSE
);
1765 } // static VOID ScanForBootloaders()
1767 // Add the second-row tags containing built-in and external tools (EFI shell,
1769 static VOID
ScanForTools(VOID
) {
1770 CHAR16
*FileName
= NULL
, Description
[256];
1771 REFIT_MENU_ENTRY
*TempMenuEntry
;
1772 UINTN i
, j
, VolumeIndex
;
1774 for (i
= 0; i
< NUM_TOOLS
; i
++) {
1775 switch(GlobalConfig
.ShowTools
[i
]) {
1777 TempMenuEntry
= CopyMenuEntry(&MenuEntryShutdown
);
1778 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_SHUTDOWN
);
1779 AddMenuEntry(&MainMenu
, TempMenuEntry
);
1782 TempMenuEntry
= CopyMenuEntry(&MenuEntryReset
);
1783 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_RESET
);
1784 AddMenuEntry(&MainMenu
, TempMenuEntry
);
1787 TempMenuEntry
= CopyMenuEntry(&MenuEntryAbout
);
1788 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT
);
1789 AddMenuEntry(&MainMenu
, TempMenuEntry
);
1792 TempMenuEntry
= CopyMenuEntry(&MenuEntryExit
);
1793 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_EXIT
);
1794 AddMenuEntry(&MainMenu
, TempMenuEntry
);
1798 MyFreePool(FileName
);
1799 while ((FileName
= FindCommaDelimited(SHELL_NAMES
, j
++)) != NULL
) {
1800 if (FileExists(SelfRootDir
, FileName
)) {
1801 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, L
"EFI Shell", BuiltinIcon(BUILTIN_ICON_TOOL_SHELL
),
1807 MyFreePool(FileName
);
1809 MergeStrings(&FileName
, L
"\\efi\\tools\\gptsync.efi", 0);
1810 if (FileExists(SelfRootDir
, FileName
)) {
1811 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, L
"Make Hybrid MBR", BuiltinIcon(BUILTIN_ICON_TOOL_PART
), 'P', FALSE
);
1814 case TAG_APPLE_RECOVERY
:
1815 MyFreePool(FileName
);
1817 MergeStrings(&FileName
, L
"\\com.apple.recovery.boot\\boot.efi", 0);
1818 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1819 if ((Volumes
[VolumeIndex
]->RootDir
!= NULL
) && (FileExists(Volumes
[VolumeIndex
]->RootDir
, FileName
))) {
1820 SPrint(Description
, 255, L
"Apple Recovery on %s", Volumes
[VolumeIndex
]->VolName
);
1821 AddToolEntry(Volumes
[VolumeIndex
]->DeviceHandle
, FileName
, Description
,
1822 BuiltinIcon(BUILTIN_ICON_TOOL_APPLE_RESCUE
), 'R', TRUE
);
1828 MyFreePool(FileName
);
1829 while ((FileName
= FindCommaDelimited(MOK_NAMES
, j
++)) != NULL
) {
1830 if (FileExists(SelfRootDir
, FileName
)) {
1831 SPrint(Description
, 255, L
"MOK Key Manager at %s", FileName
);
1832 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, Description
,
1833 BuiltinIcon(BUILTIN_ICON_TOOL_MOK_TOOL
), 'S', FALSE
);
1836 if (FileExists(SelfDir
, L
"MokManager.efi")) {
1837 MyFreePool(FileName
);
1838 FileName
= StrDuplicate(SelfDirPath
);
1839 MergeStrings(&FileName
, L
"\\MokManager.efi", 0);
1840 SPrint(Description
, 255, L
"MOK Key Manager at %s", FileName
);
1841 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, Description
,
1842 BuiltinIcon(BUILTIN_ICON_TOOL_MOK_TOOL
), 'S', FALSE
);
1846 MyFreePool(FileName
);
1849 } // static VOID ScanForTools
1851 // Rescan for boot loaders
1852 VOID
RescanAll(VOID
) {
1859 egDisplayMessage(L
"Scanning for new boot loaders; please wait....", &BGColor
);
1860 FreeList((VOID
***) &(MainMenu
.Entries
), &MainMenu
.EntryCount
);
1861 MainMenu
.Entries
= NULL
;
1862 MainMenu
.EntryCount
= 0;
1863 ReadConfig(CONFIG_FILE_NAME
);
1864 ConnectAllDriversToAllControllers();
1866 ScanForBootloaders();
1869 } // VOID RescanAll()
1871 #ifndef __MAKEWITH_GNUEFI
1873 // Minimal initialization function
1874 static VOID
InitializeLib(IN EFI_HANDLE ImageHandle
, IN EFI_SYSTEM_TABLE
*SystemTable
) {
1876 // gImageHandle = ImageHandle;
1877 gBS
= SystemTable
->BootServices
;
1878 // gRS = SystemTable->RuntimeServices;
1879 gRT
= SystemTable
->RuntimeServices
; // Some BDS functions need gRT to be set
1880 EfiGetSystemConfigurationTable (&gEfiDxeServicesTableGuid
, (VOID
**) &gDS
);
1882 InitializeConsoleSim();
1892 efi_main (IN EFI_HANDLE ImageHandle
, IN EFI_SYSTEM_TABLE
*SystemTable
)
1895 BOOLEAN MainLoopRunning
= TRUE
;
1896 REFIT_MENU_ENTRY
*ChosenEntry
;
1902 InitializeLib(ImageHandle
, SystemTable
);
1904 Status
= InitRefitLib(ImageHandle
);
1905 if (EFI_ERROR(Status
))
1908 // read configuration
1909 CopyMem(GlobalConfig
.ScanFor
, "ieom ", NUM_SCAN_OPTIONS
);
1910 FindLegacyBootType();
1911 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
)
1912 CopyMem(GlobalConfig
.ScanFor
, "ihebocm ", NUM_SCAN_OPTIONS
);
1913 ReadConfig(CONFIG_FILE_NAME
);
1914 WarnIfLegacyProblems();
1915 MainMenu
.TimeoutSeconds
= GlobalConfig
.Timeout
;
1917 // disable EFI watchdog timer
1918 refit_call4_wrapper(BS
->SetWatchdogTimer
, 0x0000, 0x0000, 0x0000, NULL
);
1920 // further bootstrap (now with config available)
1924 ScanForBootloaders();
1927 if (GlobalConfig
.ScanDelay
> 0) {
1932 egDisplayMessage(L
"Pausing before disk scan; please wait....", &BGColor
);
1933 for (i
= 0; i
< GlobalConfig
.ScanDelay
; i
++)
1934 refit_call1_wrapper(BS
->Stall
, 1000000);
1938 Selection
= StrDuplicate(GlobalConfig
.DefaultSelection
);
1939 while (MainLoopRunning
) {
1940 MenuExit
= RunMainMenu(&MainMenu
, Selection
, &ChosenEntry
);
1942 // The Escape key triggers a re-scan operation....
1943 if (MenuExit
== MENU_EXIT_ESCAPE
) {
1948 switch (ChosenEntry
->Tag
) {
1950 case TAG_REBOOT
: // Reboot
1952 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);
1953 MainLoopRunning
= FALSE
; // just in case we get this far
1956 case TAG_SHUTDOWN
: // Shut Down
1958 refit_call4_wrapper(RT
->ResetSystem
, EfiResetShutdown
, EFI_SUCCESS
, 0, NULL
);
1959 MainLoopRunning
= FALSE
; // just in case we get this far
1962 case TAG_ABOUT
: // About rEFInd
1966 case TAG_LOADER
: // Boot OS via .EFI loader
1967 StartLoader((LOADER_ENTRY
*)ChosenEntry
);
1970 case TAG_LEGACY
: // Boot legacy OS
1971 StartLegacy((LEGACY_ENTRY
*)ChosenEntry
);
1974 #ifdef __MAKEWITH_TIANO
1975 case TAG_LEGACY_UEFI
: // Boot a legacy OS on a non-Mac
1976 StartLegacyUEFI((LEGACY_ENTRY
*)ChosenEntry
);
1978 #endif // __MAKEWITH_TIANO
1980 case TAG_TOOL
: // Start a EFI tool
1981 StartTool((LOADER_ENTRY
*)ChosenEntry
);
1984 case TAG_EXIT
: // Terminate rEFInd
1985 BeginTextScreen(L
" ");
1990 MyFreePool(Selection
);
1991 Selection
= (ChosenEntry
->Title
) ? StrDuplicate(ChosenEntry
->Title
) : NULL
;
1994 // If we end up here, things have gone wrong. Try to reboot, and if that
1995 // fails, go into an endless loop.
1996 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);