3 * Main code for the boot menu
5 * Copyright (c) 2006-2010 Christoph Pfisterer
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions are
12 * * Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
15 * * Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the
20 * * Neither the name of Christoph Pfisterer nor the names of the
21 * contributors may be used to endorse or promote products derived
22 * from this software without specific prior written permission.
24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
26 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
27 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
28 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
29 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
30 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
31 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
32 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
33 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
34 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37 * Modifications copyright (c) 2012 Roderick W. Smith
39 * Modifications distributed under the terms of the GNU General Public
40 * License (GPL) version 3 (GPLv3), a copy of which must be distributed
41 * with this source code or binaries made from it.
52 #include "../include/Handle.h"
53 #include "../include/refit_call_wrapper.h"
54 #include "driver_support.h"
55 #include "../include/syslinux_mbr.h"
57 #ifdef __MAKEWITH_TIANO
58 #include "../EfiLib/BdsHelper.h"
59 #endif // __MAKEWITH_TIANO
64 #define MACOSX_LOADER_PATH L"\\System\\Library\\CoreServices\\boot.efi"
66 #define SHELL_NAMES L"\\EFI\\tools\\shell.efi,\\shellx64.efi"
67 #define DRIVER_DIRS L"drivers,drivers_x64"
69 #define SHELL_NAMES L"\\EFI\\tools\\shell.efi,\\shellia32.efi"
70 #define DRIVER_DIRS L"drivers,drivers_ia32"
72 #define SHELL_NAMES L"\\EFI\\tools\\shell.efi"
73 #define DRIVER_DIRS L"drivers"
76 #define MOK_NAMES L"\\EFI\\tools\\MokManager.efi,\\EFI\\redhat\\MokManager.efi,\\EFI\\ubuntu\\MokManager.efi,\\EFI\\suse\\MokManager"
78 // Filename patterns that identify EFI boot loaders. Note that a single case (either L"*.efi" or
79 // L"*.EFI") is fine for most systems; but Gigabyte's buggy Hybrid EFI does a case-sensitive
80 // comparison when it should do a case-insensitive comparison, so I'm doubling this up. It does
81 // no harm on other computers, AFAIK. In theory, every case variation should be done for
82 // completeness, but that's ridiculous....
83 #define LOADER_MATCH_PATTERNS L"*.efi,*.EFI"
85 // Patterns that identify Linux kernels. Added to the loader match pattern when the
86 // scan_all_linux_kernels option is set in the configuration file. Causes kernels WITHOUT
87 // a ".efi" extension to be found when scanning for boot loaders.
88 #define LINUX_MATCH_PATTERNS L"vmlinuz*,bzImage*"
90 static REFIT_MENU_ENTRY MenuEntryAbout
= { L
"About rEFInd", TAG_ABOUT
, 1, 0, 'A', NULL
, NULL
, NULL
};
91 static REFIT_MENU_ENTRY MenuEntryReset
= { L
"Reboot Computer", TAG_REBOOT
, 1, 0, 'R', NULL
, NULL
, NULL
};
92 static REFIT_MENU_ENTRY MenuEntryShutdown
= { L
"Shut Down Computer", TAG_SHUTDOWN
, 1, 0, 'U', NULL
, NULL
, NULL
};
93 static REFIT_MENU_ENTRY MenuEntryReturn
= { L
"Return to Main Menu", TAG_RETURN
, 0, 0, 0, NULL
, NULL
, NULL
};
94 static REFIT_MENU_ENTRY MenuEntryExit
= { L
"Exit rEFInd", TAG_EXIT
, 1, 0, 0, NULL
, NULL
, NULL
};
96 static REFIT_MENU_SCREEN MainMenu
= { L
"Main Menu", NULL
, 0, NULL
, 0, NULL
, 0, L
"Automatic boot" };
97 static REFIT_MENU_SCREEN AboutMenu
= { L
"About", NULL
, 0, NULL
, 0, NULL
, 0, NULL
};
99 REFIT_CONFIG GlobalConfig
= { FALSE
, FALSE
, 0, 0, 20, 0, 0, GRAPHICS_FOR_OSX
, LEGACY_TYPE_MAC
, 0,
100 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
101 {TAG_SHELL
, TAG_APPLE_RECOVERY
, TAG_MOK_TOOL
, TAG_ABOUT
, TAG_SHUTDOWN
, TAG_REBOOT
, 0, 0, 0, 0, 0 }};
103 // Structure used to hold boot loader filenames and time stamps in
104 // a linked list; used to sort entries within a directory.
108 struct LOADER_LIST
*NextEntry
;
115 static VOID
AboutrEFInd(VOID
)
117 CHAR16
*TempStr
; // Note: Don't deallocate; moved to menu structure
119 if (AboutMenu
.EntryCount
== 0) {
120 AboutMenu
.TitleImage
= BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT
);
121 AddMenuInfoLine(&AboutMenu
, L
"rEFInd Version 0.4.7.10");
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
, SecureMode
;
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 SecureMode
= secure_mode();
207 // SecureMode = TRUE;
208 for (DevicePathIndex
= 0; DevicePaths
[DevicePathIndex
] != NULL
; DevicePathIndex
++) {
209 // NOTE: Below commented-out line could simplify logic by loading the image once, but
210 // it doesn't work on my 32-bit Mac Mini or my 64-bit Intel box when launching a
211 // Linux kernel; the kernel returns a "Failed to handle fs_proto" error message.
212 // TODO: Track down the cause of this error and fix it, if possible.
213 // ReturnStatus = Status = refit_call6_wrapper(BS->LoadImage, FALSE, SelfImageHandle, DevicePaths[DevicePathIndex],
214 // ImageData, ImageSize, &ChildImageHandle);
215 // In Secure Boot mode, try to use shim/MOK-style loading first, and if
216 // that fails, try the standard EFI system call (LoadImage()). This is
217 // done for efficiency, to prevent loading a binary twice, which can
218 // take several seconds to load a Linux kernel with EFI stub support on
219 // some systems. Linux kernels are likely to be shim/MOK signed, so
220 // this is quickest for them; and delays for most other boot loaders
221 // will be unnoticeably short. To prevent delays or failures in case
222 // of buggy shim/MOK code on non-SB systems, skip that attempt and
223 // call LoadImage() directly when not in SB mode.
225 FindVolumeAndFilename(DevicePaths
[DevicePathIndex
], &DeviceVolume
, &loader
);
226 if (DeviceVolume
!= NULL
) {
227 Status
= ReadFile(DeviceVolume
->RootDir
, loader
, &File
, &ImageSize
);
228 ImageData
= File
.Buffer
;
230 Status
= EFI_NOT_FOUND
;
231 Print(L
"Error: device volume not found!\n");
233 if (Status
!= EFI_NOT_FOUND
) {
234 ReturnStatus
= Status
= start_image(SelfImageHandle
, loader
, ImageData
, ImageSize
, FullLoadOptions
,
235 DeviceVolume
, DevicePaths
[DevicePathIndex
]);
237 if (ReturnStatus
== EFI_SUCCESS
) {
240 // If shim/MOK load fails, try regular EFI load, in case it's an unsupported
243 ReturnStatus
= Status
= refit_call6_wrapper(BS
->LoadImage
, FALSE
, SelfImageHandle
, DevicePaths
[DevicePathIndex
],
244 NULL
, 0, &ChildImageHandle
);
246 } else { // Secure Boot inactive; only do standard call....
247 ReturnStatus
= Status
= refit_call6_wrapper(BS
->LoadImage
, FALSE
, SelfImageHandle
, DevicePaths
[DevicePathIndex
],
248 NULL
, 0, &ChildImageHandle
);
249 } // if/else (SecureMode)
250 if (ReturnStatus
!= EFI_NOT_FOUND
) {
254 SPrint(ErrorInfo
, 255, L
"while loading %s", ImageTitle
);
255 if (CheckError(Status
, ErrorInfo
)) {
256 if (ErrorInStep
!= NULL
)
262 ReturnStatus
= Status
= refit_call3_wrapper(BS
->HandleProtocol
, ChildImageHandle
, &LoadedImageProtocol
,
263 (VOID
**) &ChildLoadedImage
);
264 if (CheckError(Status
, L
"while getting a LoadedImageProtocol handle")) {
265 if (ErrorInStep
!= NULL
)
269 ChildLoadedImage
->LoadOptions
= (VOID
*)FullLoadOptions
;
270 ChildLoadedImage
->LoadOptionsSize
= ((UINT32
)StrLen(FullLoadOptions
) + 1) * sizeof(CHAR16
);
271 // turn control over to the image
272 // TODO: (optionally) re-enable the EFI watchdog timer!
274 // close open file handles
276 ReturnStatus
= Status
= refit_call3_wrapper(BS
->StartImage
, ChildImageHandle
, NULL
, NULL
);
277 // control returns here when the child image calls Exit()
278 SPrint(ErrorInfo
, 255, L
"returned from %s", ImageTitle
);
279 if (CheckError(Status
, ErrorInfo
)) {
280 if (ErrorInStep
!= NULL
)
284 // re-open file handles
289 // unload the image, we don't care if it works or not...
291 Status
= refit_call1_wrapper(BS
->UnloadImage
, ChildImageHandle
);
294 MyFreePool(FullLoadOptions
);
296 } /* static EFI_STATUS StartEFIImageList() */
298 static EFI_STATUS
StartEFIImage(IN EFI_DEVICE_PATH
*DevicePath
,
299 IN CHAR16
*LoadOptions
, IN CHAR16
*LoadOptionsPrefix
,
300 IN CHAR16
*ImageTitle
, IN CHAR8 OSType
,
301 OUT UINTN
*ErrorInStep
,
304 EFI_DEVICE_PATH
*DevicePaths
[2];
306 DevicePaths
[0] = DevicePath
;
307 DevicePaths
[1] = NULL
;
308 return StartEFIImageList(DevicePaths
, LoadOptions
, LoadOptionsPrefix
, ImageTitle
, OSType
, ErrorInStep
, Verbose
);
309 } /* static EFI_STATUS StartEFIImage() */
312 // EFI OS loader functions
315 static VOID
StartLoader(IN LOADER_ENTRY
*Entry
)
317 UINTN ErrorInStep
= 0;
319 BeginExternalScreen(Entry
->UseGraphicsMode
, L
"Booting OS");
320 StartEFIImage(Entry
->DevicePath
, Entry
->LoadOptions
, Basename(Entry
->LoaderPath
),
321 Basename(Entry
->LoaderPath
), Entry
->OSType
, &ErrorInStep
, !Entry
->UseGraphicsMode
);
322 FinishExternalScreen();
325 // Locate an initrd or initramfs file that matches the kernel specified by LoaderPath.
326 // The matching file has a name that begins with "init" and includes the same version
327 // number string as is found in LoaderPath -- but not a longer version number string.
328 // For instance, if LoaderPath is \EFI\kernels\bzImage-3.3.0.efi, and if \EFI\kernels
329 // has a file called initramfs-3.3.0.img, this function will return the string
330 // '\EFI\kernels\initramfs-3.3.0.img'. If the directory ALSO contains the file
331 // initramfs-3.3.0-rc7.img or initramfs-13.3.0.img, those files will NOT match;
332 // however, initmine-3.3.0.img might match. (FindInitrd() returns the first match it
333 // finds). Thus, care should be taken to avoid placing duplicate matching files in
334 // the kernel's directory.
335 // If no matching init file can be found, returns NULL.
336 static CHAR16
* FindInitrd(IN CHAR16
*LoaderPath
, IN REFIT_VOLUME
*Volume
) {
337 CHAR16
*InitrdName
= NULL
, *FileName
, *KernelVersion
, *InitrdVersion
, *Path
;
338 REFIT_DIR_ITER DirIter
;
339 EFI_FILE_INFO
*DirEntry
;
341 FileName
= Basename(LoaderPath
);
342 KernelVersion
= FindNumbers(FileName
);
343 Path
= FindPath(LoaderPath
);
345 // Add trailing backslash for root directory; necessary on some systems, but must
346 // NOT be added to all directories, since on other systems, a trailing backslash on
347 // anything but the root directory causes them to flake out!
348 if (StrLen(Path
) == 0) {
349 MergeStrings(&Path
, L
"\\", 0);
351 DirIterOpen(Volume
->RootDir
, Path
, &DirIter
);
352 // Now add a trailing backslash if it was NOT added earlier, for consistency in
353 // building the InitrdName later....
354 if ((StrLen(Path
) > 0) && (Path
[StrLen(Path
) - 1] != L
'\\'))
355 MergeStrings(&Path
, L
"\\", 0);
356 while ((DirIterNext(&DirIter
, 2, L
"init*", &DirEntry
)) && (InitrdName
== NULL
)) {
357 InitrdVersion
= FindNumbers(DirEntry
->FileName
);
358 if (KernelVersion
!= NULL
) {
359 if (StriCmp(InitrdVersion
, KernelVersion
) == 0) {
360 MergeStrings(&InitrdName
, Path
, 0);
361 MergeStrings(&InitrdName
, DirEntry
->FileName
, 0);
364 if (InitrdVersion
== NULL
) {
365 MergeStrings(&InitrdName
, Path
, 0);
366 MergeStrings(&InitrdName
, DirEntry
->FileName
, 0);
369 MyFreePool(InitrdVersion
);
371 DirIterClose(&DirIter
);
373 // Note: Don't FreePool(FileName), since Basename returns a pointer WITHIN the string it's passed.
374 MyFreePool(KernelVersion
);
377 } // static CHAR16 * FindInitrd()
379 LOADER_ENTRY
* AddPreparedLoaderEntry(LOADER_ENTRY
*Entry
) {
380 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
383 } // LOADER_ENTRY * AddPreparedLoaderEntry()
385 // Creates a copy of a menu screen.
386 // Returns a pointer to the copy of the menu screen.
387 static REFIT_MENU_SCREEN
* CopyMenuScreen(REFIT_MENU_SCREEN
*Entry
) {
388 REFIT_MENU_SCREEN
*NewEntry
;
391 NewEntry
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
392 if ((Entry
!= NULL
) && (NewEntry
!= NULL
)) {
393 CopyMem(NewEntry
, Entry
, sizeof(REFIT_MENU_SCREEN
));
394 NewEntry
->Title
= (Entry
->Title
) ? StrDuplicate(Entry
->Title
) : NULL
;
395 NewEntry
->TimeoutText
= (Entry
->TimeoutText
) ? StrDuplicate(Entry
->TimeoutText
) : NULL
;
396 if (Entry
->TitleImage
!= NULL
) {
397 NewEntry
->TitleImage
= AllocatePool(sizeof(EG_IMAGE
));
398 if (NewEntry
->TitleImage
!= NULL
)
399 CopyMem(NewEntry
->TitleImage
, Entry
->TitleImage
, sizeof(EG_IMAGE
));
401 NewEntry
->InfoLines
= (CHAR16
**) AllocateZeroPool(Entry
->InfoLineCount
* (sizeof(CHAR16
*)));
402 for (i
= 0; i
< Entry
->InfoLineCount
&& NewEntry
->InfoLines
; i
++) {
403 NewEntry
->InfoLines
[i
] = (Entry
->InfoLines
[i
]) ? StrDuplicate(Entry
->InfoLines
[i
]) : NULL
;
405 NewEntry
->Entries
= (REFIT_MENU_ENTRY
**) AllocateZeroPool(Entry
->EntryCount
* (sizeof (REFIT_MENU_ENTRY
*)));
406 for (i
= 0; i
< Entry
->EntryCount
&& NewEntry
->Entries
; i
++) {
407 AddMenuEntry(NewEntry
, Entry
->Entries
[i
]);
411 } // static REFIT_MENU_SCREEN* CopyMenuScreen()
413 // Creates a copy of a menu entry. Intended to enable moving a stack-based
414 // menu entry (such as the ones for the "reboot" and "exit" functions) to
415 // to the heap. This enables easier deletion of the whole set of menu
416 // entries when re-scanning.
417 // Returns a pointer to the copy of the menu entry.
418 static REFIT_MENU_ENTRY
* CopyMenuEntry(REFIT_MENU_ENTRY
*Entry
) {
419 REFIT_MENU_ENTRY
*NewEntry
;
421 NewEntry
= AllocateZeroPool(sizeof(REFIT_MENU_ENTRY
));
422 if ((Entry
!= NULL
) && (NewEntry
!= NULL
)) {
423 CopyMem(NewEntry
, Entry
, sizeof(REFIT_MENU_ENTRY
));
424 NewEntry
->Title
= (Entry
->Title
) ? StrDuplicate(Entry
->Title
) : NULL
;
425 if (Entry
->BadgeImage
!= NULL
) {
426 NewEntry
->BadgeImage
= AllocatePool(sizeof(EG_IMAGE
));
427 if (NewEntry
->BadgeImage
!= NULL
)
428 CopyMem(NewEntry
->BadgeImage
, Entry
->BadgeImage
, sizeof(EG_IMAGE
));
430 if (Entry
->Image
!= NULL
) {
431 NewEntry
->Image
= AllocatePool(sizeof(EG_IMAGE
));
432 if (NewEntry
->Image
!= NULL
)
433 CopyMem(NewEntry
->Image
, Entry
->Image
, sizeof(EG_IMAGE
));
435 if (Entry
->SubScreen
!= NULL
) {
436 NewEntry
->SubScreen
= CopyMenuScreen(Entry
->SubScreen
);
440 } // REFIT_MENU_ENTRY* CopyMenuEntry()
442 // Creates a new LOADER_ENTRY data structure and populates it with
443 // default values from the specified Entry, or NULL values if Entry
444 // is unspecified (NULL).
445 // Returns a pointer to the new data structure, or NULL if it
446 // couldn't be allocated
447 LOADER_ENTRY
*InitializeLoaderEntry(IN LOADER_ENTRY
*Entry
) {
448 LOADER_ENTRY
*NewEntry
= NULL
;
450 NewEntry
= AllocateZeroPool(sizeof(LOADER_ENTRY
));
451 if (NewEntry
!= NULL
) {
452 NewEntry
->me
.Title
= NULL
;
453 NewEntry
->me
.Tag
= TAG_LOADER
;
454 NewEntry
->Enabled
= TRUE
;
455 NewEntry
->UseGraphicsMode
= FALSE
;
456 NewEntry
->OSType
= 0;
458 NewEntry
->LoaderPath
= (Entry
->LoaderPath
) ? StrDuplicate(Entry
->LoaderPath
) : NULL
;
459 NewEntry
->VolName
= (Entry
->VolName
) ? StrDuplicate(Entry
->VolName
) : NULL
;
460 NewEntry
->DevicePath
= Entry
->DevicePath
;
461 NewEntry
->UseGraphicsMode
= Entry
->UseGraphicsMode
;
462 NewEntry
->LoadOptions
= (Entry
->LoadOptions
) ? StrDuplicate(Entry
->LoadOptions
) : NULL
;
463 NewEntry
->InitrdPath
= (Entry
->InitrdPath
) ? StrDuplicate(Entry
->InitrdPath
) : NULL
;
467 } // LOADER_ENTRY *InitializeLoaderEntry()
469 // Prepare a REFIT_MENU_SCREEN data structure for a subscreen entry. This sets up
470 // the default entry that launches the boot loader using the same options as the
471 // main Entry does. Subsequent options can be added by the calling function.
472 // If a subscreen already exists in the Entry that's passed to this function,
473 // it's left unchanged and a pointer to it is returned.
474 // Returns a pointer to the new subscreen data structure, or NULL if there
475 // were problems allocating memory.
476 REFIT_MENU_SCREEN
*InitializeSubScreen(IN LOADER_ENTRY
*Entry
) {
477 CHAR16
*FileName
, *Temp
= NULL
;
478 REFIT_MENU_SCREEN
*SubScreen
= NULL
;
479 LOADER_ENTRY
*SubEntry
;
481 FileName
= Basename(Entry
->LoaderPath
);
482 if (Entry
->me
.SubScreen
== NULL
) { // No subscreen yet; initialize default entry....
483 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
484 if (SubScreen
!= NULL
) {
485 SubScreen
->Title
= AllocateZeroPool(sizeof(CHAR16
) * 256);
486 SPrint(SubScreen
->Title
, 255, L
"Boot Options for %s on %s",
487 (Entry
->Title
!= NULL
) ? Entry
->Title
: FileName
, Entry
->VolName
);
488 SubScreen
->TitleImage
= Entry
->me
.Image
;
490 SubEntry
= InitializeLoaderEntry(Entry
);
491 if (SubEntry
!= NULL
) {
492 SubEntry
->me
.Title
= L
"Boot using default options";
493 if ((SubEntry
->InitrdPath
!= NULL
) && (StrLen(SubEntry
->InitrdPath
) > 0) && (!StriSubCmp(L
"initrd", SubEntry
->LoadOptions
))) {
494 MergeStrings(&Temp
, L
"initrd=", 0);
495 MergeStrings(&Temp
, SubEntry
->InitrdPath
, 0);
496 MergeStrings(&SubEntry
->LoadOptions
, Temp
, L
' ');
499 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
500 } // if (SubEntry != NULL)
501 } // if (SubScreen != NULL)
502 } else { // existing subscreen; less initialization, and just add new entry later....
503 SubScreen
= Entry
->me
.SubScreen
;
506 } // REFIT_MENU_SCREEN *InitializeSubScreen()
508 VOID
GenerateSubScreen(LOADER_ENTRY
*Entry
, IN REFIT_VOLUME
*Volume
) {
509 REFIT_MENU_SCREEN
*SubScreen
;
510 LOADER_ENTRY
*SubEntry
;
511 CHAR16
*InitrdOption
= NULL
, *Temp
;
512 CHAR16 DiagsFileName
[256];
517 // create the submenu
518 if (StrLen(Entry
->Title
) == 0) {
519 MyFreePool(Entry
->Title
);
522 SubScreen
= InitializeSubScreen(Entry
);
524 // loader-specific submenu entries
525 if (Entry
->OSType
== 'M') { // entries for Mac OS X
527 SubEntry
= InitializeLoaderEntry(Entry
);
528 if (SubEntry
!= NULL
) {
529 SubEntry
->me
.Title
= L
"Boot Mac OS X with a 64-bit kernel";
530 SubEntry
->LoadOptions
= L
"arch=x86_64";
531 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
532 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
535 SubEntry
= InitializeLoaderEntry(Entry
);
536 if (SubEntry
!= NULL
) {
537 SubEntry
->me
.Title
= L
"Boot Mac OS X with a 32-bit kernel";
538 SubEntry
->LoadOptions
= L
"arch=i386";
539 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
540 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
544 if (!(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_SINGLEUSER
)) {
545 SubEntry
= InitializeLoaderEntry(Entry
);
546 if (SubEntry
!= NULL
) {
547 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode";
548 SubEntry
->UseGraphicsMode
= FALSE
;
549 SubEntry
->LoadOptions
= L
"-v";
550 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
554 SubEntry
= InitializeLoaderEntry(Entry
);
555 if (SubEntry
!= NULL
) {
556 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode (64-bit)";
557 SubEntry
->UseGraphicsMode
= FALSE
;
558 SubEntry
->LoadOptions
= L
"-v arch=x86_64";
559 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
562 SubEntry
= InitializeLoaderEntry(Entry
);
563 if (SubEntry
!= NULL
) {
564 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode (32-bit)";
565 SubEntry
->UseGraphicsMode
= FALSE
;
566 SubEntry
->LoadOptions
= L
"-v arch=i386";
567 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
571 SubEntry
= InitializeLoaderEntry(Entry
);
572 if (SubEntry
!= NULL
) {
573 SubEntry
->me
.Title
= L
"Boot Mac OS X in single user mode";
574 SubEntry
->UseGraphicsMode
= FALSE
;
575 SubEntry
->LoadOptions
= L
"-v -s";
576 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
580 // check for Apple hardware diagnostics
581 StrCpy(DiagsFileName
, L
"\\System\\Library\\CoreServices\\.diagnostics\\diags.efi");
582 if (FileExists(Volume
->RootDir
, DiagsFileName
) && !(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_HWTEST
)) {
583 SubEntry
= InitializeLoaderEntry(Entry
);
584 if (SubEntry
!= NULL
) {
585 SubEntry
->me
.Title
= L
"Run Apple Hardware Test";
586 MyFreePool(SubEntry
->LoaderPath
);
587 SubEntry
->LoaderPath
= StrDuplicate(DiagsFileName
);
588 SubEntry
->DevicePath
= FileDevicePath(Volume
->DeviceHandle
, SubEntry
->LoaderPath
);
589 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
590 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
592 } // if diagnostics entry found
594 } else if (Entry
->OSType
== 'L') { // entries for Linux kernels with EFI stub loaders
595 File
= ReadLinuxOptionsFile(Entry
->LoaderPath
, Volume
);
597 if ((Temp
= FindInitrd(Entry
->LoaderPath
, Volume
)) != NULL
) {
598 MergeStrings(&InitrdOption
, L
"initrd=", 0);
599 MergeStrings(&InitrdOption
, Temp
, 0);
601 TokenCount
= ReadTokenLine(File
, &TokenList
); // read and discard first entry, since it's
602 FreeTokenLine(&TokenList
, &TokenCount
); // set up by InitializeSubScreen(), earlier....
603 while ((TokenCount
= ReadTokenLine(File
, &TokenList
)) > 1) {
604 SubEntry
= InitializeLoaderEntry(Entry
);
605 SubEntry
->me
.Title
= StrDuplicate(TokenList
[0]);
606 MyFreePool(SubEntry
->LoadOptions
);
607 SubEntry
->LoadOptions
= StrDuplicate(TokenList
[1]);
608 MergeStrings(&SubEntry
->LoadOptions
, InitrdOption
, L
' ');
609 FreeTokenLine(&TokenList
, &TokenCount
);
610 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_LINUX
;
611 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
613 MyFreePool(InitrdOption
);
616 } // if Linux options file exists
618 } else if (Entry
->OSType
== 'E') { // entries for ELILO
619 SubEntry
= InitializeLoaderEntry(Entry
);
620 if (SubEntry
!= NULL
) {
621 SubEntry
->me
.Title
= L
"Run ELILO in interactive mode";
622 SubEntry
->LoadOptions
= L
"-p";
623 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
624 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
627 SubEntry
= InitializeLoaderEntry(Entry
);
628 if (SubEntry
!= NULL
) {
629 SubEntry
->me
.Title
= L
"Boot Linux for a 17\" iMac or a 15\" MacBook Pro (*)";
630 SubEntry
->UseGraphicsMode
= TRUE
;
631 SubEntry
->LoadOptions
= L
"-d 0 i17";
632 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
633 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
636 SubEntry
= InitializeLoaderEntry(Entry
);
637 if (SubEntry
!= NULL
) {
638 SubEntry
->me
.Title
= L
"Boot Linux for a 20\" iMac (*)";
639 SubEntry
->UseGraphicsMode
= TRUE
;
640 SubEntry
->LoadOptions
= L
"-d 0 i20";
641 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
642 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
645 SubEntry
= InitializeLoaderEntry(Entry
);
646 if (SubEntry
!= NULL
) {
647 SubEntry
->me
.Title
= L
"Boot Linux for a Mac Mini (*)";
648 SubEntry
->UseGraphicsMode
= TRUE
;
649 SubEntry
->LoadOptions
= L
"-d 0 mini";
650 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
651 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
654 AddMenuInfoLine(SubScreen
, L
"NOTE: This is an example. Entries");
655 AddMenuInfoLine(SubScreen
, L
"marked with (*) may not work.");
657 } else if (Entry
->OSType
== 'X') { // entries for xom.efi
658 // by default, skip the built-in selection and boot from hard disk only
659 Entry
->LoadOptions
= L
"-s -h";
661 SubEntry
= InitializeLoaderEntry(Entry
);
662 if (SubEntry
!= NULL
) {
663 SubEntry
->me
.Title
= L
"Boot Windows from Hard Disk";
664 SubEntry
->LoadOptions
= L
"-s -h";
665 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
666 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
669 SubEntry
= InitializeLoaderEntry(Entry
);
670 if (SubEntry
!= NULL
) {
671 SubEntry
->me
.Title
= L
"Boot Windows from CD-ROM";
672 SubEntry
->LoadOptions
= L
"-s -c";
673 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
674 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
677 SubEntry
= InitializeLoaderEntry(Entry
);
678 if (SubEntry
!= NULL
) {
679 SubEntry
->me
.Title
= L
"Run XOM in text mode";
680 SubEntry
->UseGraphicsMode
= FALSE
;
681 SubEntry
->LoadOptions
= L
"-v";
682 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
683 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
685 } // entries for xom.efi
686 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
687 Entry
->me
.SubScreen
= SubScreen
;
688 } // VOID GenerateSubScreen()
690 // Returns options for a Linux kernel. Reads them from an options file in the
691 // kernel's directory; and if present, adds an initrd= option for an initial
692 // RAM disk file with the same version number as the kernel file.
693 static CHAR16
* GetMainLinuxOptions(IN CHAR16
* LoaderPath
, IN REFIT_VOLUME
*Volume
) {
694 CHAR16
*Options
= NULL
, *InitrdName
, *InitrdOption
= NULL
;
696 Options
= GetFirstOptionsFromFile(LoaderPath
, Volume
);
697 InitrdName
= FindInitrd(LoaderPath
, Volume
);
698 if (InitrdName
!= NULL
) {
699 MergeStrings(&InitrdOption
, L
"initrd=", 0);
700 MergeStrings(&InitrdOption
, InitrdName
, 0);
702 MergeStrings(&Options
, InitrdOption
, ' ');
703 MyFreePool(InitrdOption
);
704 MyFreePool(InitrdName
);
706 } // static CHAR16 * GetMainLinuxOptions()
708 // Sets a few defaults for a loader entry -- mainly the icon, but also the OS type
709 // code and shortcut letter. For Linux EFI stub loaders, also sets kernel options
710 // that will (with luck) work fairly automatically.
711 VOID
SetLoaderDefaults(LOADER_ENTRY
*Entry
, CHAR16
*LoaderPath
, IN REFIT_VOLUME
*Volume
) {
712 CHAR16 IconFileName
[256];
713 CHAR16
*FileName
, *PathOnly
, *OSIconName
= NULL
, *Temp
;
714 CHAR16 ShortcutLetter
= 0;
716 FileName
= Basename(LoaderPath
);
717 PathOnly
= FindPath(LoaderPath
);
719 // locate a custom icon for the loader
720 StrCpy(IconFileName
, LoaderPath
);
721 ReplaceEfiExtension(IconFileName
, L
".icns");
722 if (FileExists(Volume
->RootDir
, IconFileName
)) {
723 Entry
->me
.Image
= LoadIcns(Volume
->RootDir
, IconFileName
, 128);
724 } else if ((StrLen(PathOnly
) == 0) && (Volume
->VolIconImage
!= NULL
)) {
725 Entry
->me
.Image
= Volume
->VolIconImage
;
726 } // icon matched to loader or volume
728 Temp
= FindLastDirName(LoaderPath
);
729 MergeStrings(&OSIconName
, Temp
, L
',');
731 if (OSIconName
!= NULL
) {
732 ShortcutLetter
= OSIconName
[0];
735 // detect specific loaders
736 if (StriSubCmp(L
"bzImage", LoaderPath
) || StriSubCmp(L
"vmlinuz", LoaderPath
)) {
737 MergeStrings(&OSIconName
, L
"linux", L
',');
739 if (ShortcutLetter
== 0)
740 ShortcutLetter
= 'L';
741 Entry
->LoadOptions
= GetMainLinuxOptions(LoaderPath
, Volume
);
742 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_LINUX
;
743 } else if (StriSubCmp(L
"refit", LoaderPath
)) {
744 MergeStrings(&OSIconName
, L
"refit", L
',');
746 ShortcutLetter
= 'R';
747 } else if (StriCmp(LoaderPath
, MACOSX_LOADER_PATH
) == 0) {
748 if (Volume
->VolIconImage
!= NULL
) { // custom icon file found
749 Entry
->me
.Image
= Volume
->VolIconImage
;
751 MergeStrings(&OSIconName
, L
"mac", L
',');
753 ShortcutLetter
= 'M';
754 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
755 } else if (StriCmp(FileName
, L
"diags.efi") == 0) {
756 MergeStrings(&OSIconName
, L
"hwtest", L
',');
757 } else if (StriCmp(FileName
, L
"e.efi") == 0 || StriCmp(FileName
, L
"elilo.efi") == 0) {
758 MergeStrings(&OSIconName
, L
"elilo,linux", L
',');
760 if (ShortcutLetter
== 0)
761 ShortcutLetter
= 'L';
762 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
763 } else if (StriSubCmp(L
"grub", FileName
)) {
765 ShortcutLetter
= 'G';
766 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_GRUB
;
767 } else if (StriCmp(FileName
, L
"cdboot.efi") == 0 ||
768 StriCmp(FileName
, L
"bootmgr.efi") == 0 ||
769 StriCmp(FileName
, L
"Bootmgfw.efi") == 0) {
770 MergeStrings(&OSIconName
, L
"win", L
',');
772 ShortcutLetter
= 'W';
773 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
774 } else if (StriCmp(FileName
, L
"xom.efi") == 0) {
775 MergeStrings(&OSIconName
, L
"xom,win", L
',');
776 Entry
->UseGraphicsMode
= TRUE
;
778 ShortcutLetter
= 'W';
779 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
782 if ((ShortcutLetter
>= 'a') && (ShortcutLetter
<= 'z'))
783 ShortcutLetter
= ShortcutLetter
- 'a' + 'A'; // convert lowercase to uppercase
784 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
785 if (Entry
->me
.Image
== NULL
)
786 Entry
->me
.Image
= LoadOSIcon(OSIconName
, L
"unknown", FALSE
);
787 MyFreePool(PathOnly
);
788 } // VOID SetLoaderDefaults()
790 // Add a specified EFI boot loader to the list, using automatic settings
791 // for icons, options, etc.
792 LOADER_ENTRY
* AddLoaderEntry(IN CHAR16
*LoaderPath
, IN CHAR16
*LoaderTitle
, IN REFIT_VOLUME
*Volume
) {
795 CleanUpPathNameSlashes(LoaderPath
);
796 Entry
= InitializeLoaderEntry(NULL
);
798 Entry
->Title
= StrDuplicate((LoaderTitle
!= NULL
) ? LoaderTitle
: LoaderPath
);
799 Entry
->me
.Title
= AllocateZeroPool(sizeof(CHAR16
) * 256);
800 SPrint(Entry
->me
.Title
, 255, L
"Boot %s from %s", (LoaderTitle
!= NULL
) ? LoaderTitle
: LoaderPath
, Volume
->VolName
);
802 Entry
->me
.BadgeImage
= Volume
->VolBadgeImage
;
803 if ((LoaderPath
!= NULL
) && (LoaderPath
[0] != L
'\\')) {
804 Entry
->LoaderPath
= StrDuplicate(L
"\\");
806 Entry
->LoaderPath
= NULL
;
808 MergeStrings(&(Entry
->LoaderPath
), LoaderPath
, 0);
809 Entry
->VolName
= Volume
->VolName
;
810 Entry
->DevicePath
= FileDevicePath(Volume
->DeviceHandle
, Entry
->LoaderPath
);
811 SetLoaderDefaults(Entry
, LoaderPath
, Volume
);
812 GenerateSubScreen(Entry
, Volume
);
813 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
817 } // LOADER_ENTRY * AddLoaderEntry()
819 // Returns -1 if (Time1 < Time2), +1 if (Time1 > Time2), or 0 if
820 // (Time1 == Time2). Precision is only to the nearest second; since
821 // this is used for sorting boot loader entries, differences smaller
822 // than this are likely to be meaningless (and unlikely!).
823 INTN
TimeComp(EFI_TIME
*Time1
, EFI_TIME
*Time2
) {
824 INT64 Time1InSeconds
, Time2InSeconds
;
826 // Following values are overestimates; I'm assuming 31 days in every month.
827 // This is fine for the purpose of this function, which has a limited
829 Time1InSeconds
= Time1
->Second
+ (Time1
->Minute
* 60) + (Time1
->Hour
* 3600) + (Time1
->Day
* 86400) +
830 (Time1
->Month
* 2678400) + ((Time1
->Year
- 1998) * 32140800);
831 Time2InSeconds
= Time2
->Second
+ (Time2
->Minute
* 60) + (Time2
->Hour
* 3600) + (Time2
->Day
* 86400) +
832 (Time2
->Month
* 2678400) + ((Time2
->Year
- 1998) * 32140800);
833 if (Time1InSeconds
< Time2InSeconds
)
835 else if (Time1InSeconds
> Time2InSeconds
)
841 // Adds a loader list element, keeping it sorted by date. Returns the new
842 // first element (the one with the most recent date).
843 static struct LOADER_LIST
* AddLoaderListEntry(struct LOADER_LIST
*LoaderList
, struct LOADER_LIST
*NewEntry
) {
844 struct LOADER_LIST
*LatestEntry
, *CurrentEntry
, *PrevEntry
= NULL
;
846 LatestEntry
= CurrentEntry
= LoaderList
;
847 if (LoaderList
== NULL
) {
848 LatestEntry
= NewEntry
;
850 while ((CurrentEntry
!= NULL
) && (TimeComp(&(NewEntry
->TimeStamp
), &(CurrentEntry
->TimeStamp
)) < 0)) {
851 PrevEntry
= CurrentEntry
;
852 CurrentEntry
= CurrentEntry
->NextEntry
;
854 NewEntry
->NextEntry
= CurrentEntry
;
855 if (PrevEntry
== NULL
) {
856 LatestEntry
= NewEntry
;
858 PrevEntry
->NextEntry
= NewEntry
;
861 return (LatestEntry
);
862 } // static VOID AddLoaderListEntry()
864 // Delete the LOADER_LIST linked list
865 static VOID
CleanUpLoaderList(struct LOADER_LIST
*LoaderList
) {
866 struct LOADER_LIST
*Temp
;
868 while (LoaderList
!= NULL
) {
870 LoaderList
= LoaderList
->NextEntry
;
871 MyFreePool(Temp
->FileName
);
874 } // static VOID CleanUpLoaderList()
876 // Scan an individual directory for EFI boot loader files and, if found,
877 // add them to the list. Sorts the entries within the loader directory
878 // so that the most recent one appears first in the list.
879 static VOID
ScanLoaderDir(IN REFIT_VOLUME
*Volume
, IN CHAR16
*Path
, IN CHAR16
*Pattern
)
882 REFIT_DIR_ITER DirIter
;
883 EFI_FILE_INFO
*DirEntry
;
884 CHAR16 FileName
[256], *Extension
;
885 struct LOADER_LIST
*LoaderList
= NULL
, *NewLoader
;
887 if ((!SelfDirPath
|| !Path
|| ((StriCmp(Path
, SelfDirPath
) == 0) && Volume
->DeviceHandle
!= SelfVolume
->DeviceHandle
) ||
888 (StriCmp(Path
, SelfDirPath
) != 0)) && (!IsIn(Path
, GlobalConfig
.DontScanDirs
))) {
889 // look through contents of the directory
890 DirIterOpen(Volume
->RootDir
, Path
, &DirIter
);
891 while (DirIterNext(&DirIter
, 2, Pattern
, &DirEntry
)) {
892 Extension
= FindExtension(DirEntry
->FileName
);
893 if (DirEntry
->FileName
[0] == '.' ||
894 StriCmp(Extension
, L
".icns") == 0 ||
895 StriSubCmp(L
"shell", DirEntry
->FileName
) ||
896 IsIn(DirEntry
->FileName
, GlobalConfig
.DontScanFiles
))
897 continue; // skip this
900 SPrint(FileName
, 255, L
"\\%s\\%s", Path
, DirEntry
->FileName
);
902 SPrint(FileName
, 255, L
"\\%s", DirEntry
->FileName
);
903 CleanUpPathNameSlashes(FileName
);
904 NewLoader
= AllocateZeroPool(sizeof(struct LOADER_LIST
));
905 if (NewLoader
!= NULL
) {
906 NewLoader
->FileName
= StrDuplicate(FileName
);
907 NewLoader
->TimeStamp
= DirEntry
->ModificationTime
;
908 LoaderList
= AddLoaderListEntry(LoaderList
, NewLoader
);
910 MyFreePool(Extension
);
912 NewLoader
= LoaderList
;
913 while (NewLoader
!= NULL
) {
914 AddLoaderEntry(NewLoader
->FileName
, NULL
, Volume
);
915 NewLoader
= NewLoader
->NextEntry
;
917 CleanUpLoaderList(LoaderList
);
918 Status
= DirIterClose(&DirIter
);
919 if (Status
!= EFI_NOT_FOUND
) {
921 SPrint(FileName
, 255, L
"while scanning the %s directory", Path
);
923 StrCpy(FileName
, L
"while scanning the root directory");
924 CheckError(Status
, FileName
);
925 } // if (Status != EFI_NOT_FOUND)
926 } // if not scanning our own directory
927 } /* static VOID ScanLoaderDir() */
929 static VOID
ScanEfiFiles(REFIT_VOLUME
*Volume
) {
931 REFIT_DIR_ITER EfiDirIter
;
932 EFI_FILE_INFO
*EfiDirEntry
;
933 CHAR16 FileName
[256], *Directory
, *MatchPatterns
;
936 MatchPatterns
= StrDuplicate(LOADER_MATCH_PATTERNS
);
937 if (GlobalConfig
.ScanAllLinux
)
938 MergeStrings(&MatchPatterns
, LINUX_MATCH_PATTERNS
, L
',');
940 if ((Volume
->RootDir
!= NULL
) && (Volume
->VolName
!= NULL
)) {
941 // check for Mac OS X boot loader
942 if (!IsIn(L
"\\System\\Library\\CoreServices", GlobalConfig
.DontScanDirs
)) {
943 StrCpy(FileName
, MACOSX_LOADER_PATH
);
944 if (FileExists(Volume
->RootDir
, FileName
) && !IsIn(L
"boot.efi", GlobalConfig
.DontScanFiles
)) {
945 AddLoaderEntry(FileName
, L
"Mac OS X", Volume
);
949 StrCpy(FileName
, L
"\\System\\Library\\CoreServices\\xom.efi");
950 if (FileExists(Volume
->RootDir
, FileName
) && !IsIn(L
"boot.efi", GlobalConfig
.DontScanFiles
)) {
951 AddLoaderEntry(FileName
, L
"Windows XP (XoM)", Volume
);
953 } // if Mac directory not in GlobalConfig.DontScanDirs list
955 // check for Microsoft boot loader/menu
956 StrCpy(FileName
, L
"\\EFI\\Microsoft\\Boot\\Bootmgfw.efi");
957 if (FileExists(Volume
->RootDir
, FileName
) && !IsIn(L
"\\EFI\\Microsoft\\Boot", GlobalConfig
.DontScanDirs
) &&
958 !IsIn(L
"bootmgfw.efi", GlobalConfig
.DontScanFiles
)) {
959 AddLoaderEntry(FileName
, L
"Microsoft EFI boot", Volume
);
962 // scan the root directory for EFI executables
963 ScanLoaderDir(Volume
, L
"\\", MatchPatterns
);
965 // scan subdirectories of the EFI directory (as per the standard)
966 DirIterOpen(Volume
->RootDir
, L
"EFI", &EfiDirIter
);
967 while (DirIterNext(&EfiDirIter
, 1, NULL
, &EfiDirEntry
)) {
968 if (StriCmp(EfiDirEntry
->FileName
, L
"tools") == 0 || EfiDirEntry
->FileName
[0] == '.')
969 continue; // skip this, doesn't contain boot loaders
970 SPrint(FileName
, 255, L
"\\EFI\\%s", EfiDirEntry
->FileName
);
971 ScanLoaderDir(Volume
, FileName
, MatchPatterns
);
973 Status
= DirIterClose(&EfiDirIter
);
974 if (Status
!= EFI_NOT_FOUND
)
975 CheckError(Status
, L
"while scanning the EFI directory");
977 // Scan user-specified (or additional default) directories....
979 while ((Directory
= FindCommaDelimited(GlobalConfig
.AlsoScan
, i
++)) != NULL
) {
980 CleanUpPathNameSlashes(Directory
);
981 Length
= StrLen(Directory
);
983 ScanLoaderDir(Volume
, Directory
, MatchPatterns
);
984 MyFreePool(Directory
);
987 } // static VOID ScanEfiFiles()
989 // Scan internal disks for valid EFI boot loaders....
990 static VOID
ScanInternal(VOID
) {
993 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
994 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_INTERNAL
) {
995 ScanEfiFiles(Volumes
[VolumeIndex
]);
998 } // static VOID ScanInternal()
1000 // Scan external disks for valid EFI boot loaders....
1001 static VOID
ScanExternal(VOID
) {
1004 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1005 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_EXTERNAL
) {
1006 ScanEfiFiles(Volumes
[VolumeIndex
]);
1009 } // static VOID ScanExternal()
1011 // Scan internal disks for valid EFI boot loaders....
1012 static VOID
ScanOptical(VOID
) {
1015 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1016 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_OPTICAL
) {
1017 ScanEfiFiles(Volumes
[VolumeIndex
]);
1020 } // static VOID ScanOptical()
1023 // legacy boot functions
1026 static EFI_STATUS
ActivateMbrPartition(IN EFI_BLOCK_IO
*BlockIO
, IN UINTN PartitionIndex
)
1029 UINT8 SectorBuffer
[512];
1030 MBR_PARTITION_INFO
*MbrTable
, *EMbrTable
;
1031 UINT32 ExtBase
, ExtCurrent
, NextExtCurrent
;
1032 UINTN LogicalPartitionIndex
= 4;
1034 BOOLEAN HaveBootCode
;
1037 Status
= refit_call5_wrapper(BlockIO
->ReadBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, 0, 512, SectorBuffer
);
1038 if (EFI_ERROR(Status
))
1040 if (*((UINT16
*)(SectorBuffer
+ 510)) != 0xaa55)
1041 return EFI_NOT_FOUND
; // safety measure #1
1043 // add boot code if necessary
1044 HaveBootCode
= FALSE
;
1045 for (i
= 0; i
< MBR_BOOTCODE_SIZE
; i
++) {
1046 if (SectorBuffer
[i
] != 0) {
1047 HaveBootCode
= TRUE
;
1051 if (!HaveBootCode
) {
1052 // no boot code found in the MBR, add the syslinux MBR code
1053 SetMem(SectorBuffer
, MBR_BOOTCODE_SIZE
, 0);
1054 CopyMem(SectorBuffer
, syslinux_mbr
, SYSLINUX_MBR_SIZE
);
1057 // set the partition active
1058 MbrTable
= (MBR_PARTITION_INFO
*)(SectorBuffer
+ 446);
1060 for (i
= 0; i
< 4; i
++) {
1061 if (MbrTable
[i
].Flags
!= 0x00 && MbrTable
[i
].Flags
!= 0x80)
1062 return EFI_NOT_FOUND
; // safety measure #2
1063 if (i
== PartitionIndex
)
1064 MbrTable
[i
].Flags
= 0x80;
1065 else if (PartitionIndex
>= 4 && IS_EXTENDED_PART_TYPE(MbrTable
[i
].Type
)) {
1066 MbrTable
[i
].Flags
= 0x80;
1067 ExtBase
= MbrTable
[i
].StartLBA
;
1069 MbrTable
[i
].Flags
= 0x00;
1073 Status
= refit_call5_wrapper(BlockIO
->WriteBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, 0, 512, SectorBuffer
);
1074 if (EFI_ERROR(Status
))
1077 if (PartitionIndex
>= 4) {
1078 // we have to activate a logical partition, so walk the EMBR chain
1080 // NOTE: ExtBase was set above while looking at the MBR table
1081 for (ExtCurrent
= ExtBase
; ExtCurrent
; ExtCurrent
= NextExtCurrent
) {
1082 // read current EMBR
1083 Status
= refit_call5_wrapper(BlockIO
->ReadBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, ExtCurrent
, 512, SectorBuffer
);
1084 if (EFI_ERROR(Status
))
1086 if (*((UINT16
*)(SectorBuffer
+ 510)) != 0xaa55)
1087 return EFI_NOT_FOUND
; // safety measure #3
1089 // scan EMBR, set appropriate partition active
1090 EMbrTable
= (MBR_PARTITION_INFO
*)(SectorBuffer
+ 446);
1092 for (i
= 0; i
< 4; i
++) {
1093 if (EMbrTable
[i
].Flags
!= 0x00 && EMbrTable
[i
].Flags
!= 0x80)
1094 return EFI_NOT_FOUND
; // safety measure #4
1095 if (EMbrTable
[i
].StartLBA
== 0 || EMbrTable
[i
].Size
== 0)
1097 if (IS_EXTENDED_PART_TYPE(EMbrTable
[i
].Type
)) {
1098 // link to next EMBR
1099 NextExtCurrent
= ExtBase
+ EMbrTable
[i
].StartLBA
;
1100 EMbrTable
[i
].Flags
= (PartitionIndex
>= LogicalPartitionIndex
) ? 0x80 : 0x00;
1103 // logical partition
1104 EMbrTable
[i
].Flags
= (PartitionIndex
== LogicalPartitionIndex
) ? 0x80 : 0x00;
1105 LogicalPartitionIndex
++;
1109 // write current EMBR
1110 Status
= refit_call5_wrapper(BlockIO
->WriteBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, ExtCurrent
, 512, SectorBuffer
);
1111 if (EFI_ERROR(Status
))
1114 if (PartitionIndex
< LogicalPartitionIndex
)
1115 break; // stop the loop, no need to touch further EMBRs
1121 } /* static EFI_STATUS ActivateMbrPartition() */
1123 // early 2006 Core Duo / Core Solo models
1124 static UINT8 LegacyLoaderDevicePath1Data
[] = {
1125 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1126 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1127 0xFF, 0xFF, 0xF9, 0xFF, 0x00, 0x00, 0x00, 0x00,
1128 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1129 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1130 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1132 // mid-2006 Mac Pro (and probably other Core 2 models)
1133 static UINT8 LegacyLoaderDevicePath2Data
[] = {
1134 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1135 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1136 0xFF, 0xFF, 0xF7, 0xFF, 0x00, 0x00, 0x00, 0x00,
1137 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1138 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1139 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1141 // mid-2007 MBP ("Santa Rosa" based models)
1142 static UINT8 LegacyLoaderDevicePath3Data
[] = {
1143 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1144 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1145 0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
1146 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1147 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1148 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1151 static UINT8 LegacyLoaderDevicePath4Data
[] = {
1152 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1153 0x00, 0x00, 0xC0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1154 0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
1155 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1156 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1157 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1159 // late-2008 MB/MBP (NVidia chipset)
1160 static UINT8 LegacyLoaderDevicePath5Data
[] = {
1161 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1162 0x00, 0x40, 0xCB, 0xFF, 0x00, 0x00, 0x00, 0x00,
1163 0xFF, 0xBF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
1164 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1165 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1166 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1169 static EFI_DEVICE_PATH
*LegacyLoaderList
[] = {
1170 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath1Data
,
1171 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath2Data
,
1172 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath3Data
,
1173 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath4Data
,
1174 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath5Data
,
1178 #define MAX_DISCOVERED_PATHS (16)
1180 static VOID
StartLegacy(IN LEGACY_ENTRY
*Entry
)
1183 EG_IMAGE
*BootLogoImage
;
1184 UINTN ErrorInStep
= 0;
1185 EFI_DEVICE_PATH
*DiscoveredPathList
[MAX_DISCOVERED_PATHS
];
1187 BeginExternalScreen(TRUE
, L
"Booting Legacy OS (Mac mode)");
1189 BootLogoImage
= LoadOSIcon(Entry
->Volume
->OSIconName
, L
"legacy", TRUE
);
1190 if (BootLogoImage
!= NULL
)
1191 BltImageAlpha(BootLogoImage
,
1192 (UGAWidth
- BootLogoImage
->Width
) >> 1,
1193 (UGAHeight
- BootLogoImage
->Height
) >> 1,
1194 &StdBackgroundPixel
);
1196 if (Entry
->Volume
->IsMbrPartition
) {
1197 ActivateMbrPartition(Entry
->Volume
->WholeDiskBlockIO
, Entry
->Volume
->MbrPartitionIndex
);
1200 ExtractLegacyLoaderPaths(DiscoveredPathList
, MAX_DISCOVERED_PATHS
, LegacyLoaderList
);
1202 Status
= StartEFIImageList(DiscoveredPathList
, Entry
->LoadOptions
, NULL
, L
"legacy loader", 0, &ErrorInStep
, TRUE
);
1203 if (Status
== EFI_NOT_FOUND
) {
1204 if (ErrorInStep
== 1) {
1205 Print(L
"\nPlease make sure that you have the latest firmware update installed.\n");
1206 } else if (ErrorInStep
== 3) {
1207 Print(L
"\nThe firmware refused to boot from the selected volume. Note that external\n"
1208 L
"hard drives are not well-supported by Apple's firmware for legacy OS booting.\n");
1211 FinishExternalScreen();
1212 } /* static VOID StartLegacy() */
1214 // Start a device on a non-Mac using the EFI_LEGACY_BIOS_PROTOCOL
1215 #ifdef __MAKEWITH_TIANO
1216 static VOID
StartLegacyUEFI(IN LEGACY_ENTRY
*Entry
)
1218 BeginExternalScreen(TRUE
, L
"Booting Legacy OS (UEFI mode)");
1220 BdsLibConnectDevicePath (Entry
->BdsOption
->DevicePath
);
1221 BdsLibDoLegacyBoot(Entry
->BdsOption
);
1223 // If we get here, it means that there was a failure....
1224 Print(L
"Failure booting legacy (BIOS) OS.");
1226 FinishExternalScreen();
1227 } // static VOID StartLegacyUEFI()
1228 #endif // __MAKEWITH_TIANO
1230 static LEGACY_ENTRY
* AddLegacyEntry(IN CHAR16
*LoaderTitle
, IN REFIT_VOLUME
*Volume
)
1232 LEGACY_ENTRY
*Entry
, *SubEntry
;
1233 REFIT_MENU_SCREEN
*SubScreen
;
1235 CHAR16 ShortcutLetter
= 0;
1237 if (LoaderTitle
== NULL
) {
1238 if (Volume
->OSName
!= NULL
) {
1239 LoaderTitle
= Volume
->OSName
;
1240 if (LoaderTitle
[0] == 'W' || LoaderTitle
[0] == 'L')
1241 ShortcutLetter
= LoaderTitle
[0];
1243 LoaderTitle
= L
"Legacy OS";
1245 if (Volume
->VolName
!= NULL
)
1246 VolDesc
= Volume
->VolName
;
1248 VolDesc
= (Volume
->DiskKind
== DISK_KIND_OPTICAL
) ? L
"CD" : L
"HD";
1250 // prepare the menu entry
1251 Entry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1252 Entry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1253 SPrint(Entry
->me
.Title
, 255, L
"Boot %s from %s", LoaderTitle
, VolDesc
);
1254 Entry
->me
.Tag
= TAG_LEGACY
;
1256 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1257 Entry
->me
.Image
= LoadOSIcon(Volume
->OSIconName
, L
"legacy", FALSE
);
1258 Entry
->me
.BadgeImage
= Volume
->VolBadgeImage
;
1259 Entry
->Volume
= Volume
;
1260 Entry
->LoadOptions
= (Volume
->DiskKind
== DISK_KIND_OPTICAL
) ? L
"CD" :
1261 ((Volume
->DiskKind
== DISK_KIND_EXTERNAL
) ? L
"USB" : L
"HD");
1262 Entry
->Enabled
= TRUE
;
1264 // create the submenu
1265 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
1266 SubScreen
->Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1267 SPrint(SubScreen
->Title
, 255, L
"Boot Options for %s on %s", LoaderTitle
, VolDesc
);
1268 SubScreen
->TitleImage
= Entry
->me
.Image
;
1271 SubEntry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1272 SubEntry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1273 SPrint(SubEntry
->me
.Title
, 255, L
"Boot %s", LoaderTitle
);
1274 SubEntry
->me
.Tag
= TAG_LEGACY
;
1275 SubEntry
->Volume
= Entry
->Volume
;
1276 SubEntry
->LoadOptions
= Entry
->LoadOptions
;
1277 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
1279 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
1280 Entry
->me
.SubScreen
= SubScreen
;
1281 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1283 } /* static LEGACY_ENTRY * AddLegacyEntry() */
1286 #ifdef __MAKEWITH_TIANO
1287 // default volume badge icon based on disk kind
1288 static EG_IMAGE
* GetDiskBadge(IN UINTN DiskType
) {
1289 EG_IMAGE
* Badge
= NULL
;
1293 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_INTERNAL
);
1296 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_EXTERNAL
);
1299 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_OPTICAL
);
1303 } // static EG_IMAGE * GetDiskBadge()
1306 Create a rEFInd boot option from a Legacy BIOS protocol option.
1308 static LEGACY_ENTRY
* AddLegacyEntryUEFI(BDS_COMMON_OPTION
*BdsOption
, IN UINT16 DiskType
)
1310 LEGACY_ENTRY
*Entry
, *SubEntry
;
1311 REFIT_MENU_SCREEN
*SubScreen
;
1312 CHAR16 ShortcutLetter
= 0;
1313 CHAR16
*LegacyDescription
= BdsOption
->Description
;
1315 // ScanVolume(Volume);
1317 // prepare the menu entry
1318 Entry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1319 Entry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1320 SPrint(Entry
->me
.Title
, 255, L
"Boot legacy target %s", LegacyDescription
);
1321 Entry
->me
.Tag
= TAG_LEGACY_UEFI
;
1323 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1324 Entry
->me
.Image
= LoadOSIcon(L
"legacy", L
"legacy", TRUE
);
1325 Entry
->LoadOptions
= (DiskType
== BBS_CDROM
) ? L
"CD" :
1326 ((DiskType
== BBS_USB
) ? L
"USB" : L
"HD");
1327 Entry
->me
.BadgeImage
= GetDiskBadge(DiskType
);
1328 // Entry->me.BadgeImage = Volume->VolBadgeImage;
1329 Entry
->BdsOption
= BdsOption
;
1330 Entry
->Enabled
= TRUE
;
1332 // create the submenu
1333 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
1334 SubScreen
->Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1335 SPrint(SubScreen
->Title
, 255, L
"No boot options for legacy target");
1336 SubScreen
->TitleImage
= Entry
->me
.Image
;
1339 SubEntry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1340 SubEntry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1341 SPrint(SubEntry
->me
.Title
, 255, L
"Boot %s", LegacyDescription
);
1342 SubEntry
->me
.Tag
= TAG_LEGACY_UEFI
;
1343 Entry
->BdsOption
= BdsOption
;
1344 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
1346 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
1347 Entry
->me
.SubScreen
= SubScreen
;
1348 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1350 } /* static LEGACY_ENTRY * AddLegacyEntryUEFI() */
1353 Scan for legacy BIOS targets on machines that implement EFI_LEGACY_BIOS_PROTOCOL.
1354 In testing, protocol has not been implemented on Macs but has been
1355 implemented on several Dell PCs and an ASUS motherboard.
1356 Restricts output to disks of the specified DiskType.
1358 static VOID
ScanLegacyUEFI(IN UINTN DiskType
)
1361 EFI_LEGACY_BIOS_PROTOCOL
*LegacyBios
;
1362 UINT16
*BootOrder
= NULL
;
1364 CHAR16 BootOption
[10];
1365 UINTN BootOrderSize
= 0;
1367 BDS_COMMON_OPTION
*BdsOption
;
1368 LIST_ENTRY TempList
;
1369 BBS_BBS_DEVICE_PATH
* BbsDevicePath
= NULL
;
1370 // REFIT_VOLUME Volume;
1372 InitializeListHead (&TempList
);
1373 ZeroMem (Buffer
, sizeof (Buffer
));
1375 // If LegacyBios protocol is not implemented on this platform, then
1376 //we do not support this type of legacy boot on this machine.
1377 Status
= gBS
->LocateProtocol(&gEfiLegacyBootProtocolGuid
, NULL
, (VOID
**) &LegacyBios
);
1378 if (EFI_ERROR (Status
))
1381 // Grab the boot order
1382 BootOrder
= BdsLibGetVariableAndSize(L
"BootOrder", &gEfiGlobalVariableGuid
, &BootOrderSize
);
1383 if (BootOrder
== NULL
) {
1388 while (Index
< BootOrderSize
/ sizeof (UINT16
))
1390 // Grab each boot option variable from the boot order, and convert
1391 // the variable into a BDS boot option
1392 UnicodeSPrint (BootOption
, sizeof (BootOption
), L
"Boot%04x", BootOrder
[Index
]);
1393 BdsOption
= BdsLibVariableToOption (&TempList
, BootOption
);
1395 if (BdsOption
!= NULL
) {
1396 BbsDevicePath
= (BBS_BBS_DEVICE_PATH
*)BdsOption
->DevicePath
;
1398 // Only add the entry if it is of a requested type (e.g. USB, HD)
1400 // Two checks necessary because some systems return EFI boot loaders
1401 // with a DeviceType value that would inappropriately include them
1402 // as legacy loaders....
1403 if ((BbsDevicePath
->DeviceType
== DiskType
) && (BdsOption
->DevicePath
->Type
== DEVICE_TYPE_BIOS
)) {
1404 AddLegacyEntryUEFI(BdsOption
, BbsDevicePath
->DeviceType
);
1409 } /* static VOID ScanLegacyUEFI() */
1411 static VOID
ScanLegacyUEFI(IN UINTN DiskType
){}
1412 #endif // __MAKEWITH_TIANO
1414 static VOID
ScanLegacyVolume(REFIT_VOLUME
*Volume
, UINTN VolumeIndex
) {
1416 BOOLEAN ShowVolume
, HideIfOthersFound
;
1419 HideIfOthersFound
= FALSE
;
1420 if (Volume
->IsAppleLegacy
) {
1422 HideIfOthersFound
= TRUE
;
1423 } else if (Volume
->HasBootCode
) {
1425 if (Volume
->BlockIO
== Volume
->WholeDiskBlockIO
&&
1426 Volume
->BlockIOOffset
== 0 &&
1427 Volume
->OSName
== NULL
)
1428 // this is a whole disk (MBR) entry; hide if we have entries for partitions
1429 HideIfOthersFound
= TRUE
;
1431 if (HideIfOthersFound
) {
1432 // check for other bootable entries on the same disk
1433 for (VolumeIndex2
= 0; VolumeIndex2
< VolumesCount
; VolumeIndex2
++) {
1434 if (VolumeIndex2
!= VolumeIndex
&& Volumes
[VolumeIndex2
]->HasBootCode
&&
1435 Volumes
[VolumeIndex2
]->WholeDiskBlockIO
== Volume
->WholeDiskBlockIO
)
1441 AddLegacyEntry(NULL
, Volume
);
1442 } // static VOID ScanLegacyVolume()
1444 // Scan attached optical discs for legacy (BIOS) boot code
1445 // and add anything found to the list....
1446 static VOID
ScanLegacyDisc(VOID
)
1449 REFIT_VOLUME
*Volume
;
1451 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
) {
1452 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1453 Volume
= Volumes
[VolumeIndex
];
1454 if (Volume
->DiskKind
== DISK_KIND_OPTICAL
)
1455 ScanLegacyVolume(Volume
, VolumeIndex
);
1457 } else if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
1458 ScanLegacyUEFI(BBS_CDROM
);
1460 } /* static VOID ScanLegacyDisc() */
1462 // Scan internal hard disks for legacy (BIOS) boot code
1463 // and add anything found to the list....
1464 static VOID
ScanLegacyInternal(VOID
)
1467 REFIT_VOLUME
*Volume
;
1469 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
) {
1470 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1471 Volume
= Volumes
[VolumeIndex
];
1472 if (Volume
->DiskKind
== DISK_KIND_INTERNAL
)
1473 ScanLegacyVolume(Volume
, VolumeIndex
);
1475 } else if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
1476 ScanLegacyUEFI(BBS_HARDDISK
);
1478 } /* static VOID ScanLegacyInternal() */
1480 // Scan external disks for legacy (BIOS) boot code
1481 // and add anything found to the list....
1482 static VOID
ScanLegacyExternal(VOID
)
1485 REFIT_VOLUME
*Volume
;
1487 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
) {
1488 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1489 Volume
= Volumes
[VolumeIndex
];
1490 if (Volume
->DiskKind
== DISK_KIND_EXTERNAL
)
1491 ScanLegacyVolume(Volume
, VolumeIndex
);
1493 } else if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
1494 ScanLegacyUEFI(BBS_USB
);
1496 } /* static VOID ScanLegacyExternal() */
1499 // pre-boot tool functions
1502 static VOID
StartTool(IN LOADER_ENTRY
*Entry
)
1504 BeginExternalScreen(Entry
->UseGraphicsMode
, Entry
->me
.Title
+ 6); // assumes "Start <title>" as assigned below
1505 StartEFIImage(Entry
->DevicePath
, Entry
->LoadOptions
, Basename(Entry
->LoaderPath
),
1506 Basename(Entry
->LoaderPath
), Entry
->OSType
, NULL
, TRUE
);
1507 FinishExternalScreen();
1508 } /* static VOID StartTool() */
1510 static LOADER_ENTRY
* AddToolEntry(EFI_HANDLE DeviceHandle
, IN CHAR16
*LoaderPath
, IN CHAR16
*LoaderTitle
, IN EG_IMAGE
*Image
,
1511 IN CHAR16 ShortcutLetter
, IN BOOLEAN UseGraphicsMode
)
1513 LOADER_ENTRY
*Entry
;
1514 CHAR16
*TitleStr
= NULL
;
1516 Entry
= AllocateZeroPool(sizeof(LOADER_ENTRY
));
1518 MergeStrings(&TitleStr
, L
"Start ", 0);
1519 MergeStrings(&TitleStr
, LoaderTitle
, 0);
1520 Entry
->me
.Title
= TitleStr
;
1521 Entry
->me
.Tag
= TAG_TOOL
;
1523 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1524 Entry
->me
.Image
= Image
;
1525 Entry
->LoaderPath
= (LoaderPath
) ? StrDuplicate(LoaderPath
) : NULL
;
1526 Entry
->DevicePath
= FileDevicePath(DeviceHandle
, Entry
->LoaderPath
);
1527 Entry
->UseGraphicsMode
= UseGraphicsMode
;
1529 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1531 } /* static LOADER_ENTRY * AddToolEntry() */
1534 // pre-boot driver functions
1537 static UINTN
ScanDriverDir(IN CHAR16
*Path
)
1540 REFIT_DIR_ITER DirIter
;
1542 EFI_FILE_INFO
*DirEntry
;
1543 CHAR16 FileName
[256];
1545 CleanUpPathNameSlashes(Path
);
1546 // look through contents of the directory
1547 DirIterOpen(SelfRootDir
, Path
, &DirIter
);
1548 while (DirIterNext(&DirIter
, 2, LOADER_MATCH_PATTERNS
, &DirEntry
)) {
1549 if (DirEntry
->FileName
[0] == '.')
1550 continue; // skip this
1552 SPrint(FileName
, 255, L
"%s\\%s", Path
, DirEntry
->FileName
);
1554 Status
= StartEFIImage(FileDevicePath(SelfLoadedImage
->DeviceHandle
, FileName
),
1555 L
"", DirEntry
->FileName
, DirEntry
->FileName
, 0, NULL
, FALSE
);
1557 Status
= DirIterClose(&DirIter
);
1558 if (Status
!= EFI_NOT_FOUND
) {
1559 SPrint(FileName
, 255, L
"while scanning the %s directory", Path
);
1560 CheckError(Status
, FileName
);
1565 #ifdef __MAKEWITH_GNUEFI
1566 static EFI_STATUS
ConnectAllDriversToAllControllers(VOID
)
1569 UINTN AllHandleCount
;
1570 EFI_HANDLE
*AllHandleBuffer
;
1573 EFI_HANDLE
*HandleBuffer
;
1579 Status
= LibLocateHandle(AllHandles
,
1584 if (EFI_ERROR(Status
))
1587 for (Index
= 0; Index
< AllHandleCount
; Index
++) {
1589 // Scan the handle database
1591 Status
= LibScanHandleDatabase(NULL
,
1593 AllHandleBuffer
[Index
],
1598 if (EFI_ERROR (Status
))
1602 if (HandleType
[Index
] & EFI_HANDLE_TYPE_DRIVER_BINDING_HANDLE
)
1604 if (HandleType
[Index
] & EFI_HANDLE_TYPE_IMAGE_HANDLE
)
1609 for (HandleIndex
= 0; HandleIndex
< HandleCount
; HandleIndex
++) {
1610 if (HandleType
[HandleIndex
] & EFI_HANDLE_TYPE_PARENT_HANDLE
)
1615 if (HandleType
[Index
] & EFI_HANDLE_TYPE_DEVICE_HANDLE
) {
1616 Status
= refit_call4_wrapper(BS
->ConnectController
,
1617 AllHandleBuffer
[Index
],
1625 MyFreePool (HandleBuffer
);
1626 MyFreePool (HandleType
);
1630 MyFreePool (AllHandleBuffer
);
1632 } /* EFI_STATUS ConnectAllDriversToAllControllers() */
1634 static EFI_STATUS
ConnectAllDriversToAllControllers(VOID
) {
1635 BdsLibConnectAllDriversToAllControllers();
1640 // Load all EFI drivers from rEFInd's "drivers" subdirectory and from the
1641 // directories specified by the user in the "scan_driver_dirs" configuration
1643 static VOID
LoadDrivers(VOID
)
1645 CHAR16
*Directory
, *SelfDirectory
;
1646 UINTN i
= 0, Length
, NumFound
= 0;
1648 // load drivers from the subdirectories of rEFInd's home directory specified
1649 // in the DRIVER_DIRS constant.
1650 while ((Directory
= FindCommaDelimited(DRIVER_DIRS
, i
++)) != NULL
) {
1651 SelfDirectory
= SelfDirPath
? StrDuplicate(SelfDirPath
) : NULL
;
1652 CleanUpPathNameSlashes(SelfDirectory
);
1653 MergeStrings(&SelfDirectory
, Directory
, L
'\\');
1654 NumFound
+= ScanDriverDir(SelfDirectory
);
1655 MyFreePool(Directory
);
1656 MyFreePool(SelfDirectory
);
1659 // Scan additional user-specified driver directories....
1661 while ((Directory
= FindCommaDelimited(GlobalConfig
.DriverDirs
, i
++)) != NULL
) {
1662 CleanUpPathNameSlashes(Directory
);
1663 Length
= StrLen(Directory
);
1665 NumFound
+= ScanDriverDir(Directory
);
1667 MyFreePool(Directory
);
1670 // connect all devices
1672 ConnectAllDriversToAllControllers();
1673 } /* static VOID LoadDrivers() */
1675 // Determine what (if any) type of legacy (BIOS) boot support is available
1676 static VOID
FindLegacyBootType(VOID
) {
1677 #ifdef __MAKEWITH_TIANO
1679 EFI_LEGACY_BIOS_PROTOCOL
*LegacyBios
;
1682 GlobalConfig
.LegacyType
= LEGACY_TYPE_NONE
;
1684 // UEFI-style legacy BIOS support is available only with the TianoCore EDK2
1685 // build environment, and then only with some implementations....
1686 #ifdef __MAKEWITH_TIANO
1687 Status
= gBS
->LocateProtocol (&gEfiLegacyBootProtocolGuid
, NULL
, (VOID
**) &LegacyBios
);
1688 if (!EFI_ERROR (Status
))
1689 GlobalConfig
.LegacyType
= LEGACY_TYPE_UEFI
;
1692 // Macs have their own system. If the firmware vendor code contains the
1693 // string "Apple", assume it's available. Note that this overrides the
1694 // UEFI type, and might yield false positives if the vendor string
1695 // contains "Apple" as part of something bigger, so this isn't 100%
1697 if (StriSubCmp(L
"Apple", ST
->FirmwareVendor
))
1698 GlobalConfig
.LegacyType
= LEGACY_TYPE_MAC
;
1699 } // static VOID FindLegacyBootType
1701 // Warn the user if legacy OS scans are enabled but the firmware or this
1702 // application can't support them....
1703 static VOID
WarnIfLegacyProblems() {
1704 BOOLEAN found
= FALSE
;
1707 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_NONE
) {
1709 if (GlobalConfig
.ScanFor
[i
] == 'h' || GlobalConfig
.ScanFor
[i
] == 'b' || GlobalConfig
.ScanFor
[i
] == 'c')
1712 } while ((i
< NUM_SCAN_OPTIONS
) && (!found
));
1714 Print(L
"NOTE: refind.conf's 'scanfor' line specifies scanning for one or more legacy\n");
1715 Print(L
"(BIOS) boot options; however, this is not possible because ");
1716 #ifdef __MAKEWITH_TIANO
1717 Print(L
"your computer lacks\n");
1718 Print(L
"the necessary Compatibility Support Module (CSM) support.\n");
1720 Print(L
"this program was\n");
1721 Print(L
"compiled without the necessary support. Please visit\n");
1722 Print(L
"http://www.rodsbooks.com/refind/getting.html and download and install a rEFInd\n");
1723 Print(L
"binary built with the TianoCore EDK2 to enable legacy boot support.\n");
1727 } // if no legacy support
1728 } // static VOID WarnIfLegacyProblems()
1730 // Locates boot loaders. NOTE: This assumes that GlobalConfig.LegacyType is set correctly.
1731 static VOID
ScanForBootloaders(VOID
) {
1736 // scan for loaders and tools, add them to the menu
1737 for (i
= 0; i
< NUM_SCAN_OPTIONS
; i
++) {
1738 switch(GlobalConfig
.ScanFor
[i
]) {
1743 ScanLegacyInternal();
1746 ScanLegacyExternal();
1749 ScanUserConfigured();
1763 // assign shortcut keys
1764 for (i
= 0; i
< MainMenu
.EntryCount
&& MainMenu
.Entries
[i
]->Row
== 0 && i
< 9; i
++)
1765 MainMenu
.Entries
[i
]->ShortcutDigit
= (CHAR16
)('1' + i
);
1767 // wait for user ACK when there were errors
1768 FinishTextScreen(FALSE
);
1769 } // static VOID ScanForBootloaders()
1771 // Add the second-row tags containing built-in and external tools (EFI shell,
1773 static VOID
ScanForTools(VOID
) {
1774 CHAR16
*FileName
= NULL
, Description
[256];
1775 REFIT_MENU_ENTRY
*TempMenuEntry
;
1776 UINTN i
, j
, VolumeIndex
;
1778 for (i
= 0; i
< NUM_TOOLS
; i
++) {
1779 switch(GlobalConfig
.ShowTools
[i
]) {
1781 TempMenuEntry
= CopyMenuEntry(&MenuEntryShutdown
);
1782 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_SHUTDOWN
);
1783 AddMenuEntry(&MainMenu
, TempMenuEntry
);
1786 TempMenuEntry
= CopyMenuEntry(&MenuEntryReset
);
1787 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_RESET
);
1788 AddMenuEntry(&MainMenu
, TempMenuEntry
);
1791 TempMenuEntry
= CopyMenuEntry(&MenuEntryAbout
);
1792 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT
);
1793 AddMenuEntry(&MainMenu
, TempMenuEntry
);
1796 TempMenuEntry
= CopyMenuEntry(&MenuEntryExit
);
1797 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_EXIT
);
1798 AddMenuEntry(&MainMenu
, TempMenuEntry
);
1802 MyFreePool(FileName
);
1803 while ((FileName
= FindCommaDelimited(SHELL_NAMES
, j
++)) != NULL
) {
1804 if (FileExists(SelfRootDir
, FileName
)) {
1805 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, L
"EFI Shell", BuiltinIcon(BUILTIN_ICON_TOOL_SHELL
),
1811 MyFreePool(FileName
);
1813 MergeStrings(&FileName
, L
"\\efi\\tools\\gptsync.efi", 0);
1814 if (FileExists(SelfRootDir
, FileName
)) {
1815 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, L
"Make Hybrid MBR", BuiltinIcon(BUILTIN_ICON_TOOL_PART
), 'P', FALSE
);
1818 case TAG_APPLE_RECOVERY
:
1819 MyFreePool(FileName
);
1821 MergeStrings(&FileName
, L
"\\com.apple.recovery.boot\\boot.efi", 0);
1822 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1823 if ((Volumes
[VolumeIndex
]->RootDir
!= NULL
) && (FileExists(Volumes
[VolumeIndex
]->RootDir
, FileName
))) {
1824 SPrint(Description
, 255, L
"Apple Recovery on %s", Volumes
[VolumeIndex
]->VolName
);
1825 AddToolEntry(Volumes
[VolumeIndex
]->DeviceHandle
, FileName
, Description
,
1826 BuiltinIcon(BUILTIN_ICON_TOOL_APPLE_RESCUE
), 'R', TRUE
);
1832 MyFreePool(FileName
);
1833 while ((FileName
= FindCommaDelimited(MOK_NAMES
, j
++)) != NULL
) {
1834 if (FileExists(SelfRootDir
, FileName
)) {
1835 SPrint(Description
, 255, L
"MOK Key Manager at %s", FileName
);
1836 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, Description
,
1837 BuiltinIcon(BUILTIN_ICON_TOOL_MOK_TOOL
), 'S', FALSE
);
1840 if (FileExists(SelfDir
, L
"MokManager.efi")) {
1841 MyFreePool(FileName
);
1842 FileName
= StrDuplicate(SelfDirPath
);
1843 MergeStrings(&FileName
, L
"\\MokManager.efi", 0);
1844 SPrint(Description
, 255, L
"MOK Key Manager at %s", FileName
);
1845 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, Description
,
1846 BuiltinIcon(BUILTIN_ICON_TOOL_MOK_TOOL
), 'S', FALSE
);
1850 MyFreePool(FileName
);
1853 } // static VOID ScanForTools
1855 // Rescan for boot loaders
1856 VOID
RescanAll(VOID
) {
1863 egDisplayMessage(L
"Scanning for new boot loaders; please wait....", &BGColor
);
1864 FreeList((VOID
***) &(MainMenu
.Entries
), &MainMenu
.EntryCount
);
1865 MainMenu
.Entries
= NULL
;
1866 MainMenu
.EntryCount
= 0;
1868 ConnectAllDriversToAllControllers();
1870 ScanForBootloaders();
1873 } // VOID RescanAll()
1875 #ifndef __MAKEWITH_GNUEFI
1877 // Minimal initialization function
1878 static VOID
InitializeLib(IN EFI_HANDLE ImageHandle
, IN EFI_SYSTEM_TABLE
*SystemTable
) {
1880 // gImageHandle = ImageHandle;
1881 gBS
= SystemTable
->BootServices
;
1882 // gRS = SystemTable->RuntimeServices;
1883 gRT
= SystemTable
->RuntimeServices
; // Some BDS functions need gRT to be set
1884 EfiGetSystemConfigurationTable (&gEfiDxeServicesTableGuid
, (VOID
**) &gDS
);
1886 InitializeConsoleSim();
1896 efi_main (IN EFI_HANDLE ImageHandle
, IN EFI_SYSTEM_TABLE
*SystemTable
)
1899 BOOLEAN MainLoopRunning
= TRUE
;
1900 REFIT_MENU_ENTRY
*ChosenEntry
;
1906 InitializeLib(ImageHandle
, SystemTable
);
1908 Status
= InitRefitLib(ImageHandle
);
1909 if (EFI_ERROR(Status
))
1912 // read configuration
1913 CopyMem(GlobalConfig
.ScanFor
, "ieom ", NUM_SCAN_OPTIONS
);
1914 FindLegacyBootType();
1915 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
)
1916 CopyMem(GlobalConfig
.ScanFor
, "ihebocm ", NUM_SCAN_OPTIONS
);
1918 WarnIfLegacyProblems();
1919 MainMenu
.TimeoutSeconds
= GlobalConfig
.Timeout
;
1921 // disable EFI watchdog timer
1922 refit_call4_wrapper(BS
->SetWatchdogTimer
, 0x0000, 0x0000, 0x0000, NULL
);
1924 // further bootstrap (now with config available)
1928 ScanForBootloaders();
1931 if (GlobalConfig
.ScanDelay
> 0) {
1936 egDisplayMessage(L
"Pausing before disk scan; please wait....", &BGColor
);
1937 for (i
= 0; i
< GlobalConfig
.ScanDelay
; i
++)
1938 refit_call1_wrapper(BS
->Stall
, 1000000);
1942 Selection
= StrDuplicate(GlobalConfig
.DefaultSelection
);
1943 while (MainLoopRunning
) {
1944 MenuExit
= RunMainMenu(&MainMenu
, Selection
, &ChosenEntry
);
1946 // The Escape key triggers a re-scan operation....
1947 if (MenuExit
== MENU_EXIT_ESCAPE
) {
1952 switch (ChosenEntry
->Tag
) {
1954 case TAG_REBOOT
: // Reboot
1956 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);
1957 MainLoopRunning
= FALSE
; // just in case we get this far
1960 case TAG_SHUTDOWN
: // Shut Down
1962 refit_call4_wrapper(RT
->ResetSystem
, EfiResetShutdown
, EFI_SUCCESS
, 0, NULL
);
1963 MainLoopRunning
= FALSE
; // just in case we get this far
1966 case TAG_ABOUT
: // About rEFInd
1970 case TAG_LOADER
: // Boot OS via .EFI loader
1971 StartLoader((LOADER_ENTRY
*)ChosenEntry
);
1974 case TAG_LEGACY
: // Boot legacy OS
1975 StartLegacy((LEGACY_ENTRY
*)ChosenEntry
);
1978 #ifdef __MAKEWITH_TIANO
1979 case TAG_LEGACY_UEFI
: // Boot a legacy OS on a non-Mac
1980 StartLegacyUEFI((LEGACY_ENTRY
*)ChosenEntry
);
1982 #endif // __MAKEWITH_TIANO
1984 case TAG_TOOL
: // Start a EFI tool
1985 StartTool((LOADER_ENTRY
*)ChosenEntry
);
1988 case TAG_EXIT
: // Terminate rEFInd
1989 BeginTextScreen(L
" ");
1994 MyFreePool(Selection
);
1995 Selection
= (ChosenEntry
->Title
) ? StrDuplicate(ChosenEntry
->Title
) : NULL
;
1998 // If we end up here, things have gone wrong. Try to reboot, and if that
1999 // fails, go into an endless loop.
2000 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);