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,\\EFI\\tools\\shellx64.efi,\\shellx64.efi"
67 #define DRIVER_DIRS L"drivers,drivers_x64"
69 #define SHELL_NAMES L"\\EFI\\tools\\shell.efi,\\EFI\\tools\shellia32.efi,\\shellia32.efi"
70 #define DRIVER_DIRS L"drivers,drivers_ia32"
72 #define SHELL_NAMES L"\\EFI\\tools\\shell.efi"
73 #define DRIVER_DIRS L"drivers"
76 #define MOK_NAMES L"\\EFI\\tools\\MokManager.efi,\\EFI\\redhat\\MokManager.efi,\\EFI\\ubuntu\\MokManager.efi,\\EFI\\suse\\MokManager"
78 // Filename patterns that identify EFI boot loaders. Note that a single case (either L"*.efi" or
79 // L"*.EFI") is fine for most systems; but Gigabyte's buggy Hybrid EFI does a case-sensitive
80 // comparison when it should do a case-insensitive comparison, so I'm doubling this up. It does
81 // no harm on other computers, AFAIK. In theory, every case variation should be done for
82 // completeness, but that's ridiculous....
83 #define LOADER_MATCH_PATTERNS L"*.efi,*.EFI"
85 // Patterns that identify Linux kernels. Added to the loader match pattern when the
86 // scan_all_linux_kernels option is set in the configuration file. Causes kernels WITHOUT
87 // a ".efi" extension to be found when scanning for boot loaders.
88 #define LINUX_MATCH_PATTERNS L"vmlinuz*,bzImage*"
90 static REFIT_MENU_ENTRY MenuEntryAbout
= { L
"About rEFInd", TAG_ABOUT
, 1, 0, 'A', NULL
, NULL
, NULL
};
91 static REFIT_MENU_ENTRY MenuEntryReset
= { L
"Reboot Computer", TAG_REBOOT
, 1, 0, 'R', NULL
, NULL
, NULL
};
92 static REFIT_MENU_ENTRY MenuEntryShutdown
= { L
"Shut Down Computer", TAG_SHUTDOWN
, 1, 0, 'U', NULL
, NULL
, NULL
};
93 static REFIT_MENU_ENTRY MenuEntryReturn
= { L
"Return to Main Menu", TAG_RETURN
, 0, 0, 0, NULL
, NULL
, NULL
};
94 static REFIT_MENU_ENTRY MenuEntryExit
= { L
"Exit rEFInd", TAG_EXIT
, 1, 0, 0, NULL
, NULL
, NULL
};
96 static REFIT_MENU_SCREEN MainMenu
= { L
"Main Menu", NULL
, 0, NULL
, 0, NULL
, 0, L
"Automatic boot" };
97 static REFIT_MENU_SCREEN AboutMenu
= { L
"About", NULL
, 0, NULL
, 0, NULL
, 0, NULL
};
99 REFIT_CONFIG GlobalConfig
= { FALSE
, FALSE
, 0, 0, 0, 20, 0, 0, GRAPHICS_FOR_OSX
, LEGACY_TYPE_MAC
, 0,
100 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, 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.6.0.2");
122 AddMenuInfoLine(&AboutMenu
, L
"");
123 AddMenuInfoLine(&AboutMenu
, L
"Copyright (c) 2006-2010 Christoph Pfisterer");
124 AddMenuInfoLine(&AboutMenu
, L
"Copyright (c) 2012 Roderick W. Smith");
125 AddMenuInfoLine(&AboutMenu
, L
"Portions Copyright (c) Intel Corporation and others");
126 AddMenuInfoLine(&AboutMenu
, L
"Distributed under the terms of the GNU GPLv3 license");
127 AddMenuInfoLine(&AboutMenu
, L
"");
128 AddMenuInfoLine(&AboutMenu
, L
"Running on:");
129 TempStr
= AllocateZeroPool(256 * sizeof(CHAR16
));
130 SPrint(TempStr
, 255, L
" EFI Revision %d.%02d", ST
->Hdr
.Revision
>> 16, ST
->Hdr
.Revision
& ((1 << 16) - 1));
131 AddMenuInfoLine(&AboutMenu
, TempStr
);
133 AddMenuInfoLine(&AboutMenu
, L
" Platform: x86 (32 bit)");
134 #elif defined(EFIX64)
135 TempStr
= AllocateZeroPool(256 * sizeof(CHAR16
));
136 SPrint(TempStr
, 255, L
" Platform: x86_64 (64 bit); Secure Boot %s", secure_mode() ? L
"active" : L
"inactive");
137 AddMenuInfoLine(&AboutMenu
, TempStr
);
139 AddMenuInfoLine(&AboutMenu
, L
" Platform: unknown");
141 TempStr
= AllocateZeroPool(256 * sizeof(CHAR16
));
142 SPrint(TempStr
, 255, L
" Firmware: %s %d.%02d",
143 ST
->FirmwareVendor
, ST
->FirmwareRevision
>> 16, ST
->FirmwareRevision
& ((1 << 16) - 1));
144 AddMenuInfoLine(&AboutMenu
, TempStr
);
145 TempStr
= AllocateZeroPool(256 * sizeof(CHAR16
));
146 SPrint(TempStr
, 255, L
" Screen Output: %s", egScreenDescription());
147 AddMenuInfoLine(&AboutMenu
, TempStr
);
148 AddMenuInfoLine(&AboutMenu
, L
"");
149 #if defined(__MAKEWITH_GNUEFI)
150 AddMenuInfoLine(&AboutMenu
, L
"Built with GNU-EFI");
152 AddMenuInfoLine(&AboutMenu
, L
"Built with TianoCore EDK2");
154 AddMenuInfoLine(&AboutMenu
, L
"");
155 AddMenuInfoLine(&AboutMenu
, L
"For more information, see the rEFInd Web site:");
156 AddMenuInfoLine(&AboutMenu
, L
"http://www.rodsbooks.com/refind/");
157 AddMenuEntry(&AboutMenu
, &MenuEntryReturn
);
160 RunMenu(&AboutMenu
, NULL
);
161 } /* VOID AboutrEFInd() */
163 // Launch an EFI binary.
164 static EFI_STATUS
StartEFIImageList(IN EFI_DEVICE_PATH
**DevicePaths
,
165 IN CHAR16
*LoadOptions
, IN CHAR16
*LoadOptionsPrefix
,
166 IN CHAR16
*ImageTitle
, IN CHAR8 OSType
,
167 OUT UINTN
*ErrorInStep
,
170 EFI_STATUS Status
, ReturnStatus
;
171 EFI_HANDLE ChildImageHandle
;
172 EFI_LOADED_IMAGE
*ChildLoadedImage
= NULL
;
174 VOID
*ImageData
= NULL
;
176 REFIT_VOLUME
*DeviceVolume
= NULL
;
177 UINTN DevicePathIndex
;
178 CHAR16 ErrorInfo
[256];
179 CHAR16
*FullLoadOptions
= NULL
;
180 CHAR16
*loader
= NULL
;
181 BOOLEAN UseMok
= FALSE
;
183 if (ErrorInStep
!= NULL
)
187 if (LoadOptions
!= NULL
) {
188 if (LoadOptionsPrefix
!= NULL
) {
189 // MergeStrings(&FullLoadOptions, LoadOptionsPrefix, 0);
190 MergeStrings(&FullLoadOptions
, LoadOptions
, L
' ');
192 MergeStrings(&FullLoadOptions
, L
" ", 0);
193 // NOTE: That last space is also added by the EFI shell and seems to be significant
194 // when passing options to Apple's boot.efi...
197 MergeStrings(&FullLoadOptions
, LoadOptions
, 0);
199 } else { // LoadOptions == NULL
200 // NOTE: We provide a non-null string when no options are specified for safety;
201 // some systems (at least DUET) can hang when launching some programs (such as
202 // an EFI shell) without this.
203 FullLoadOptions
= StrDuplicate(L
" ");
206 Print(L
"Starting %s\nUsing load options '%s'\n", ImageTitle
, FullLoadOptions
);
208 // load the image into memory (and execute it, in the case of a shim/MOK image).
209 ReturnStatus
= Status
= EFI_NOT_FOUND
; // in case the list is empty
210 for (DevicePathIndex
= 0; DevicePaths
[DevicePathIndex
] != NULL
; DevicePathIndex
++) {
211 // NOTE: Below commented-out line could be more efficient if the ReadFile() and
212 // FindVolumeAndFilename() calls were moved earlier, but it doesn't work on my
213 // 32-bit Mac Mini or my 64-bit Intel box when launching a Linux kernel; the
214 // kernel returns a "Failed to handle fs_proto" error message.
215 // TODO: Track down the cause of this error and fix it, if possible.
216 // ReturnStatus = Status = refit_call6_wrapper(BS->LoadImage, FALSE, SelfImageHandle, DevicePaths[DevicePathIndex],
217 // ImageData, ImageSize, &ChildImageHandle);
218 ReturnStatus
= Status
= refit_call6_wrapper(BS
->LoadImage
, FALSE
, SelfImageHandle
, DevicePaths
[DevicePathIndex
],
219 NULL
, 0, &ChildImageHandle
);
220 if ((Status
== EFI_ACCESS_DENIED
) && (ShimLoaded())) {
221 FindVolumeAndFilename(DevicePaths
[DevicePathIndex
], &DeviceVolume
, &loader
);
222 if (DeviceVolume
!= NULL
) {
223 Status
= ReadFile(DeviceVolume
->RootDir
, loader
, &File
, &ImageSize
);
224 ImageData
= File
.Buffer
;
226 Status
= EFI_NOT_FOUND
;
227 Print(L
"Error: device volume not found!\n");
229 if (Status
!= EFI_NOT_FOUND
) {
230 ReturnStatus
= Status
= start_image(SelfImageHandle
, loader
, ImageData
, ImageSize
, FullLoadOptions
,
231 DeviceVolume
, FileDevicePath(DeviceVolume
->DeviceHandle
, loader
));
232 // ReturnStatus = Status = start_image(SelfImageHandle, loader, ImageData, ImageSize, FullLoadOptions,
233 // DeviceVolume, DevicePaths[DevicePathIndex]);
235 if (ReturnStatus
== EFI_SUCCESS
) {
238 } // if (UEFI SB failed; use shim)
239 if (ReturnStatus
!= EFI_NOT_FOUND
) {
243 SPrint(ErrorInfo
, 255, L
"while loading %s", ImageTitle
);
244 if (CheckError(Status
, ErrorInfo
)) {
245 if (ErrorInStep
!= NULL
)
251 ReturnStatus
= Status
= refit_call3_wrapper(BS
->HandleProtocol
, ChildImageHandle
, &LoadedImageProtocol
,
252 (VOID
**) &ChildLoadedImage
);
253 if (CheckError(Status
, L
"while getting a LoadedImageProtocol handle")) {
254 if (ErrorInStep
!= NULL
)
258 ChildLoadedImage
->LoadOptions
= (VOID
*)FullLoadOptions
;
259 ChildLoadedImage
->LoadOptionsSize
= ((UINT32
)StrLen(FullLoadOptions
) + 1) * sizeof(CHAR16
);
260 // turn control over to the image
261 // TODO: (optionally) re-enable the EFI watchdog timer!
263 // close open file handles
265 ReturnStatus
= Status
= refit_call3_wrapper(BS
->StartImage
, ChildImageHandle
, NULL
, NULL
);
266 // control returns here when the child image calls Exit()
267 SPrint(ErrorInfo
, 255, L
"returned from %s", ImageTitle
);
268 if (CheckError(Status
, ErrorInfo
)) {
269 if (ErrorInStep
!= NULL
)
273 // re-open file handles
278 // unload the image, we don't care if it works or not...
280 Status
= refit_call1_wrapper(BS
->UnloadImage
, ChildImageHandle
);
283 MyFreePool(FullLoadOptions
);
285 } /* static EFI_STATUS StartEFIImageList() */
287 static EFI_STATUS
StartEFIImage(IN EFI_DEVICE_PATH
*DevicePath
,
288 IN CHAR16
*LoadOptions
, IN CHAR16
*LoadOptionsPrefix
,
289 IN CHAR16
*ImageTitle
, IN CHAR8 OSType
,
290 OUT UINTN
*ErrorInStep
,
293 EFI_DEVICE_PATH
*DevicePaths
[2];
295 DevicePaths
[0] = DevicePath
;
296 DevicePaths
[1] = NULL
;
297 return StartEFIImageList(DevicePaths
, LoadOptions
, LoadOptionsPrefix
, ImageTitle
, OSType
, ErrorInStep
, Verbose
);
298 } /* static EFI_STATUS StartEFIImage() */
301 // EFI OS loader functions
304 static VOID
StartLoader(IN LOADER_ENTRY
*Entry
)
306 UINTN ErrorInStep
= 0;
308 BeginExternalScreen(Entry
->UseGraphicsMode
, L
"Booting OS");
309 StartEFIImage(Entry
->DevicePath
, Entry
->LoadOptions
, Basename(Entry
->LoaderPath
),
310 Basename(Entry
->LoaderPath
), Entry
->OSType
, &ErrorInStep
, !Entry
->UseGraphicsMode
);
311 FinishExternalScreen();
314 // Locate an initrd or initramfs file that matches the kernel specified by LoaderPath.
315 // The matching file has a name that begins with "init" and includes the same version
316 // number string as is found in LoaderPath -- but not a longer version number string.
317 // For instance, if LoaderPath is \EFI\kernels\bzImage-3.3.0.efi, and if \EFI\kernels
318 // has a file called initramfs-3.3.0.img, this function will return the string
319 // '\EFI\kernels\initramfs-3.3.0.img'. If the directory ALSO contains the file
320 // initramfs-3.3.0-rc7.img or initramfs-13.3.0.img, those files will NOT match;
321 // however, initmine-3.3.0.img might match. (FindInitrd() returns the first match it
322 // finds). Thus, care should be taken to avoid placing duplicate matching files in
323 // the kernel's directory.
324 // If no matching init file can be found, returns NULL.
325 static CHAR16
* FindInitrd(IN CHAR16
*LoaderPath
, IN REFIT_VOLUME
*Volume
) {
326 CHAR16
*InitrdName
= NULL
, *FileName
, *KernelVersion
, *InitrdVersion
, *Path
;
327 REFIT_DIR_ITER DirIter
;
328 EFI_FILE_INFO
*DirEntry
;
330 FileName
= Basename(LoaderPath
);
331 KernelVersion
= FindNumbers(FileName
);
332 Path
= FindPath(LoaderPath
);
334 // Add trailing backslash for root directory; necessary on some systems, but must
335 // NOT be added to all directories, since on other systems, a trailing backslash on
336 // anything but the root directory causes them to flake out!
337 if (StrLen(Path
) == 0) {
338 MergeStrings(&Path
, L
"\\", 0);
340 DirIterOpen(Volume
->RootDir
, Path
, &DirIter
);
341 // Now add a trailing backslash if it was NOT added earlier, for consistency in
342 // building the InitrdName later....
343 if ((StrLen(Path
) > 0) && (Path
[StrLen(Path
) - 1] != L
'\\'))
344 MergeStrings(&Path
, L
"\\", 0);
345 while ((DirIterNext(&DirIter
, 2, L
"init*", &DirEntry
)) && (InitrdName
== NULL
)) {
346 InitrdVersion
= FindNumbers(DirEntry
->FileName
);
347 if (KernelVersion
!= NULL
) {
348 if (StriCmp(InitrdVersion
, KernelVersion
) == 0) {
349 MergeStrings(&InitrdName
, Path
, 0);
350 MergeStrings(&InitrdName
, DirEntry
->FileName
, 0);
353 if (InitrdVersion
== NULL
) {
354 MergeStrings(&InitrdName
, Path
, 0);
355 MergeStrings(&InitrdName
, DirEntry
->FileName
, 0);
358 MyFreePool(InitrdVersion
);
360 DirIterClose(&DirIter
);
362 // Note: Don't FreePool(FileName), since Basename returns a pointer WITHIN the string it's passed.
363 MyFreePool(KernelVersion
);
366 } // static CHAR16 * FindInitrd()
368 LOADER_ENTRY
* AddPreparedLoaderEntry(LOADER_ENTRY
*Entry
) {
369 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
372 } // LOADER_ENTRY * AddPreparedLoaderEntry()
374 // Creates a copy of a menu screen.
375 // Returns a pointer to the copy of the menu screen.
376 static REFIT_MENU_SCREEN
* CopyMenuScreen(REFIT_MENU_SCREEN
*Entry
) {
377 REFIT_MENU_SCREEN
*NewEntry
;
380 NewEntry
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
381 if ((Entry
!= NULL
) && (NewEntry
!= NULL
)) {
382 CopyMem(NewEntry
, Entry
, sizeof(REFIT_MENU_SCREEN
));
383 NewEntry
->Title
= (Entry
->Title
) ? StrDuplicate(Entry
->Title
) : NULL
;
384 NewEntry
->TimeoutText
= (Entry
->TimeoutText
) ? StrDuplicate(Entry
->TimeoutText
) : NULL
;
385 if (Entry
->TitleImage
!= NULL
) {
386 NewEntry
->TitleImage
= AllocatePool(sizeof(EG_IMAGE
));
387 if (NewEntry
->TitleImage
!= NULL
)
388 CopyMem(NewEntry
->TitleImage
, Entry
->TitleImage
, sizeof(EG_IMAGE
));
390 NewEntry
->InfoLines
= (CHAR16
**) AllocateZeroPool(Entry
->InfoLineCount
* (sizeof(CHAR16
*)));
391 for (i
= 0; i
< Entry
->InfoLineCount
&& NewEntry
->InfoLines
; i
++) {
392 NewEntry
->InfoLines
[i
] = (Entry
->InfoLines
[i
]) ? StrDuplicate(Entry
->InfoLines
[i
]) : NULL
;
394 NewEntry
->Entries
= (REFIT_MENU_ENTRY
**) AllocateZeroPool(Entry
->EntryCount
* (sizeof (REFIT_MENU_ENTRY
*)));
395 for (i
= 0; i
< Entry
->EntryCount
&& NewEntry
->Entries
; i
++) {
396 AddMenuEntry(NewEntry
, Entry
->Entries
[i
]);
400 } // static REFIT_MENU_SCREEN* CopyMenuScreen()
402 // Creates a copy of a menu entry. Intended to enable moving a stack-based
403 // menu entry (such as the ones for the "reboot" and "exit" functions) to
404 // to the heap. This enables easier deletion of the whole set of menu
405 // entries when re-scanning.
406 // Returns a pointer to the copy of the menu entry.
407 static REFIT_MENU_ENTRY
* CopyMenuEntry(REFIT_MENU_ENTRY
*Entry
) {
408 REFIT_MENU_ENTRY
*NewEntry
;
410 NewEntry
= AllocateZeroPool(sizeof(REFIT_MENU_ENTRY
));
411 if ((Entry
!= NULL
) && (NewEntry
!= NULL
)) {
412 CopyMem(NewEntry
, Entry
, sizeof(REFIT_MENU_ENTRY
));
413 NewEntry
->Title
= (Entry
->Title
) ? StrDuplicate(Entry
->Title
) : NULL
;
414 if (Entry
->BadgeImage
!= NULL
) {
415 NewEntry
->BadgeImage
= AllocatePool(sizeof(EG_IMAGE
));
416 if (NewEntry
->BadgeImage
!= NULL
)
417 CopyMem(NewEntry
->BadgeImage
, Entry
->BadgeImage
, sizeof(EG_IMAGE
));
419 if (Entry
->Image
!= NULL
) {
420 NewEntry
->Image
= AllocatePool(sizeof(EG_IMAGE
));
421 if (NewEntry
->Image
!= NULL
)
422 CopyMem(NewEntry
->Image
, Entry
->Image
, sizeof(EG_IMAGE
));
424 if (Entry
->SubScreen
!= NULL
) {
425 NewEntry
->SubScreen
= CopyMenuScreen(Entry
->SubScreen
);
429 } // REFIT_MENU_ENTRY* CopyMenuEntry()
431 // Creates a new LOADER_ENTRY data structure and populates it with
432 // default values from the specified Entry, or NULL values if Entry
433 // is unspecified (NULL).
434 // Returns a pointer to the new data structure, or NULL if it
435 // couldn't be allocated
436 LOADER_ENTRY
*InitializeLoaderEntry(IN LOADER_ENTRY
*Entry
) {
437 LOADER_ENTRY
*NewEntry
= NULL
;
439 NewEntry
= AllocateZeroPool(sizeof(LOADER_ENTRY
));
440 if (NewEntry
!= NULL
) {
441 NewEntry
->me
.Title
= NULL
;
442 NewEntry
->me
.Tag
= TAG_LOADER
;
443 NewEntry
->Enabled
= TRUE
;
444 NewEntry
->UseGraphicsMode
= FALSE
;
445 NewEntry
->OSType
= 0;
447 NewEntry
->LoaderPath
= (Entry
->LoaderPath
) ? StrDuplicate(Entry
->LoaderPath
) : NULL
;
448 NewEntry
->VolName
= (Entry
->VolName
) ? StrDuplicate(Entry
->VolName
) : NULL
;
449 NewEntry
->DevicePath
= Entry
->DevicePath
;
450 NewEntry
->UseGraphicsMode
= Entry
->UseGraphicsMode
;
451 NewEntry
->LoadOptions
= (Entry
->LoadOptions
) ? StrDuplicate(Entry
->LoadOptions
) : NULL
;
452 NewEntry
->InitrdPath
= (Entry
->InitrdPath
) ? StrDuplicate(Entry
->InitrdPath
) : NULL
;
456 } // LOADER_ENTRY *InitializeLoaderEntry()
458 // Adds InitrdPath to Options, but only if Options doesn't already include an
459 // initrd= line. Done to enable overriding the default initrd selection in a
460 // refind_linux.conf file's options list.
461 // Returns a pointer to a new string. The calling function is responsible for
462 // freeing its memory.
463 static CHAR16
*AddInitrdToOptions(CHAR16
*Options
, CHAR16
*InitrdPath
) {
464 CHAR16
*NewOptions
= NULL
;
467 NewOptions
= StrDuplicate(Options
);
468 if ((InitrdPath
!= NULL
) && !StriSubCmp(L
"initrd=", Options
)) {
469 MergeStrings(&NewOptions
, L
"initrd=", L
' ');
470 MergeStrings(&NewOptions
, InitrdPath
, 0);
473 } // CHAR16 *AddInitrdToOptions()
475 // Prepare a REFIT_MENU_SCREEN data structure for a subscreen entry. This sets up
476 // the default entry that launches the boot loader using the same options as the
477 // main Entry does. Subsequent options can be added by the calling function.
478 // If a subscreen already exists in the Entry that's passed to this function,
479 // it's left unchanged and a pointer to it is returned.
480 // Returns a pointer to the new subscreen data structure, or NULL if there
481 // were problems allocating memory.
482 REFIT_MENU_SCREEN
*InitializeSubScreen(IN LOADER_ENTRY
*Entry
) {
483 CHAR16
*FileName
, *MainOptions
= NULL
;
484 REFIT_MENU_SCREEN
*SubScreen
= NULL
;
485 LOADER_ENTRY
*SubEntry
;
487 FileName
= Basename(Entry
->LoaderPath
);
488 if (Entry
->me
.SubScreen
== NULL
) { // No subscreen yet; initialize default entry....
489 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
490 if (SubScreen
!= NULL
) {
491 SubScreen
->Title
= AllocateZeroPool(sizeof(CHAR16
) * 256);
492 SPrint(SubScreen
->Title
, 255, L
"Boot Options for %s on %s",
493 (Entry
->Title
!= NULL
) ? Entry
->Title
: FileName
, Entry
->VolName
);
494 SubScreen
->TitleImage
= Entry
->me
.Image
;
496 SubEntry
= InitializeLoaderEntry(Entry
);
497 if (SubEntry
!= NULL
) {
498 SubEntry
->me
.Title
= StrDuplicate(L
"Boot using default options");
499 MainOptions
= SubEntry
->LoadOptions
;
500 SubEntry
->LoadOptions
= AddInitrdToOptions(MainOptions
, SubEntry
->InitrdPath
);
501 MyFreePool(MainOptions
);
502 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
503 } // if (SubEntry != NULL)
504 } // if (SubScreen != NULL)
505 } else { // existing subscreen; less initialization, and just add new entry later....
506 SubScreen
= Entry
->me
.SubScreen
;
509 } // REFIT_MENU_SCREEN *InitializeSubScreen()
511 VOID
GenerateSubScreen(LOADER_ENTRY
*Entry
, IN REFIT_VOLUME
*Volume
) {
512 REFIT_MENU_SCREEN
*SubScreen
;
513 LOADER_ENTRY
*SubEntry
;
515 CHAR16 DiagsFileName
[256];
520 // create the submenu
521 if (StrLen(Entry
->Title
) == 0) {
522 MyFreePool(Entry
->Title
);
525 SubScreen
= InitializeSubScreen(Entry
);
527 // loader-specific submenu entries
528 if (Entry
->OSType
== 'M') { // entries for Mac OS X
530 SubEntry
= InitializeLoaderEntry(Entry
);
531 if (SubEntry
!= NULL
) {
532 SubEntry
->me
.Title
= L
"Boot Mac OS X with a 64-bit kernel";
533 SubEntry
->LoadOptions
= L
"arch=x86_64";
534 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
535 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
538 SubEntry
= InitializeLoaderEntry(Entry
);
539 if (SubEntry
!= NULL
) {
540 SubEntry
->me
.Title
= L
"Boot Mac OS X with a 32-bit kernel";
541 SubEntry
->LoadOptions
= L
"arch=i386";
542 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
543 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
547 if (!(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_SINGLEUSER
)) {
548 SubEntry
= InitializeLoaderEntry(Entry
);
549 if (SubEntry
!= NULL
) {
550 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode";
551 SubEntry
->UseGraphicsMode
= FALSE
;
552 SubEntry
->LoadOptions
= L
"-v";
553 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
557 SubEntry
= InitializeLoaderEntry(Entry
);
558 if (SubEntry
!= NULL
) {
559 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode (64-bit)";
560 SubEntry
->UseGraphicsMode
= FALSE
;
561 SubEntry
->LoadOptions
= L
"-v arch=x86_64";
562 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
565 SubEntry
= InitializeLoaderEntry(Entry
);
566 if (SubEntry
!= NULL
) {
567 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode (32-bit)";
568 SubEntry
->UseGraphicsMode
= FALSE
;
569 SubEntry
->LoadOptions
= L
"-v arch=i386";
570 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
574 SubEntry
= InitializeLoaderEntry(Entry
);
575 if (SubEntry
!= NULL
) {
576 SubEntry
->me
.Title
= L
"Boot Mac OS X in single user mode";
577 SubEntry
->UseGraphicsMode
= FALSE
;
578 SubEntry
->LoadOptions
= L
"-v -s";
579 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
583 // check for Apple hardware diagnostics
584 StrCpy(DiagsFileName
, L
"System\\Library\\CoreServices\\.diagnostics\\diags.efi");
585 if (FileExists(Volume
->RootDir
, DiagsFileName
) && !(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_HWTEST
)) {
586 SubEntry
= InitializeLoaderEntry(Entry
);
587 if (SubEntry
!= NULL
) {
588 SubEntry
->me
.Title
= L
"Run Apple Hardware Test";
589 MyFreePool(SubEntry
->LoaderPath
);
590 SubEntry
->LoaderPath
= StrDuplicate(DiagsFileName
);
591 SubEntry
->DevicePath
= FileDevicePath(Volume
->DeviceHandle
, SubEntry
->LoaderPath
);
592 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
593 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
595 } // if diagnostics entry found
597 } else if (Entry
->OSType
== 'L') { // entries for Linux kernels with EFI stub loaders
598 File
= ReadLinuxOptionsFile(Entry
->LoaderPath
, Volume
);
600 InitrdName
= FindInitrd(Entry
->LoaderPath
, Volume
);
601 TokenCount
= ReadTokenLine(File
, &TokenList
);
602 // first entry requires special processing, since it was initially set
603 // up with a default title but correct options by InitializeSubScreen(),
605 if ((SubScreen
->Entries
!= NULL
) && (SubScreen
->Entries
[0] != NULL
)) {
606 MyFreePool(SubScreen
->Entries
[0]->Title
);
607 SubScreen
->Entries
[0]->Title
= StrDuplicate(TokenList
[0]);
609 FreeTokenLine(&TokenList
, &TokenCount
);
610 while ((TokenCount
= ReadTokenLine(File
, &TokenList
)) > 1) {
611 SubEntry
= InitializeLoaderEntry(Entry
);
612 SubEntry
->me
.Title
= StrDuplicate(TokenList
[0]);
613 MyFreePool(SubEntry
->LoadOptions
);
614 SubEntry
->LoadOptions
= AddInitrdToOptions(TokenList
[1], InitrdName
);
615 FreeTokenLine(&TokenList
, &TokenCount
);
616 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_LINUX
;
617 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
619 MyFreePool(InitrdName
);
621 } // if Linux options file exists
623 } else if (Entry
->OSType
== 'E') { // entries for ELILO
624 SubEntry
= InitializeLoaderEntry(Entry
);
625 if (SubEntry
!= NULL
) {
626 SubEntry
->me
.Title
= L
"Run ELILO in interactive mode";
627 SubEntry
->LoadOptions
= L
"-p";
628 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
629 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
632 SubEntry
= InitializeLoaderEntry(Entry
);
633 if (SubEntry
!= NULL
) {
634 SubEntry
->me
.Title
= L
"Boot Linux for a 17\" iMac or a 15\" MacBook Pro (*)";
635 SubEntry
->UseGraphicsMode
= TRUE
;
636 SubEntry
->LoadOptions
= L
"-d 0 i17";
637 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
638 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
641 SubEntry
= InitializeLoaderEntry(Entry
);
642 if (SubEntry
!= NULL
) {
643 SubEntry
->me
.Title
= L
"Boot Linux for a 20\" iMac (*)";
644 SubEntry
->UseGraphicsMode
= TRUE
;
645 SubEntry
->LoadOptions
= L
"-d 0 i20";
646 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
647 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
650 SubEntry
= InitializeLoaderEntry(Entry
);
651 if (SubEntry
!= NULL
) {
652 SubEntry
->me
.Title
= L
"Boot Linux for a Mac Mini (*)";
653 SubEntry
->UseGraphicsMode
= TRUE
;
654 SubEntry
->LoadOptions
= L
"-d 0 mini";
655 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
656 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
659 AddMenuInfoLine(SubScreen
, L
"NOTE: This is an example. Entries");
660 AddMenuInfoLine(SubScreen
, L
"marked with (*) may not work.");
662 } else if (Entry
->OSType
== 'X') { // entries for xom.efi
663 // by default, skip the built-in selection and boot from hard disk only
664 Entry
->LoadOptions
= L
"-s -h";
666 SubEntry
= InitializeLoaderEntry(Entry
);
667 if (SubEntry
!= NULL
) {
668 SubEntry
->me
.Title
= L
"Boot Windows from Hard Disk";
669 SubEntry
->LoadOptions
= L
"-s -h";
670 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
671 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
674 SubEntry
= InitializeLoaderEntry(Entry
);
675 if (SubEntry
!= NULL
) {
676 SubEntry
->me
.Title
= L
"Boot Windows from CD-ROM";
677 SubEntry
->LoadOptions
= L
"-s -c";
678 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
679 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
682 SubEntry
= InitializeLoaderEntry(Entry
);
683 if (SubEntry
!= NULL
) {
684 SubEntry
->me
.Title
= L
"Run XOM in text mode";
685 SubEntry
->UseGraphicsMode
= FALSE
;
686 SubEntry
->LoadOptions
= L
"-v";
687 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
688 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
690 } // entries for xom.efi
691 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
692 Entry
->me
.SubScreen
= SubScreen
;
693 } // VOID GenerateSubScreen()
695 // Returns options for a Linux kernel. Reads them from an options file in the
696 // kernel's directory; and if present, adds an initrd= option for an initial
697 // RAM disk file with the same version number as the kernel file.
698 static CHAR16
* GetMainLinuxOptions(IN CHAR16
* LoaderPath
, IN REFIT_VOLUME
*Volume
) {
699 CHAR16
*Options
= NULL
, *InitrdName
, *FullOptions
= NULL
;
701 Options
= GetFirstOptionsFromFile(LoaderPath
, Volume
);
702 InitrdName
= FindInitrd(LoaderPath
, Volume
);
703 FullOptions
= AddInitrdToOptions(Options
, InitrdName
);
706 MyFreePool(InitrdName
);
707 return (FullOptions
);
708 } // static CHAR16 * GetMainLinuxOptions()
710 // Sets a few defaults for a loader entry -- mainly the icon, but also the OS type
711 // code and shortcut letter. For Linux EFI stub loaders, also sets kernel options
712 // that will (with luck) work fairly automatically.
713 VOID
SetLoaderDefaults(LOADER_ENTRY
*Entry
, CHAR16
*LoaderPath
, IN REFIT_VOLUME
*Volume
) {
714 CHAR16 IconFileName
[256];
715 CHAR16
*FileName
, *PathOnly
, *OSIconName
= NULL
, *Temp
;
716 CHAR16 ShortcutLetter
= 0;
718 FileName
= Basename(LoaderPath
);
719 PathOnly
= FindPath(LoaderPath
);
721 // locate a custom icon for the loader
722 StrCpy(IconFileName
, LoaderPath
);
723 ReplaceEfiExtension(IconFileName
, L
".icns");
724 if (FileExists(Volume
->RootDir
, IconFileName
)) {
725 Entry
->me
.Image
= LoadIcns(Volume
->RootDir
, IconFileName
, 128);
726 } else if ((StrLen(PathOnly
) == 0) && (Volume
->VolIconImage
!= NULL
)) {
727 Entry
->me
.Image
= Volume
->VolIconImage
;
728 } // icon matched to loader or volume
730 Temp
= FindLastDirName(LoaderPath
);
731 MergeStrings(&OSIconName
, Temp
, L
',');
734 if (OSIconName
!= NULL
) {
735 ShortcutLetter
= OSIconName
[0];
738 // detect specific loaders
739 if (StriSubCmp(L
"bzImage", LoaderPath
) || StriSubCmp(L
"vmlinuz", LoaderPath
)) {
740 MergeStrings(&OSIconName
, L
"linux", L
',');
742 if (ShortcutLetter
== 0)
743 ShortcutLetter
= 'L';
744 Entry
->LoadOptions
= GetMainLinuxOptions(LoaderPath
, Volume
);
745 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_LINUX
;
746 } else if (StriSubCmp(L
"refit", LoaderPath
)) {
747 MergeStrings(&OSIconName
, L
"refit", L
',');
749 ShortcutLetter
= 'R';
750 } else if (StriCmp(LoaderPath
, MACOSX_LOADER_PATH
) == 0) {
751 if (Volume
->VolIconImage
!= NULL
) { // custom icon file found
752 Entry
->me
.Image
= Volume
->VolIconImage
;
754 MergeStrings(&OSIconName
, L
"mac", L
',');
756 ShortcutLetter
= 'M';
757 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
758 } else if (StriCmp(FileName
, L
"diags.efi") == 0) {
759 MergeStrings(&OSIconName
, L
"hwtest", L
',');
760 } else if (StriCmp(FileName
, L
"e.efi") == 0 || StriCmp(FileName
, L
"elilo.efi") == 0 || StriSubCmp(L
"elilo", FileName
)) {
761 MergeStrings(&OSIconName
, L
"elilo,linux", L
',');
763 if (ShortcutLetter
== 0)
764 ShortcutLetter
= 'L';
765 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
766 } else if (StriSubCmp(L
"grub", FileName
)) {
768 ShortcutLetter
= 'G';
769 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_GRUB
;
770 } else if (StriCmp(FileName
, L
"cdboot.efi") == 0 ||
771 StriCmp(FileName
, L
"bootmgr.efi") == 0 ||
772 StriCmp(FileName
, L
"Bootmgfw.efi") == 0) {
773 MergeStrings(&OSIconName
, L
"win", L
',');
775 ShortcutLetter
= 'W';
776 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
777 } else if (StriCmp(FileName
, L
"xom.efi") == 0) {
778 MergeStrings(&OSIconName
, L
"xom,win", L
',');
779 Entry
->UseGraphicsMode
= TRUE
;
781 ShortcutLetter
= 'W';
782 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
785 if ((ShortcutLetter
>= 'a') && (ShortcutLetter
<= 'z'))
786 ShortcutLetter
= ShortcutLetter
- 'a' + 'A'; // convert lowercase to uppercase
787 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
788 if (Entry
->me
.Image
== NULL
)
789 Entry
->me
.Image
= LoadOSIcon(OSIconName
, L
"unknown", FALSE
);
790 MyFreePool(PathOnly
);
791 } // VOID SetLoaderDefaults()
793 // Add a specified EFI boot loader to the list, using automatic settings
794 // for icons, options, etc.
795 LOADER_ENTRY
* AddLoaderEntry(IN CHAR16
*LoaderPath
, IN CHAR16
*LoaderTitle
, IN REFIT_VOLUME
*Volume
) {
798 CleanUpPathNameSlashes(LoaderPath
);
799 Entry
= InitializeLoaderEntry(NULL
);
801 Entry
->Title
= StrDuplicate((LoaderTitle
!= NULL
) ? LoaderTitle
: LoaderPath
);
802 Entry
->me
.Title
= AllocateZeroPool(sizeof(CHAR16
) * 256);
803 SPrint(Entry
->me
.Title
, 255, L
"Boot %s from %s", (LoaderTitle
!= NULL
) ? LoaderTitle
: LoaderPath
, Volume
->VolName
);
805 Entry
->me
.BadgeImage
= Volume
->VolBadgeImage
;
806 if ((LoaderPath
!= NULL
) && (LoaderPath
[0] != L
'\\')) {
807 Entry
->LoaderPath
= StrDuplicate(L
"\\");
809 Entry
->LoaderPath
= NULL
;
811 MergeStrings(&(Entry
->LoaderPath
), LoaderPath
, 0);
812 Entry
->VolName
= Volume
->VolName
;
813 Entry
->DevicePath
= FileDevicePath(Volume
->DeviceHandle
, Entry
->LoaderPath
);
814 SetLoaderDefaults(Entry
, LoaderPath
, Volume
);
815 GenerateSubScreen(Entry
, Volume
);
816 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
820 } // LOADER_ENTRY * AddLoaderEntry()
822 // Returns -1 if (Time1 < Time2), +1 if (Time1 > Time2), or 0 if
823 // (Time1 == Time2). Precision is only to the nearest second; since
824 // this is used for sorting boot loader entries, differences smaller
825 // than this are likely to be meaningless (and unlikely!).
826 INTN
TimeComp(EFI_TIME
*Time1
, EFI_TIME
*Time2
) {
827 INT64 Time1InSeconds
, Time2InSeconds
;
829 // Following values are overestimates; I'm assuming 31 days in every month.
830 // This is fine for the purpose of this function, which is limited
831 Time1InSeconds
= Time1
->Second
+ (Time1
->Minute
* 60) + (Time1
->Hour
* 3600) + (Time1
->Day
* 86400) +
832 (Time1
->Month
* 2678400) + ((Time1
->Year
- 1998) * 32140800);
833 Time2InSeconds
= Time2
->Second
+ (Time2
->Minute
* 60) + (Time2
->Hour
* 3600) + (Time2
->Day
* 86400) +
834 (Time2
->Month
* 2678400) + ((Time2
->Year
- 1998) * 32140800);
835 if (Time1InSeconds
< Time2InSeconds
)
837 else if (Time1InSeconds
> Time2InSeconds
)
843 // Adds a loader list element, keeping it sorted by date. Returns the new
844 // first element (the one with the most recent date).
845 static struct LOADER_LIST
* AddLoaderListEntry(struct LOADER_LIST
*LoaderList
, struct LOADER_LIST
*NewEntry
) {
846 struct LOADER_LIST
*LatestEntry
, *CurrentEntry
, *PrevEntry
= NULL
;
848 LatestEntry
= CurrentEntry
= LoaderList
;
849 if (LoaderList
== NULL
) {
850 LatestEntry
= NewEntry
;
852 while ((CurrentEntry
!= NULL
) && (TimeComp(&(NewEntry
->TimeStamp
), &(CurrentEntry
->TimeStamp
)) < 0)) {
853 PrevEntry
= CurrentEntry
;
854 CurrentEntry
= CurrentEntry
->NextEntry
;
856 NewEntry
->NextEntry
= CurrentEntry
;
857 if (PrevEntry
== NULL
) {
858 LatestEntry
= NewEntry
;
860 PrevEntry
->NextEntry
= NewEntry
;
863 return (LatestEntry
);
864 } // static VOID AddLoaderListEntry()
866 // Delete the LOADER_LIST linked list
867 static VOID
CleanUpLoaderList(struct LOADER_LIST
*LoaderList
) {
868 struct LOADER_LIST
*Temp
;
870 while (LoaderList
!= NULL
) {
872 LoaderList
= LoaderList
->NextEntry
;
873 MyFreePool(Temp
->FileName
);
876 } // static VOID CleanUpLoaderList()
878 // Scan an individual directory for EFI boot loader files and, if found,
879 // add them to the list. Sorts the entries within the loader directory
880 // so that the most recent one appears first in the list.
881 static VOID
ScanLoaderDir(IN REFIT_VOLUME
*Volume
, IN CHAR16
*Path
, IN CHAR16
*Pattern
)
884 REFIT_DIR_ITER DirIter
;
885 EFI_FILE_INFO
*DirEntry
;
886 CHAR16 FileName
[256], *Extension
;
887 struct LOADER_LIST
*LoaderList
= NULL
, *NewLoader
;
889 if ((!SelfDirPath
|| !Path
|| ((StriCmp(Path
, SelfDirPath
) == 0) && (Volume
->DeviceHandle
!= SelfVolume
->DeviceHandle
)) ||
890 (StriCmp(Path
, SelfDirPath
) != 0)) &&
891 (!IsIn(Path
, GlobalConfig
.DontScanDirs
)) &&
892 (!IsIn(Volume
->VolName
, GlobalConfig
.DontScanVolumes
))) {
893 // look through contents of the directory
894 DirIterOpen(Volume
->RootDir
, Path
, &DirIter
);
895 while (DirIterNext(&DirIter
, 2, Pattern
, &DirEntry
)) {
896 Extension
= FindExtension(DirEntry
->FileName
);
897 if (DirEntry
->FileName
[0] == '.' ||
898 StriCmp(Extension
, L
".icns") == 0 ||
899 StriSubCmp(L
"shell", DirEntry
->FileName
) ||
900 IsIn(DirEntry
->FileName
, GlobalConfig
.DontScanFiles
))
901 continue; // skip this
904 SPrint(FileName
, 255, L
"\\%s\\%s", Path
, DirEntry
->FileName
);
906 SPrint(FileName
, 255, L
"\\%s", DirEntry
->FileName
);
907 CleanUpPathNameSlashes(FileName
);
908 NewLoader
= AllocateZeroPool(sizeof(struct LOADER_LIST
));
909 if (NewLoader
!= NULL
) {
910 NewLoader
->FileName
= StrDuplicate(FileName
);
911 NewLoader
->TimeStamp
= DirEntry
->ModificationTime
;
912 LoaderList
= AddLoaderListEntry(LoaderList
, NewLoader
);
914 MyFreePool(Extension
);
916 NewLoader
= LoaderList
;
917 while (NewLoader
!= NULL
) {
918 AddLoaderEntry(NewLoader
->FileName
, NULL
, Volume
);
919 NewLoader
= NewLoader
->NextEntry
;
921 CleanUpLoaderList(LoaderList
);
922 Status
= DirIterClose(&DirIter
);
923 if (Status
!= EFI_NOT_FOUND
) {
925 SPrint(FileName
, 255, L
"while scanning the %s directory", Path
);
927 StrCpy(FileName
, L
"while scanning the root directory");
928 CheckError(Status
, FileName
);
929 } // if (Status != EFI_NOT_FOUND)
930 } // if not scanning our own directory
931 } /* static VOID ScanLoaderDir() */
933 static VOID
ScanEfiFiles(REFIT_VOLUME
*Volume
) {
935 REFIT_DIR_ITER EfiDirIter
;
936 EFI_FILE_INFO
*EfiDirEntry
;
937 CHAR16 FileName
[256], *Directory
, *MatchPatterns
;
940 MatchPatterns
= StrDuplicate(LOADER_MATCH_PATTERNS
);
941 if (GlobalConfig
.ScanAllLinux
)
942 MergeStrings(&MatchPatterns
, LINUX_MATCH_PATTERNS
, L
',');
944 if ((Volume
->RootDir
!= NULL
) && (Volume
->VolName
!= NULL
)) {
945 // check for Mac OS X boot loader
946 if (!IsIn(L
"System\\Library\\CoreServices", GlobalConfig
.DontScanDirs
)) {
947 StrCpy(FileName
, MACOSX_LOADER_PATH
);
948 if (FileExists(Volume
->RootDir
, FileName
) && !IsIn(L
"boot.efi", GlobalConfig
.DontScanFiles
)) {
949 AddLoaderEntry(FileName
, L
"Mac OS X", Volume
);
953 StrCpy(FileName
, L
"System\\Library\\CoreServices\\xom.efi");
954 if (FileExists(Volume
->RootDir
, FileName
) && !IsIn(L
"boot.efi", GlobalConfig
.DontScanFiles
)) {
955 AddLoaderEntry(FileName
, L
"Windows XP (XoM)", Volume
);
957 } // if Mac directory not in GlobalConfig.DontScanDirs list
959 // check for Microsoft boot loader/menu
960 StrCpy(FileName
, L
"EFI\\Microsoft\\Boot\\Bootmgfw.efi");
961 if (FileExists(Volume
->RootDir
, FileName
) && !IsIn(L
"EFI\\Microsoft\\Boot", GlobalConfig
.DontScanDirs
) &&
962 !IsIn(L
"bootmgfw.efi", GlobalConfig
.DontScanFiles
)) {
963 AddLoaderEntry(FileName
, L
"Microsoft EFI boot", Volume
);
966 // scan the root directory for EFI executables
967 ScanLoaderDir(Volume
, L
"\\", MatchPatterns
);
969 // scan subdirectories of the EFI directory (as per the standard)
970 DirIterOpen(Volume
->RootDir
, L
"EFI", &EfiDirIter
);
971 while (DirIterNext(&EfiDirIter
, 1, NULL
, &EfiDirEntry
)) {
972 if (StriCmp(EfiDirEntry
->FileName
, L
"tools") == 0 || EfiDirEntry
->FileName
[0] == '.')
973 continue; // skip this, doesn't contain boot loaders
974 SPrint(FileName
, 255, L
"EFI\\%s", EfiDirEntry
->FileName
);
975 ScanLoaderDir(Volume
, FileName
, MatchPatterns
);
977 Status
= DirIterClose(&EfiDirIter
);
978 if (Status
!= EFI_NOT_FOUND
)
979 CheckError(Status
, L
"while scanning the EFI directory");
981 // Scan user-specified (or additional default) directories....
983 while ((Directory
= FindCommaDelimited(GlobalConfig
.AlsoScan
, i
++)) != NULL
) {
984 CleanUpPathNameSlashes(Directory
);
985 Length
= StrLen(Directory
);
987 ScanLoaderDir(Volume
, Directory
, MatchPatterns
);
988 MyFreePool(Directory
);
991 } // static VOID ScanEfiFiles()
993 // Scan internal disks for valid EFI boot loaders....
994 static VOID
ScanInternal(VOID
) {
997 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
998 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_INTERNAL
) {
999 ScanEfiFiles(Volumes
[VolumeIndex
]);
1002 } // static VOID ScanInternal()
1004 // Scan external disks for valid EFI boot loaders....
1005 static VOID
ScanExternal(VOID
) {
1008 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1009 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_EXTERNAL
) {
1010 ScanEfiFiles(Volumes
[VolumeIndex
]);
1013 } // static VOID ScanExternal()
1015 // Scan internal disks for valid EFI boot loaders....
1016 static VOID
ScanOptical(VOID
) {
1019 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1020 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_OPTICAL
) {
1021 ScanEfiFiles(Volumes
[VolumeIndex
]);
1024 } // static VOID ScanOptical()
1027 // legacy boot functions
1030 static EFI_STATUS
ActivateMbrPartition(IN EFI_BLOCK_IO
*BlockIO
, IN UINTN PartitionIndex
)
1033 UINT8 SectorBuffer
[512];
1034 MBR_PARTITION_INFO
*MbrTable
, *EMbrTable
;
1035 UINT32 ExtBase
, ExtCurrent
, NextExtCurrent
;
1036 UINTN LogicalPartitionIndex
= 4;
1038 BOOLEAN HaveBootCode
;
1041 Status
= refit_call5_wrapper(BlockIO
->ReadBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, 0, 512, SectorBuffer
);
1042 if (EFI_ERROR(Status
))
1044 if (*((UINT16
*)(SectorBuffer
+ 510)) != 0xaa55)
1045 return EFI_NOT_FOUND
; // safety measure #1
1047 // add boot code if necessary
1048 HaveBootCode
= FALSE
;
1049 for (i
= 0; i
< MBR_BOOTCODE_SIZE
; i
++) {
1050 if (SectorBuffer
[i
] != 0) {
1051 HaveBootCode
= TRUE
;
1055 if (!HaveBootCode
) {
1056 // no boot code found in the MBR, add the syslinux MBR code
1057 SetMem(SectorBuffer
, MBR_BOOTCODE_SIZE
, 0);
1058 CopyMem(SectorBuffer
, syslinux_mbr
, SYSLINUX_MBR_SIZE
);
1061 // set the partition active
1062 MbrTable
= (MBR_PARTITION_INFO
*)(SectorBuffer
+ 446);
1064 for (i
= 0; i
< 4; i
++) {
1065 if (MbrTable
[i
].Flags
!= 0x00 && MbrTable
[i
].Flags
!= 0x80)
1066 return EFI_NOT_FOUND
; // safety measure #2
1067 if (i
== PartitionIndex
)
1068 MbrTable
[i
].Flags
= 0x80;
1069 else if (PartitionIndex
>= 4 && IS_EXTENDED_PART_TYPE(MbrTable
[i
].Type
)) {
1070 MbrTable
[i
].Flags
= 0x80;
1071 ExtBase
= MbrTable
[i
].StartLBA
;
1073 MbrTable
[i
].Flags
= 0x00;
1077 Status
= refit_call5_wrapper(BlockIO
->WriteBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, 0, 512, SectorBuffer
);
1078 if (EFI_ERROR(Status
))
1081 if (PartitionIndex
>= 4) {
1082 // we have to activate a logical partition, so walk the EMBR chain
1084 // NOTE: ExtBase was set above while looking at the MBR table
1085 for (ExtCurrent
= ExtBase
; ExtCurrent
; ExtCurrent
= NextExtCurrent
) {
1086 // read current EMBR
1087 Status
= refit_call5_wrapper(BlockIO
->ReadBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, ExtCurrent
, 512, SectorBuffer
);
1088 if (EFI_ERROR(Status
))
1090 if (*((UINT16
*)(SectorBuffer
+ 510)) != 0xaa55)
1091 return EFI_NOT_FOUND
; // safety measure #3
1093 // scan EMBR, set appropriate partition active
1094 EMbrTable
= (MBR_PARTITION_INFO
*)(SectorBuffer
+ 446);
1096 for (i
= 0; i
< 4; i
++) {
1097 if (EMbrTable
[i
].Flags
!= 0x00 && EMbrTable
[i
].Flags
!= 0x80)
1098 return EFI_NOT_FOUND
; // safety measure #4
1099 if (EMbrTable
[i
].StartLBA
== 0 || EMbrTable
[i
].Size
== 0)
1101 if (IS_EXTENDED_PART_TYPE(EMbrTable
[i
].Type
)) {
1102 // link to next EMBR
1103 NextExtCurrent
= ExtBase
+ EMbrTable
[i
].StartLBA
;
1104 EMbrTable
[i
].Flags
= (PartitionIndex
>= LogicalPartitionIndex
) ? 0x80 : 0x00;
1107 // logical partition
1108 EMbrTable
[i
].Flags
= (PartitionIndex
== LogicalPartitionIndex
) ? 0x80 : 0x00;
1109 LogicalPartitionIndex
++;
1113 // write current EMBR
1114 Status
= refit_call5_wrapper(BlockIO
->WriteBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, ExtCurrent
, 512, SectorBuffer
);
1115 if (EFI_ERROR(Status
))
1118 if (PartitionIndex
< LogicalPartitionIndex
)
1119 break; // stop the loop, no need to touch further EMBRs
1125 } /* static EFI_STATUS ActivateMbrPartition() */
1127 // early 2006 Core Duo / Core Solo models
1128 static UINT8 LegacyLoaderDevicePath1Data
[] = {
1129 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1130 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1131 0xFF, 0xFF, 0xF9, 0xFF, 0x00, 0x00, 0x00, 0x00,
1132 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1133 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1134 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1136 // mid-2006 Mac Pro (and probably other Core 2 models)
1137 static UINT8 LegacyLoaderDevicePath2Data
[] = {
1138 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1139 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1140 0xFF, 0xFF, 0xF7, 0xFF, 0x00, 0x00, 0x00, 0x00,
1141 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1142 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1143 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1145 // mid-2007 MBP ("Santa Rosa" based models)
1146 static UINT8 LegacyLoaderDevicePath3Data
[] = {
1147 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1148 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1149 0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
1150 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1151 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1152 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1155 static UINT8 LegacyLoaderDevicePath4Data
[] = {
1156 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1157 0x00, 0x00, 0xC0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1158 0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
1159 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1160 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1161 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1163 // late-2008 MB/MBP (NVidia chipset)
1164 static UINT8 LegacyLoaderDevicePath5Data
[] = {
1165 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1166 0x00, 0x40, 0xCB, 0xFF, 0x00, 0x00, 0x00, 0x00,
1167 0xFF, 0xBF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
1168 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1169 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1170 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1173 static EFI_DEVICE_PATH
*LegacyLoaderList
[] = {
1174 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath1Data
,
1175 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath2Data
,
1176 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath3Data
,
1177 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath4Data
,
1178 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath5Data
,
1182 #define MAX_DISCOVERED_PATHS (16)
1184 static VOID
StartLegacy(IN LEGACY_ENTRY
*Entry
)
1187 EG_IMAGE
*BootLogoImage
;
1188 UINTN ErrorInStep
= 0;
1189 EFI_DEVICE_PATH
*DiscoveredPathList
[MAX_DISCOVERED_PATHS
];
1191 BeginExternalScreen(TRUE
, L
"Booting Legacy OS (Mac mode)");
1193 BootLogoImage
= LoadOSIcon(Entry
->Volume
->OSIconName
, L
"legacy", TRUE
);
1194 if (BootLogoImage
!= NULL
)
1195 BltImageAlpha(BootLogoImage
,
1196 (UGAWidth
- BootLogoImage
->Width
) >> 1,
1197 (UGAHeight
- BootLogoImage
->Height
) >> 1,
1198 &StdBackgroundPixel
);
1200 if (Entry
->Volume
->IsMbrPartition
) {
1201 ActivateMbrPartition(Entry
->Volume
->WholeDiskBlockIO
, Entry
->Volume
->MbrPartitionIndex
);
1204 ExtractLegacyLoaderPaths(DiscoveredPathList
, MAX_DISCOVERED_PATHS
, LegacyLoaderList
);
1206 Status
= StartEFIImageList(DiscoveredPathList
, Entry
->LoadOptions
, NULL
, L
"legacy loader", 0, &ErrorInStep
, TRUE
);
1207 if (Status
== EFI_NOT_FOUND
) {
1208 if (ErrorInStep
== 1) {
1209 Print(L
"\nPlease make sure that you have the latest firmware update installed.\n");
1210 } else if (ErrorInStep
== 3) {
1211 Print(L
"\nThe firmware refused to boot from the selected volume. Note that external\n"
1212 L
"hard drives are not well-supported by Apple's firmware for legacy OS booting.\n");
1215 FinishExternalScreen();
1216 } /* static VOID StartLegacy() */
1218 // Start a device on a non-Mac using the EFI_LEGACY_BIOS_PROTOCOL
1219 #ifdef __MAKEWITH_TIANO
1220 static VOID
StartLegacyUEFI(IN LEGACY_ENTRY
*Entry
)
1222 BeginExternalScreen(TRUE
, L
"Booting Legacy OS (UEFI mode)");
1224 BdsLibConnectDevicePath (Entry
->BdsOption
->DevicePath
);
1225 BdsLibDoLegacyBoot(Entry
->BdsOption
);
1227 // If we get here, it means that there was a failure....
1228 Print(L
"Failure booting legacy (BIOS) OS.");
1230 FinishExternalScreen();
1231 } // static VOID StartLegacyUEFI()
1232 #endif // __MAKEWITH_TIANO
1234 static LEGACY_ENTRY
* AddLegacyEntry(IN CHAR16
*LoaderTitle
, IN REFIT_VOLUME
*Volume
)
1236 LEGACY_ENTRY
*Entry
, *SubEntry
;
1237 REFIT_MENU_SCREEN
*SubScreen
;
1239 CHAR16 ShortcutLetter
= 0;
1241 if (LoaderTitle
== NULL
) {
1242 if (Volume
->OSName
!= NULL
) {
1243 LoaderTitle
= Volume
->OSName
;
1244 if (LoaderTitle
[0] == 'W' || LoaderTitle
[0] == 'L')
1245 ShortcutLetter
= LoaderTitle
[0];
1247 LoaderTitle
= L
"Legacy OS";
1249 if (Volume
->VolName
!= NULL
)
1250 VolDesc
= Volume
->VolName
;
1252 VolDesc
= (Volume
->DiskKind
== DISK_KIND_OPTICAL
) ? L
"CD" : L
"HD";
1254 // prepare the menu entry
1255 Entry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1256 Entry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1257 SPrint(Entry
->me
.Title
, 255, L
"Boot %s from %s", LoaderTitle
, VolDesc
);
1258 Entry
->me
.Tag
= TAG_LEGACY
;
1260 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1261 Entry
->me
.Image
= LoadOSIcon(Volume
->OSIconName
, L
"legacy", FALSE
);
1262 Entry
->me
.BadgeImage
= Volume
->VolBadgeImage
;
1263 Entry
->Volume
= Volume
;
1264 Entry
->LoadOptions
= (Volume
->DiskKind
== DISK_KIND_OPTICAL
) ? L
"CD" :
1265 ((Volume
->DiskKind
== DISK_KIND_EXTERNAL
) ? L
"USB" : L
"HD");
1266 Entry
->Enabled
= TRUE
;
1268 // create the submenu
1269 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
1270 SubScreen
->Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1271 SPrint(SubScreen
->Title
, 255, L
"Boot Options for %s on %s", LoaderTitle
, VolDesc
);
1272 SubScreen
->TitleImage
= Entry
->me
.Image
;
1275 SubEntry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1276 SubEntry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1277 SPrint(SubEntry
->me
.Title
, 255, L
"Boot %s", LoaderTitle
);
1278 SubEntry
->me
.Tag
= TAG_LEGACY
;
1279 SubEntry
->Volume
= Entry
->Volume
;
1280 SubEntry
->LoadOptions
= Entry
->LoadOptions
;
1281 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
1283 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
1284 Entry
->me
.SubScreen
= SubScreen
;
1285 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1287 } /* static LEGACY_ENTRY * AddLegacyEntry() */
1290 #ifdef __MAKEWITH_TIANO
1291 // default volume badge icon based on disk kind
1292 static EG_IMAGE
* GetDiskBadge(IN UINTN DiskType
) {
1293 EG_IMAGE
* Badge
= NULL
;
1297 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_INTERNAL
);
1300 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_EXTERNAL
);
1303 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_OPTICAL
);
1307 } // static EG_IMAGE * GetDiskBadge()
1310 Create a rEFInd boot option from a Legacy BIOS protocol option.
1312 static LEGACY_ENTRY
* AddLegacyEntryUEFI(BDS_COMMON_OPTION
*BdsOption
, IN UINT16 DiskType
)
1314 LEGACY_ENTRY
*Entry
, *SubEntry
;
1315 REFIT_MENU_SCREEN
*SubScreen
;
1316 CHAR16 ShortcutLetter
= 0;
1317 CHAR16
*LegacyDescription
= BdsOption
->Description
;
1319 // ScanVolume(Volume);
1321 // prepare the menu entry
1322 Entry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1323 Entry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1324 SPrint(Entry
->me
.Title
, 255, L
"Boot legacy target %s", LegacyDescription
);
1325 Entry
->me
.Tag
= TAG_LEGACY_UEFI
;
1327 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1328 Entry
->me
.Image
= LoadOSIcon(L
"legacy", L
"legacy", TRUE
);
1329 Entry
->LoadOptions
= (DiskType
== BBS_CDROM
) ? L
"CD" :
1330 ((DiskType
== BBS_USB
) ? L
"USB" : L
"HD");
1331 Entry
->me
.BadgeImage
= GetDiskBadge(DiskType
);
1332 // Entry->me.BadgeImage = Volume->VolBadgeImage;
1333 Entry
->BdsOption
= BdsOption
;
1334 Entry
->Enabled
= TRUE
;
1336 // create the submenu
1337 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
1338 SubScreen
->Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1339 SPrint(SubScreen
->Title
, 255, L
"No boot options for legacy target");
1340 SubScreen
->TitleImage
= Entry
->me
.Image
;
1343 SubEntry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1344 SubEntry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1345 SPrint(SubEntry
->me
.Title
, 255, L
"Boot %s", LegacyDescription
);
1346 SubEntry
->me
.Tag
= TAG_LEGACY_UEFI
;
1347 Entry
->BdsOption
= BdsOption
;
1348 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
1350 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
1351 Entry
->me
.SubScreen
= SubScreen
;
1352 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1354 } /* static LEGACY_ENTRY * AddLegacyEntryUEFI() */
1357 Scan for legacy BIOS targets on machines that implement EFI_LEGACY_BIOS_PROTOCOL.
1358 In testing, protocol has not been implemented on Macs but has been
1359 implemented on several Dell PCs and an ASUS motherboard.
1360 Restricts output to disks of the specified DiskType.
1362 static VOID
ScanLegacyUEFI(IN UINTN DiskType
)
1365 EFI_LEGACY_BIOS_PROTOCOL
*LegacyBios
;
1366 UINT16
*BootOrder
= NULL
;
1368 CHAR16 BootOption
[10];
1369 UINTN BootOrderSize
= 0;
1371 BDS_COMMON_OPTION
*BdsOption
;
1372 LIST_ENTRY TempList
;
1373 BBS_BBS_DEVICE_PATH
* BbsDevicePath
= NULL
;
1374 // REFIT_VOLUME Volume;
1376 InitializeListHead (&TempList
);
1377 ZeroMem (Buffer
, sizeof (Buffer
));
1379 // If LegacyBios protocol is not implemented on this platform, then
1380 //we do not support this type of legacy boot on this machine.
1381 Status
= gBS
->LocateProtocol(&gEfiLegacyBootProtocolGuid
, NULL
, (VOID
**) &LegacyBios
);
1382 if (EFI_ERROR (Status
))
1385 // Grab the boot order
1386 BootOrder
= BdsLibGetVariableAndSize(L
"BootOrder", &gEfiGlobalVariableGuid
, &BootOrderSize
);
1387 if (BootOrder
== NULL
) {
1392 while (Index
< BootOrderSize
/ sizeof (UINT16
))
1394 // Grab each boot option variable from the boot order, and convert
1395 // the variable into a BDS boot option
1396 UnicodeSPrint (BootOption
, sizeof (BootOption
), L
"Boot%04x", BootOrder
[Index
]);
1397 BdsOption
= BdsLibVariableToOption (&TempList
, BootOption
);
1399 if (BdsOption
!= NULL
) {
1400 BbsDevicePath
= (BBS_BBS_DEVICE_PATH
*)BdsOption
->DevicePath
;
1402 // Only add the entry if it is of a requested type (e.g. USB, HD)
1404 // Two checks necessary because some systems return EFI boot loaders
1405 // with a DeviceType value that would inappropriately include them
1406 // as legacy loaders....
1407 if ((BbsDevicePath
->DeviceType
== DiskType
) && (BdsOption
->DevicePath
->Type
== DEVICE_TYPE_BIOS
)) {
1408 AddLegacyEntryUEFI(BdsOption
, BbsDevicePath
->DeviceType
);
1413 } /* static VOID ScanLegacyUEFI() */
1415 static VOID
ScanLegacyUEFI(IN UINTN DiskType
){}
1416 #endif // __MAKEWITH_TIANO
1418 static VOID
ScanLegacyVolume(REFIT_VOLUME
*Volume
, UINTN VolumeIndex
) {
1420 BOOLEAN ShowVolume
, HideIfOthersFound
;
1423 HideIfOthersFound
= FALSE
;
1424 if (Volume
->IsAppleLegacy
) {
1426 HideIfOthersFound
= TRUE
;
1427 } else if (Volume
->HasBootCode
) {
1429 if (Volume
->BlockIO
== Volume
->WholeDiskBlockIO
&&
1430 Volume
->BlockIOOffset
== 0 &&
1431 Volume
->OSName
== NULL
)
1432 // this is a whole disk (MBR) entry; hide if we have entries for partitions
1433 HideIfOthersFound
= TRUE
;
1435 if (HideIfOthersFound
) {
1436 // check for other bootable entries on the same disk
1437 for (VolumeIndex2
= 0; VolumeIndex2
< VolumesCount
; VolumeIndex2
++) {
1438 if (VolumeIndex2
!= VolumeIndex
&& Volumes
[VolumeIndex2
]->HasBootCode
&&
1439 Volumes
[VolumeIndex2
]->WholeDiskBlockIO
== Volume
->WholeDiskBlockIO
)
1445 AddLegacyEntry(NULL
, Volume
);
1446 } // static VOID ScanLegacyVolume()
1448 // Scan attached optical discs for legacy (BIOS) boot code
1449 // and add anything found to the list....
1450 static VOID
ScanLegacyDisc(VOID
)
1453 REFIT_VOLUME
*Volume
;
1455 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
) {
1456 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1457 Volume
= Volumes
[VolumeIndex
];
1458 if (Volume
->DiskKind
== DISK_KIND_OPTICAL
)
1459 ScanLegacyVolume(Volume
, VolumeIndex
);
1461 } else if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
1462 ScanLegacyUEFI(BBS_CDROM
);
1464 } /* static VOID ScanLegacyDisc() */
1466 // Scan internal hard disks for legacy (BIOS) boot code
1467 // and add anything found to the list....
1468 static VOID
ScanLegacyInternal(VOID
)
1471 REFIT_VOLUME
*Volume
;
1473 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
) {
1474 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1475 Volume
= Volumes
[VolumeIndex
];
1476 if (Volume
->DiskKind
== DISK_KIND_INTERNAL
)
1477 ScanLegacyVolume(Volume
, VolumeIndex
);
1479 } else if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
1480 ScanLegacyUEFI(BBS_HARDDISK
);
1482 } /* static VOID ScanLegacyInternal() */
1484 // Scan external disks for legacy (BIOS) boot code
1485 // and add anything found to the list....
1486 static VOID
ScanLegacyExternal(VOID
)
1489 REFIT_VOLUME
*Volume
;
1491 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
) {
1492 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1493 Volume
= Volumes
[VolumeIndex
];
1494 if (Volume
->DiskKind
== DISK_KIND_EXTERNAL
)
1495 ScanLegacyVolume(Volume
, VolumeIndex
);
1497 } else if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
1498 ScanLegacyUEFI(BBS_USB
);
1500 } /* static VOID ScanLegacyExternal() */
1503 // pre-boot tool functions
1506 static VOID
StartTool(IN LOADER_ENTRY
*Entry
)
1508 BeginExternalScreen(Entry
->UseGraphicsMode
, Entry
->me
.Title
+ 6); // assumes "Start <title>" as assigned below
1509 StartEFIImage(Entry
->DevicePath
, Entry
->LoadOptions
, Basename(Entry
->LoaderPath
),
1510 Basename(Entry
->LoaderPath
), Entry
->OSType
, NULL
, TRUE
);
1511 FinishExternalScreen();
1512 } /* static VOID StartTool() */
1514 static LOADER_ENTRY
* AddToolEntry(EFI_HANDLE DeviceHandle
, IN CHAR16
*LoaderPath
, IN CHAR16
*LoaderTitle
, IN EG_IMAGE
*Image
,
1515 IN CHAR16 ShortcutLetter
, IN BOOLEAN UseGraphicsMode
)
1517 LOADER_ENTRY
*Entry
;
1518 CHAR16
*TitleStr
= NULL
;
1520 Entry
= AllocateZeroPool(sizeof(LOADER_ENTRY
));
1522 MergeStrings(&TitleStr
, L
"Start ", 0);
1523 MergeStrings(&TitleStr
, LoaderTitle
, 0);
1524 Entry
->me
.Title
= TitleStr
;
1525 Entry
->me
.Tag
= TAG_TOOL
;
1527 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1528 Entry
->me
.Image
= Image
;
1529 Entry
->LoaderPath
= (LoaderPath
) ? StrDuplicate(LoaderPath
) : NULL
;
1530 Entry
->DevicePath
= FileDevicePath(DeviceHandle
, Entry
->LoaderPath
);
1531 Entry
->UseGraphicsMode
= UseGraphicsMode
;
1533 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1535 } /* static LOADER_ENTRY * AddToolEntry() */
1538 // pre-boot driver functions
1541 static UINTN
ScanDriverDir(IN CHAR16
*Path
)
1544 REFIT_DIR_ITER DirIter
;
1546 EFI_FILE_INFO
*DirEntry
;
1547 CHAR16 FileName
[256];
1549 CleanUpPathNameSlashes(Path
);
1550 // look through contents of the directory
1551 DirIterOpen(SelfRootDir
, Path
, &DirIter
);
1552 while (DirIterNext(&DirIter
, 2, LOADER_MATCH_PATTERNS
, &DirEntry
)) {
1553 if (DirEntry
->FileName
[0] == '.')
1554 continue; // skip this
1556 SPrint(FileName
, 255, L
"%s\\%s", Path
, DirEntry
->FileName
);
1558 Status
= StartEFIImage(FileDevicePath(SelfLoadedImage
->DeviceHandle
, FileName
),
1559 L
"", DirEntry
->FileName
, DirEntry
->FileName
, 0, NULL
, FALSE
);
1561 Status
= DirIterClose(&DirIter
);
1562 if (Status
!= EFI_NOT_FOUND
) {
1563 SPrint(FileName
, 255, L
"while scanning the %s directory", Path
);
1564 CheckError(Status
, FileName
);
1569 #ifdef __MAKEWITH_GNUEFI
1570 static EFI_STATUS
ConnectAllDriversToAllControllers(VOID
)
1573 UINTN AllHandleCount
;
1574 EFI_HANDLE
*AllHandleBuffer
;
1577 EFI_HANDLE
*HandleBuffer
;
1583 Status
= LibLocateHandle(AllHandles
,
1588 if (EFI_ERROR(Status
))
1591 for (Index
= 0; Index
< AllHandleCount
; Index
++) {
1593 // Scan the handle database
1595 Status
= LibScanHandleDatabase(NULL
,
1597 AllHandleBuffer
[Index
],
1602 if (EFI_ERROR (Status
))
1606 if (HandleType
[Index
] & EFI_HANDLE_TYPE_DRIVER_BINDING_HANDLE
)
1608 if (HandleType
[Index
] & EFI_HANDLE_TYPE_IMAGE_HANDLE
)
1613 for (HandleIndex
= 0; HandleIndex
< HandleCount
; HandleIndex
++) {
1614 if (HandleType
[HandleIndex
] & EFI_HANDLE_TYPE_PARENT_HANDLE
)
1619 if (HandleType
[Index
] & EFI_HANDLE_TYPE_DEVICE_HANDLE
) {
1620 Status
= refit_call4_wrapper(BS
->ConnectController
,
1621 AllHandleBuffer
[Index
],
1629 MyFreePool (HandleBuffer
);
1630 MyFreePool (HandleType
);
1634 MyFreePool (AllHandleBuffer
);
1636 } /* EFI_STATUS ConnectAllDriversToAllControllers() */
1638 static EFI_STATUS
ConnectAllDriversToAllControllers(VOID
) {
1639 BdsLibConnectAllDriversToAllControllers();
1644 // Load all EFI drivers from rEFInd's "drivers" subdirectory and from the
1645 // directories specified by the user in the "scan_driver_dirs" configuration
1647 static VOID
LoadDrivers(VOID
)
1649 CHAR16
*Directory
, *SelfDirectory
;
1650 UINTN i
= 0, Length
, NumFound
= 0;
1652 // load drivers from the subdirectories of rEFInd's home directory specified
1653 // in the DRIVER_DIRS constant.
1654 while ((Directory
= FindCommaDelimited(DRIVER_DIRS
, i
++)) != NULL
) {
1655 SelfDirectory
= SelfDirPath
? StrDuplicate(SelfDirPath
) : NULL
;
1656 CleanUpPathNameSlashes(SelfDirectory
);
1657 MergeStrings(&SelfDirectory
, Directory
, L
'\\');
1658 NumFound
+= ScanDriverDir(SelfDirectory
);
1659 MyFreePool(Directory
);
1660 MyFreePool(SelfDirectory
);
1663 // Scan additional user-specified driver directories....
1665 while ((Directory
= FindCommaDelimited(GlobalConfig
.DriverDirs
, i
++)) != NULL
) {
1666 CleanUpPathNameSlashes(Directory
);
1667 Length
= StrLen(Directory
);
1669 NumFound
+= ScanDriverDir(Directory
);
1671 MyFreePool(Directory
);
1674 // connect all devices
1676 ConnectAllDriversToAllControllers();
1677 } /* static VOID LoadDrivers() */
1679 // Determine what (if any) type of legacy (BIOS) boot support is available
1680 static VOID
FindLegacyBootType(VOID
) {
1681 #ifdef __MAKEWITH_TIANO
1683 EFI_LEGACY_BIOS_PROTOCOL
*LegacyBios
;
1686 GlobalConfig
.LegacyType
= LEGACY_TYPE_NONE
;
1688 // UEFI-style legacy BIOS support is available only with the TianoCore EDK2
1689 // build environment, and then only with some implementations....
1690 #ifdef __MAKEWITH_TIANO
1691 Status
= gBS
->LocateProtocol (&gEfiLegacyBootProtocolGuid
, NULL
, (VOID
**) &LegacyBios
);
1692 if (!EFI_ERROR (Status
))
1693 GlobalConfig
.LegacyType
= LEGACY_TYPE_UEFI
;
1696 // Macs have their own system. If the firmware vendor code contains the
1697 // string "Apple", assume it's available. Note that this overrides the
1698 // UEFI type, and might yield false positives if the vendor string
1699 // contains "Apple" as part of something bigger, so this isn't 100%
1701 if (StriSubCmp(L
"Apple", ST
->FirmwareVendor
))
1702 GlobalConfig
.LegacyType
= LEGACY_TYPE_MAC
;
1703 } // static VOID FindLegacyBootType
1705 // Warn the user if legacy OS scans are enabled but the firmware or this
1706 // application can't support them....
1707 static VOID
WarnIfLegacyProblems() {
1708 BOOLEAN found
= FALSE
;
1711 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_NONE
) {
1713 if (GlobalConfig
.ScanFor
[i
] == 'h' || GlobalConfig
.ScanFor
[i
] == 'b' || GlobalConfig
.ScanFor
[i
] == 'c')
1716 } while ((i
< NUM_SCAN_OPTIONS
) && (!found
));
1718 Print(L
"NOTE: refind.conf's 'scanfor' line specifies scanning for one or more legacy\n");
1719 Print(L
"(BIOS) boot options; however, this is not possible because ");
1720 #ifdef __MAKEWITH_TIANO
1721 Print(L
"your computer lacks\n");
1722 Print(L
"the necessary Compatibility Support Module (CSM) support.\n");
1724 Print(L
"this program was\n");
1725 Print(L
"compiled without the necessary support. Please visit\n");
1726 Print(L
"http://www.rodsbooks.com/refind/getting.html and download and install a rEFInd\n");
1727 Print(L
"binary built with the TianoCore EDK2 to enable legacy boot support.\n");
1731 } // if no legacy support
1732 } // static VOID WarnIfLegacyProblems()
1734 // Locates boot loaders. NOTE: This assumes that GlobalConfig.LegacyType is set correctly.
1735 static VOID
ScanForBootloaders(VOID
) {
1740 // scan for loaders and tools, add them to the menu
1741 for (i
= 0; i
< NUM_SCAN_OPTIONS
; i
++) {
1742 switch(GlobalConfig
.ScanFor
[i
]) {
1747 ScanLegacyInternal();
1750 ScanLegacyExternal();
1753 ScanUserConfigured();
1767 // assign shortcut keys
1768 for (i
= 0; i
< MainMenu
.EntryCount
&& MainMenu
.Entries
[i
]->Row
== 0 && i
< 9; i
++)
1769 MainMenu
.Entries
[i
]->ShortcutDigit
= (CHAR16
)('1' + i
);
1771 // wait for user ACK when there were errors
1772 FinishTextScreen(FALSE
);
1773 } // static VOID ScanForBootloaders()
1775 // Add the second-row tags containing built-in and external tools (EFI shell,
1777 static VOID
ScanForTools(VOID
) {
1778 CHAR16
*FileName
= NULL
, Description
[256];
1779 REFIT_MENU_ENTRY
*TempMenuEntry
;
1780 UINTN i
, j
, VolumeIndex
;
1782 for (i
= 0; i
< NUM_TOOLS
; i
++) {
1783 switch(GlobalConfig
.ShowTools
[i
]) {
1785 TempMenuEntry
= CopyMenuEntry(&MenuEntryShutdown
);
1786 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_SHUTDOWN
);
1787 AddMenuEntry(&MainMenu
, TempMenuEntry
);
1790 TempMenuEntry
= CopyMenuEntry(&MenuEntryReset
);
1791 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_RESET
);
1792 AddMenuEntry(&MainMenu
, TempMenuEntry
);
1795 TempMenuEntry
= CopyMenuEntry(&MenuEntryAbout
);
1796 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT
);
1797 AddMenuEntry(&MainMenu
, TempMenuEntry
);
1800 TempMenuEntry
= CopyMenuEntry(&MenuEntryExit
);
1801 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_EXIT
);
1802 AddMenuEntry(&MainMenu
, TempMenuEntry
);
1806 MyFreePool(FileName
);
1807 while ((FileName
= FindCommaDelimited(SHELL_NAMES
, j
++)) != NULL
) {
1808 if (FileExists(SelfRootDir
, FileName
)) {
1809 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, L
"EFI Shell", BuiltinIcon(BUILTIN_ICON_TOOL_SHELL
),
1815 MyFreePool(FileName
);
1816 FileName
= StrDuplicate(L
"\\efi\\tools\\gptsync.efi");
1817 // MergeStrings(&FileName, L"\\efi\\tools\\gptsync.efi", 0);
1818 if (FileExists(SelfRootDir
, FileName
)) {
1819 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, L
"Make Hybrid MBR", BuiltinIcon(BUILTIN_ICON_TOOL_PART
), 'P', FALSE
);
1822 case TAG_APPLE_RECOVERY
:
1823 MyFreePool(FileName
);
1824 FileName
= StrDuplicate(L
"\\com.apple.recovery.boot\\boot.efi");
1825 // MergeStrings(&FileName, L"\\com.apple.recovery.boot\\boot.efi", 0);
1826 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1827 if ((Volumes
[VolumeIndex
]->RootDir
!= NULL
) && (FileExists(Volumes
[VolumeIndex
]->RootDir
, FileName
))) {
1828 SPrint(Description
, 255, L
"Apple Recovery on %s", Volumes
[VolumeIndex
]->VolName
);
1829 AddToolEntry(Volumes
[VolumeIndex
]->DeviceHandle
, FileName
, Description
,
1830 BuiltinIcon(BUILTIN_ICON_TOOL_APPLE_RESCUE
), 'R', TRUE
);
1836 MyFreePool(FileName
);
1837 while ((FileName
= FindCommaDelimited(MOK_NAMES
, j
++)) != NULL
) {
1838 if (FileExists(SelfRootDir
, FileName
)) {
1839 SPrint(Description
, 255, L
"MOK Key Manager at %s", FileName
);
1840 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, Description
,
1841 BuiltinIcon(BUILTIN_ICON_TOOL_MOK_TOOL
), 'S', FALSE
);
1844 if (FileExists(SelfDir
, L
"MokManager.efi")) {
1845 MyFreePool(FileName
);
1846 FileName
= StrDuplicate(SelfDirPath
);
1847 MergeStrings(&FileName
, L
"\\MokManager.efi", 0);
1848 SPrint(Description
, 255, L
"MOK Key Manager at %s", FileName
);
1849 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, Description
,
1850 BuiltinIcon(BUILTIN_ICON_TOOL_MOK_TOOL
), 'S', FALSE
);
1854 MyFreePool(FileName
);
1857 } // static VOID ScanForTools
1859 // Rescan for boot loaders
1860 VOID
RescanAll(VOID
) {
1867 egDisplayMessage(L
"Scanning for new boot loaders; please wait....", &BGColor
);
1868 FreeList((VOID
***) &(MainMenu
.Entries
), &MainMenu
.EntryCount
);
1869 MainMenu
.Entries
= NULL
;
1870 MainMenu
.EntryCount
= 0;
1871 ReadConfig(CONFIG_FILE_NAME
);
1872 ConnectAllDriversToAllControllers();
1874 ScanForBootloaders();
1877 } // VOID RescanAll()
1879 #ifndef __MAKEWITH_GNUEFI
1881 // Minimal initialization function
1882 static VOID
InitializeLib(IN EFI_HANDLE ImageHandle
, IN EFI_SYSTEM_TABLE
*SystemTable
) {
1884 // gImageHandle = ImageHandle;
1885 gBS
= SystemTable
->BootServices
;
1886 // gRS = SystemTable->RuntimeServices;
1887 gRT
= SystemTable
->RuntimeServices
; // Some BDS functions need gRT to be set
1888 EfiGetSystemConfigurationTable (&gEfiDxeServicesTableGuid
, (VOID
**) &gDS
);
1890 InitializeConsoleSim();
1900 efi_main (IN EFI_HANDLE ImageHandle
, IN EFI_SYSTEM_TABLE
*SystemTable
)
1903 BOOLEAN MainLoopRunning
= TRUE
;
1904 REFIT_MENU_ENTRY
*ChosenEntry
;
1910 InitializeLib(ImageHandle
, SystemTable
);
1912 Status
= InitRefitLib(ImageHandle
);
1913 if (EFI_ERROR(Status
))
1916 // read configuration
1917 CopyMem(GlobalConfig
.ScanFor
, "ieom ", NUM_SCAN_OPTIONS
);
1918 FindLegacyBootType();
1919 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
)
1920 CopyMem(GlobalConfig
.ScanFor
, "ihebocm ", NUM_SCAN_OPTIONS
);
1921 ReadConfig(CONFIG_FILE_NAME
);
1922 WarnIfLegacyProblems();
1923 MainMenu
.TimeoutSeconds
= GlobalConfig
.Timeout
;
1925 // disable EFI watchdog timer
1926 refit_call4_wrapper(BS
->SetWatchdogTimer
, 0x0000, 0x0000, 0x0000, NULL
);
1928 // further bootstrap (now with config available)
1932 ScanForBootloaders();
1935 if (GlobalConfig
.ScanDelay
> 0) {
1940 egDisplayMessage(L
"Pausing before disk scan; please wait....", &BGColor
);
1941 for (i
= 0; i
< GlobalConfig
.ScanDelay
; i
++)
1942 refit_call1_wrapper(BS
->Stall
, 1000000);
1946 Selection
= StrDuplicate(GlobalConfig
.DefaultSelection
);
1947 while (MainLoopRunning
) {
1948 MenuExit
= RunMainMenu(&MainMenu
, Selection
, &ChosenEntry
);
1950 // The Escape key triggers a re-scan operation....
1951 if (MenuExit
== MENU_EXIT_ESCAPE
) {
1956 switch (ChosenEntry
->Tag
) {
1958 case TAG_REBOOT
: // Reboot
1960 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);
1961 MainLoopRunning
= FALSE
; // just in case we get this far
1964 case TAG_SHUTDOWN
: // Shut Down
1966 refit_call4_wrapper(RT
->ResetSystem
, EfiResetShutdown
, EFI_SUCCESS
, 0, NULL
);
1967 MainLoopRunning
= FALSE
; // just in case we get this far
1970 case TAG_ABOUT
: // About rEFInd
1974 case TAG_LOADER
: // Boot OS via .EFI loader
1975 StartLoader((LOADER_ENTRY
*)ChosenEntry
);
1978 case TAG_LEGACY
: // Boot legacy OS
1979 StartLegacy((LEGACY_ENTRY
*)ChosenEntry
);
1982 #ifdef __MAKEWITH_TIANO
1983 case TAG_LEGACY_UEFI
: // Boot a legacy OS on a non-Mac
1984 StartLegacyUEFI((LEGACY_ENTRY
*)ChosenEntry
);
1986 #endif // __MAKEWITH_TIANO
1988 case TAG_TOOL
: // Start a EFI tool
1989 StartTool((LOADER_ENTRY
*)ChosenEntry
);
1992 case TAG_EXIT
: // Terminate rEFInd
1993 BeginTextScreen(L
" ");
1998 MyFreePool(Selection
);
1999 Selection
= (ChosenEntry
->Title
) ? StrDuplicate(ChosenEntry
->Title
) : NULL
;
2002 // If we end up here, things have gone wrong. Try to reboot, and if that
2003 // fails, go into an endless loop.
2004 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);