3 * Main code for the boot menu
5 * Copyright (c) 2006-2010 Christoph Pfisterer
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions are
12 * * Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
15 * * Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the
20 * * Neither the name of Christoph Pfisterer nor the names of the
21 * contributors may be used to endorse or promote products derived
22 * from this software without specific prior written permission.
24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
26 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
27 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
28 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
29 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
30 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
31 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
32 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
33 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
34 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37 * Modifications copyright (c) 2012 Roderick W. Smith
39 * Modifications distributed under the terms of the GNU General Public
40 * License (GPL) version 3 (GPLv3), a copy of which must be distributed
41 * with this source code or binaries made from it.
52 #include "../include/Handle.h"
53 #include "../include/refit_call_wrapper.h"
54 #include "driver_support.h"
55 #include "../include/syslinux_mbr.h"
57 #ifdef __MAKEWITH_TIANO
58 #include "../EfiLib/BdsHelper.h"
59 #endif // __MAKEWITH_TIANO
64 #define MACOSX_LOADER_PATH L"\\System\\Library\\CoreServices\\boot.efi"
66 #define SHELL_NAMES L"\\EFI\\tools\\shell.efi,\\shellx64.efi"
67 #define DRIVER_DIRS L"drivers,drivers_x64"
69 #define SHELL_NAMES L"\\EFI\\tools\\shell.efi,\\shellia32.efi"
70 #define DRIVER_DIRS L"drivers,drivers_ia32"
72 #define SHELL_NAMES L"\\EFI\\tools\\shell.efi"
73 #define DRIVER_DIRS L"drivers"
76 #define MOK_NAMES L"\\EFI\\tools\\MokManager.efi,\\EFI\\redhat\\MokManager.efi,\\EFI\\ubuntu\\MokManager.efi,\\EFI\\suse\\MokManager"
78 // Filename patterns that identify EFI boot loaders. Note that a single case (either L"*.efi" or
79 // L"*.EFI") is fine for most systems; but Gigabyte's buggy Hybrid EFI does a case-sensitive
80 // comparison when it should do a case-insensitive comparison, so I'm doubling this up. It does
81 // no harm on other computers, AFAIK. In theory, every case variation should be done for
82 // completeness, but that's ridiculous....
83 #define LOADER_MATCH_PATTERNS L"*.efi,*.EFI"
85 // Patterns that identify Linux kernels. Added to the loader match pattern when the
86 // scan_all_linux_kernels option is set in the configuration file. Causes kernels WITHOUT
87 // a ".efi" extension to be found when scanning for boot loaders.
88 #define LINUX_MATCH_PATTERNS L"vmlinuz*,bzImage*"
90 static REFIT_MENU_ENTRY MenuEntryAbout
= { L
"About rEFInd", TAG_ABOUT
, 1, 0, 'A', NULL
, NULL
, NULL
};
91 static REFIT_MENU_ENTRY MenuEntryReset
= { L
"Reboot Computer", TAG_REBOOT
, 1, 0, 'R', NULL
, NULL
, NULL
};
92 static REFIT_MENU_ENTRY MenuEntryShutdown
= { L
"Shut Down Computer", TAG_SHUTDOWN
, 1, 0, 'U', NULL
, NULL
, NULL
};
93 static REFIT_MENU_ENTRY MenuEntryReturn
= { L
"Return to Main Menu", TAG_RETURN
, 0, 0, 0, NULL
, NULL
, NULL
};
94 static REFIT_MENU_ENTRY MenuEntryExit
= { L
"Exit rEFInd", TAG_EXIT
, 1, 0, 0, NULL
, NULL
, NULL
};
96 static REFIT_MENU_SCREEN MainMenu
= { L
"Main Menu", NULL
, 0, NULL
, 0, NULL
, 0, L
"Automatic boot" };
97 static REFIT_MENU_SCREEN AboutMenu
= { L
"About", NULL
, 0, NULL
, 0, NULL
, 0, NULL
};
99 REFIT_CONFIG GlobalConfig
= { FALSE
, FALSE
, 0, 0, 20, 0, 0, GRAPHICS_FOR_OSX
, LEGACY_TYPE_MAC
, 0,
100 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
101 {TAG_SHELL
, TAG_APPLE_RECOVERY
, TAG_MOK_TOOL
, TAG_ABOUT
, TAG_SHUTDOWN
, TAG_REBOOT
, 0, 0, 0, 0, 0 }};
103 // Structure used to hold boot loader filenames and time stamps in
104 // a linked list; used to sort entries within a directory.
108 struct LOADER_LIST
*NextEntry
;
115 static VOID
AboutrEFInd(VOID
)
117 CHAR16
*TempStr
; // Note: Don't deallocate; moved to menu structure
119 if (AboutMenu
.EntryCount
== 0) {
120 AboutMenu
.TitleImage
= BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT
);
121 AddMenuInfoLine(&AboutMenu
, L
"rEFInd Version 0.5.0.1");
122 AddMenuInfoLine(&AboutMenu
, L
"");
123 AddMenuInfoLine(&AboutMenu
, L
"Copyright (c) 2006-2010 Christoph Pfisterer");
124 AddMenuInfoLine(&AboutMenu
, L
"Copyright (c) 2012 Roderick W. Smith");
125 AddMenuInfoLine(&AboutMenu
, L
"Portions Copyright (c) Intel Corporation and others");
126 AddMenuInfoLine(&AboutMenu
, L
"Distributed under the terms of the GNU GPLv3 license");
127 AddMenuInfoLine(&AboutMenu
, L
"");
128 AddMenuInfoLine(&AboutMenu
, L
"Running on:");
129 TempStr
= AllocateZeroPool(256 * sizeof(CHAR16
));
130 SPrint(TempStr
, 255, L
" EFI Revision %d.%02d", ST
->Hdr
.Revision
>> 16, ST
->Hdr
.Revision
& ((1 << 16) - 1));
131 AddMenuInfoLine(&AboutMenu
, TempStr
);
133 AddMenuInfoLine(&AboutMenu
, L
" Platform: x86 (32 bit)");
134 #elif defined(EFIX64)
135 TempStr
= AllocateZeroPool(256 * sizeof(CHAR16
));
136 SPrint(TempStr
, 255, L
" Platform: x86_64 (64 bit); Secure Boot %s", secure_mode() ? L
"active" : L
"inactive");
137 AddMenuInfoLine(&AboutMenu
, TempStr
);
139 AddMenuInfoLine(&AboutMenu
, L
" Platform: unknown");
141 TempStr
= AllocateZeroPool(256 * sizeof(CHAR16
));
142 SPrint(TempStr
, 255, L
" Firmware: %s %d.%02d",
143 ST
->FirmwareVendor
, ST
->FirmwareRevision
>> 16, ST
->FirmwareRevision
& ((1 << 16) - 1));
144 AddMenuInfoLine(&AboutMenu
, TempStr
);
145 TempStr
= AllocateZeroPool(256 * sizeof(CHAR16
));
146 SPrint(TempStr
, 255, L
" Screen Output: %s", egScreenDescription());
147 AddMenuInfoLine(&AboutMenu
, TempStr
);
148 AddMenuInfoLine(&AboutMenu
, L
"");
149 #if defined(__MAKEWITH_GNUEFI)
150 AddMenuInfoLine(&AboutMenu
, L
"Built with GNU-EFI");
152 AddMenuInfoLine(&AboutMenu
, L
"Built with TianoCore EDK2");
154 AddMenuInfoLine(&AboutMenu
, L
"");
155 AddMenuInfoLine(&AboutMenu
, L
"For more information, see the rEFInd Web site:");
156 AddMenuInfoLine(&AboutMenu
, L
"http://www.rodsbooks.com/refind/");
157 AddMenuEntry(&AboutMenu
, &MenuEntryReturn
);
160 RunMenu(&AboutMenu
, NULL
);
161 } /* VOID AboutrEFInd() */
163 // Launch an EFI binary.
164 static EFI_STATUS
StartEFIImageList(IN EFI_DEVICE_PATH
**DevicePaths
,
165 IN CHAR16
*LoadOptions
, IN CHAR16
*LoadOptionsPrefix
,
166 IN CHAR16
*ImageTitle
, IN CHAR8 OSType
,
167 OUT UINTN
*ErrorInStep
,
170 EFI_STATUS Status
, ReturnStatus
;
171 EFI_HANDLE ChildImageHandle
;
172 EFI_LOADED_IMAGE
*ChildLoadedImage
= NULL
;
174 VOID
*ImageData
= NULL
;
176 REFIT_VOLUME
*DeviceVolume
= NULL
;
177 UINTN DevicePathIndex
;
178 CHAR16 ErrorInfo
[256];
179 CHAR16
*FullLoadOptions
= NULL
;
180 CHAR16
*loader
= NULL
;
181 BOOLEAN UseMok
= FALSE
;
183 if (ErrorInStep
!= NULL
)
187 if (LoadOptions
!= NULL
) {
188 if (LoadOptionsPrefix
!= NULL
) {
189 MergeStrings(&FullLoadOptions
, LoadOptionsPrefix
, 0);
190 MergeStrings(&FullLoadOptions
, LoadOptions
, L
' ');
192 MergeStrings(&FullLoadOptions
, L
" ", 0);
193 // NOTE: That last space is also added by the EFI shell and seems to be significant
194 // when passing options to Apple's boot.efi...
197 MergeStrings(&FullLoadOptions
, LoadOptions
, 0);
199 // NOTE: We also include the terminating null in the length for safety.
200 } // if (LoadOptions != NULL)
202 Print(L
"Starting %s\nUsing load options '%s'\n", ImageTitle
, FullLoadOptions
);
204 // load the image into memory (and execute it, in the case of a shim/MOK image).
205 ReturnStatus
= Status
= EFI_NOT_FOUND
; // in case the list is empty
206 for (DevicePathIndex
= 0; DevicePaths
[DevicePathIndex
] != NULL
; DevicePathIndex
++) {
207 // NOTE: Below commented-out line could be more efficient if the ReadFile() and
208 // FindVolumeAndFilename() calls were moved earlier, but it doesn't work on my
209 // 32-bit Mac Mini or my 64-bit Intel box when launching a Linux kernel; the
210 // kernel returns a "Failed to handle fs_proto" error message.
211 // TODO: Track down the cause of this error and fix it, if possible.
212 // ReturnStatus = Status = refit_call6_wrapper(BS->LoadImage, FALSE, SelfImageHandle, DevicePaths[DevicePathIndex],
213 // ImageData, ImageSize, &ChildImageHandle);
214 ReturnStatus
= Status
= refit_call6_wrapper(BS
->LoadImage
, FALSE
, SelfImageHandle
, DevicePaths
[DevicePathIndex
],
215 NULL
, 0, &ChildImageHandle
);
216 if ((Status
== EFI_ACCESS_DENIED
) && (ShimLoaded())) {
217 FindVolumeAndFilename(DevicePaths
[DevicePathIndex
], &DeviceVolume
, &loader
);
218 if (DeviceVolume
!= NULL
) {
219 Status
= ReadFile(DeviceVolume
->RootDir
, loader
, &File
, &ImageSize
);
220 ImageData
= File
.Buffer
;
222 Status
= EFI_NOT_FOUND
;
223 Print(L
"Error: device volume not found!\n");
225 if (Status
!= EFI_NOT_FOUND
) {
226 ReturnStatus
= Status
= start_image(SelfImageHandle
, loader
, ImageData
, ImageSize
, FullLoadOptions
,
227 DeviceVolume
, FileDevicePath(DeviceVolume
->DeviceHandle
, loader
));
228 // ReturnStatus = Status = start_image(SelfImageHandle, loader, ImageData, ImageSize, FullLoadOptions,
229 // DeviceVolume, DevicePaths[DevicePathIndex]);
231 if (ReturnStatus
== EFI_SUCCESS
) {
234 } // if (UEFI SB failed; use shim)
235 if (ReturnStatus
!= EFI_NOT_FOUND
) {
239 SPrint(ErrorInfo
, 255, L
"while loading %s", ImageTitle
);
240 if (CheckError(Status
, ErrorInfo
)) {
241 if (ErrorInStep
!= NULL
)
247 ReturnStatus
= Status
= refit_call3_wrapper(BS
->HandleProtocol
, ChildImageHandle
, &LoadedImageProtocol
,
248 (VOID
**) &ChildLoadedImage
);
249 if (CheckError(Status
, L
"while getting a LoadedImageProtocol handle")) {
250 if (ErrorInStep
!= NULL
)
254 ChildLoadedImage
->LoadOptions
= (VOID
*)FullLoadOptions
;
255 ChildLoadedImage
->LoadOptionsSize
= ((UINT32
)StrLen(FullLoadOptions
) + 1) * sizeof(CHAR16
);
256 // turn control over to the image
257 // TODO: (optionally) re-enable the EFI watchdog timer!
259 // close open file handles
261 ReturnStatus
= Status
= refit_call3_wrapper(BS
->StartImage
, ChildImageHandle
, NULL
, NULL
);
262 // control returns here when the child image calls Exit()
263 SPrint(ErrorInfo
, 255, L
"returned from %s", ImageTitle
);
264 if (CheckError(Status
, ErrorInfo
)) {
265 if (ErrorInStep
!= NULL
)
269 // re-open file handles
274 // unload the image, we don't care if it works or not...
276 Status
= refit_call1_wrapper(BS
->UnloadImage
, ChildImageHandle
);
279 MyFreePool(FullLoadOptions
);
281 } /* static EFI_STATUS StartEFIImageList() */
283 static EFI_STATUS
StartEFIImage(IN EFI_DEVICE_PATH
*DevicePath
,
284 IN CHAR16
*LoadOptions
, IN CHAR16
*LoadOptionsPrefix
,
285 IN CHAR16
*ImageTitle
, IN CHAR8 OSType
,
286 OUT UINTN
*ErrorInStep
,
289 EFI_DEVICE_PATH
*DevicePaths
[2];
291 DevicePaths
[0] = DevicePath
;
292 DevicePaths
[1] = NULL
;
293 return StartEFIImageList(DevicePaths
, LoadOptions
, LoadOptionsPrefix
, ImageTitle
, OSType
, ErrorInStep
, Verbose
);
294 } /* static EFI_STATUS StartEFIImage() */
297 // EFI OS loader functions
300 static VOID
StartLoader(IN LOADER_ENTRY
*Entry
)
302 UINTN ErrorInStep
= 0;
304 BeginExternalScreen(Entry
->UseGraphicsMode
, L
"Booting OS");
305 StartEFIImage(Entry
->DevicePath
, Entry
->LoadOptions
, Basename(Entry
->LoaderPath
),
306 Basename(Entry
->LoaderPath
), Entry
->OSType
, &ErrorInStep
, !Entry
->UseGraphicsMode
);
307 FinishExternalScreen();
310 // Locate an initrd or initramfs file that matches the kernel specified by LoaderPath.
311 // The matching file has a name that begins with "init" and includes the same version
312 // number string as is found in LoaderPath -- but not a longer version number string.
313 // For instance, if LoaderPath is \EFI\kernels\bzImage-3.3.0.efi, and if \EFI\kernels
314 // has a file called initramfs-3.3.0.img, this function will return the string
315 // '\EFI\kernels\initramfs-3.3.0.img'. If the directory ALSO contains the file
316 // initramfs-3.3.0-rc7.img or initramfs-13.3.0.img, those files will NOT match;
317 // however, initmine-3.3.0.img might match. (FindInitrd() returns the first match it
318 // finds). Thus, care should be taken to avoid placing duplicate matching files in
319 // the kernel's directory.
320 // If no matching init file can be found, returns NULL.
321 static CHAR16
* FindInitrd(IN CHAR16
*LoaderPath
, IN REFIT_VOLUME
*Volume
) {
322 CHAR16
*InitrdName
= NULL
, *FileName
, *KernelVersion
, *InitrdVersion
, *Path
;
323 REFIT_DIR_ITER DirIter
;
324 EFI_FILE_INFO
*DirEntry
;
326 FileName
= Basename(LoaderPath
);
327 KernelVersion
= FindNumbers(FileName
);
328 Path
= FindPath(LoaderPath
);
330 // Add trailing backslash for root directory; necessary on some systems, but must
331 // NOT be added to all directories, since on other systems, a trailing backslash on
332 // anything but the root directory causes them to flake out!
333 if (StrLen(Path
) == 0) {
334 MergeStrings(&Path
, L
"\\", 0);
336 DirIterOpen(Volume
->RootDir
, Path
, &DirIter
);
337 // Now add a trailing backslash if it was NOT added earlier, for consistency in
338 // building the InitrdName later....
339 if ((StrLen(Path
) > 0) && (Path
[StrLen(Path
) - 1] != L
'\\'))
340 MergeStrings(&Path
, L
"\\", 0);
341 while ((DirIterNext(&DirIter
, 2, L
"init*", &DirEntry
)) && (InitrdName
== NULL
)) {
342 InitrdVersion
= FindNumbers(DirEntry
->FileName
);
343 if (KernelVersion
!= NULL
) {
344 if (StriCmp(InitrdVersion
, KernelVersion
) == 0) {
345 MergeStrings(&InitrdName
, Path
, 0);
346 MergeStrings(&InitrdName
, DirEntry
->FileName
, 0);
349 if (InitrdVersion
== NULL
) {
350 MergeStrings(&InitrdName
, Path
, 0);
351 MergeStrings(&InitrdName
, DirEntry
->FileName
, 0);
354 MyFreePool(InitrdVersion
);
356 DirIterClose(&DirIter
);
358 // Note: Don't FreePool(FileName), since Basename returns a pointer WITHIN the string it's passed.
359 MyFreePool(KernelVersion
);
362 } // static CHAR16 * FindInitrd()
364 LOADER_ENTRY
* AddPreparedLoaderEntry(LOADER_ENTRY
*Entry
) {
365 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
368 } // LOADER_ENTRY * AddPreparedLoaderEntry()
370 // Creates a copy of a menu screen.
371 // Returns a pointer to the copy of the menu screen.
372 static REFIT_MENU_SCREEN
* CopyMenuScreen(REFIT_MENU_SCREEN
*Entry
) {
373 REFIT_MENU_SCREEN
*NewEntry
;
376 NewEntry
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
377 if ((Entry
!= NULL
) && (NewEntry
!= NULL
)) {
378 CopyMem(NewEntry
, Entry
, sizeof(REFIT_MENU_SCREEN
));
379 NewEntry
->Title
= (Entry
->Title
) ? StrDuplicate(Entry
->Title
) : NULL
;
380 NewEntry
->TimeoutText
= (Entry
->TimeoutText
) ? StrDuplicate(Entry
->TimeoutText
) : NULL
;
381 if (Entry
->TitleImage
!= NULL
) {
382 NewEntry
->TitleImage
= AllocatePool(sizeof(EG_IMAGE
));
383 if (NewEntry
->TitleImage
!= NULL
)
384 CopyMem(NewEntry
->TitleImage
, Entry
->TitleImage
, sizeof(EG_IMAGE
));
386 NewEntry
->InfoLines
= (CHAR16
**) AllocateZeroPool(Entry
->InfoLineCount
* (sizeof(CHAR16
*)));
387 for (i
= 0; i
< Entry
->InfoLineCount
&& NewEntry
->InfoLines
; i
++) {
388 NewEntry
->InfoLines
[i
] = (Entry
->InfoLines
[i
]) ? StrDuplicate(Entry
->InfoLines
[i
]) : NULL
;
390 NewEntry
->Entries
= (REFIT_MENU_ENTRY
**) AllocateZeroPool(Entry
->EntryCount
* (sizeof (REFIT_MENU_ENTRY
*)));
391 for (i
= 0; i
< Entry
->EntryCount
&& NewEntry
->Entries
; i
++) {
392 AddMenuEntry(NewEntry
, Entry
->Entries
[i
]);
396 } // static REFIT_MENU_SCREEN* CopyMenuScreen()
398 // Creates a copy of a menu entry. Intended to enable moving a stack-based
399 // menu entry (such as the ones for the "reboot" and "exit" functions) to
400 // to the heap. This enables easier deletion of the whole set of menu
401 // entries when re-scanning.
402 // Returns a pointer to the copy of the menu entry.
403 static REFIT_MENU_ENTRY
* CopyMenuEntry(REFIT_MENU_ENTRY
*Entry
) {
404 REFIT_MENU_ENTRY
*NewEntry
;
406 NewEntry
= AllocateZeroPool(sizeof(REFIT_MENU_ENTRY
));
407 if ((Entry
!= NULL
) && (NewEntry
!= NULL
)) {
408 CopyMem(NewEntry
, Entry
, sizeof(REFIT_MENU_ENTRY
));
409 NewEntry
->Title
= (Entry
->Title
) ? StrDuplicate(Entry
->Title
) : NULL
;
410 if (Entry
->BadgeImage
!= NULL
) {
411 NewEntry
->BadgeImage
= AllocatePool(sizeof(EG_IMAGE
));
412 if (NewEntry
->BadgeImage
!= NULL
)
413 CopyMem(NewEntry
->BadgeImage
, Entry
->BadgeImage
, sizeof(EG_IMAGE
));
415 if (Entry
->Image
!= NULL
) {
416 NewEntry
->Image
= AllocatePool(sizeof(EG_IMAGE
));
417 if (NewEntry
->Image
!= NULL
)
418 CopyMem(NewEntry
->Image
, Entry
->Image
, sizeof(EG_IMAGE
));
420 if (Entry
->SubScreen
!= NULL
) {
421 NewEntry
->SubScreen
= CopyMenuScreen(Entry
->SubScreen
);
425 } // REFIT_MENU_ENTRY* CopyMenuEntry()
427 // Creates a new LOADER_ENTRY data structure and populates it with
428 // default values from the specified Entry, or NULL values if Entry
429 // is unspecified (NULL).
430 // Returns a pointer to the new data structure, or NULL if it
431 // couldn't be allocated
432 LOADER_ENTRY
*InitializeLoaderEntry(IN LOADER_ENTRY
*Entry
) {
433 LOADER_ENTRY
*NewEntry
= NULL
;
435 NewEntry
= AllocateZeroPool(sizeof(LOADER_ENTRY
));
436 if (NewEntry
!= NULL
) {
437 NewEntry
->me
.Title
= NULL
;
438 NewEntry
->me
.Tag
= TAG_LOADER
;
439 NewEntry
->Enabled
= TRUE
;
440 NewEntry
->UseGraphicsMode
= FALSE
;
441 NewEntry
->OSType
= 0;
443 NewEntry
->LoaderPath
= (Entry
->LoaderPath
) ? StrDuplicate(Entry
->LoaderPath
) : NULL
;
444 NewEntry
->VolName
= (Entry
->VolName
) ? StrDuplicate(Entry
->VolName
) : NULL
;
445 NewEntry
->DevicePath
= Entry
->DevicePath
;
446 NewEntry
->UseGraphicsMode
= Entry
->UseGraphicsMode
;
447 NewEntry
->LoadOptions
= (Entry
->LoadOptions
) ? StrDuplicate(Entry
->LoadOptions
) : NULL
;
448 NewEntry
->InitrdPath
= (Entry
->InitrdPath
) ? StrDuplicate(Entry
->InitrdPath
) : NULL
;
452 } // LOADER_ENTRY *InitializeLoaderEntry()
454 // Prepare a REFIT_MENU_SCREEN data structure for a subscreen entry. This sets up
455 // the default entry that launches the boot loader using the same options as the
456 // main Entry does. Subsequent options can be added by the calling function.
457 // If a subscreen already exists in the Entry that's passed to this function,
458 // it's left unchanged and a pointer to it is returned.
459 // Returns a pointer to the new subscreen data structure, or NULL if there
460 // were problems allocating memory.
461 REFIT_MENU_SCREEN
*InitializeSubScreen(IN LOADER_ENTRY
*Entry
) {
462 CHAR16
*FileName
, *Temp
= NULL
;
463 REFIT_MENU_SCREEN
*SubScreen
= NULL
;
464 LOADER_ENTRY
*SubEntry
;
466 FileName
= Basename(Entry
->LoaderPath
);
467 if (Entry
->me
.SubScreen
== NULL
) { // No subscreen yet; initialize default entry....
468 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
469 if (SubScreen
!= NULL
) {
470 SubScreen
->Title
= AllocateZeroPool(sizeof(CHAR16
) * 256);
471 SPrint(SubScreen
->Title
, 255, L
"Boot Options for %s on %s",
472 (Entry
->Title
!= NULL
) ? Entry
->Title
: FileName
, Entry
->VolName
);
473 SubScreen
->TitleImage
= Entry
->me
.Image
;
475 SubEntry
= InitializeLoaderEntry(Entry
);
476 if (SubEntry
!= NULL
) {
477 SubEntry
->me
.Title
= L
"Boot using default options";
478 if ((SubEntry
->InitrdPath
!= NULL
) && (StrLen(SubEntry
->InitrdPath
) > 0) && (!StriSubCmp(L
"initrd", SubEntry
->LoadOptions
))) {
479 MergeStrings(&Temp
, L
"initrd=", 0);
480 MergeStrings(&Temp
, SubEntry
->InitrdPath
, 0);
481 MergeStrings(&SubEntry
->LoadOptions
, Temp
, L
' ');
484 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
485 } // if (SubEntry != NULL)
486 } // if (SubScreen != NULL)
487 } else { // existing subscreen; less initialization, and just add new entry later....
488 SubScreen
= Entry
->me
.SubScreen
;
491 } // REFIT_MENU_SCREEN *InitializeSubScreen()
493 VOID
GenerateSubScreen(LOADER_ENTRY
*Entry
, IN REFIT_VOLUME
*Volume
) {
494 REFIT_MENU_SCREEN
*SubScreen
;
495 LOADER_ENTRY
*SubEntry
;
496 CHAR16
*InitrdOption
= NULL
, *Temp
;
497 CHAR16 DiagsFileName
[256];
502 // create the submenu
503 if (StrLen(Entry
->Title
) == 0) {
504 MyFreePool(Entry
->Title
);
507 SubScreen
= InitializeSubScreen(Entry
);
509 // loader-specific submenu entries
510 if (Entry
->OSType
== 'M') { // entries for Mac OS X
512 SubEntry
= InitializeLoaderEntry(Entry
);
513 if (SubEntry
!= NULL
) {
514 SubEntry
->me
.Title
= L
"Boot Mac OS X with a 64-bit kernel";
515 SubEntry
->LoadOptions
= L
"arch=x86_64";
516 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
517 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
520 SubEntry
= InitializeLoaderEntry(Entry
);
521 if (SubEntry
!= NULL
) {
522 SubEntry
->me
.Title
= L
"Boot Mac OS X with a 32-bit kernel";
523 SubEntry
->LoadOptions
= L
"arch=i386";
524 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
525 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
529 if (!(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_SINGLEUSER
)) {
530 SubEntry
= InitializeLoaderEntry(Entry
);
531 if (SubEntry
!= NULL
) {
532 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode";
533 SubEntry
->UseGraphicsMode
= FALSE
;
534 SubEntry
->LoadOptions
= L
"-v";
535 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
539 SubEntry
= InitializeLoaderEntry(Entry
);
540 if (SubEntry
!= NULL
) {
541 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode (64-bit)";
542 SubEntry
->UseGraphicsMode
= FALSE
;
543 SubEntry
->LoadOptions
= L
"-v arch=x86_64";
544 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
547 SubEntry
= InitializeLoaderEntry(Entry
);
548 if (SubEntry
!= NULL
) {
549 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode (32-bit)";
550 SubEntry
->UseGraphicsMode
= FALSE
;
551 SubEntry
->LoadOptions
= L
"-v arch=i386";
552 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
556 SubEntry
= InitializeLoaderEntry(Entry
);
557 if (SubEntry
!= NULL
) {
558 SubEntry
->me
.Title
= L
"Boot Mac OS X in single user mode";
559 SubEntry
->UseGraphicsMode
= FALSE
;
560 SubEntry
->LoadOptions
= L
"-v -s";
561 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
565 // check for Apple hardware diagnostics
566 StrCpy(DiagsFileName
, L
"\\System\\Library\\CoreServices\\.diagnostics\\diags.efi");
567 if (FileExists(Volume
->RootDir
, DiagsFileName
) && !(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_HWTEST
)) {
568 SubEntry
= InitializeLoaderEntry(Entry
);
569 if (SubEntry
!= NULL
) {
570 SubEntry
->me
.Title
= L
"Run Apple Hardware Test";
571 MyFreePool(SubEntry
->LoaderPath
);
572 SubEntry
->LoaderPath
= StrDuplicate(DiagsFileName
);
573 SubEntry
->DevicePath
= FileDevicePath(Volume
->DeviceHandle
, SubEntry
->LoaderPath
);
574 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
575 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
577 } // if diagnostics entry found
579 } else if (Entry
->OSType
== 'L') { // entries for Linux kernels with EFI stub loaders
580 File
= ReadLinuxOptionsFile(Entry
->LoaderPath
, Volume
);
582 if ((Temp
= FindInitrd(Entry
->LoaderPath
, Volume
)) != NULL
) {
583 MergeStrings(&InitrdOption
, L
"initrd=", 0);
584 MergeStrings(&InitrdOption
, Temp
, 0);
586 TokenCount
= ReadTokenLine(File
, &TokenList
); // read and discard first entry, since it's
587 FreeTokenLine(&TokenList
, &TokenCount
); // set up by InitializeSubScreen(), earlier....
588 while ((TokenCount
= ReadTokenLine(File
, &TokenList
)) > 1) {
589 SubEntry
= InitializeLoaderEntry(Entry
);
590 SubEntry
->me
.Title
= StrDuplicate(TokenList
[0]);
591 MyFreePool(SubEntry
->LoadOptions
);
592 SubEntry
->LoadOptions
= StrDuplicate(TokenList
[1]);
593 MergeStrings(&SubEntry
->LoadOptions
, InitrdOption
, L
' ');
594 FreeTokenLine(&TokenList
, &TokenCount
);
595 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_LINUX
;
596 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
598 MyFreePool(InitrdOption
);
601 } // if Linux options file exists
603 } else if (Entry
->OSType
== 'E') { // entries for ELILO
604 SubEntry
= InitializeLoaderEntry(Entry
);
605 if (SubEntry
!= NULL
) {
606 SubEntry
->me
.Title
= L
"Run ELILO in interactive mode";
607 SubEntry
->LoadOptions
= L
"-p";
608 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
609 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
612 SubEntry
= InitializeLoaderEntry(Entry
);
613 if (SubEntry
!= NULL
) {
614 SubEntry
->me
.Title
= L
"Boot Linux for a 17\" iMac or a 15\" MacBook Pro (*)";
615 SubEntry
->UseGraphicsMode
= TRUE
;
616 SubEntry
->LoadOptions
= L
"-d 0 i17";
617 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
618 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
621 SubEntry
= InitializeLoaderEntry(Entry
);
622 if (SubEntry
!= NULL
) {
623 SubEntry
->me
.Title
= L
"Boot Linux for a 20\" iMac (*)";
624 SubEntry
->UseGraphicsMode
= TRUE
;
625 SubEntry
->LoadOptions
= L
"-d 0 i20";
626 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
627 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
630 SubEntry
= InitializeLoaderEntry(Entry
);
631 if (SubEntry
!= NULL
) {
632 SubEntry
->me
.Title
= L
"Boot Linux for a Mac Mini (*)";
633 SubEntry
->UseGraphicsMode
= TRUE
;
634 SubEntry
->LoadOptions
= L
"-d 0 mini";
635 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
636 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
639 AddMenuInfoLine(SubScreen
, L
"NOTE: This is an example. Entries");
640 AddMenuInfoLine(SubScreen
, L
"marked with (*) may not work.");
642 } else if (Entry
->OSType
== 'X') { // entries for xom.efi
643 // by default, skip the built-in selection and boot from hard disk only
644 Entry
->LoadOptions
= L
"-s -h";
646 SubEntry
= InitializeLoaderEntry(Entry
);
647 if (SubEntry
!= NULL
) {
648 SubEntry
->me
.Title
= L
"Boot Windows from Hard Disk";
649 SubEntry
->LoadOptions
= L
"-s -h";
650 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
651 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
654 SubEntry
= InitializeLoaderEntry(Entry
);
655 if (SubEntry
!= NULL
) {
656 SubEntry
->me
.Title
= L
"Boot Windows from CD-ROM";
657 SubEntry
->LoadOptions
= L
"-s -c";
658 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
659 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
662 SubEntry
= InitializeLoaderEntry(Entry
);
663 if (SubEntry
!= NULL
) {
664 SubEntry
->me
.Title
= L
"Run XOM in text mode";
665 SubEntry
->UseGraphicsMode
= FALSE
;
666 SubEntry
->LoadOptions
= L
"-v";
667 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
668 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
670 } // entries for xom.efi
671 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
672 Entry
->me
.SubScreen
= SubScreen
;
673 } // VOID GenerateSubScreen()
675 // Returns options for a Linux kernel. Reads them from an options file in the
676 // kernel's directory; and if present, adds an initrd= option for an initial
677 // RAM disk file with the same version number as the kernel file.
678 static CHAR16
* GetMainLinuxOptions(IN CHAR16
* LoaderPath
, IN REFIT_VOLUME
*Volume
) {
679 CHAR16
*Options
= NULL
, *InitrdName
, *InitrdOption
= NULL
;
681 Options
= GetFirstOptionsFromFile(LoaderPath
, Volume
);
682 InitrdName
= FindInitrd(LoaderPath
, Volume
);
683 if (InitrdName
!= NULL
) {
684 MergeStrings(&InitrdOption
, L
"initrd=", 0);
685 MergeStrings(&InitrdOption
, InitrdName
, 0);
687 MergeStrings(&Options
, InitrdOption
, ' ');
688 MyFreePool(InitrdOption
);
689 MyFreePool(InitrdName
);
691 } // static CHAR16 * GetMainLinuxOptions()
693 // Sets a few defaults for a loader entry -- mainly the icon, but also the OS type
694 // code and shortcut letter. For Linux EFI stub loaders, also sets kernel options
695 // that will (with luck) work fairly automatically.
696 VOID
SetLoaderDefaults(LOADER_ENTRY
*Entry
, CHAR16
*LoaderPath
, IN REFIT_VOLUME
*Volume
) {
697 CHAR16 IconFileName
[256];
698 CHAR16
*FileName
, *PathOnly
, *OSIconName
= NULL
, *Temp
;
699 CHAR16 ShortcutLetter
= 0;
701 FileName
= Basename(LoaderPath
);
702 PathOnly
= FindPath(LoaderPath
);
704 // locate a custom icon for the loader
705 StrCpy(IconFileName
, LoaderPath
);
706 ReplaceEfiExtension(IconFileName
, L
".icns");
707 if (FileExists(Volume
->RootDir
, IconFileName
)) {
708 Entry
->me
.Image
= LoadIcns(Volume
->RootDir
, IconFileName
, 128);
709 } else if ((StrLen(PathOnly
) == 0) && (Volume
->VolIconImage
!= NULL
)) {
710 Entry
->me
.Image
= Volume
->VolIconImage
;
711 } // icon matched to loader or volume
713 Temp
= FindLastDirName(LoaderPath
);
714 MergeStrings(&OSIconName
, Temp
, L
',');
717 if (OSIconName
!= NULL
) {
718 ShortcutLetter
= OSIconName
[0];
721 // detect specific loaders
722 if (StriSubCmp(L
"bzImage", LoaderPath
) || StriSubCmp(L
"vmlinuz", LoaderPath
)) {
723 MergeStrings(&OSIconName
, L
"linux", L
',');
725 if (ShortcutLetter
== 0)
726 ShortcutLetter
= 'L';
727 Entry
->LoadOptions
= GetMainLinuxOptions(LoaderPath
, Volume
);
728 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_LINUX
;
729 } else if (StriSubCmp(L
"refit", LoaderPath
)) {
730 MergeStrings(&OSIconName
, L
"refit", L
',');
732 ShortcutLetter
= 'R';
733 } else if (StriCmp(LoaderPath
, MACOSX_LOADER_PATH
) == 0) {
734 if (Volume
->VolIconImage
!= NULL
) { // custom icon file found
735 Entry
->me
.Image
= Volume
->VolIconImage
;
737 MergeStrings(&OSIconName
, L
"mac", L
',');
739 ShortcutLetter
= 'M';
740 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
741 } else if (StriCmp(FileName
, L
"diags.efi") == 0) {
742 MergeStrings(&OSIconName
, L
"hwtest", L
',');
743 } else if (StriCmp(FileName
, L
"e.efi") == 0 || StriCmp(FileName
, L
"elilo.efi") == 0 || StriSubCmp(L
"elilo", FileName
)) {
744 MergeStrings(&OSIconName
, L
"elilo,linux", L
',');
746 if (secure_mode()) { // hack to enable ELILO to boot in secure mode
747 Temp
= StrDuplicate(L
"-C ");
748 MergeStrings(&Temp
, PathOnly
, 0);
749 MergeStrings(&Temp
, L
"elilo.conf", L
'\\');
750 Entry
->LoadOptions
= Temp
;
752 if (ShortcutLetter
== 0)
753 ShortcutLetter
= 'L';
754 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
755 } else if (StriSubCmp(L
"grub", FileName
)) {
757 ShortcutLetter
= 'G';
758 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_GRUB
;
759 } else if (StriCmp(FileName
, L
"cdboot.efi") == 0 ||
760 StriCmp(FileName
, L
"bootmgr.efi") == 0 ||
761 StriCmp(FileName
, L
"Bootmgfw.efi") == 0) {
762 MergeStrings(&OSIconName
, L
"win", L
',');
764 ShortcutLetter
= 'W';
765 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
766 } else if (StriCmp(FileName
, L
"xom.efi") == 0) {
767 MergeStrings(&OSIconName
, L
"xom,win", L
',');
768 Entry
->UseGraphicsMode
= TRUE
;
770 ShortcutLetter
= 'W';
771 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
774 if ((ShortcutLetter
>= 'a') && (ShortcutLetter
<= 'z'))
775 ShortcutLetter
= ShortcutLetter
- 'a' + 'A'; // convert lowercase to uppercase
776 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
777 if (Entry
->me
.Image
== NULL
)
778 Entry
->me
.Image
= LoadOSIcon(OSIconName
, L
"unknown", FALSE
);
779 MyFreePool(PathOnly
);
780 } // VOID SetLoaderDefaults()
782 // Add a specified EFI boot loader to the list, using automatic settings
783 // for icons, options, etc.
784 LOADER_ENTRY
* AddLoaderEntry(IN CHAR16
*LoaderPath
, IN CHAR16
*LoaderTitle
, IN REFIT_VOLUME
*Volume
) {
787 CleanUpPathNameSlashes(LoaderPath
);
788 Entry
= InitializeLoaderEntry(NULL
);
790 Entry
->Title
= StrDuplicate((LoaderTitle
!= NULL
) ? LoaderTitle
: LoaderPath
);
791 Entry
->me
.Title
= AllocateZeroPool(sizeof(CHAR16
) * 256);
792 SPrint(Entry
->me
.Title
, 255, L
"Boot %s from %s", (LoaderTitle
!= NULL
) ? LoaderTitle
: LoaderPath
, Volume
->VolName
);
794 Entry
->me
.BadgeImage
= Volume
->VolBadgeImage
;
795 if ((LoaderPath
!= NULL
) && (LoaderPath
[0] != L
'\\')) {
796 Entry
->LoaderPath
= StrDuplicate(L
"\\");
798 Entry
->LoaderPath
= NULL
;
800 MergeStrings(&(Entry
->LoaderPath
), LoaderPath
, 0);
801 Entry
->VolName
= Volume
->VolName
;
802 Entry
->DevicePath
= FileDevicePath(Volume
->DeviceHandle
, Entry
->LoaderPath
);
803 SetLoaderDefaults(Entry
, LoaderPath
, Volume
);
804 GenerateSubScreen(Entry
, Volume
);
805 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
809 } // LOADER_ENTRY * AddLoaderEntry()
811 // Returns -1 if (Time1 < Time2), +1 if (Time1 > Time2), or 0 if
812 // (Time1 == Time2). Precision is only to the nearest second; since
813 // this is used for sorting boot loader entries, differences smaller
814 // than this are likely to be meaningless (and unlikely!).
815 INTN
TimeComp(EFI_TIME
*Time1
, EFI_TIME
*Time2
) {
816 INT64 Time1InSeconds
, Time2InSeconds
;
818 // Following values are overestimates; I'm assuming 31 days in every month.
819 // This is fine for the purpose of this function, which has a limited
821 Time1InSeconds
= Time1
->Second
+ (Time1
->Minute
* 60) + (Time1
->Hour
* 3600) + (Time1
->Day
* 86400) +
822 (Time1
->Month
* 2678400) + ((Time1
->Year
- 1998) * 32140800);
823 Time2InSeconds
= Time2
->Second
+ (Time2
->Minute
* 60) + (Time2
->Hour
* 3600) + (Time2
->Day
* 86400) +
824 (Time2
->Month
* 2678400) + ((Time2
->Year
- 1998) * 32140800);
825 if (Time1InSeconds
< Time2InSeconds
)
827 else if (Time1InSeconds
> Time2InSeconds
)
833 // Adds a loader list element, keeping it sorted by date. Returns the new
834 // first element (the one with the most recent date).
835 static struct LOADER_LIST
* AddLoaderListEntry(struct LOADER_LIST
*LoaderList
, struct LOADER_LIST
*NewEntry
) {
836 struct LOADER_LIST
*LatestEntry
, *CurrentEntry
, *PrevEntry
= NULL
;
838 LatestEntry
= CurrentEntry
= LoaderList
;
839 if (LoaderList
== NULL
) {
840 LatestEntry
= NewEntry
;
842 while ((CurrentEntry
!= NULL
) && (TimeComp(&(NewEntry
->TimeStamp
), &(CurrentEntry
->TimeStamp
)) < 0)) {
843 PrevEntry
= CurrentEntry
;
844 CurrentEntry
= CurrentEntry
->NextEntry
;
846 NewEntry
->NextEntry
= CurrentEntry
;
847 if (PrevEntry
== NULL
) {
848 LatestEntry
= NewEntry
;
850 PrevEntry
->NextEntry
= NewEntry
;
853 return (LatestEntry
);
854 } // static VOID AddLoaderListEntry()
856 // Delete the LOADER_LIST linked list
857 static VOID
CleanUpLoaderList(struct LOADER_LIST
*LoaderList
) {
858 struct LOADER_LIST
*Temp
;
860 while (LoaderList
!= NULL
) {
862 LoaderList
= LoaderList
->NextEntry
;
863 MyFreePool(Temp
->FileName
);
866 } // static VOID CleanUpLoaderList()
868 // Scan an individual directory for EFI boot loader files and, if found,
869 // add them to the list. Sorts the entries within the loader directory
870 // so that the most recent one appears first in the list.
871 static VOID
ScanLoaderDir(IN REFIT_VOLUME
*Volume
, IN CHAR16
*Path
, IN CHAR16
*Pattern
)
874 REFIT_DIR_ITER DirIter
;
875 EFI_FILE_INFO
*DirEntry
;
876 CHAR16 FileName
[256], *Extension
;
877 struct LOADER_LIST
*LoaderList
= NULL
, *NewLoader
;
879 if ((!SelfDirPath
|| !Path
|| ((StriCmp(Path
, SelfDirPath
) == 0) && Volume
->DeviceHandle
!= SelfVolume
->DeviceHandle
) ||
880 (StriCmp(Path
, SelfDirPath
) != 0)) && (!IsIn(Path
, GlobalConfig
.DontScanDirs
))) {
881 // look through contents of the directory
882 DirIterOpen(Volume
->RootDir
, Path
, &DirIter
);
883 while (DirIterNext(&DirIter
, 2, Pattern
, &DirEntry
)) {
884 Extension
= FindExtension(DirEntry
->FileName
);
885 if (DirEntry
->FileName
[0] == '.' ||
886 StriCmp(Extension
, L
".icns") == 0 ||
887 StriSubCmp(L
"shell", DirEntry
->FileName
) ||
888 IsIn(DirEntry
->FileName
, GlobalConfig
.DontScanFiles
))
889 continue; // skip this
892 SPrint(FileName
, 255, L
"\\%s\\%s", Path
, DirEntry
->FileName
);
894 SPrint(FileName
, 255, L
"\\%s", DirEntry
->FileName
);
895 CleanUpPathNameSlashes(FileName
);
896 NewLoader
= AllocateZeroPool(sizeof(struct LOADER_LIST
));
897 if (NewLoader
!= NULL
) {
898 NewLoader
->FileName
= StrDuplicate(FileName
);
899 NewLoader
->TimeStamp
= DirEntry
->ModificationTime
;
900 LoaderList
= AddLoaderListEntry(LoaderList
, NewLoader
);
902 MyFreePool(Extension
);
904 NewLoader
= LoaderList
;
905 while (NewLoader
!= NULL
) {
906 AddLoaderEntry(NewLoader
->FileName
, NULL
, Volume
);
907 NewLoader
= NewLoader
->NextEntry
;
909 CleanUpLoaderList(LoaderList
);
910 Status
= DirIterClose(&DirIter
);
911 if (Status
!= EFI_NOT_FOUND
) {
913 SPrint(FileName
, 255, L
"while scanning the %s directory", Path
);
915 StrCpy(FileName
, L
"while scanning the root directory");
916 CheckError(Status
, FileName
);
917 } // if (Status != EFI_NOT_FOUND)
918 } // if not scanning our own directory
919 } /* static VOID ScanLoaderDir() */
921 static VOID
ScanEfiFiles(REFIT_VOLUME
*Volume
) {
923 REFIT_DIR_ITER EfiDirIter
;
924 EFI_FILE_INFO
*EfiDirEntry
;
925 CHAR16 FileName
[256], *Directory
, *MatchPatterns
;
928 MatchPatterns
= StrDuplicate(LOADER_MATCH_PATTERNS
);
929 if (GlobalConfig
.ScanAllLinux
)
930 MergeStrings(&MatchPatterns
, LINUX_MATCH_PATTERNS
, L
',');
932 if ((Volume
->RootDir
!= NULL
) && (Volume
->VolName
!= NULL
)) {
933 // check for Mac OS X boot loader
934 if (!IsIn(L
"\\System\\Library\\CoreServices", GlobalConfig
.DontScanDirs
)) {
935 StrCpy(FileName
, MACOSX_LOADER_PATH
);
936 if (FileExists(Volume
->RootDir
, FileName
) && !IsIn(L
"boot.efi", GlobalConfig
.DontScanFiles
)) {
937 AddLoaderEntry(FileName
, L
"Mac OS X", Volume
);
941 StrCpy(FileName
, L
"\\System\\Library\\CoreServices\\xom.efi");
942 if (FileExists(Volume
->RootDir
, FileName
) && !IsIn(L
"boot.efi", GlobalConfig
.DontScanFiles
)) {
943 AddLoaderEntry(FileName
, L
"Windows XP (XoM)", Volume
);
945 } // if Mac directory not in GlobalConfig.DontScanDirs list
947 // check for Microsoft boot loader/menu
948 StrCpy(FileName
, L
"\\EFI\\Microsoft\\Boot\\Bootmgfw.efi");
949 if (FileExists(Volume
->RootDir
, FileName
) && !IsIn(L
"\\EFI\\Microsoft\\Boot", GlobalConfig
.DontScanDirs
) &&
950 !IsIn(L
"bootmgfw.efi", GlobalConfig
.DontScanFiles
)) {
951 AddLoaderEntry(FileName
, L
"Microsoft EFI boot", Volume
);
954 // scan the root directory for EFI executables
955 ScanLoaderDir(Volume
, L
"\\", MatchPatterns
);
957 // scan subdirectories of the EFI directory (as per the standard)
958 DirIterOpen(Volume
->RootDir
, L
"EFI", &EfiDirIter
);
959 while (DirIterNext(&EfiDirIter
, 1, NULL
, &EfiDirEntry
)) {
960 if (StriCmp(EfiDirEntry
->FileName
, L
"tools") == 0 || EfiDirEntry
->FileName
[0] == '.')
961 continue; // skip this, doesn't contain boot loaders
962 SPrint(FileName
, 255, L
"\\EFI\\%s", EfiDirEntry
->FileName
);
963 ScanLoaderDir(Volume
, FileName
, MatchPatterns
);
965 Status
= DirIterClose(&EfiDirIter
);
966 if (Status
!= EFI_NOT_FOUND
)
967 CheckError(Status
, L
"while scanning the EFI directory");
969 // Scan user-specified (or additional default) directories....
971 while ((Directory
= FindCommaDelimited(GlobalConfig
.AlsoScan
, i
++)) != NULL
) {
972 CleanUpPathNameSlashes(Directory
);
973 Length
= StrLen(Directory
);
975 ScanLoaderDir(Volume
, Directory
, MatchPatterns
);
976 MyFreePool(Directory
);
979 } // static VOID ScanEfiFiles()
981 // Scan internal disks for valid EFI boot loaders....
982 static VOID
ScanInternal(VOID
) {
985 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
986 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_INTERNAL
) {
987 ScanEfiFiles(Volumes
[VolumeIndex
]);
990 } // static VOID ScanInternal()
992 // Scan external disks for valid EFI boot loaders....
993 static VOID
ScanExternal(VOID
) {
996 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
997 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_EXTERNAL
) {
998 ScanEfiFiles(Volumes
[VolumeIndex
]);
1001 } // static VOID ScanExternal()
1003 // Scan internal disks for valid EFI boot loaders....
1004 static VOID
ScanOptical(VOID
) {
1007 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1008 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_OPTICAL
) {
1009 ScanEfiFiles(Volumes
[VolumeIndex
]);
1012 } // static VOID ScanOptical()
1015 // legacy boot functions
1018 static EFI_STATUS
ActivateMbrPartition(IN EFI_BLOCK_IO
*BlockIO
, IN UINTN PartitionIndex
)
1021 UINT8 SectorBuffer
[512];
1022 MBR_PARTITION_INFO
*MbrTable
, *EMbrTable
;
1023 UINT32 ExtBase
, ExtCurrent
, NextExtCurrent
;
1024 UINTN LogicalPartitionIndex
= 4;
1026 BOOLEAN HaveBootCode
;
1029 Status
= refit_call5_wrapper(BlockIO
->ReadBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, 0, 512, SectorBuffer
);
1030 if (EFI_ERROR(Status
))
1032 if (*((UINT16
*)(SectorBuffer
+ 510)) != 0xaa55)
1033 return EFI_NOT_FOUND
; // safety measure #1
1035 // add boot code if necessary
1036 HaveBootCode
= FALSE
;
1037 for (i
= 0; i
< MBR_BOOTCODE_SIZE
; i
++) {
1038 if (SectorBuffer
[i
] != 0) {
1039 HaveBootCode
= TRUE
;
1043 if (!HaveBootCode
) {
1044 // no boot code found in the MBR, add the syslinux MBR code
1045 SetMem(SectorBuffer
, MBR_BOOTCODE_SIZE
, 0);
1046 CopyMem(SectorBuffer
, syslinux_mbr
, SYSLINUX_MBR_SIZE
);
1049 // set the partition active
1050 MbrTable
= (MBR_PARTITION_INFO
*)(SectorBuffer
+ 446);
1052 for (i
= 0; i
< 4; i
++) {
1053 if (MbrTable
[i
].Flags
!= 0x00 && MbrTable
[i
].Flags
!= 0x80)
1054 return EFI_NOT_FOUND
; // safety measure #2
1055 if (i
== PartitionIndex
)
1056 MbrTable
[i
].Flags
= 0x80;
1057 else if (PartitionIndex
>= 4 && IS_EXTENDED_PART_TYPE(MbrTable
[i
].Type
)) {
1058 MbrTable
[i
].Flags
= 0x80;
1059 ExtBase
= MbrTable
[i
].StartLBA
;
1061 MbrTable
[i
].Flags
= 0x00;
1065 Status
= refit_call5_wrapper(BlockIO
->WriteBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, 0, 512, SectorBuffer
);
1066 if (EFI_ERROR(Status
))
1069 if (PartitionIndex
>= 4) {
1070 // we have to activate a logical partition, so walk the EMBR chain
1072 // NOTE: ExtBase was set above while looking at the MBR table
1073 for (ExtCurrent
= ExtBase
; ExtCurrent
; ExtCurrent
= NextExtCurrent
) {
1074 // read current EMBR
1075 Status
= refit_call5_wrapper(BlockIO
->ReadBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, ExtCurrent
, 512, SectorBuffer
);
1076 if (EFI_ERROR(Status
))
1078 if (*((UINT16
*)(SectorBuffer
+ 510)) != 0xaa55)
1079 return EFI_NOT_FOUND
; // safety measure #3
1081 // scan EMBR, set appropriate partition active
1082 EMbrTable
= (MBR_PARTITION_INFO
*)(SectorBuffer
+ 446);
1084 for (i
= 0; i
< 4; i
++) {
1085 if (EMbrTable
[i
].Flags
!= 0x00 && EMbrTable
[i
].Flags
!= 0x80)
1086 return EFI_NOT_FOUND
; // safety measure #4
1087 if (EMbrTable
[i
].StartLBA
== 0 || EMbrTable
[i
].Size
== 0)
1089 if (IS_EXTENDED_PART_TYPE(EMbrTable
[i
].Type
)) {
1090 // link to next EMBR
1091 NextExtCurrent
= ExtBase
+ EMbrTable
[i
].StartLBA
;
1092 EMbrTable
[i
].Flags
= (PartitionIndex
>= LogicalPartitionIndex
) ? 0x80 : 0x00;
1095 // logical partition
1096 EMbrTable
[i
].Flags
= (PartitionIndex
== LogicalPartitionIndex
) ? 0x80 : 0x00;
1097 LogicalPartitionIndex
++;
1101 // write current EMBR
1102 Status
= refit_call5_wrapper(BlockIO
->WriteBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, ExtCurrent
, 512, SectorBuffer
);
1103 if (EFI_ERROR(Status
))
1106 if (PartitionIndex
< LogicalPartitionIndex
)
1107 break; // stop the loop, no need to touch further EMBRs
1113 } /* static EFI_STATUS ActivateMbrPartition() */
1115 // early 2006 Core Duo / Core Solo models
1116 static UINT8 LegacyLoaderDevicePath1Data
[] = {
1117 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1118 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1119 0xFF, 0xFF, 0xF9, 0xFF, 0x00, 0x00, 0x00, 0x00,
1120 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1121 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1122 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1124 // mid-2006 Mac Pro (and probably other Core 2 models)
1125 static UINT8 LegacyLoaderDevicePath2Data
[] = {
1126 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1127 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1128 0xFF, 0xFF, 0xF7, 0xFF, 0x00, 0x00, 0x00, 0x00,
1129 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1130 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1131 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1133 // mid-2007 MBP ("Santa Rosa" based models)
1134 static UINT8 LegacyLoaderDevicePath3Data
[] = {
1135 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1136 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1137 0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
1138 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1139 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1140 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1143 static UINT8 LegacyLoaderDevicePath4Data
[] = {
1144 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1145 0x00, 0x00, 0xC0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1146 0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
1147 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1148 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1149 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1151 // late-2008 MB/MBP (NVidia chipset)
1152 static UINT8 LegacyLoaderDevicePath5Data
[] = {
1153 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1154 0x00, 0x40, 0xCB, 0xFF, 0x00, 0x00, 0x00, 0x00,
1155 0xFF, 0xBF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
1156 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1157 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1158 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1161 static EFI_DEVICE_PATH
*LegacyLoaderList
[] = {
1162 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath1Data
,
1163 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath2Data
,
1164 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath3Data
,
1165 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath4Data
,
1166 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath5Data
,
1170 #define MAX_DISCOVERED_PATHS (16)
1172 static VOID
StartLegacy(IN LEGACY_ENTRY
*Entry
)
1175 EG_IMAGE
*BootLogoImage
;
1176 UINTN ErrorInStep
= 0;
1177 EFI_DEVICE_PATH
*DiscoveredPathList
[MAX_DISCOVERED_PATHS
];
1179 BeginExternalScreen(TRUE
, L
"Booting Legacy OS (Mac mode)");
1181 BootLogoImage
= LoadOSIcon(Entry
->Volume
->OSIconName
, L
"legacy", TRUE
);
1182 if (BootLogoImage
!= NULL
)
1183 BltImageAlpha(BootLogoImage
,
1184 (UGAWidth
- BootLogoImage
->Width
) >> 1,
1185 (UGAHeight
- BootLogoImage
->Height
) >> 1,
1186 &StdBackgroundPixel
);
1188 if (Entry
->Volume
->IsMbrPartition
) {
1189 ActivateMbrPartition(Entry
->Volume
->WholeDiskBlockIO
, Entry
->Volume
->MbrPartitionIndex
);
1192 ExtractLegacyLoaderPaths(DiscoveredPathList
, MAX_DISCOVERED_PATHS
, LegacyLoaderList
);
1194 Status
= StartEFIImageList(DiscoveredPathList
, Entry
->LoadOptions
, NULL
, L
"legacy loader", 0, &ErrorInStep
, TRUE
);
1195 if (Status
== EFI_NOT_FOUND
) {
1196 if (ErrorInStep
== 1) {
1197 Print(L
"\nPlease make sure that you have the latest firmware update installed.\n");
1198 } else if (ErrorInStep
== 3) {
1199 Print(L
"\nThe firmware refused to boot from the selected volume. Note that external\n"
1200 L
"hard drives are not well-supported by Apple's firmware for legacy OS booting.\n");
1203 FinishExternalScreen();
1204 } /* static VOID StartLegacy() */
1206 // Start a device on a non-Mac using the EFI_LEGACY_BIOS_PROTOCOL
1207 #ifdef __MAKEWITH_TIANO
1208 static VOID
StartLegacyUEFI(IN LEGACY_ENTRY
*Entry
)
1210 BeginExternalScreen(TRUE
, L
"Booting Legacy OS (UEFI mode)");
1212 BdsLibConnectDevicePath (Entry
->BdsOption
->DevicePath
);
1213 BdsLibDoLegacyBoot(Entry
->BdsOption
);
1215 // If we get here, it means that there was a failure....
1216 Print(L
"Failure booting legacy (BIOS) OS.");
1218 FinishExternalScreen();
1219 } // static VOID StartLegacyUEFI()
1220 #endif // __MAKEWITH_TIANO
1222 static LEGACY_ENTRY
* AddLegacyEntry(IN CHAR16
*LoaderTitle
, IN REFIT_VOLUME
*Volume
)
1224 LEGACY_ENTRY
*Entry
, *SubEntry
;
1225 REFIT_MENU_SCREEN
*SubScreen
;
1227 CHAR16 ShortcutLetter
= 0;
1229 if (LoaderTitle
== NULL
) {
1230 if (Volume
->OSName
!= NULL
) {
1231 LoaderTitle
= Volume
->OSName
;
1232 if (LoaderTitle
[0] == 'W' || LoaderTitle
[0] == 'L')
1233 ShortcutLetter
= LoaderTitle
[0];
1235 LoaderTitle
= L
"Legacy OS";
1237 if (Volume
->VolName
!= NULL
)
1238 VolDesc
= Volume
->VolName
;
1240 VolDesc
= (Volume
->DiskKind
== DISK_KIND_OPTICAL
) ? L
"CD" : L
"HD";
1242 // prepare the menu entry
1243 Entry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1244 Entry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1245 SPrint(Entry
->me
.Title
, 255, L
"Boot %s from %s", LoaderTitle
, VolDesc
);
1246 Entry
->me
.Tag
= TAG_LEGACY
;
1248 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1249 Entry
->me
.Image
= LoadOSIcon(Volume
->OSIconName
, L
"legacy", FALSE
);
1250 Entry
->me
.BadgeImage
= Volume
->VolBadgeImage
;
1251 Entry
->Volume
= Volume
;
1252 Entry
->LoadOptions
= (Volume
->DiskKind
== DISK_KIND_OPTICAL
) ? L
"CD" :
1253 ((Volume
->DiskKind
== DISK_KIND_EXTERNAL
) ? L
"USB" : L
"HD");
1254 Entry
->Enabled
= TRUE
;
1256 // create the submenu
1257 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
1258 SubScreen
->Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1259 SPrint(SubScreen
->Title
, 255, L
"Boot Options for %s on %s", LoaderTitle
, VolDesc
);
1260 SubScreen
->TitleImage
= Entry
->me
.Image
;
1263 SubEntry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1264 SubEntry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1265 SPrint(SubEntry
->me
.Title
, 255, L
"Boot %s", LoaderTitle
);
1266 SubEntry
->me
.Tag
= TAG_LEGACY
;
1267 SubEntry
->Volume
= Entry
->Volume
;
1268 SubEntry
->LoadOptions
= Entry
->LoadOptions
;
1269 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
1271 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
1272 Entry
->me
.SubScreen
= SubScreen
;
1273 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1275 } /* static LEGACY_ENTRY * AddLegacyEntry() */
1278 #ifdef __MAKEWITH_TIANO
1279 // default volume badge icon based on disk kind
1280 static EG_IMAGE
* GetDiskBadge(IN UINTN DiskType
) {
1281 EG_IMAGE
* Badge
= NULL
;
1285 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_INTERNAL
);
1288 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_EXTERNAL
);
1291 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_OPTICAL
);
1295 } // static EG_IMAGE * GetDiskBadge()
1298 Create a rEFInd boot option from a Legacy BIOS protocol option.
1300 static LEGACY_ENTRY
* AddLegacyEntryUEFI(BDS_COMMON_OPTION
*BdsOption
, IN UINT16 DiskType
)
1302 LEGACY_ENTRY
*Entry
, *SubEntry
;
1303 REFIT_MENU_SCREEN
*SubScreen
;
1304 CHAR16 ShortcutLetter
= 0;
1305 CHAR16
*LegacyDescription
= BdsOption
->Description
;
1307 // ScanVolume(Volume);
1309 // prepare the menu entry
1310 Entry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1311 Entry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1312 SPrint(Entry
->me
.Title
, 255, L
"Boot legacy target %s", LegacyDescription
);
1313 Entry
->me
.Tag
= TAG_LEGACY_UEFI
;
1315 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1316 Entry
->me
.Image
= LoadOSIcon(L
"legacy", L
"legacy", TRUE
);
1317 Entry
->LoadOptions
= (DiskType
== BBS_CDROM
) ? L
"CD" :
1318 ((DiskType
== BBS_USB
) ? L
"USB" : L
"HD");
1319 Entry
->me
.BadgeImage
= GetDiskBadge(DiskType
);
1320 // Entry->me.BadgeImage = Volume->VolBadgeImage;
1321 Entry
->BdsOption
= BdsOption
;
1322 Entry
->Enabled
= TRUE
;
1324 // create the submenu
1325 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
1326 SubScreen
->Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1327 SPrint(SubScreen
->Title
, 255, L
"No boot options for legacy target");
1328 SubScreen
->TitleImage
= Entry
->me
.Image
;
1331 SubEntry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1332 SubEntry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1333 SPrint(SubEntry
->me
.Title
, 255, L
"Boot %s", LegacyDescription
);
1334 SubEntry
->me
.Tag
= TAG_LEGACY_UEFI
;
1335 Entry
->BdsOption
= BdsOption
;
1336 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
1338 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
1339 Entry
->me
.SubScreen
= SubScreen
;
1340 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1342 } /* static LEGACY_ENTRY * AddLegacyEntryUEFI() */
1345 Scan for legacy BIOS targets on machines that implement EFI_LEGACY_BIOS_PROTOCOL.
1346 In testing, protocol has not been implemented on Macs but has been
1347 implemented on several Dell PCs and an ASUS motherboard.
1348 Restricts output to disks of the specified DiskType.
1350 static VOID
ScanLegacyUEFI(IN UINTN DiskType
)
1353 EFI_LEGACY_BIOS_PROTOCOL
*LegacyBios
;
1354 UINT16
*BootOrder
= NULL
;
1356 CHAR16 BootOption
[10];
1357 UINTN BootOrderSize
= 0;
1359 BDS_COMMON_OPTION
*BdsOption
;
1360 LIST_ENTRY TempList
;
1361 BBS_BBS_DEVICE_PATH
* BbsDevicePath
= NULL
;
1362 // REFIT_VOLUME Volume;
1364 InitializeListHead (&TempList
);
1365 ZeroMem (Buffer
, sizeof (Buffer
));
1367 // If LegacyBios protocol is not implemented on this platform, then
1368 //we do not support this type of legacy boot on this machine.
1369 Status
= gBS
->LocateProtocol(&gEfiLegacyBootProtocolGuid
, NULL
, (VOID
**) &LegacyBios
);
1370 if (EFI_ERROR (Status
))
1373 // Grab the boot order
1374 BootOrder
= BdsLibGetVariableAndSize(L
"BootOrder", &gEfiGlobalVariableGuid
, &BootOrderSize
);
1375 if (BootOrder
== NULL
) {
1380 while (Index
< BootOrderSize
/ sizeof (UINT16
))
1382 // Grab each boot option variable from the boot order, and convert
1383 // the variable into a BDS boot option
1384 UnicodeSPrint (BootOption
, sizeof (BootOption
), L
"Boot%04x", BootOrder
[Index
]);
1385 BdsOption
= BdsLibVariableToOption (&TempList
, BootOption
);
1387 if (BdsOption
!= NULL
) {
1388 BbsDevicePath
= (BBS_BBS_DEVICE_PATH
*)BdsOption
->DevicePath
;
1390 // Only add the entry if it is of a requested type (e.g. USB, HD)
1392 // Two checks necessary because some systems return EFI boot loaders
1393 // with a DeviceType value that would inappropriately include them
1394 // as legacy loaders....
1395 if ((BbsDevicePath
->DeviceType
== DiskType
) && (BdsOption
->DevicePath
->Type
== DEVICE_TYPE_BIOS
)) {
1396 AddLegacyEntryUEFI(BdsOption
, BbsDevicePath
->DeviceType
);
1401 } /* static VOID ScanLegacyUEFI() */
1403 static VOID
ScanLegacyUEFI(IN UINTN DiskType
){}
1404 #endif // __MAKEWITH_TIANO
1406 static VOID
ScanLegacyVolume(REFIT_VOLUME
*Volume
, UINTN VolumeIndex
) {
1408 BOOLEAN ShowVolume
, HideIfOthersFound
;
1411 HideIfOthersFound
= FALSE
;
1412 if (Volume
->IsAppleLegacy
) {
1414 HideIfOthersFound
= TRUE
;
1415 } else if (Volume
->HasBootCode
) {
1417 if (Volume
->BlockIO
== Volume
->WholeDiskBlockIO
&&
1418 Volume
->BlockIOOffset
== 0 &&
1419 Volume
->OSName
== NULL
)
1420 // this is a whole disk (MBR) entry; hide if we have entries for partitions
1421 HideIfOthersFound
= TRUE
;
1423 if (HideIfOthersFound
) {
1424 // check for other bootable entries on the same disk
1425 for (VolumeIndex2
= 0; VolumeIndex2
< VolumesCount
; VolumeIndex2
++) {
1426 if (VolumeIndex2
!= VolumeIndex
&& Volumes
[VolumeIndex2
]->HasBootCode
&&
1427 Volumes
[VolumeIndex2
]->WholeDiskBlockIO
== Volume
->WholeDiskBlockIO
)
1433 AddLegacyEntry(NULL
, Volume
);
1434 } // static VOID ScanLegacyVolume()
1436 // Scan attached optical discs for legacy (BIOS) boot code
1437 // and add anything found to the list....
1438 static VOID
ScanLegacyDisc(VOID
)
1441 REFIT_VOLUME
*Volume
;
1443 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
) {
1444 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1445 Volume
= Volumes
[VolumeIndex
];
1446 if (Volume
->DiskKind
== DISK_KIND_OPTICAL
)
1447 ScanLegacyVolume(Volume
, VolumeIndex
);
1449 } else if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
1450 ScanLegacyUEFI(BBS_CDROM
);
1452 } /* static VOID ScanLegacyDisc() */
1454 // Scan internal hard disks for legacy (BIOS) boot code
1455 // and add anything found to the list....
1456 static VOID
ScanLegacyInternal(VOID
)
1459 REFIT_VOLUME
*Volume
;
1461 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
) {
1462 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1463 Volume
= Volumes
[VolumeIndex
];
1464 if (Volume
->DiskKind
== DISK_KIND_INTERNAL
)
1465 ScanLegacyVolume(Volume
, VolumeIndex
);
1467 } else if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
1468 ScanLegacyUEFI(BBS_HARDDISK
);
1470 } /* static VOID ScanLegacyInternal() */
1472 // Scan external disks for legacy (BIOS) boot code
1473 // and add anything found to the list....
1474 static VOID
ScanLegacyExternal(VOID
)
1477 REFIT_VOLUME
*Volume
;
1479 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
) {
1480 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1481 Volume
= Volumes
[VolumeIndex
];
1482 if (Volume
->DiskKind
== DISK_KIND_EXTERNAL
)
1483 ScanLegacyVolume(Volume
, VolumeIndex
);
1485 } else if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
1486 ScanLegacyUEFI(BBS_USB
);
1488 } /* static VOID ScanLegacyExternal() */
1491 // pre-boot tool functions
1494 static VOID
StartTool(IN LOADER_ENTRY
*Entry
)
1496 BeginExternalScreen(Entry
->UseGraphicsMode
, Entry
->me
.Title
+ 6); // assumes "Start <title>" as assigned below
1497 StartEFIImage(Entry
->DevicePath
, Entry
->LoadOptions
, Basename(Entry
->LoaderPath
),
1498 Basename(Entry
->LoaderPath
), Entry
->OSType
, NULL
, TRUE
);
1499 FinishExternalScreen();
1500 } /* static VOID StartTool() */
1502 static LOADER_ENTRY
* AddToolEntry(EFI_HANDLE DeviceHandle
, IN CHAR16
*LoaderPath
, IN CHAR16
*LoaderTitle
, IN EG_IMAGE
*Image
,
1503 IN CHAR16 ShortcutLetter
, IN BOOLEAN UseGraphicsMode
)
1505 LOADER_ENTRY
*Entry
;
1506 CHAR16
*TitleStr
= NULL
;
1508 Entry
= AllocateZeroPool(sizeof(LOADER_ENTRY
));
1510 MergeStrings(&TitleStr
, L
"Start ", 0);
1511 MergeStrings(&TitleStr
, LoaderTitle
, 0);
1512 Entry
->me
.Title
= TitleStr
;
1513 Entry
->me
.Tag
= TAG_TOOL
;
1515 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1516 Entry
->me
.Image
= Image
;
1517 Entry
->LoaderPath
= (LoaderPath
) ? StrDuplicate(LoaderPath
) : NULL
;
1518 Entry
->DevicePath
= FileDevicePath(DeviceHandle
, Entry
->LoaderPath
);
1519 Entry
->UseGraphicsMode
= UseGraphicsMode
;
1521 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1523 } /* static LOADER_ENTRY * AddToolEntry() */
1526 // pre-boot driver functions
1529 static UINTN
ScanDriverDir(IN CHAR16
*Path
)
1532 REFIT_DIR_ITER DirIter
;
1534 EFI_FILE_INFO
*DirEntry
;
1535 CHAR16 FileName
[256];
1537 CleanUpPathNameSlashes(Path
);
1538 // look through contents of the directory
1539 DirIterOpen(SelfRootDir
, Path
, &DirIter
);
1540 while (DirIterNext(&DirIter
, 2, LOADER_MATCH_PATTERNS
, &DirEntry
)) {
1541 if (DirEntry
->FileName
[0] == '.')
1542 continue; // skip this
1544 SPrint(FileName
, 255, L
"%s\\%s", Path
, DirEntry
->FileName
);
1546 Status
= StartEFIImage(FileDevicePath(SelfLoadedImage
->DeviceHandle
, FileName
),
1547 L
"", DirEntry
->FileName
, DirEntry
->FileName
, 0, NULL
, FALSE
);
1549 Status
= DirIterClose(&DirIter
);
1550 if (Status
!= EFI_NOT_FOUND
) {
1551 SPrint(FileName
, 255, L
"while scanning the %s directory", Path
);
1552 CheckError(Status
, FileName
);
1557 #ifdef __MAKEWITH_GNUEFI
1558 static EFI_STATUS
ConnectAllDriversToAllControllers(VOID
)
1561 UINTN AllHandleCount
;
1562 EFI_HANDLE
*AllHandleBuffer
;
1565 EFI_HANDLE
*HandleBuffer
;
1571 Status
= LibLocateHandle(AllHandles
,
1576 if (EFI_ERROR(Status
))
1579 for (Index
= 0; Index
< AllHandleCount
; Index
++) {
1581 // Scan the handle database
1583 Status
= LibScanHandleDatabase(NULL
,
1585 AllHandleBuffer
[Index
],
1590 if (EFI_ERROR (Status
))
1594 if (HandleType
[Index
] & EFI_HANDLE_TYPE_DRIVER_BINDING_HANDLE
)
1596 if (HandleType
[Index
] & EFI_HANDLE_TYPE_IMAGE_HANDLE
)
1601 for (HandleIndex
= 0; HandleIndex
< HandleCount
; HandleIndex
++) {
1602 if (HandleType
[HandleIndex
] & EFI_HANDLE_TYPE_PARENT_HANDLE
)
1607 if (HandleType
[Index
] & EFI_HANDLE_TYPE_DEVICE_HANDLE
) {
1608 Status
= refit_call4_wrapper(BS
->ConnectController
,
1609 AllHandleBuffer
[Index
],
1617 MyFreePool (HandleBuffer
);
1618 MyFreePool (HandleType
);
1622 MyFreePool (AllHandleBuffer
);
1624 } /* EFI_STATUS ConnectAllDriversToAllControllers() */
1626 static EFI_STATUS
ConnectAllDriversToAllControllers(VOID
) {
1627 BdsLibConnectAllDriversToAllControllers();
1632 // Load all EFI drivers from rEFInd's "drivers" subdirectory and from the
1633 // directories specified by the user in the "scan_driver_dirs" configuration
1635 static VOID
LoadDrivers(VOID
)
1637 CHAR16
*Directory
, *SelfDirectory
;
1638 UINTN i
= 0, Length
, NumFound
= 0;
1640 // load drivers from the subdirectories of rEFInd's home directory specified
1641 // in the DRIVER_DIRS constant.
1642 while ((Directory
= FindCommaDelimited(DRIVER_DIRS
, i
++)) != NULL
) {
1643 SelfDirectory
= SelfDirPath
? StrDuplicate(SelfDirPath
) : NULL
;
1644 CleanUpPathNameSlashes(SelfDirectory
);
1645 MergeStrings(&SelfDirectory
, Directory
, L
'\\');
1646 NumFound
+= ScanDriverDir(SelfDirectory
);
1647 MyFreePool(Directory
);
1648 MyFreePool(SelfDirectory
);
1651 // Scan additional user-specified driver directories....
1653 while ((Directory
= FindCommaDelimited(GlobalConfig
.DriverDirs
, i
++)) != NULL
) {
1654 CleanUpPathNameSlashes(Directory
);
1655 Length
= StrLen(Directory
);
1657 NumFound
+= ScanDriverDir(Directory
);
1659 MyFreePool(Directory
);
1662 // connect all devices
1664 ConnectAllDriversToAllControllers();
1665 } /* static VOID LoadDrivers() */
1667 // Determine what (if any) type of legacy (BIOS) boot support is available
1668 static VOID
FindLegacyBootType(VOID
) {
1669 #ifdef __MAKEWITH_TIANO
1671 EFI_LEGACY_BIOS_PROTOCOL
*LegacyBios
;
1674 GlobalConfig
.LegacyType
= LEGACY_TYPE_NONE
;
1676 // UEFI-style legacy BIOS support is available only with the TianoCore EDK2
1677 // build environment, and then only with some implementations....
1678 #ifdef __MAKEWITH_TIANO
1679 Status
= gBS
->LocateProtocol (&gEfiLegacyBootProtocolGuid
, NULL
, (VOID
**) &LegacyBios
);
1680 if (!EFI_ERROR (Status
))
1681 GlobalConfig
.LegacyType
= LEGACY_TYPE_UEFI
;
1684 // Macs have their own system. If the firmware vendor code contains the
1685 // string "Apple", assume it's available. Note that this overrides the
1686 // UEFI type, and might yield false positives if the vendor string
1687 // contains "Apple" as part of something bigger, so this isn't 100%
1689 if (StriSubCmp(L
"Apple", ST
->FirmwareVendor
))
1690 GlobalConfig
.LegacyType
= LEGACY_TYPE_MAC
;
1691 } // static VOID FindLegacyBootType
1693 // Warn the user if legacy OS scans are enabled but the firmware or this
1694 // application can't support them....
1695 static VOID
WarnIfLegacyProblems() {
1696 BOOLEAN found
= FALSE
;
1699 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_NONE
) {
1701 if (GlobalConfig
.ScanFor
[i
] == 'h' || GlobalConfig
.ScanFor
[i
] == 'b' || GlobalConfig
.ScanFor
[i
] == 'c')
1704 } while ((i
< NUM_SCAN_OPTIONS
) && (!found
));
1706 Print(L
"NOTE: refind.conf's 'scanfor' line specifies scanning for one or more legacy\n");
1707 Print(L
"(BIOS) boot options; however, this is not possible because ");
1708 #ifdef __MAKEWITH_TIANO
1709 Print(L
"your computer lacks\n");
1710 Print(L
"the necessary Compatibility Support Module (CSM) support.\n");
1712 Print(L
"this program was\n");
1713 Print(L
"compiled without the necessary support. Please visit\n");
1714 Print(L
"http://www.rodsbooks.com/refind/getting.html and download and install a rEFInd\n");
1715 Print(L
"binary built with the TianoCore EDK2 to enable legacy boot support.\n");
1719 } // if no legacy support
1720 } // static VOID WarnIfLegacyProblems()
1722 // Locates boot loaders. NOTE: This assumes that GlobalConfig.LegacyType is set correctly.
1723 static VOID
ScanForBootloaders(VOID
) {
1728 // scan for loaders and tools, add them to the menu
1729 for (i
= 0; i
< NUM_SCAN_OPTIONS
; i
++) {
1730 switch(GlobalConfig
.ScanFor
[i
]) {
1735 ScanLegacyInternal();
1738 ScanLegacyExternal();
1741 ScanUserConfigured();
1755 // assign shortcut keys
1756 for (i
= 0; i
< MainMenu
.EntryCount
&& MainMenu
.Entries
[i
]->Row
== 0 && i
< 9; i
++)
1757 MainMenu
.Entries
[i
]->ShortcutDigit
= (CHAR16
)('1' + i
);
1759 // wait for user ACK when there were errors
1760 FinishTextScreen(FALSE
);
1761 } // static VOID ScanForBootloaders()
1763 // Add the second-row tags containing built-in and external tools (EFI shell,
1765 static VOID
ScanForTools(VOID
) {
1766 CHAR16
*FileName
= NULL
, Description
[256];
1767 REFIT_MENU_ENTRY
*TempMenuEntry
;
1768 UINTN i
, j
, VolumeIndex
;
1770 for (i
= 0; i
< NUM_TOOLS
; i
++) {
1771 switch(GlobalConfig
.ShowTools
[i
]) {
1773 TempMenuEntry
= CopyMenuEntry(&MenuEntryShutdown
);
1774 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_SHUTDOWN
);
1775 AddMenuEntry(&MainMenu
, TempMenuEntry
);
1778 TempMenuEntry
= CopyMenuEntry(&MenuEntryReset
);
1779 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_RESET
);
1780 AddMenuEntry(&MainMenu
, TempMenuEntry
);
1783 TempMenuEntry
= CopyMenuEntry(&MenuEntryAbout
);
1784 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT
);
1785 AddMenuEntry(&MainMenu
, TempMenuEntry
);
1788 TempMenuEntry
= CopyMenuEntry(&MenuEntryExit
);
1789 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_EXIT
);
1790 AddMenuEntry(&MainMenu
, TempMenuEntry
);
1794 MyFreePool(FileName
);
1795 while ((FileName
= FindCommaDelimited(SHELL_NAMES
, j
++)) != NULL
) {
1796 if (FileExists(SelfRootDir
, FileName
)) {
1797 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, L
"EFI Shell", BuiltinIcon(BUILTIN_ICON_TOOL_SHELL
),
1803 MyFreePool(FileName
);
1805 MergeStrings(&FileName
, L
"\\efi\\tools\\gptsync.efi", 0);
1806 if (FileExists(SelfRootDir
, FileName
)) {
1807 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, L
"Make Hybrid MBR", BuiltinIcon(BUILTIN_ICON_TOOL_PART
), 'P', FALSE
);
1810 case TAG_APPLE_RECOVERY
:
1811 MyFreePool(FileName
);
1813 MergeStrings(&FileName
, L
"\\com.apple.recovery.boot\\boot.efi", 0);
1814 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1815 if ((Volumes
[VolumeIndex
]->RootDir
!= NULL
) && (FileExists(Volumes
[VolumeIndex
]->RootDir
, FileName
))) {
1816 SPrint(Description
, 255, L
"Apple Recovery on %s", Volumes
[VolumeIndex
]->VolName
);
1817 AddToolEntry(Volumes
[VolumeIndex
]->DeviceHandle
, FileName
, Description
,
1818 BuiltinIcon(BUILTIN_ICON_TOOL_APPLE_RESCUE
), 'R', TRUE
);
1824 MyFreePool(FileName
);
1825 while ((FileName
= FindCommaDelimited(MOK_NAMES
, j
++)) != NULL
) {
1826 if (FileExists(SelfRootDir
, FileName
)) {
1827 SPrint(Description
, 255, L
"MOK Key Manager at %s", FileName
);
1828 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, Description
,
1829 BuiltinIcon(BUILTIN_ICON_TOOL_MOK_TOOL
), 'S', FALSE
);
1832 if (FileExists(SelfDir
, L
"MokManager.efi")) {
1833 MyFreePool(FileName
);
1834 FileName
= StrDuplicate(SelfDirPath
);
1835 MergeStrings(&FileName
, L
"\\MokManager.efi", 0);
1836 SPrint(Description
, 255, L
"MOK Key Manager at %s", FileName
);
1837 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, Description
,
1838 BuiltinIcon(BUILTIN_ICON_TOOL_MOK_TOOL
), 'S', FALSE
);
1842 MyFreePool(FileName
);
1845 } // static VOID ScanForTools
1847 // Rescan for boot loaders
1848 VOID
RescanAll(VOID
) {
1855 egDisplayMessage(L
"Scanning for new boot loaders; please wait....", &BGColor
);
1856 FreeList((VOID
***) &(MainMenu
.Entries
), &MainMenu
.EntryCount
);
1857 MainMenu
.Entries
= NULL
;
1858 MainMenu
.EntryCount
= 0;
1860 ConnectAllDriversToAllControllers();
1862 ScanForBootloaders();
1865 } // VOID RescanAll()
1867 #ifndef __MAKEWITH_GNUEFI
1869 // Minimal initialization function
1870 static VOID
InitializeLib(IN EFI_HANDLE ImageHandle
, IN EFI_SYSTEM_TABLE
*SystemTable
) {
1872 // gImageHandle = ImageHandle;
1873 gBS
= SystemTable
->BootServices
;
1874 // gRS = SystemTable->RuntimeServices;
1875 gRT
= SystemTable
->RuntimeServices
; // Some BDS functions need gRT to be set
1876 EfiGetSystemConfigurationTable (&gEfiDxeServicesTableGuid
, (VOID
**) &gDS
);
1878 InitializeConsoleSim();
1888 efi_main (IN EFI_HANDLE ImageHandle
, IN EFI_SYSTEM_TABLE
*SystemTable
)
1891 BOOLEAN MainLoopRunning
= TRUE
;
1892 REFIT_MENU_ENTRY
*ChosenEntry
;
1898 InitializeLib(ImageHandle
, SystemTable
);
1900 Status
= InitRefitLib(ImageHandle
);
1901 if (EFI_ERROR(Status
))
1904 // read configuration
1905 CopyMem(GlobalConfig
.ScanFor
, "ieom ", NUM_SCAN_OPTIONS
);
1906 FindLegacyBootType();
1907 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
)
1908 CopyMem(GlobalConfig
.ScanFor
, "ihebocm ", NUM_SCAN_OPTIONS
);
1910 WarnIfLegacyProblems();
1911 MainMenu
.TimeoutSeconds
= GlobalConfig
.Timeout
;
1913 // disable EFI watchdog timer
1914 refit_call4_wrapper(BS
->SetWatchdogTimer
, 0x0000, 0x0000, 0x0000, NULL
);
1916 // further bootstrap (now with config available)
1920 ScanForBootloaders();
1923 if (GlobalConfig
.ScanDelay
> 0) {
1928 egDisplayMessage(L
"Pausing before disk scan; please wait....", &BGColor
);
1929 for (i
= 0; i
< GlobalConfig
.ScanDelay
; i
++)
1930 refit_call1_wrapper(BS
->Stall
, 1000000);
1934 Selection
= StrDuplicate(GlobalConfig
.DefaultSelection
);
1935 while (MainLoopRunning
) {
1936 MenuExit
= RunMainMenu(&MainMenu
, Selection
, &ChosenEntry
);
1938 // The Escape key triggers a re-scan operation....
1939 if (MenuExit
== MENU_EXIT_ESCAPE
) {
1944 switch (ChosenEntry
->Tag
) {
1946 case TAG_REBOOT
: // Reboot
1948 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);
1949 MainLoopRunning
= FALSE
; // just in case we get this far
1952 case TAG_SHUTDOWN
: // Shut Down
1954 refit_call4_wrapper(RT
->ResetSystem
, EfiResetShutdown
, EFI_SUCCESS
, 0, NULL
);
1955 MainLoopRunning
= FALSE
; // just in case we get this far
1958 case TAG_ABOUT
: // About rEFInd
1962 case TAG_LOADER
: // Boot OS via .EFI loader
1963 StartLoader((LOADER_ENTRY
*)ChosenEntry
);
1966 case TAG_LEGACY
: // Boot legacy OS
1967 StartLegacy((LEGACY_ENTRY
*)ChosenEntry
);
1970 #ifdef __MAKEWITH_TIANO
1971 case TAG_LEGACY_UEFI
: // Boot a legacy OS on a non-Mac
1972 StartLegacyUEFI((LEGACY_ENTRY
*)ChosenEntry
);
1974 #endif // __MAKEWITH_TIANO
1976 case TAG_TOOL
: // Start a EFI tool
1977 StartTool((LOADER_ENTRY
*)ChosenEntry
);
1980 case TAG_EXIT
: // Terminate rEFInd
1981 BeginTextScreen(L
" ");
1986 MyFreePool(Selection
);
1987 Selection
= (ChosenEntry
->Title
) ? StrDuplicate(ChosenEntry
->Title
) : NULL
;
1990 // If we end up here, things have gone wrong. Try to reboot, and if that
1991 // fails, go into an endless loop.
1992 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);