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.5.0");
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 // NOTE: We also include the terminating null in the length for safety.
200 } // if (LoadOptions != NULL)
202 Print(L
"Starting %s\nUsing load options '%s'\n", ImageTitle
, FullLoadOptions
);
204 // load the image into memory (and execute it, in the case of a shim/MOK image).
205 ReturnStatus
= Status
= EFI_NOT_FOUND
; // in case the list is empty
206 for (DevicePathIndex
= 0; DevicePaths
[DevicePathIndex
] != NULL
; DevicePathIndex
++) {
207 // NOTE: Below commented-out line could be more efficient if the ReadFile() and
208 // FindVolumeAndFilename() calls were moved earlier, but it doesn't work on my
209 // 32-bit Mac Mini or my 64-bit Intel box when launching a Linux kernel; the
210 // kernel returns a "Failed to handle fs_proto" error message.
211 // TODO: Track down the cause of this error and fix it, if possible.
212 // ReturnStatus = Status = refit_call6_wrapper(BS->LoadImage, FALSE, SelfImageHandle, DevicePaths[DevicePathIndex],
213 // ImageData, ImageSize, &ChildImageHandle);
214 ReturnStatus
= Status
= refit_call6_wrapper(BS
->LoadImage
, FALSE
, SelfImageHandle
, DevicePaths
[DevicePathIndex
],
215 NULL
, 0, &ChildImageHandle
);
216 if ((Status
== EFI_ACCESS_DENIED
) && (ShimLoaded())) {
217 FindVolumeAndFilename(DevicePaths
[DevicePathIndex
], &DeviceVolume
, &loader
);
218 if (DeviceVolume
!= NULL
) {
219 Status
= ReadFile(DeviceVolume
->RootDir
, loader
, &File
, &ImageSize
);
220 ImageData
= File
.Buffer
;
222 Status
= EFI_NOT_FOUND
;
223 Print(L
"Error: device volume not found!\n");
225 if (Status
!= EFI_NOT_FOUND
) {
226 ReturnStatus
= Status
= start_image(SelfImageHandle
, loader
, ImageData
, ImageSize
, FullLoadOptions
,
227 DeviceVolume
, FileDevicePath(DeviceVolume
->DeviceHandle
, loader
));
228 // ReturnStatus = Status = start_image(SelfImageHandle, loader, ImageData, ImageSize, FullLoadOptions,
229 // DeviceVolume, DevicePaths[DevicePathIndex]);
231 if (ReturnStatus
== EFI_SUCCESS
) {
234 } // if (UEFI SB failed; use shim)
235 if (ReturnStatus
!= EFI_NOT_FOUND
) {
239 SPrint(ErrorInfo
, 255, L
"while loading %s", ImageTitle
);
240 if (CheckError(Status
, ErrorInfo
)) {
241 if (ErrorInStep
!= NULL
)
247 ReturnStatus
= Status
= refit_call3_wrapper(BS
->HandleProtocol
, ChildImageHandle
, &LoadedImageProtocol
,
248 (VOID
**) &ChildLoadedImage
);
249 if (CheckError(Status
, L
"while getting a LoadedImageProtocol handle")) {
250 if (ErrorInStep
!= NULL
)
254 ChildLoadedImage
->LoadOptions
= (VOID
*)FullLoadOptions
;
255 ChildLoadedImage
->LoadOptionsSize
= ((UINT32
)StrLen(FullLoadOptions
) + 1) * sizeof(CHAR16
);
256 // turn control over to the image
257 // TODO: (optionally) re-enable the EFI watchdog timer!
259 // close open file handles
261 ReturnStatus
= Status
= refit_call3_wrapper(BS
->StartImage
, ChildImageHandle
, NULL
, NULL
);
262 // control returns here when the child image calls Exit()
263 SPrint(ErrorInfo
, 255, L
"returned from %s", ImageTitle
);
264 if (CheckError(Status
, ErrorInfo
)) {
265 if (ErrorInStep
!= NULL
)
269 // re-open file handles
274 // unload the image, we don't care if it works or not...
276 Status
= refit_call1_wrapper(BS
->UnloadImage
, ChildImageHandle
);
279 MyFreePool(FullLoadOptions
);
281 } /* static EFI_STATUS StartEFIImageList() */
283 static EFI_STATUS
StartEFIImage(IN EFI_DEVICE_PATH
*DevicePath
,
284 IN CHAR16
*LoadOptions
, IN CHAR16
*LoadOptionsPrefix
,
285 IN CHAR16
*ImageTitle
, IN CHAR8 OSType
,
286 OUT UINTN
*ErrorInStep
,
289 EFI_DEVICE_PATH
*DevicePaths
[2];
291 DevicePaths
[0] = DevicePath
;
292 DevicePaths
[1] = NULL
;
293 return StartEFIImageList(DevicePaths
, LoadOptions
, LoadOptionsPrefix
, ImageTitle
, OSType
, ErrorInStep
, Verbose
);
294 } /* static EFI_STATUS StartEFIImage() */
297 // EFI OS loader functions
300 static VOID
StartLoader(IN LOADER_ENTRY
*Entry
)
302 UINTN ErrorInStep
= 0;
304 BeginExternalScreen(Entry
->UseGraphicsMode
, L
"Booting OS");
305 StartEFIImage(Entry
->DevicePath
, Entry
->LoadOptions
, Basename(Entry
->LoaderPath
),
306 Basename(Entry
->LoaderPath
), Entry
->OSType
, &ErrorInStep
, !Entry
->UseGraphicsMode
);
307 FinishExternalScreen();
310 // Locate an initrd or initramfs file that matches the kernel specified by LoaderPath.
311 // The matching file has a name that begins with "init" and includes the same version
312 // number string as is found in LoaderPath -- but not a longer version number string.
313 // For instance, if LoaderPath is \EFI\kernels\bzImage-3.3.0.efi, and if \EFI\kernels
314 // has a file called initramfs-3.3.0.img, this function will return the string
315 // '\EFI\kernels\initramfs-3.3.0.img'. If the directory ALSO contains the file
316 // initramfs-3.3.0-rc7.img or initramfs-13.3.0.img, those files will NOT match;
317 // however, initmine-3.3.0.img might match. (FindInitrd() returns the first match it
318 // finds). Thus, care should be taken to avoid placing duplicate matching files in
319 // the kernel's directory.
320 // If no matching init file can be found, returns NULL.
321 static CHAR16
* FindInitrd(IN CHAR16
*LoaderPath
, IN REFIT_VOLUME
*Volume
) {
322 CHAR16
*InitrdName
= NULL
, *FileName
, *KernelVersion
, *InitrdVersion
, *Path
;
323 REFIT_DIR_ITER DirIter
;
324 EFI_FILE_INFO
*DirEntry
;
326 FileName
= Basename(LoaderPath
);
327 KernelVersion
= FindNumbers(FileName
);
328 Path
= FindPath(LoaderPath
);
330 // Add trailing backslash for root directory; necessary on some systems, but must
331 // NOT be added to all directories, since on other systems, a trailing backslash on
332 // anything but the root directory causes them to flake out!
333 if (StrLen(Path
) == 0) {
334 MergeStrings(&Path
, L
"\\", 0);
336 DirIterOpen(Volume
->RootDir
, Path
, &DirIter
);
337 // Now add a trailing backslash if it was NOT added earlier, for consistency in
338 // building the InitrdName later....
339 if ((StrLen(Path
) > 0) && (Path
[StrLen(Path
) - 1] != L
'\\'))
340 MergeStrings(&Path
, L
"\\", 0);
341 while ((DirIterNext(&DirIter
, 2, L
"init*", &DirEntry
)) && (InitrdName
== NULL
)) {
342 InitrdVersion
= FindNumbers(DirEntry
->FileName
);
343 if (KernelVersion
!= NULL
) {
344 if (StriCmp(InitrdVersion
, KernelVersion
) == 0) {
345 MergeStrings(&InitrdName
, Path
, 0);
346 MergeStrings(&InitrdName
, DirEntry
->FileName
, 0);
349 if (InitrdVersion
== NULL
) {
350 MergeStrings(&InitrdName
, Path
, 0);
351 MergeStrings(&InitrdName
, DirEntry
->FileName
, 0);
354 MyFreePool(InitrdVersion
);
356 DirIterClose(&DirIter
);
358 // Note: Don't FreePool(FileName), since Basename returns a pointer WITHIN the string it's passed.
359 MyFreePool(KernelVersion
);
362 } // static CHAR16 * FindInitrd()
364 LOADER_ENTRY
* AddPreparedLoaderEntry(LOADER_ENTRY
*Entry
) {
365 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
368 } // LOADER_ENTRY * AddPreparedLoaderEntry()
370 // Creates a copy of a menu screen.
371 // Returns a pointer to the copy of the menu screen.
372 static REFIT_MENU_SCREEN
* CopyMenuScreen(REFIT_MENU_SCREEN
*Entry
) {
373 REFIT_MENU_SCREEN
*NewEntry
;
376 NewEntry
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
377 if ((Entry
!= NULL
) && (NewEntry
!= NULL
)) {
378 CopyMem(NewEntry
, Entry
, sizeof(REFIT_MENU_SCREEN
));
379 NewEntry
->Title
= (Entry
->Title
) ? StrDuplicate(Entry
->Title
) : NULL
;
380 NewEntry
->TimeoutText
= (Entry
->TimeoutText
) ? StrDuplicate(Entry
->TimeoutText
) : NULL
;
381 if (Entry
->TitleImage
!= NULL
) {
382 NewEntry
->TitleImage
= AllocatePool(sizeof(EG_IMAGE
));
383 if (NewEntry
->TitleImage
!= NULL
)
384 CopyMem(NewEntry
->TitleImage
, Entry
->TitleImage
, sizeof(EG_IMAGE
));
386 NewEntry
->InfoLines
= (CHAR16
**) AllocateZeroPool(Entry
->InfoLineCount
* (sizeof(CHAR16
*)));
387 for (i
= 0; i
< Entry
->InfoLineCount
&& NewEntry
->InfoLines
; i
++) {
388 NewEntry
->InfoLines
[i
] = (Entry
->InfoLines
[i
]) ? StrDuplicate(Entry
->InfoLines
[i
]) : NULL
;
390 NewEntry
->Entries
= (REFIT_MENU_ENTRY
**) AllocateZeroPool(Entry
->EntryCount
* (sizeof (REFIT_MENU_ENTRY
*)));
391 for (i
= 0; i
< Entry
->EntryCount
&& NewEntry
->Entries
; i
++) {
392 AddMenuEntry(NewEntry
, Entry
->Entries
[i
]);
396 } // static REFIT_MENU_SCREEN* CopyMenuScreen()
398 // Creates a copy of a menu entry. Intended to enable moving a stack-based
399 // menu entry (such as the ones for the "reboot" and "exit" functions) to
400 // to the heap. This enables easier deletion of the whole set of menu
401 // entries when re-scanning.
402 // Returns a pointer to the copy of the menu entry.
403 static REFIT_MENU_ENTRY
* CopyMenuEntry(REFIT_MENU_ENTRY
*Entry
) {
404 REFIT_MENU_ENTRY
*NewEntry
;
406 NewEntry
= AllocateZeroPool(sizeof(REFIT_MENU_ENTRY
));
407 if ((Entry
!= NULL
) && (NewEntry
!= NULL
)) {
408 CopyMem(NewEntry
, Entry
, sizeof(REFIT_MENU_ENTRY
));
409 NewEntry
->Title
= (Entry
->Title
) ? StrDuplicate(Entry
->Title
) : NULL
;
410 if (Entry
->BadgeImage
!= NULL
) {
411 NewEntry
->BadgeImage
= AllocatePool(sizeof(EG_IMAGE
));
412 if (NewEntry
->BadgeImage
!= NULL
)
413 CopyMem(NewEntry
->BadgeImage
, Entry
->BadgeImage
, sizeof(EG_IMAGE
));
415 if (Entry
->Image
!= NULL
) {
416 NewEntry
->Image
= AllocatePool(sizeof(EG_IMAGE
));
417 if (NewEntry
->Image
!= NULL
)
418 CopyMem(NewEntry
->Image
, Entry
->Image
, sizeof(EG_IMAGE
));
420 if (Entry
->SubScreen
!= NULL
) {
421 NewEntry
->SubScreen
= CopyMenuScreen(Entry
->SubScreen
);
425 } // REFIT_MENU_ENTRY* CopyMenuEntry()
427 // Creates a new LOADER_ENTRY data structure and populates it with
428 // default values from the specified Entry, or NULL values if Entry
429 // is unspecified (NULL).
430 // Returns a pointer to the new data structure, or NULL if it
431 // couldn't be allocated
432 LOADER_ENTRY
*InitializeLoaderEntry(IN LOADER_ENTRY
*Entry
) {
433 LOADER_ENTRY
*NewEntry
= NULL
;
435 NewEntry
= AllocateZeroPool(sizeof(LOADER_ENTRY
));
436 if (NewEntry
!= NULL
) {
437 NewEntry
->me
.Title
= NULL
;
438 NewEntry
->me
.Tag
= TAG_LOADER
;
439 NewEntry
->Enabled
= TRUE
;
440 NewEntry
->UseGraphicsMode
= FALSE
;
441 NewEntry
->OSType
= 0;
443 NewEntry
->LoaderPath
= (Entry
->LoaderPath
) ? StrDuplicate(Entry
->LoaderPath
) : NULL
;
444 NewEntry
->VolName
= (Entry
->VolName
) ? StrDuplicate(Entry
->VolName
) : NULL
;
445 NewEntry
->DevicePath
= Entry
->DevicePath
;
446 NewEntry
->UseGraphicsMode
= Entry
->UseGraphicsMode
;
447 NewEntry
->LoadOptions
= (Entry
->LoadOptions
) ? StrDuplicate(Entry
->LoadOptions
) : NULL
;
448 NewEntry
->InitrdPath
= (Entry
->InitrdPath
) ? StrDuplicate(Entry
->InitrdPath
) : NULL
;
452 } // LOADER_ENTRY *InitializeLoaderEntry()
454 // Prepare a REFIT_MENU_SCREEN data structure for a subscreen entry. This sets up
455 // the default entry that launches the boot loader using the same options as the
456 // main Entry does. Subsequent options can be added by the calling function.
457 // If a subscreen already exists in the Entry that's passed to this function,
458 // it's left unchanged and a pointer to it is returned.
459 // Returns a pointer to the new subscreen data structure, or NULL if there
460 // were problems allocating memory.
461 REFIT_MENU_SCREEN
*InitializeSubScreen(IN LOADER_ENTRY
*Entry
) {
462 CHAR16
*FileName
, *Temp
= NULL
;
463 REFIT_MENU_SCREEN
*SubScreen
= NULL
;
464 LOADER_ENTRY
*SubEntry
;
466 FileName
= Basename(Entry
->LoaderPath
);
467 if (Entry
->me
.SubScreen
== NULL
) { // No subscreen yet; initialize default entry....
468 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
469 if (SubScreen
!= NULL
) {
470 SubScreen
->Title
= AllocateZeroPool(sizeof(CHAR16
) * 256);
471 SPrint(SubScreen
->Title
, 255, L
"Boot Options for %s on %s",
472 (Entry
->Title
!= NULL
) ? Entry
->Title
: FileName
, Entry
->VolName
);
473 SubScreen
->TitleImage
= Entry
->me
.Image
;
475 SubEntry
= InitializeLoaderEntry(Entry
);
476 if (SubEntry
!= NULL
) {
477 SubEntry
->me
.Title
= L
"Boot using default options";
478 if ((SubEntry
->InitrdPath
!= NULL
) && (StrLen(SubEntry
->InitrdPath
) > 0) && (!StriSubCmp(L
"initrd", SubEntry
->LoadOptions
))) {
479 MergeStrings(&Temp
, L
"initrd=", 0);
480 MergeStrings(&Temp
, SubEntry
->InitrdPath
, 0);
481 MergeStrings(&SubEntry
->LoadOptions
, Temp
, L
' ');
484 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
485 } // if (SubEntry != NULL)
486 } // if (SubScreen != NULL)
487 } else { // existing subscreen; less initialization, and just add new entry later....
488 SubScreen
= Entry
->me
.SubScreen
;
491 } // REFIT_MENU_SCREEN *InitializeSubScreen()
493 VOID
GenerateSubScreen(LOADER_ENTRY
*Entry
, IN REFIT_VOLUME
*Volume
) {
494 REFIT_MENU_SCREEN
*SubScreen
;
495 LOADER_ENTRY
*SubEntry
;
496 CHAR16
*InitrdOption
= NULL
, *Temp
;
497 CHAR16 DiagsFileName
[256];
502 // create the submenu
503 if (StrLen(Entry
->Title
) == 0) {
504 MyFreePool(Entry
->Title
);
507 SubScreen
= InitializeSubScreen(Entry
);
509 // loader-specific submenu entries
510 if (Entry
->OSType
== 'M') { // entries for Mac OS X
512 SubEntry
= InitializeLoaderEntry(Entry
);
513 if (SubEntry
!= NULL
) {
514 SubEntry
->me
.Title
= L
"Boot Mac OS X with a 64-bit kernel";
515 SubEntry
->LoadOptions
= L
"arch=x86_64";
516 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
517 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
520 SubEntry
= InitializeLoaderEntry(Entry
);
521 if (SubEntry
!= NULL
) {
522 SubEntry
->me
.Title
= L
"Boot Mac OS X with a 32-bit kernel";
523 SubEntry
->LoadOptions
= L
"arch=i386";
524 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
525 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
529 if (!(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_SINGLEUSER
)) {
530 SubEntry
= InitializeLoaderEntry(Entry
);
531 if (SubEntry
!= NULL
) {
532 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode";
533 SubEntry
->UseGraphicsMode
= FALSE
;
534 SubEntry
->LoadOptions
= L
"-v";
535 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
539 SubEntry
= InitializeLoaderEntry(Entry
);
540 if (SubEntry
!= NULL
) {
541 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode (64-bit)";
542 SubEntry
->UseGraphicsMode
= FALSE
;
543 SubEntry
->LoadOptions
= L
"-v arch=x86_64";
544 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
547 SubEntry
= InitializeLoaderEntry(Entry
);
548 if (SubEntry
!= NULL
) {
549 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode (32-bit)";
550 SubEntry
->UseGraphicsMode
= FALSE
;
551 SubEntry
->LoadOptions
= L
"-v arch=i386";
552 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
556 SubEntry
= InitializeLoaderEntry(Entry
);
557 if (SubEntry
!= NULL
) {
558 SubEntry
->me
.Title
= L
"Boot Mac OS X in single user mode";
559 SubEntry
->UseGraphicsMode
= FALSE
;
560 SubEntry
->LoadOptions
= L
"-v -s";
561 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
565 // check for Apple hardware diagnostics
566 StrCpy(DiagsFileName
, L
"\\System\\Library\\CoreServices\\.diagnostics\\diags.efi");
567 if (FileExists(Volume
->RootDir
, DiagsFileName
) && !(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_HWTEST
)) {
568 SubEntry
= InitializeLoaderEntry(Entry
);
569 if (SubEntry
!= NULL
) {
570 SubEntry
->me
.Title
= L
"Run Apple Hardware Test";
571 MyFreePool(SubEntry
->LoaderPath
);
572 SubEntry
->LoaderPath
= StrDuplicate(DiagsFileName
);
573 SubEntry
->DevicePath
= FileDevicePath(Volume
->DeviceHandle
, SubEntry
->LoaderPath
);
574 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
575 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
577 } // if diagnostics entry found
579 } else if (Entry
->OSType
== 'L') { // entries for Linux kernels with EFI stub loaders
580 File
= ReadLinuxOptionsFile(Entry
->LoaderPath
, Volume
);
582 if ((Temp
= FindInitrd(Entry
->LoaderPath
, Volume
)) != NULL
) {
583 MergeStrings(&InitrdOption
, L
"initrd=", 0);
584 MergeStrings(&InitrdOption
, Temp
, 0);
586 TokenCount
= ReadTokenLine(File
, &TokenList
); // read and discard first entry, since it's
587 FreeTokenLine(&TokenList
, &TokenCount
); // set up by InitializeSubScreen(), earlier....
588 while ((TokenCount
= ReadTokenLine(File
, &TokenList
)) > 1) {
589 SubEntry
= InitializeLoaderEntry(Entry
);
590 SubEntry
->me
.Title
= StrDuplicate(TokenList
[0]);
591 MyFreePool(SubEntry
->LoadOptions
);
592 SubEntry
->LoadOptions
= StrDuplicate(TokenList
[1]);
593 MergeStrings(&SubEntry
->LoadOptions
, InitrdOption
, L
' ');
594 FreeTokenLine(&TokenList
, &TokenCount
);
595 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_LINUX
;
596 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
598 MyFreePool(InitrdOption
);
601 } // if Linux options file exists
603 } else if (Entry
->OSType
== 'E') { // entries for ELILO
604 SubEntry
= InitializeLoaderEntry(Entry
);
605 if (SubEntry
!= NULL
) {
606 SubEntry
->me
.Title
= L
"Run ELILO in interactive mode";
607 SubEntry
->LoadOptions
= L
"-p";
608 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
609 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
612 SubEntry
= InitializeLoaderEntry(Entry
);
613 if (SubEntry
!= NULL
) {
614 SubEntry
->me
.Title
= L
"Boot Linux for a 17\" iMac or a 15\" MacBook Pro (*)";
615 SubEntry
->UseGraphicsMode
= TRUE
;
616 SubEntry
->LoadOptions
= L
"-d 0 i17";
617 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
618 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
621 SubEntry
= InitializeLoaderEntry(Entry
);
622 if (SubEntry
!= NULL
) {
623 SubEntry
->me
.Title
= L
"Boot Linux for a 20\" iMac (*)";
624 SubEntry
->UseGraphicsMode
= TRUE
;
625 SubEntry
->LoadOptions
= L
"-d 0 i20";
626 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
627 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
630 SubEntry
= InitializeLoaderEntry(Entry
);
631 if (SubEntry
!= NULL
) {
632 SubEntry
->me
.Title
= L
"Boot Linux for a Mac Mini (*)";
633 SubEntry
->UseGraphicsMode
= TRUE
;
634 SubEntry
->LoadOptions
= L
"-d 0 mini";
635 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
636 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
639 AddMenuInfoLine(SubScreen
, L
"NOTE: This is an example. Entries");
640 AddMenuInfoLine(SubScreen
, L
"marked with (*) may not work.");
642 } else if (Entry
->OSType
== 'X') { // entries for xom.efi
643 // by default, skip the built-in selection and boot from hard disk only
644 Entry
->LoadOptions
= L
"-s -h";
646 SubEntry
= InitializeLoaderEntry(Entry
);
647 if (SubEntry
!= NULL
) {
648 SubEntry
->me
.Title
= L
"Boot Windows from Hard Disk";
649 SubEntry
->LoadOptions
= L
"-s -h";
650 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
651 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
654 SubEntry
= InitializeLoaderEntry(Entry
);
655 if (SubEntry
!= NULL
) {
656 SubEntry
->me
.Title
= L
"Boot Windows from CD-ROM";
657 SubEntry
->LoadOptions
= L
"-s -c";
658 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
659 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
662 SubEntry
= InitializeLoaderEntry(Entry
);
663 if (SubEntry
!= NULL
) {
664 SubEntry
->me
.Title
= L
"Run XOM in text mode";
665 SubEntry
->UseGraphicsMode
= FALSE
;
666 SubEntry
->LoadOptions
= L
"-v";
667 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
668 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
670 } // entries for xom.efi
671 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
672 Entry
->me
.SubScreen
= SubScreen
;
673 } // VOID GenerateSubScreen()
675 // Returns options for a Linux kernel. Reads them from an options file in the
676 // kernel's directory; and if present, adds an initrd= option for an initial
677 // RAM disk file with the same version number as the kernel file.
678 static CHAR16
* GetMainLinuxOptions(IN CHAR16
* LoaderPath
, IN REFIT_VOLUME
*Volume
) {
679 CHAR16
*Options
= NULL
, *InitrdName
, *InitrdOption
= NULL
;
681 Options
= GetFirstOptionsFromFile(LoaderPath
, Volume
);
682 InitrdName
= FindInitrd(LoaderPath
, Volume
);
683 if (InitrdName
!= NULL
) {
684 MergeStrings(&InitrdOption
, L
"initrd=", 0);
685 MergeStrings(&InitrdOption
, InitrdName
, 0);
687 MergeStrings(&Options
, InitrdOption
, ' ');
688 MyFreePool(InitrdOption
);
689 MyFreePool(InitrdName
);
691 } // static CHAR16 * GetMainLinuxOptions()
693 // Sets a few defaults for a loader entry -- mainly the icon, but also the OS type
694 // code and shortcut letter. For Linux EFI stub loaders, also sets kernel options
695 // that will (with luck) work fairly automatically.
696 VOID
SetLoaderDefaults(LOADER_ENTRY
*Entry
, CHAR16
*LoaderPath
, IN REFIT_VOLUME
*Volume
) {
697 CHAR16 IconFileName
[256];
698 CHAR16
*FileName
, *PathOnly
, *OSIconName
= NULL
, *Temp
;
699 CHAR16 ShortcutLetter
= 0;
701 FileName
= Basename(LoaderPath
);
702 PathOnly
= FindPath(LoaderPath
);
704 // locate a custom icon for the loader
705 StrCpy(IconFileName
, LoaderPath
);
706 ReplaceEfiExtension(IconFileName
, L
".icns");
707 if (FileExists(Volume
->RootDir
, IconFileName
)) {
708 Entry
->me
.Image
= LoadIcns(Volume
->RootDir
, IconFileName
, 128);
709 } else if ((StrLen(PathOnly
) == 0) && (Volume
->VolIconImage
!= NULL
)) {
710 Entry
->me
.Image
= Volume
->VolIconImage
;
711 } // icon matched to loader or volume
713 Temp
= FindLastDirName(LoaderPath
);
714 MergeStrings(&OSIconName
, Temp
, L
',');
716 if (OSIconName
!= NULL
) {
717 ShortcutLetter
= OSIconName
[0];
720 // detect specific loaders
721 if (StriSubCmp(L
"bzImage", LoaderPath
) || StriSubCmp(L
"vmlinuz", LoaderPath
)) {
722 MergeStrings(&OSIconName
, L
"linux", L
',');
724 if (ShortcutLetter
== 0)
725 ShortcutLetter
= 'L';
726 Entry
->LoadOptions
= GetMainLinuxOptions(LoaderPath
, Volume
);
727 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_LINUX
;
728 } else if (StriSubCmp(L
"refit", LoaderPath
)) {
729 MergeStrings(&OSIconName
, L
"refit", L
',');
731 ShortcutLetter
= 'R';
732 } else if (StriCmp(LoaderPath
, MACOSX_LOADER_PATH
) == 0) {
733 if (Volume
->VolIconImage
!= NULL
) { // custom icon file found
734 Entry
->me
.Image
= Volume
->VolIconImage
;
736 MergeStrings(&OSIconName
, L
"mac", L
',');
738 ShortcutLetter
= 'M';
739 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
740 } else if (StriCmp(FileName
, L
"diags.efi") == 0) {
741 MergeStrings(&OSIconName
, L
"hwtest", L
',');
742 } else if (StriCmp(FileName
, L
"e.efi") == 0 || StriCmp(FileName
, L
"elilo.efi") == 0) {
743 MergeStrings(&OSIconName
, L
"elilo,linux", L
',');
745 if (ShortcutLetter
== 0)
746 ShortcutLetter
= 'L';
747 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
748 } else if (StriSubCmp(L
"grub", FileName
)) {
750 ShortcutLetter
= 'G';
751 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_GRUB
;
752 } else if (StriCmp(FileName
, L
"cdboot.efi") == 0 ||
753 StriCmp(FileName
, L
"bootmgr.efi") == 0 ||
754 StriCmp(FileName
, L
"Bootmgfw.efi") == 0) {
755 MergeStrings(&OSIconName
, L
"win", L
',');
757 ShortcutLetter
= 'W';
758 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
759 } else if (StriCmp(FileName
, L
"xom.efi") == 0) {
760 MergeStrings(&OSIconName
, L
"xom,win", L
',');
761 Entry
->UseGraphicsMode
= TRUE
;
763 ShortcutLetter
= 'W';
764 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
767 if ((ShortcutLetter
>= 'a') && (ShortcutLetter
<= 'z'))
768 ShortcutLetter
= ShortcutLetter
- 'a' + 'A'; // convert lowercase to uppercase
769 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
770 if (Entry
->me
.Image
== NULL
)
771 Entry
->me
.Image
= LoadOSIcon(OSIconName
, L
"unknown", FALSE
);
772 MyFreePool(PathOnly
);
773 } // VOID SetLoaderDefaults()
775 // Add a specified EFI boot loader to the list, using automatic settings
776 // for icons, options, etc.
777 LOADER_ENTRY
* AddLoaderEntry(IN CHAR16
*LoaderPath
, IN CHAR16
*LoaderTitle
, IN REFIT_VOLUME
*Volume
) {
780 CleanUpPathNameSlashes(LoaderPath
);
781 Entry
= InitializeLoaderEntry(NULL
);
783 Entry
->Title
= StrDuplicate((LoaderTitle
!= NULL
) ? LoaderTitle
: LoaderPath
);
784 Entry
->me
.Title
= AllocateZeroPool(sizeof(CHAR16
) * 256);
785 SPrint(Entry
->me
.Title
, 255, L
"Boot %s from %s", (LoaderTitle
!= NULL
) ? LoaderTitle
: LoaderPath
, Volume
->VolName
);
787 Entry
->me
.BadgeImage
= Volume
->VolBadgeImage
;
788 if ((LoaderPath
!= NULL
) && (LoaderPath
[0] != L
'\\')) {
789 Entry
->LoaderPath
= StrDuplicate(L
"\\");
791 Entry
->LoaderPath
= NULL
;
793 MergeStrings(&(Entry
->LoaderPath
), LoaderPath
, 0);
794 Entry
->VolName
= Volume
->VolName
;
795 Entry
->DevicePath
= FileDevicePath(Volume
->DeviceHandle
, Entry
->LoaderPath
);
796 SetLoaderDefaults(Entry
, LoaderPath
, Volume
);
797 GenerateSubScreen(Entry
, Volume
);
798 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
802 } // LOADER_ENTRY * AddLoaderEntry()
804 // Returns -1 if (Time1 < Time2), +1 if (Time1 > Time2), or 0 if
805 // (Time1 == Time2). Precision is only to the nearest second; since
806 // this is used for sorting boot loader entries, differences smaller
807 // than this are likely to be meaningless (and unlikely!).
808 INTN
TimeComp(EFI_TIME
*Time1
, EFI_TIME
*Time2
) {
809 INT64 Time1InSeconds
, Time2InSeconds
;
811 // Following values are overestimates; I'm assuming 31 days in every month.
812 // This is fine for the purpose of this function, which has a limited
814 Time1InSeconds
= Time1
->Second
+ (Time1
->Minute
* 60) + (Time1
->Hour
* 3600) + (Time1
->Day
* 86400) +
815 (Time1
->Month
* 2678400) + ((Time1
->Year
- 1998) * 32140800);
816 Time2InSeconds
= Time2
->Second
+ (Time2
->Minute
* 60) + (Time2
->Hour
* 3600) + (Time2
->Day
* 86400) +
817 (Time2
->Month
* 2678400) + ((Time2
->Year
- 1998) * 32140800);
818 if (Time1InSeconds
< Time2InSeconds
)
820 else if (Time1InSeconds
> Time2InSeconds
)
826 // Adds a loader list element, keeping it sorted by date. Returns the new
827 // first element (the one with the most recent date).
828 static struct LOADER_LIST
* AddLoaderListEntry(struct LOADER_LIST
*LoaderList
, struct LOADER_LIST
*NewEntry
) {
829 struct LOADER_LIST
*LatestEntry
, *CurrentEntry
, *PrevEntry
= NULL
;
831 LatestEntry
= CurrentEntry
= LoaderList
;
832 if (LoaderList
== NULL
) {
833 LatestEntry
= NewEntry
;
835 while ((CurrentEntry
!= NULL
) && (TimeComp(&(NewEntry
->TimeStamp
), &(CurrentEntry
->TimeStamp
)) < 0)) {
836 PrevEntry
= CurrentEntry
;
837 CurrentEntry
= CurrentEntry
->NextEntry
;
839 NewEntry
->NextEntry
= CurrentEntry
;
840 if (PrevEntry
== NULL
) {
841 LatestEntry
= NewEntry
;
843 PrevEntry
->NextEntry
= NewEntry
;
846 return (LatestEntry
);
847 } // static VOID AddLoaderListEntry()
849 // Delete the LOADER_LIST linked list
850 static VOID
CleanUpLoaderList(struct LOADER_LIST
*LoaderList
) {
851 struct LOADER_LIST
*Temp
;
853 while (LoaderList
!= NULL
) {
855 LoaderList
= LoaderList
->NextEntry
;
856 MyFreePool(Temp
->FileName
);
859 } // static VOID CleanUpLoaderList()
861 // Scan an individual directory for EFI boot loader files and, if found,
862 // add them to the list. Sorts the entries within the loader directory
863 // so that the most recent one appears first in the list.
864 static VOID
ScanLoaderDir(IN REFIT_VOLUME
*Volume
, IN CHAR16
*Path
, IN CHAR16
*Pattern
)
867 REFIT_DIR_ITER DirIter
;
868 EFI_FILE_INFO
*DirEntry
;
869 CHAR16 FileName
[256], *Extension
;
870 struct LOADER_LIST
*LoaderList
= NULL
, *NewLoader
;
872 if ((!SelfDirPath
|| !Path
|| ((StriCmp(Path
, SelfDirPath
) == 0) && Volume
->DeviceHandle
!= SelfVolume
->DeviceHandle
) ||
873 (StriCmp(Path
, SelfDirPath
) != 0)) && (!IsIn(Path
, GlobalConfig
.DontScanDirs
))) {
874 // look through contents of the directory
875 DirIterOpen(Volume
->RootDir
, Path
, &DirIter
);
876 while (DirIterNext(&DirIter
, 2, Pattern
, &DirEntry
)) {
877 Extension
= FindExtension(DirEntry
->FileName
);
878 if (DirEntry
->FileName
[0] == '.' ||
879 StriCmp(Extension
, L
".icns") == 0 ||
880 StriSubCmp(L
"shell", DirEntry
->FileName
) ||
881 IsIn(DirEntry
->FileName
, GlobalConfig
.DontScanFiles
))
882 continue; // skip this
885 SPrint(FileName
, 255, L
"\\%s\\%s", Path
, DirEntry
->FileName
);
887 SPrint(FileName
, 255, L
"\\%s", DirEntry
->FileName
);
888 CleanUpPathNameSlashes(FileName
);
889 NewLoader
= AllocateZeroPool(sizeof(struct LOADER_LIST
));
890 if (NewLoader
!= NULL
) {
891 NewLoader
->FileName
= StrDuplicate(FileName
);
892 NewLoader
->TimeStamp
= DirEntry
->ModificationTime
;
893 LoaderList
= AddLoaderListEntry(LoaderList
, NewLoader
);
895 MyFreePool(Extension
);
897 NewLoader
= LoaderList
;
898 while (NewLoader
!= NULL
) {
899 AddLoaderEntry(NewLoader
->FileName
, NULL
, Volume
);
900 NewLoader
= NewLoader
->NextEntry
;
902 CleanUpLoaderList(LoaderList
);
903 Status
= DirIterClose(&DirIter
);
904 if (Status
!= EFI_NOT_FOUND
) {
906 SPrint(FileName
, 255, L
"while scanning the %s directory", Path
);
908 StrCpy(FileName
, L
"while scanning the root directory");
909 CheckError(Status
, FileName
);
910 } // if (Status != EFI_NOT_FOUND)
911 } // if not scanning our own directory
912 } /* static VOID ScanLoaderDir() */
914 static VOID
ScanEfiFiles(REFIT_VOLUME
*Volume
) {
916 REFIT_DIR_ITER EfiDirIter
;
917 EFI_FILE_INFO
*EfiDirEntry
;
918 CHAR16 FileName
[256], *Directory
, *MatchPatterns
;
921 MatchPatterns
= StrDuplicate(LOADER_MATCH_PATTERNS
);
922 if (GlobalConfig
.ScanAllLinux
)
923 MergeStrings(&MatchPatterns
, LINUX_MATCH_PATTERNS
, L
',');
925 if ((Volume
->RootDir
!= NULL
) && (Volume
->VolName
!= NULL
)) {
926 // check for Mac OS X boot loader
927 if (!IsIn(L
"\\System\\Library\\CoreServices", GlobalConfig
.DontScanDirs
)) {
928 StrCpy(FileName
, MACOSX_LOADER_PATH
);
929 if (FileExists(Volume
->RootDir
, FileName
) && !IsIn(L
"boot.efi", GlobalConfig
.DontScanFiles
)) {
930 AddLoaderEntry(FileName
, L
"Mac OS X", Volume
);
934 StrCpy(FileName
, L
"\\System\\Library\\CoreServices\\xom.efi");
935 if (FileExists(Volume
->RootDir
, FileName
) && !IsIn(L
"boot.efi", GlobalConfig
.DontScanFiles
)) {
936 AddLoaderEntry(FileName
, L
"Windows XP (XoM)", Volume
);
938 } // if Mac directory not in GlobalConfig.DontScanDirs list
940 // check for Microsoft boot loader/menu
941 StrCpy(FileName
, L
"\\EFI\\Microsoft\\Boot\\Bootmgfw.efi");
942 if (FileExists(Volume
->RootDir
, FileName
) && !IsIn(L
"\\EFI\\Microsoft\\Boot", GlobalConfig
.DontScanDirs
) &&
943 !IsIn(L
"bootmgfw.efi", GlobalConfig
.DontScanFiles
)) {
944 AddLoaderEntry(FileName
, L
"Microsoft EFI boot", Volume
);
947 // scan the root directory for EFI executables
948 ScanLoaderDir(Volume
, L
"\\", MatchPatterns
);
950 // scan subdirectories of the EFI directory (as per the standard)
951 DirIterOpen(Volume
->RootDir
, L
"EFI", &EfiDirIter
);
952 while (DirIterNext(&EfiDirIter
, 1, NULL
, &EfiDirEntry
)) {
953 if (StriCmp(EfiDirEntry
->FileName
, L
"tools") == 0 || EfiDirEntry
->FileName
[0] == '.')
954 continue; // skip this, doesn't contain boot loaders
955 SPrint(FileName
, 255, L
"\\EFI\\%s", EfiDirEntry
->FileName
);
956 ScanLoaderDir(Volume
, FileName
, MatchPatterns
);
958 Status
= DirIterClose(&EfiDirIter
);
959 if (Status
!= EFI_NOT_FOUND
)
960 CheckError(Status
, L
"while scanning the EFI directory");
962 // Scan user-specified (or additional default) directories....
964 while ((Directory
= FindCommaDelimited(GlobalConfig
.AlsoScan
, i
++)) != NULL
) {
965 CleanUpPathNameSlashes(Directory
);
966 Length
= StrLen(Directory
);
968 ScanLoaderDir(Volume
, Directory
, MatchPatterns
);
969 MyFreePool(Directory
);
972 } // static VOID ScanEfiFiles()
974 // Scan internal disks for valid EFI boot loaders....
975 static VOID
ScanInternal(VOID
) {
978 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
979 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_INTERNAL
) {
980 ScanEfiFiles(Volumes
[VolumeIndex
]);
983 } // static VOID ScanInternal()
985 // Scan external disks for valid EFI boot loaders....
986 static VOID
ScanExternal(VOID
) {
989 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
990 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_EXTERNAL
) {
991 ScanEfiFiles(Volumes
[VolumeIndex
]);
994 } // static VOID ScanExternal()
996 // Scan internal disks for valid EFI boot loaders....
997 static VOID
ScanOptical(VOID
) {
1000 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1001 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_OPTICAL
) {
1002 ScanEfiFiles(Volumes
[VolumeIndex
]);
1005 } // static VOID ScanOptical()
1008 // legacy boot functions
1011 static EFI_STATUS
ActivateMbrPartition(IN EFI_BLOCK_IO
*BlockIO
, IN UINTN PartitionIndex
)
1014 UINT8 SectorBuffer
[512];
1015 MBR_PARTITION_INFO
*MbrTable
, *EMbrTable
;
1016 UINT32 ExtBase
, ExtCurrent
, NextExtCurrent
;
1017 UINTN LogicalPartitionIndex
= 4;
1019 BOOLEAN HaveBootCode
;
1022 Status
= refit_call5_wrapper(BlockIO
->ReadBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, 0, 512, SectorBuffer
);
1023 if (EFI_ERROR(Status
))
1025 if (*((UINT16
*)(SectorBuffer
+ 510)) != 0xaa55)
1026 return EFI_NOT_FOUND
; // safety measure #1
1028 // add boot code if necessary
1029 HaveBootCode
= FALSE
;
1030 for (i
= 0; i
< MBR_BOOTCODE_SIZE
; i
++) {
1031 if (SectorBuffer
[i
] != 0) {
1032 HaveBootCode
= TRUE
;
1036 if (!HaveBootCode
) {
1037 // no boot code found in the MBR, add the syslinux MBR code
1038 SetMem(SectorBuffer
, MBR_BOOTCODE_SIZE
, 0);
1039 CopyMem(SectorBuffer
, syslinux_mbr
, SYSLINUX_MBR_SIZE
);
1042 // set the partition active
1043 MbrTable
= (MBR_PARTITION_INFO
*)(SectorBuffer
+ 446);
1045 for (i
= 0; i
< 4; i
++) {
1046 if (MbrTable
[i
].Flags
!= 0x00 && MbrTable
[i
].Flags
!= 0x80)
1047 return EFI_NOT_FOUND
; // safety measure #2
1048 if (i
== PartitionIndex
)
1049 MbrTable
[i
].Flags
= 0x80;
1050 else if (PartitionIndex
>= 4 && IS_EXTENDED_PART_TYPE(MbrTable
[i
].Type
)) {
1051 MbrTable
[i
].Flags
= 0x80;
1052 ExtBase
= MbrTable
[i
].StartLBA
;
1054 MbrTable
[i
].Flags
= 0x00;
1058 Status
= refit_call5_wrapper(BlockIO
->WriteBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, 0, 512, SectorBuffer
);
1059 if (EFI_ERROR(Status
))
1062 if (PartitionIndex
>= 4) {
1063 // we have to activate a logical partition, so walk the EMBR chain
1065 // NOTE: ExtBase was set above while looking at the MBR table
1066 for (ExtCurrent
= ExtBase
; ExtCurrent
; ExtCurrent
= NextExtCurrent
) {
1067 // read current EMBR
1068 Status
= refit_call5_wrapper(BlockIO
->ReadBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, ExtCurrent
, 512, SectorBuffer
);
1069 if (EFI_ERROR(Status
))
1071 if (*((UINT16
*)(SectorBuffer
+ 510)) != 0xaa55)
1072 return EFI_NOT_FOUND
; // safety measure #3
1074 // scan EMBR, set appropriate partition active
1075 EMbrTable
= (MBR_PARTITION_INFO
*)(SectorBuffer
+ 446);
1077 for (i
= 0; i
< 4; i
++) {
1078 if (EMbrTable
[i
].Flags
!= 0x00 && EMbrTable
[i
].Flags
!= 0x80)
1079 return EFI_NOT_FOUND
; // safety measure #4
1080 if (EMbrTable
[i
].StartLBA
== 0 || EMbrTable
[i
].Size
== 0)
1082 if (IS_EXTENDED_PART_TYPE(EMbrTable
[i
].Type
)) {
1083 // link to next EMBR
1084 NextExtCurrent
= ExtBase
+ EMbrTable
[i
].StartLBA
;
1085 EMbrTable
[i
].Flags
= (PartitionIndex
>= LogicalPartitionIndex
) ? 0x80 : 0x00;
1088 // logical partition
1089 EMbrTable
[i
].Flags
= (PartitionIndex
== LogicalPartitionIndex
) ? 0x80 : 0x00;
1090 LogicalPartitionIndex
++;
1094 // write current EMBR
1095 Status
= refit_call5_wrapper(BlockIO
->WriteBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, ExtCurrent
, 512, SectorBuffer
);
1096 if (EFI_ERROR(Status
))
1099 if (PartitionIndex
< LogicalPartitionIndex
)
1100 break; // stop the loop, no need to touch further EMBRs
1106 } /* static EFI_STATUS ActivateMbrPartition() */
1108 // early 2006 Core Duo / Core Solo models
1109 static UINT8 LegacyLoaderDevicePath1Data
[] = {
1110 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1111 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1112 0xFF, 0xFF, 0xF9, 0xFF, 0x00, 0x00, 0x00, 0x00,
1113 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1114 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1115 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1117 // mid-2006 Mac Pro (and probably other Core 2 models)
1118 static UINT8 LegacyLoaderDevicePath2Data
[] = {
1119 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1120 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1121 0xFF, 0xFF, 0xF7, 0xFF, 0x00, 0x00, 0x00, 0x00,
1122 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1123 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1124 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1126 // mid-2007 MBP ("Santa Rosa" based models)
1127 static UINT8 LegacyLoaderDevicePath3Data
[] = {
1128 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1129 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1130 0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
1131 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1132 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1133 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1136 static UINT8 LegacyLoaderDevicePath4Data
[] = {
1137 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1138 0x00, 0x00, 0xC0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1139 0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
1140 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1141 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1142 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1144 // late-2008 MB/MBP (NVidia chipset)
1145 static UINT8 LegacyLoaderDevicePath5Data
[] = {
1146 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1147 0x00, 0x40, 0xCB, 0xFF, 0x00, 0x00, 0x00, 0x00,
1148 0xFF, 0xBF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
1149 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1150 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1151 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1154 static EFI_DEVICE_PATH
*LegacyLoaderList
[] = {
1155 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath1Data
,
1156 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath2Data
,
1157 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath3Data
,
1158 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath4Data
,
1159 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath5Data
,
1163 #define MAX_DISCOVERED_PATHS (16)
1165 static VOID
StartLegacy(IN LEGACY_ENTRY
*Entry
)
1168 EG_IMAGE
*BootLogoImage
;
1169 UINTN ErrorInStep
= 0;
1170 EFI_DEVICE_PATH
*DiscoveredPathList
[MAX_DISCOVERED_PATHS
];
1172 BeginExternalScreen(TRUE
, L
"Booting Legacy OS (Mac mode)");
1174 BootLogoImage
= LoadOSIcon(Entry
->Volume
->OSIconName
, L
"legacy", TRUE
);
1175 if (BootLogoImage
!= NULL
)
1176 BltImageAlpha(BootLogoImage
,
1177 (UGAWidth
- BootLogoImage
->Width
) >> 1,
1178 (UGAHeight
- BootLogoImage
->Height
) >> 1,
1179 &StdBackgroundPixel
);
1181 if (Entry
->Volume
->IsMbrPartition
) {
1182 ActivateMbrPartition(Entry
->Volume
->WholeDiskBlockIO
, Entry
->Volume
->MbrPartitionIndex
);
1185 ExtractLegacyLoaderPaths(DiscoveredPathList
, MAX_DISCOVERED_PATHS
, LegacyLoaderList
);
1187 Status
= StartEFIImageList(DiscoveredPathList
, Entry
->LoadOptions
, NULL
, L
"legacy loader", 0, &ErrorInStep
, TRUE
);
1188 if (Status
== EFI_NOT_FOUND
) {
1189 if (ErrorInStep
== 1) {
1190 Print(L
"\nPlease make sure that you have the latest firmware update installed.\n");
1191 } else if (ErrorInStep
== 3) {
1192 Print(L
"\nThe firmware refused to boot from the selected volume. Note that external\n"
1193 L
"hard drives are not well-supported by Apple's firmware for legacy OS booting.\n");
1196 FinishExternalScreen();
1197 } /* static VOID StartLegacy() */
1199 // Start a device on a non-Mac using the EFI_LEGACY_BIOS_PROTOCOL
1200 #ifdef __MAKEWITH_TIANO
1201 static VOID
StartLegacyUEFI(IN LEGACY_ENTRY
*Entry
)
1203 BeginExternalScreen(TRUE
, L
"Booting Legacy OS (UEFI mode)");
1205 BdsLibConnectDevicePath (Entry
->BdsOption
->DevicePath
);
1206 BdsLibDoLegacyBoot(Entry
->BdsOption
);
1208 // If we get here, it means that there was a failure....
1209 Print(L
"Failure booting legacy (BIOS) OS.");
1211 FinishExternalScreen();
1212 } // static VOID StartLegacyUEFI()
1213 #endif // __MAKEWITH_TIANO
1215 static LEGACY_ENTRY
* AddLegacyEntry(IN CHAR16
*LoaderTitle
, IN REFIT_VOLUME
*Volume
)
1217 LEGACY_ENTRY
*Entry
, *SubEntry
;
1218 REFIT_MENU_SCREEN
*SubScreen
;
1220 CHAR16 ShortcutLetter
= 0;
1222 if (LoaderTitle
== NULL
) {
1223 if (Volume
->OSName
!= NULL
) {
1224 LoaderTitle
= Volume
->OSName
;
1225 if (LoaderTitle
[0] == 'W' || LoaderTitle
[0] == 'L')
1226 ShortcutLetter
= LoaderTitle
[0];
1228 LoaderTitle
= L
"Legacy OS";
1230 if (Volume
->VolName
!= NULL
)
1231 VolDesc
= Volume
->VolName
;
1233 VolDesc
= (Volume
->DiskKind
== DISK_KIND_OPTICAL
) ? L
"CD" : L
"HD";
1235 // prepare the menu entry
1236 Entry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1237 Entry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1238 SPrint(Entry
->me
.Title
, 255, L
"Boot %s from %s", LoaderTitle
, VolDesc
);
1239 Entry
->me
.Tag
= TAG_LEGACY
;
1241 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1242 Entry
->me
.Image
= LoadOSIcon(Volume
->OSIconName
, L
"legacy", FALSE
);
1243 Entry
->me
.BadgeImage
= Volume
->VolBadgeImage
;
1244 Entry
->Volume
= Volume
;
1245 Entry
->LoadOptions
= (Volume
->DiskKind
== DISK_KIND_OPTICAL
) ? L
"CD" :
1246 ((Volume
->DiskKind
== DISK_KIND_EXTERNAL
) ? L
"USB" : L
"HD");
1247 Entry
->Enabled
= TRUE
;
1249 // create the submenu
1250 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
1251 SubScreen
->Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1252 SPrint(SubScreen
->Title
, 255, L
"Boot Options for %s on %s", LoaderTitle
, VolDesc
);
1253 SubScreen
->TitleImage
= Entry
->me
.Image
;
1256 SubEntry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1257 SubEntry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1258 SPrint(SubEntry
->me
.Title
, 255, L
"Boot %s", LoaderTitle
);
1259 SubEntry
->me
.Tag
= TAG_LEGACY
;
1260 SubEntry
->Volume
= Entry
->Volume
;
1261 SubEntry
->LoadOptions
= Entry
->LoadOptions
;
1262 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
1264 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
1265 Entry
->me
.SubScreen
= SubScreen
;
1266 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1268 } /* static LEGACY_ENTRY * AddLegacyEntry() */
1271 #ifdef __MAKEWITH_TIANO
1272 // default volume badge icon based on disk kind
1273 static EG_IMAGE
* GetDiskBadge(IN UINTN DiskType
) {
1274 EG_IMAGE
* Badge
= NULL
;
1278 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_INTERNAL
);
1281 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_EXTERNAL
);
1284 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_OPTICAL
);
1288 } // static EG_IMAGE * GetDiskBadge()
1291 Create a rEFInd boot option from a Legacy BIOS protocol option.
1293 static LEGACY_ENTRY
* AddLegacyEntryUEFI(BDS_COMMON_OPTION
*BdsOption
, IN UINT16 DiskType
)
1295 LEGACY_ENTRY
*Entry
, *SubEntry
;
1296 REFIT_MENU_SCREEN
*SubScreen
;
1297 CHAR16 ShortcutLetter
= 0;
1298 CHAR16
*LegacyDescription
= BdsOption
->Description
;
1300 // ScanVolume(Volume);
1302 // prepare the menu entry
1303 Entry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1304 Entry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1305 SPrint(Entry
->me
.Title
, 255, L
"Boot legacy target %s", LegacyDescription
);
1306 Entry
->me
.Tag
= TAG_LEGACY_UEFI
;
1308 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1309 Entry
->me
.Image
= LoadOSIcon(L
"legacy", L
"legacy", TRUE
);
1310 Entry
->LoadOptions
= (DiskType
== BBS_CDROM
) ? L
"CD" :
1311 ((DiskType
== BBS_USB
) ? L
"USB" : L
"HD");
1312 Entry
->me
.BadgeImage
= GetDiskBadge(DiskType
);
1313 // Entry->me.BadgeImage = Volume->VolBadgeImage;
1314 Entry
->BdsOption
= BdsOption
;
1315 Entry
->Enabled
= TRUE
;
1317 // create the submenu
1318 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
1319 SubScreen
->Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1320 SPrint(SubScreen
->Title
, 255, L
"No boot options for legacy target");
1321 SubScreen
->TitleImage
= Entry
->me
.Image
;
1324 SubEntry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1325 SubEntry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1326 SPrint(SubEntry
->me
.Title
, 255, L
"Boot %s", LegacyDescription
);
1327 SubEntry
->me
.Tag
= TAG_LEGACY_UEFI
;
1328 Entry
->BdsOption
= BdsOption
;
1329 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
1331 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
1332 Entry
->me
.SubScreen
= SubScreen
;
1333 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1335 } /* static LEGACY_ENTRY * AddLegacyEntryUEFI() */
1338 Scan for legacy BIOS targets on machines that implement EFI_LEGACY_BIOS_PROTOCOL.
1339 In testing, protocol has not been implemented on Macs but has been
1340 implemented on several Dell PCs and an ASUS motherboard.
1341 Restricts output to disks of the specified DiskType.
1343 static VOID
ScanLegacyUEFI(IN UINTN DiskType
)
1346 EFI_LEGACY_BIOS_PROTOCOL
*LegacyBios
;
1347 UINT16
*BootOrder
= NULL
;
1349 CHAR16 BootOption
[10];
1350 UINTN BootOrderSize
= 0;
1352 BDS_COMMON_OPTION
*BdsOption
;
1353 LIST_ENTRY TempList
;
1354 BBS_BBS_DEVICE_PATH
* BbsDevicePath
= NULL
;
1355 // REFIT_VOLUME Volume;
1357 InitializeListHead (&TempList
);
1358 ZeroMem (Buffer
, sizeof (Buffer
));
1360 // If LegacyBios protocol is not implemented on this platform, then
1361 //we do not support this type of legacy boot on this machine.
1362 Status
= gBS
->LocateProtocol(&gEfiLegacyBootProtocolGuid
, NULL
, (VOID
**) &LegacyBios
);
1363 if (EFI_ERROR (Status
))
1366 // Grab the boot order
1367 BootOrder
= BdsLibGetVariableAndSize(L
"BootOrder", &gEfiGlobalVariableGuid
, &BootOrderSize
);
1368 if (BootOrder
== NULL
) {
1373 while (Index
< BootOrderSize
/ sizeof (UINT16
))
1375 // Grab each boot option variable from the boot order, and convert
1376 // the variable into a BDS boot option
1377 UnicodeSPrint (BootOption
, sizeof (BootOption
), L
"Boot%04x", BootOrder
[Index
]);
1378 BdsOption
= BdsLibVariableToOption (&TempList
, BootOption
);
1380 if (BdsOption
!= NULL
) {
1381 BbsDevicePath
= (BBS_BBS_DEVICE_PATH
*)BdsOption
->DevicePath
;
1383 // Only add the entry if it is of a requested type (e.g. USB, HD)
1385 // Two checks necessary because some systems return EFI boot loaders
1386 // with a DeviceType value that would inappropriately include them
1387 // as legacy loaders....
1388 if ((BbsDevicePath
->DeviceType
== DiskType
) && (BdsOption
->DevicePath
->Type
== DEVICE_TYPE_BIOS
)) {
1389 AddLegacyEntryUEFI(BdsOption
, BbsDevicePath
->DeviceType
);
1394 } /* static VOID ScanLegacyUEFI() */
1396 static VOID
ScanLegacyUEFI(IN UINTN DiskType
){}
1397 #endif // __MAKEWITH_TIANO
1399 static VOID
ScanLegacyVolume(REFIT_VOLUME
*Volume
, UINTN VolumeIndex
) {
1401 BOOLEAN ShowVolume
, HideIfOthersFound
;
1404 HideIfOthersFound
= FALSE
;
1405 if (Volume
->IsAppleLegacy
) {
1407 HideIfOthersFound
= TRUE
;
1408 } else if (Volume
->HasBootCode
) {
1410 if (Volume
->BlockIO
== Volume
->WholeDiskBlockIO
&&
1411 Volume
->BlockIOOffset
== 0 &&
1412 Volume
->OSName
== NULL
)
1413 // this is a whole disk (MBR) entry; hide if we have entries for partitions
1414 HideIfOthersFound
= TRUE
;
1416 if (HideIfOthersFound
) {
1417 // check for other bootable entries on the same disk
1418 for (VolumeIndex2
= 0; VolumeIndex2
< VolumesCount
; VolumeIndex2
++) {
1419 if (VolumeIndex2
!= VolumeIndex
&& Volumes
[VolumeIndex2
]->HasBootCode
&&
1420 Volumes
[VolumeIndex2
]->WholeDiskBlockIO
== Volume
->WholeDiskBlockIO
)
1426 AddLegacyEntry(NULL
, Volume
);
1427 } // static VOID ScanLegacyVolume()
1429 // Scan attached optical discs for legacy (BIOS) boot code
1430 // and add anything found to the list....
1431 static VOID
ScanLegacyDisc(VOID
)
1434 REFIT_VOLUME
*Volume
;
1436 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
) {
1437 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1438 Volume
= Volumes
[VolumeIndex
];
1439 if (Volume
->DiskKind
== DISK_KIND_OPTICAL
)
1440 ScanLegacyVolume(Volume
, VolumeIndex
);
1442 } else if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
1443 ScanLegacyUEFI(BBS_CDROM
);
1445 } /* static VOID ScanLegacyDisc() */
1447 // Scan internal hard disks for legacy (BIOS) boot code
1448 // and add anything found to the list....
1449 static VOID
ScanLegacyInternal(VOID
)
1452 REFIT_VOLUME
*Volume
;
1454 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
) {
1455 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1456 Volume
= Volumes
[VolumeIndex
];
1457 if (Volume
->DiskKind
== DISK_KIND_INTERNAL
)
1458 ScanLegacyVolume(Volume
, VolumeIndex
);
1460 } else if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
1461 ScanLegacyUEFI(BBS_HARDDISK
);
1463 } /* static VOID ScanLegacyInternal() */
1465 // Scan external disks for legacy (BIOS) boot code
1466 // and add anything found to the list....
1467 static VOID
ScanLegacyExternal(VOID
)
1470 REFIT_VOLUME
*Volume
;
1472 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
) {
1473 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1474 Volume
= Volumes
[VolumeIndex
];
1475 if (Volume
->DiskKind
== DISK_KIND_EXTERNAL
)
1476 ScanLegacyVolume(Volume
, VolumeIndex
);
1478 } else if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
1479 ScanLegacyUEFI(BBS_USB
);
1481 } /* static VOID ScanLegacyExternal() */
1484 // pre-boot tool functions
1487 static VOID
StartTool(IN LOADER_ENTRY
*Entry
)
1489 BeginExternalScreen(Entry
->UseGraphicsMode
, Entry
->me
.Title
+ 6); // assumes "Start <title>" as assigned below
1490 StartEFIImage(Entry
->DevicePath
, Entry
->LoadOptions
, Basename(Entry
->LoaderPath
),
1491 Basename(Entry
->LoaderPath
), Entry
->OSType
, NULL
, TRUE
);
1492 FinishExternalScreen();
1493 } /* static VOID StartTool() */
1495 static LOADER_ENTRY
* AddToolEntry(EFI_HANDLE DeviceHandle
, IN CHAR16
*LoaderPath
, IN CHAR16
*LoaderTitle
, IN EG_IMAGE
*Image
,
1496 IN CHAR16 ShortcutLetter
, IN BOOLEAN UseGraphicsMode
)
1498 LOADER_ENTRY
*Entry
;
1499 CHAR16
*TitleStr
= NULL
;
1501 Entry
= AllocateZeroPool(sizeof(LOADER_ENTRY
));
1503 MergeStrings(&TitleStr
, L
"Start ", 0);
1504 MergeStrings(&TitleStr
, LoaderTitle
, 0);
1505 Entry
->me
.Title
= TitleStr
;
1506 Entry
->me
.Tag
= TAG_TOOL
;
1508 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1509 Entry
->me
.Image
= Image
;
1510 Entry
->LoaderPath
= (LoaderPath
) ? StrDuplicate(LoaderPath
) : NULL
;
1511 Entry
->DevicePath
= FileDevicePath(DeviceHandle
, Entry
->LoaderPath
);
1512 Entry
->UseGraphicsMode
= UseGraphicsMode
;
1514 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1516 } /* static LOADER_ENTRY * AddToolEntry() */
1519 // pre-boot driver functions
1522 static UINTN
ScanDriverDir(IN CHAR16
*Path
)
1525 REFIT_DIR_ITER DirIter
;
1527 EFI_FILE_INFO
*DirEntry
;
1528 CHAR16 FileName
[256];
1530 CleanUpPathNameSlashes(Path
);
1531 // look through contents of the directory
1532 DirIterOpen(SelfRootDir
, Path
, &DirIter
);
1533 while (DirIterNext(&DirIter
, 2, LOADER_MATCH_PATTERNS
, &DirEntry
)) {
1534 if (DirEntry
->FileName
[0] == '.')
1535 continue; // skip this
1537 SPrint(FileName
, 255, L
"%s\\%s", Path
, DirEntry
->FileName
);
1539 Status
= StartEFIImage(FileDevicePath(SelfLoadedImage
->DeviceHandle
, FileName
),
1540 L
"", DirEntry
->FileName
, DirEntry
->FileName
, 0, NULL
, FALSE
);
1542 Status
= DirIterClose(&DirIter
);
1543 if (Status
!= EFI_NOT_FOUND
) {
1544 SPrint(FileName
, 255, L
"while scanning the %s directory", Path
);
1545 CheckError(Status
, FileName
);
1550 #ifdef __MAKEWITH_GNUEFI
1551 static EFI_STATUS
ConnectAllDriversToAllControllers(VOID
)
1554 UINTN AllHandleCount
;
1555 EFI_HANDLE
*AllHandleBuffer
;
1558 EFI_HANDLE
*HandleBuffer
;
1564 Status
= LibLocateHandle(AllHandles
,
1569 if (EFI_ERROR(Status
))
1572 for (Index
= 0; Index
< AllHandleCount
; Index
++) {
1574 // Scan the handle database
1576 Status
= LibScanHandleDatabase(NULL
,
1578 AllHandleBuffer
[Index
],
1583 if (EFI_ERROR (Status
))
1587 if (HandleType
[Index
] & EFI_HANDLE_TYPE_DRIVER_BINDING_HANDLE
)
1589 if (HandleType
[Index
] & EFI_HANDLE_TYPE_IMAGE_HANDLE
)
1594 for (HandleIndex
= 0; HandleIndex
< HandleCount
; HandleIndex
++) {
1595 if (HandleType
[HandleIndex
] & EFI_HANDLE_TYPE_PARENT_HANDLE
)
1600 if (HandleType
[Index
] & EFI_HANDLE_TYPE_DEVICE_HANDLE
) {
1601 Status
= refit_call4_wrapper(BS
->ConnectController
,
1602 AllHandleBuffer
[Index
],
1610 MyFreePool (HandleBuffer
);
1611 MyFreePool (HandleType
);
1615 MyFreePool (AllHandleBuffer
);
1617 } /* EFI_STATUS ConnectAllDriversToAllControllers() */
1619 static EFI_STATUS
ConnectAllDriversToAllControllers(VOID
) {
1620 BdsLibConnectAllDriversToAllControllers();
1625 // Load all EFI drivers from rEFInd's "drivers" subdirectory and from the
1626 // directories specified by the user in the "scan_driver_dirs" configuration
1628 static VOID
LoadDrivers(VOID
)
1630 CHAR16
*Directory
, *SelfDirectory
;
1631 UINTN i
= 0, Length
, NumFound
= 0;
1633 // load drivers from the subdirectories of rEFInd's home directory specified
1634 // in the DRIVER_DIRS constant.
1635 while ((Directory
= FindCommaDelimited(DRIVER_DIRS
, i
++)) != NULL
) {
1636 SelfDirectory
= SelfDirPath
? StrDuplicate(SelfDirPath
) : NULL
;
1637 CleanUpPathNameSlashes(SelfDirectory
);
1638 MergeStrings(&SelfDirectory
, Directory
, L
'\\');
1639 NumFound
+= ScanDriverDir(SelfDirectory
);
1640 MyFreePool(Directory
);
1641 MyFreePool(SelfDirectory
);
1644 // Scan additional user-specified driver directories....
1646 while ((Directory
= FindCommaDelimited(GlobalConfig
.DriverDirs
, i
++)) != NULL
) {
1647 CleanUpPathNameSlashes(Directory
);
1648 Length
= StrLen(Directory
);
1650 NumFound
+= ScanDriverDir(Directory
);
1652 MyFreePool(Directory
);
1655 // connect all devices
1657 ConnectAllDriversToAllControllers();
1658 } /* static VOID LoadDrivers() */
1660 // Determine what (if any) type of legacy (BIOS) boot support is available
1661 static VOID
FindLegacyBootType(VOID
) {
1662 #ifdef __MAKEWITH_TIANO
1664 EFI_LEGACY_BIOS_PROTOCOL
*LegacyBios
;
1667 GlobalConfig
.LegacyType
= LEGACY_TYPE_NONE
;
1669 // UEFI-style legacy BIOS support is available only with the TianoCore EDK2
1670 // build environment, and then only with some implementations....
1671 #ifdef __MAKEWITH_TIANO
1672 Status
= gBS
->LocateProtocol (&gEfiLegacyBootProtocolGuid
, NULL
, (VOID
**) &LegacyBios
);
1673 if (!EFI_ERROR (Status
))
1674 GlobalConfig
.LegacyType
= LEGACY_TYPE_UEFI
;
1677 // Macs have their own system. If the firmware vendor code contains the
1678 // string "Apple", assume it's available. Note that this overrides the
1679 // UEFI type, and might yield false positives if the vendor string
1680 // contains "Apple" as part of something bigger, so this isn't 100%
1682 if (StriSubCmp(L
"Apple", ST
->FirmwareVendor
))
1683 GlobalConfig
.LegacyType
= LEGACY_TYPE_MAC
;
1684 } // static VOID FindLegacyBootType
1686 // Warn the user if legacy OS scans are enabled but the firmware or this
1687 // application can't support them....
1688 static VOID
WarnIfLegacyProblems() {
1689 BOOLEAN found
= FALSE
;
1692 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_NONE
) {
1694 if (GlobalConfig
.ScanFor
[i
] == 'h' || GlobalConfig
.ScanFor
[i
] == 'b' || GlobalConfig
.ScanFor
[i
] == 'c')
1697 } while ((i
< NUM_SCAN_OPTIONS
) && (!found
));
1699 Print(L
"NOTE: refind.conf's 'scanfor' line specifies scanning for one or more legacy\n");
1700 Print(L
"(BIOS) boot options; however, this is not possible because ");
1701 #ifdef __MAKEWITH_TIANO
1702 Print(L
"your computer lacks\n");
1703 Print(L
"the necessary Compatibility Support Module (CSM) support.\n");
1705 Print(L
"this program was\n");
1706 Print(L
"compiled without the necessary support. Please visit\n");
1707 Print(L
"http://www.rodsbooks.com/refind/getting.html and download and install a rEFInd\n");
1708 Print(L
"binary built with the TianoCore EDK2 to enable legacy boot support.\n");
1712 } // if no legacy support
1713 } // static VOID WarnIfLegacyProblems()
1715 // Locates boot loaders. NOTE: This assumes that GlobalConfig.LegacyType is set correctly.
1716 static VOID
ScanForBootloaders(VOID
) {
1721 // scan for loaders and tools, add them to the menu
1722 for (i
= 0; i
< NUM_SCAN_OPTIONS
; i
++) {
1723 switch(GlobalConfig
.ScanFor
[i
]) {
1728 ScanLegacyInternal();
1731 ScanLegacyExternal();
1734 ScanUserConfigured();
1748 // assign shortcut keys
1749 for (i
= 0; i
< MainMenu
.EntryCount
&& MainMenu
.Entries
[i
]->Row
== 0 && i
< 9; i
++)
1750 MainMenu
.Entries
[i
]->ShortcutDigit
= (CHAR16
)('1' + i
);
1752 // wait for user ACK when there were errors
1753 FinishTextScreen(FALSE
);
1754 } // static VOID ScanForBootloaders()
1756 // Add the second-row tags containing built-in and external tools (EFI shell,
1758 static VOID
ScanForTools(VOID
) {
1759 CHAR16
*FileName
= NULL
, Description
[256];
1760 REFIT_MENU_ENTRY
*TempMenuEntry
;
1761 UINTN i
, j
, VolumeIndex
;
1763 for (i
= 0; i
< NUM_TOOLS
; i
++) {
1764 switch(GlobalConfig
.ShowTools
[i
]) {
1766 TempMenuEntry
= CopyMenuEntry(&MenuEntryShutdown
);
1767 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_SHUTDOWN
);
1768 AddMenuEntry(&MainMenu
, TempMenuEntry
);
1771 TempMenuEntry
= CopyMenuEntry(&MenuEntryReset
);
1772 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_RESET
);
1773 AddMenuEntry(&MainMenu
, TempMenuEntry
);
1776 TempMenuEntry
= CopyMenuEntry(&MenuEntryAbout
);
1777 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT
);
1778 AddMenuEntry(&MainMenu
, TempMenuEntry
);
1781 TempMenuEntry
= CopyMenuEntry(&MenuEntryExit
);
1782 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_EXIT
);
1783 AddMenuEntry(&MainMenu
, TempMenuEntry
);
1787 MyFreePool(FileName
);
1788 while ((FileName
= FindCommaDelimited(SHELL_NAMES
, j
++)) != NULL
) {
1789 if (FileExists(SelfRootDir
, FileName
)) {
1790 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, L
"EFI Shell", BuiltinIcon(BUILTIN_ICON_TOOL_SHELL
),
1796 MyFreePool(FileName
);
1798 MergeStrings(&FileName
, L
"\\efi\\tools\\gptsync.efi", 0);
1799 if (FileExists(SelfRootDir
, FileName
)) {
1800 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, L
"Make Hybrid MBR", BuiltinIcon(BUILTIN_ICON_TOOL_PART
), 'P', FALSE
);
1803 case TAG_APPLE_RECOVERY
:
1804 MyFreePool(FileName
);
1806 MergeStrings(&FileName
, L
"\\com.apple.recovery.boot\\boot.efi", 0);
1807 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1808 if ((Volumes
[VolumeIndex
]->RootDir
!= NULL
) && (FileExists(Volumes
[VolumeIndex
]->RootDir
, FileName
))) {
1809 SPrint(Description
, 255, L
"Apple Recovery on %s", Volumes
[VolumeIndex
]->VolName
);
1810 AddToolEntry(Volumes
[VolumeIndex
]->DeviceHandle
, FileName
, Description
,
1811 BuiltinIcon(BUILTIN_ICON_TOOL_APPLE_RESCUE
), 'R', TRUE
);
1817 MyFreePool(FileName
);
1818 while ((FileName
= FindCommaDelimited(MOK_NAMES
, j
++)) != NULL
) {
1819 if (FileExists(SelfRootDir
, FileName
)) {
1820 SPrint(Description
, 255, L
"MOK Key Manager at %s", FileName
);
1821 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, Description
,
1822 BuiltinIcon(BUILTIN_ICON_TOOL_MOK_TOOL
), 'S', FALSE
);
1825 if (FileExists(SelfDir
, L
"MokManager.efi")) {
1826 MyFreePool(FileName
);
1827 FileName
= StrDuplicate(SelfDirPath
);
1828 MergeStrings(&FileName
, L
"\\MokManager.efi", 0);
1829 SPrint(Description
, 255, L
"MOK Key Manager at %s", FileName
);
1830 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, Description
,
1831 BuiltinIcon(BUILTIN_ICON_TOOL_MOK_TOOL
), 'S', FALSE
);
1835 MyFreePool(FileName
);
1838 } // static VOID ScanForTools
1840 // Rescan for boot loaders
1841 VOID
RescanAll(VOID
) {
1848 egDisplayMessage(L
"Scanning for new boot loaders; please wait....", &BGColor
);
1849 FreeList((VOID
***) &(MainMenu
.Entries
), &MainMenu
.EntryCount
);
1850 MainMenu
.Entries
= NULL
;
1851 MainMenu
.EntryCount
= 0;
1853 ConnectAllDriversToAllControllers();
1855 ScanForBootloaders();
1858 } // VOID RescanAll()
1860 #ifndef __MAKEWITH_GNUEFI
1862 // Minimal initialization function
1863 static VOID
InitializeLib(IN EFI_HANDLE ImageHandle
, IN EFI_SYSTEM_TABLE
*SystemTable
) {
1865 // gImageHandle = ImageHandle;
1866 gBS
= SystemTable
->BootServices
;
1867 // gRS = SystemTable->RuntimeServices;
1868 gRT
= SystemTable
->RuntimeServices
; // Some BDS functions need gRT to be set
1869 EfiGetSystemConfigurationTable (&gEfiDxeServicesTableGuid
, (VOID
**) &gDS
);
1871 InitializeConsoleSim();
1881 efi_main (IN EFI_HANDLE ImageHandle
, IN EFI_SYSTEM_TABLE
*SystemTable
)
1884 BOOLEAN MainLoopRunning
= TRUE
;
1885 REFIT_MENU_ENTRY
*ChosenEntry
;
1891 InitializeLib(ImageHandle
, SystemTable
);
1893 Status
= InitRefitLib(ImageHandle
);
1894 if (EFI_ERROR(Status
))
1897 // read configuration
1898 CopyMem(GlobalConfig
.ScanFor
, "ieom ", NUM_SCAN_OPTIONS
);
1899 FindLegacyBootType();
1900 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
)
1901 CopyMem(GlobalConfig
.ScanFor
, "ihebocm ", NUM_SCAN_OPTIONS
);
1903 WarnIfLegacyProblems();
1904 MainMenu
.TimeoutSeconds
= GlobalConfig
.Timeout
;
1906 // disable EFI watchdog timer
1907 refit_call4_wrapper(BS
->SetWatchdogTimer
, 0x0000, 0x0000, 0x0000, NULL
);
1909 // further bootstrap (now with config available)
1913 ScanForBootloaders();
1916 if (GlobalConfig
.ScanDelay
> 0) {
1921 egDisplayMessage(L
"Pausing before disk scan; please wait....", &BGColor
);
1922 for (i
= 0; i
< GlobalConfig
.ScanDelay
; i
++)
1923 refit_call1_wrapper(BS
->Stall
, 1000000);
1927 Selection
= StrDuplicate(GlobalConfig
.DefaultSelection
);
1928 while (MainLoopRunning
) {
1929 MenuExit
= RunMainMenu(&MainMenu
, Selection
, &ChosenEntry
);
1931 // The Escape key triggers a re-scan operation....
1932 if (MenuExit
== MENU_EXIT_ESCAPE
) {
1937 switch (ChosenEntry
->Tag
) {
1939 case TAG_REBOOT
: // Reboot
1941 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);
1942 MainLoopRunning
= FALSE
; // just in case we get this far
1945 case TAG_SHUTDOWN
: // Shut Down
1947 refit_call4_wrapper(RT
->ResetSystem
, EfiResetShutdown
, EFI_SUCCESS
, 0, NULL
);
1948 MainLoopRunning
= FALSE
; // just in case we get this far
1951 case TAG_ABOUT
: // About rEFInd
1955 case TAG_LOADER
: // Boot OS via .EFI loader
1956 StartLoader((LOADER_ENTRY
*)ChosenEntry
);
1959 case TAG_LEGACY
: // Boot legacy OS
1960 StartLegacy((LEGACY_ENTRY
*)ChosenEntry
);
1963 #ifdef __MAKEWITH_TIANO
1964 case TAG_LEGACY_UEFI
: // Boot a legacy OS on a non-Mac
1965 StartLegacyUEFI((LEGACY_ENTRY
*)ChosenEntry
);
1967 #endif // __MAKEWITH_TIANO
1969 case TAG_TOOL
: // Start a EFI tool
1970 StartTool((LOADER_ENTRY
*)ChosenEntry
);
1973 case TAG_EXIT
: // Terminate rEFInd
1974 BeginTextScreen(L
" ");
1979 MyFreePool(Selection
);
1980 Selection
= (ChosenEntry
->Title
) ? StrDuplicate(ChosenEntry
->Title
) : NULL
;
1983 // If we end up here, things have gone wrong. Try to reboot, and if that
1984 // fails, go into an endless loop.
1985 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);