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"
60 #define EFI_SECURITY_VIOLATION EFIERR (26)
61 #endif // __MAKEWITH_TIANO
66 #define MACOSX_LOADER_PATH L"System\\Library\\CoreServices\\boot.efi"
68 #define SHELL_NAMES L"\\EFI\\tools\\shell.efi,\\EFI\\tools\\shellx64.efi,\\shellx64.efi"
69 #define DRIVER_DIRS L"drivers,drivers_x64"
71 #define SHELL_NAMES L"\\EFI\\tools\\shell.efi,\\EFI\\tools\\shellia32.efi,\\shellia32.efi"
72 #define DRIVER_DIRS L"drivers,drivers_ia32"
74 #define SHELL_NAMES L"\\EFI\\tools\\shell.efi"
75 #define DRIVER_DIRS L"drivers"
78 #define MOK_NAMES L"\\EFI\\tools\\MokManager.efi,\\EFI\\redhat\\MokManager.efi,\\EFI\\ubuntu\\MokManager.efi,\\EFI\\suse\\MokManager"
80 // Filename patterns that identify EFI boot loaders. Note that a single case (either L"*.efi" or
81 // L"*.EFI") is fine for most systems; but Gigabyte's buggy Hybrid EFI does a case-sensitive
82 // comparison when it should do a case-insensitive comparison, so I'm doubling this up. It does
83 // no harm on other computers, AFAIK. In theory, every case variation should be done for
84 // completeness, but that's ridiculous....
85 #define LOADER_MATCH_PATTERNS L"*.efi,*.EFI"
87 // Patterns that identify Linux kernels. Added to the loader match pattern when the
88 // scan_all_linux_kernels option is set in the configuration file. Causes kernels WITHOUT
89 // a ".efi" extension to be found when scanning for boot loaders.
90 #define LINUX_MATCH_PATTERNS L"vmlinuz*,bzImage*"
92 // Default hint text for program-launch submenus
93 #define SUBSCREEN_HINT1 L"Use arrow keys to move cursor; Enter to boot;"
94 #define SUBSCREEN_HINT2 L"Insert or F2 to edit options; Esc to return to main menu"
95 #define SUBSCREEN_HINT2_NO_EDITOR L"Esc to return to main menu"
97 static REFIT_MENU_ENTRY MenuEntryAbout
= { L
"About rEFInd", TAG_ABOUT
, 1, 0, 'A', NULL
, NULL
, NULL
};
98 static REFIT_MENU_ENTRY MenuEntryReset
= { L
"Reboot Computer", TAG_REBOOT
, 1, 0, 'R', NULL
, NULL
, NULL
};
99 static REFIT_MENU_ENTRY MenuEntryShutdown
= { L
"Shut Down Computer", TAG_SHUTDOWN
, 1, 0, 'U', NULL
, NULL
, NULL
};
100 static REFIT_MENU_ENTRY MenuEntryReturn
= { L
"Return to Main Menu", TAG_RETURN
, 0, 0, 0, NULL
, NULL
, NULL
};
101 static REFIT_MENU_ENTRY MenuEntryExit
= { L
"Exit rEFInd", TAG_EXIT
, 1, 0, 0, NULL
, NULL
, NULL
};
103 static REFIT_MENU_SCREEN MainMenu
= { L
"Main Menu", NULL
, 0, NULL
, 0, NULL
, 0, L
"Automatic boot",
104 L
"Use arrow keys to move cursor; Enter to boot;",
105 L
"Insert or F2 for more options; Esc to refresh" };
106 static REFIT_MENU_SCREEN AboutMenu
= { L
"About", NULL
, 0, NULL
, 0, NULL
, 0, NULL
, L
"Press Enter to return to main menu", L
"" };
108 REFIT_CONFIG GlobalConfig
= { FALSE
, FALSE
, 0, 0, 0, 20, 0, 0, GRAPHICS_FOR_OSX
, LEGACY_TYPE_MAC
, 0,
109 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
110 {TAG_SHELL
, TAG_APPLE_RECOVERY
, TAG_MOK_TOOL
, TAG_ABOUT
, TAG_SHUTDOWN
, TAG_REBOOT
, 0, 0, 0, 0, 0 }};
112 // Structure used to hold boot loader filenames and time stamps in
113 // a linked list; used to sort entries within a directory.
117 struct LOADER_LIST
*NextEntry
;
124 static VOID
AboutrEFInd(VOID
)
126 CHAR16
*TempStr
; // Note: Don't deallocate; moved to menu structure
128 if (AboutMenu
.EntryCount
== 0) {
129 AboutMenu
.TitleImage
= BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT
);
130 AddMenuInfoLine(&AboutMenu
, L
"rEFInd Version 0.6.0.5");
131 AddMenuInfoLine(&AboutMenu
, L
"");
132 AddMenuInfoLine(&AboutMenu
, L
"Copyright (c) 2006-2010 Christoph Pfisterer");
133 AddMenuInfoLine(&AboutMenu
, L
"Copyright (c) 2012 Roderick W. Smith");
134 AddMenuInfoLine(&AboutMenu
, L
"Portions Copyright (c) Intel Corporation and others");
135 AddMenuInfoLine(&AboutMenu
, L
"Distributed under the terms of the GNU GPLv3 license");
136 AddMenuInfoLine(&AboutMenu
, L
"");
137 AddMenuInfoLine(&AboutMenu
, L
"Running on:");
138 TempStr
= AllocateZeroPool(256 * sizeof(CHAR16
));
139 SPrint(TempStr
, 255, L
" EFI Revision %d.%02d", ST
->Hdr
.Revision
>> 16, ST
->Hdr
.Revision
& ((1 << 16) - 1));
140 AddMenuInfoLine(&AboutMenu
, TempStr
);
142 AddMenuInfoLine(&AboutMenu
, L
" Platform: x86 (32 bit)");
143 #elif defined(EFIX64)
144 TempStr
= AllocateZeroPool(256 * sizeof(CHAR16
));
145 SPrint(TempStr
, 255, L
" Platform: x86_64 (64 bit); Secure Boot %s", secure_mode() ? L
"active" : L
"inactive");
146 AddMenuInfoLine(&AboutMenu
, TempStr
);
148 AddMenuInfoLine(&AboutMenu
, L
" Platform: unknown");
150 TempStr
= AllocateZeroPool(256 * sizeof(CHAR16
));
151 SPrint(TempStr
, 255, L
" Firmware: %s %d.%02d",
152 ST
->FirmwareVendor
, ST
->FirmwareRevision
>> 16, ST
->FirmwareRevision
& ((1 << 16) - 1));
153 AddMenuInfoLine(&AboutMenu
, TempStr
);
154 TempStr
= AllocateZeroPool(256 * sizeof(CHAR16
));
155 SPrint(TempStr
, 255, L
" Screen Output: %s", egScreenDescription());
156 AddMenuInfoLine(&AboutMenu
, TempStr
);
157 AddMenuInfoLine(&AboutMenu
, L
"");
158 #if defined(__MAKEWITH_GNUEFI)
159 AddMenuInfoLine(&AboutMenu
, L
"Built with GNU-EFI");
161 AddMenuInfoLine(&AboutMenu
, L
"Built with TianoCore EDK2");
163 AddMenuInfoLine(&AboutMenu
, L
"");
164 AddMenuInfoLine(&AboutMenu
, L
"For more information, see the rEFInd Web site:");
165 AddMenuInfoLine(&AboutMenu
, L
"http://www.rodsbooks.com/refind/");
166 AddMenuEntry(&AboutMenu
, &MenuEntryReturn
);
169 RunMenu(&AboutMenu
, NULL
);
170 } /* VOID AboutrEFInd() */
172 // Launch an EFI binary.
173 static EFI_STATUS
StartEFIImageList(IN EFI_DEVICE_PATH
**DevicePaths
,
174 IN CHAR16
*LoadOptions
, IN CHAR16
*LoadOptionsPrefix
,
175 IN CHAR16
*ImageTitle
, IN CHAR8 OSType
,
176 OUT UINTN
*ErrorInStep
,
179 EFI_STATUS Status
, ReturnStatus
;
180 EFI_HANDLE ChildImageHandle
;
181 EFI_LOADED_IMAGE
*ChildLoadedImage
= NULL
;
183 VOID
*ImageData
= NULL
;
185 REFIT_VOLUME
*DeviceVolume
= NULL
;
186 UINTN DevicePathIndex
;
187 CHAR16 ErrorInfo
[256];
188 CHAR16
*FullLoadOptions
= NULL
;
189 CHAR16
*loader
= NULL
;
190 BOOLEAN UseMok
= FALSE
;
192 if (ErrorInStep
!= NULL
)
196 if (LoadOptions
!= NULL
) {
197 if (LoadOptionsPrefix
!= NULL
) {
198 // MergeStrings(&FullLoadOptions, LoadOptionsPrefix, 0);
199 MergeStrings(&FullLoadOptions
, LoadOptions
, L
' ');
201 MergeStrings(&FullLoadOptions
, L
" ", 0);
202 // NOTE: That last space is also added by the EFI shell and seems to be significant
203 // when passing options to Apple's boot.efi...
206 MergeStrings(&FullLoadOptions
, LoadOptions
, 0);
208 } else { // LoadOptions == NULL
209 // NOTE: We provide a non-null string when no options are specified for safety;
210 // some systems (at least DUET) can hang when launching some programs (such as
211 // an EFI shell) without this.
212 FullLoadOptions
= StrDuplicate(L
" ");
215 Print(L
"Starting %s\nUsing load options '%s'\n", ImageTitle
, FullLoadOptions
);
217 // load the image into memory (and execute it, in the case of a shim/MOK image).
218 ReturnStatus
= Status
= EFI_NOT_FOUND
; // in case the list is empty
219 for (DevicePathIndex
= 0; DevicePaths
[DevicePathIndex
] != NULL
; DevicePathIndex
++) {
220 // NOTE: Below commented-out line could be more efficient if the ReadFile() and
221 // FindVolumeAndFilename() calls were moved earlier, but it doesn't work on my
222 // 32-bit Mac Mini or my 64-bit Intel box when launching a Linux kernel; the
223 // kernel returns a "Failed to handle fs_proto" error message.
224 // TODO: Track down the cause of this error and fix it, if possible.
225 // ReturnStatus = Status = refit_call6_wrapper(BS->LoadImage, FALSE, SelfImageHandle, DevicePaths[DevicePathIndex],
226 // ImageData, ImageSize, &ChildImageHandle);
227 ReturnStatus
= Status
= refit_call6_wrapper(BS
->LoadImage
, FALSE
, SelfImageHandle
, DevicePaths
[DevicePathIndex
],
228 NULL
, 0, &ChildImageHandle
);
229 if (((Status
== EFI_ACCESS_DENIED
) || (Status
== EFI_SECURITY_VIOLATION
)) && (ShimLoaded())) {
230 FindVolumeAndFilename(DevicePaths
[DevicePathIndex
], &DeviceVolume
, &loader
);
231 if (DeviceVolume
!= NULL
) {
232 Status
= ReadFile(DeviceVolume
->RootDir
, loader
, &File
, &ImageSize
);
233 ImageData
= File
.Buffer
;
235 Status
= EFI_NOT_FOUND
;
236 Print(L
"Error: device volume not found!\n");
238 if (Status
!= EFI_NOT_FOUND
) {
239 ReturnStatus
= Status
= start_image(SelfImageHandle
, loader
, ImageData
, ImageSize
, FullLoadOptions
,
240 DeviceVolume
, FileDevicePath(DeviceVolume
->DeviceHandle
, loader
));
241 // ReturnStatus = Status = start_image(SelfImageHandle, loader, ImageData, ImageSize, FullLoadOptions,
242 // DeviceVolume, DevicePaths[DevicePathIndex]);
244 if (ReturnStatus
== EFI_SUCCESS
) {
247 } // if (UEFI SB failed; use shim)
248 if (ReturnStatus
!= EFI_NOT_FOUND
) {
252 SPrint(ErrorInfo
, 255, L
"while loading %s", ImageTitle
);
253 if (CheckError(Status
, ErrorInfo
)) {
254 if (ErrorInStep
!= NULL
)
260 ReturnStatus
= Status
= refit_call3_wrapper(BS
->HandleProtocol
, ChildImageHandle
, &LoadedImageProtocol
,
261 (VOID
**) &ChildLoadedImage
);
262 if (CheckError(Status
, L
"while getting a LoadedImageProtocol handle")) {
263 if (ErrorInStep
!= NULL
)
267 ChildLoadedImage
->LoadOptions
= (VOID
*)FullLoadOptions
;
268 ChildLoadedImage
->LoadOptionsSize
= ((UINT32
)StrLen(FullLoadOptions
) + 1) * sizeof(CHAR16
);
269 // turn control over to the image
270 // TODO: (optionally) re-enable the EFI watchdog timer!
272 // close open file handles
274 ReturnStatus
= Status
= refit_call3_wrapper(BS
->StartImage
, ChildImageHandle
, NULL
, NULL
);
275 // control returns here when the child image calls Exit()
276 SPrint(ErrorInfo
, 255, L
"returned from %s", ImageTitle
);
277 if (CheckError(Status
, ErrorInfo
)) {
278 if (ErrorInStep
!= NULL
)
282 // re-open file handles
287 // unload the image, we don't care if it works or not...
289 Status
= refit_call1_wrapper(BS
->UnloadImage
, ChildImageHandle
);
292 MyFreePool(FullLoadOptions
);
294 } /* static EFI_STATUS StartEFIImageList() */
296 static EFI_STATUS
StartEFIImage(IN EFI_DEVICE_PATH
*DevicePath
,
297 IN CHAR16
*LoadOptions
, IN CHAR16
*LoadOptionsPrefix
,
298 IN CHAR16
*ImageTitle
, IN CHAR8 OSType
,
299 OUT UINTN
*ErrorInStep
,
302 EFI_DEVICE_PATH
*DevicePaths
[2];
304 DevicePaths
[0] = DevicePath
;
305 DevicePaths
[1] = NULL
;
306 return StartEFIImageList(DevicePaths
, LoadOptions
, LoadOptionsPrefix
, ImageTitle
, OSType
, ErrorInStep
, Verbose
);
307 } /* static EFI_STATUS StartEFIImage() */
310 // EFI OS loader functions
313 static VOID
StartLoader(IN LOADER_ENTRY
*Entry
)
315 UINTN ErrorInStep
= 0;
317 BeginExternalScreen(Entry
->UseGraphicsMode
, L
"Booting OS");
318 StartEFIImage(Entry
->DevicePath
, Entry
->LoadOptions
, Basename(Entry
->LoaderPath
),
319 Basename(Entry
->LoaderPath
), Entry
->OSType
, &ErrorInStep
, !Entry
->UseGraphicsMode
);
320 FinishExternalScreen();
323 // Locate an initrd or initramfs file that matches the kernel specified by LoaderPath.
324 // The matching file has a name that begins with "init" and includes the same version
325 // number string as is found in LoaderPath -- but not a longer version number string.
326 // For instance, if LoaderPath is \EFI\kernels\bzImage-3.3.0.efi, and if \EFI\kernels
327 // has a file called initramfs-3.3.0.img, this function will return the string
328 // '\EFI\kernels\initramfs-3.3.0.img'. If the directory ALSO contains the file
329 // initramfs-3.3.0-rc7.img or initramfs-13.3.0.img, those files will NOT match;
330 // however, initmine-3.3.0.img might match. (FindInitrd() returns the first match it
331 // finds). Thus, care should be taken to avoid placing duplicate matching files in
332 // the kernel's directory.
333 // If no matching init file can be found, returns NULL.
334 static CHAR16
* FindInitrd(IN CHAR16
*LoaderPath
, IN REFIT_VOLUME
*Volume
) {
335 CHAR16
*InitrdName
= NULL
, *FileName
, *KernelVersion
, *InitrdVersion
, *Path
;
336 REFIT_DIR_ITER DirIter
;
337 EFI_FILE_INFO
*DirEntry
;
339 FileName
= Basename(LoaderPath
);
340 KernelVersion
= FindNumbers(FileName
);
341 Path
= FindPath(LoaderPath
);
343 // Add trailing backslash for root directory; necessary on some systems, but must
344 // NOT be added to all directories, since on other systems, a trailing backslash on
345 // anything but the root directory causes them to flake out!
346 if (StrLen(Path
) == 0) {
347 MergeStrings(&Path
, L
"\\", 0);
349 DirIterOpen(Volume
->RootDir
, Path
, &DirIter
);
350 // Now add a trailing backslash if it was NOT added earlier, for consistency in
351 // building the InitrdName later....
352 if ((StrLen(Path
) > 0) && (Path
[StrLen(Path
) - 1] != L
'\\'))
353 MergeStrings(&Path
, L
"\\", 0);
354 while ((DirIterNext(&DirIter
, 2, L
"init*", &DirEntry
)) && (InitrdName
== NULL
)) {
355 InitrdVersion
= FindNumbers(DirEntry
->FileName
);
356 if (KernelVersion
!= NULL
) {
357 if (StriCmp(InitrdVersion
, KernelVersion
) == 0) {
358 MergeStrings(&InitrdName
, Path
, 0);
359 MergeStrings(&InitrdName
, DirEntry
->FileName
, 0);
362 if (InitrdVersion
== NULL
) {
363 MergeStrings(&InitrdName
, Path
, 0);
364 MergeStrings(&InitrdName
, DirEntry
->FileName
, 0);
367 MyFreePool(InitrdVersion
);
369 DirIterClose(&DirIter
);
371 // Note: Don't FreePool(FileName), since Basename returns a pointer WITHIN the string it's passed.
372 MyFreePool(KernelVersion
);
375 } // static CHAR16 * FindInitrd()
377 LOADER_ENTRY
* AddPreparedLoaderEntry(LOADER_ENTRY
*Entry
) {
378 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
381 } // LOADER_ENTRY * AddPreparedLoaderEntry()
383 // Creates a copy of a menu screen.
384 // Returns a pointer to the copy of the menu screen.
385 static REFIT_MENU_SCREEN
* CopyMenuScreen(REFIT_MENU_SCREEN
*Entry
) {
386 REFIT_MENU_SCREEN
*NewEntry
;
389 NewEntry
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
390 if ((Entry
!= NULL
) && (NewEntry
!= NULL
)) {
391 CopyMem(NewEntry
, Entry
, sizeof(REFIT_MENU_SCREEN
));
392 NewEntry
->Title
= (Entry
->Title
) ? StrDuplicate(Entry
->Title
) : NULL
;
393 NewEntry
->TimeoutText
= (Entry
->TimeoutText
) ? StrDuplicate(Entry
->TimeoutText
) : NULL
;
394 if (Entry
->TitleImage
!= NULL
) {
395 NewEntry
->TitleImage
= AllocatePool(sizeof(EG_IMAGE
));
396 if (NewEntry
->TitleImage
!= NULL
)
397 CopyMem(NewEntry
->TitleImage
, Entry
->TitleImage
, sizeof(EG_IMAGE
));
399 NewEntry
->InfoLines
= (CHAR16
**) AllocateZeroPool(Entry
->InfoLineCount
* (sizeof(CHAR16
*)));
400 for (i
= 0; i
< Entry
->InfoLineCount
&& NewEntry
->InfoLines
; i
++) {
401 NewEntry
->InfoLines
[i
] = (Entry
->InfoLines
[i
]) ? StrDuplicate(Entry
->InfoLines
[i
]) : NULL
;
403 NewEntry
->Entries
= (REFIT_MENU_ENTRY
**) AllocateZeroPool(Entry
->EntryCount
* (sizeof (REFIT_MENU_ENTRY
*)));
404 for (i
= 0; i
< Entry
->EntryCount
&& NewEntry
->Entries
; i
++) {
405 AddMenuEntry(NewEntry
, Entry
->Entries
[i
]);
407 NewEntry
->Hint1
= (Entry
->Hint1
) ? StrDuplicate(Entry
->Hint1
) : NULL
;
408 NewEntry
->Hint2
= (Entry
->Hint2
) ? StrDuplicate(Entry
->Hint2
) : NULL
;
411 } // static REFIT_MENU_SCREEN* CopyMenuScreen()
413 // Creates a copy of a menu entry. Intended to enable moving a stack-based
414 // menu entry (such as the ones for the "reboot" and "exit" functions) to
415 // to the heap. This enables easier deletion of the whole set of menu
416 // entries when re-scanning.
417 // Returns a pointer to the copy of the menu entry.
418 static REFIT_MENU_ENTRY
* CopyMenuEntry(REFIT_MENU_ENTRY
*Entry
) {
419 REFIT_MENU_ENTRY
*NewEntry
;
421 NewEntry
= AllocateZeroPool(sizeof(REFIT_MENU_ENTRY
));
422 if ((Entry
!= NULL
) && (NewEntry
!= NULL
)) {
423 CopyMem(NewEntry
, Entry
, sizeof(REFIT_MENU_ENTRY
));
424 NewEntry
->Title
= (Entry
->Title
) ? StrDuplicate(Entry
->Title
) : NULL
;
425 if (Entry
->BadgeImage
!= NULL
) {
426 NewEntry
->BadgeImage
= AllocatePool(sizeof(EG_IMAGE
));
427 if (NewEntry
->BadgeImage
!= NULL
)
428 CopyMem(NewEntry
->BadgeImage
, Entry
->BadgeImage
, sizeof(EG_IMAGE
));
430 if (Entry
->Image
!= NULL
) {
431 NewEntry
->Image
= AllocatePool(sizeof(EG_IMAGE
));
432 if (NewEntry
->Image
!= NULL
)
433 CopyMem(NewEntry
->Image
, Entry
->Image
, sizeof(EG_IMAGE
));
435 if (Entry
->SubScreen
!= NULL
) {
436 NewEntry
->SubScreen
= CopyMenuScreen(Entry
->SubScreen
);
440 } // REFIT_MENU_ENTRY* CopyMenuEntry()
442 // Creates a new LOADER_ENTRY data structure and populates it with
443 // default values from the specified Entry, or NULL values if Entry
444 // is unspecified (NULL).
445 // Returns a pointer to the new data structure, or NULL if it
446 // couldn't be allocated
447 LOADER_ENTRY
*InitializeLoaderEntry(IN LOADER_ENTRY
*Entry
) {
448 LOADER_ENTRY
*NewEntry
= NULL
;
450 NewEntry
= AllocateZeroPool(sizeof(LOADER_ENTRY
));
451 if (NewEntry
!= NULL
) {
452 NewEntry
->me
.Title
= NULL
;
453 NewEntry
->me
.Tag
= TAG_LOADER
;
454 NewEntry
->Enabled
= TRUE
;
455 NewEntry
->UseGraphicsMode
= FALSE
;
456 NewEntry
->OSType
= 0;
458 NewEntry
->LoaderPath
= (Entry
->LoaderPath
) ? StrDuplicate(Entry
->LoaderPath
) : NULL
;
459 NewEntry
->VolName
= (Entry
->VolName
) ? StrDuplicate(Entry
->VolName
) : NULL
;
460 NewEntry
->DevicePath
= Entry
->DevicePath
;
461 NewEntry
->UseGraphicsMode
= Entry
->UseGraphicsMode
;
462 NewEntry
->LoadOptions
= (Entry
->LoadOptions
) ? StrDuplicate(Entry
->LoadOptions
) : NULL
;
463 NewEntry
->InitrdPath
= (Entry
->InitrdPath
) ? StrDuplicate(Entry
->InitrdPath
) : NULL
;
467 } // LOADER_ENTRY *InitializeLoaderEntry()
469 // Adds InitrdPath to Options, but only if Options doesn't already include an
470 // initrd= line. Done to enable overriding the default initrd selection in a
471 // refind_linux.conf file's options list.
472 // Returns a pointer to a new string. The calling function is responsible for
473 // freeing its memory.
474 static CHAR16
*AddInitrdToOptions(CHAR16
*Options
, CHAR16
*InitrdPath
) {
475 CHAR16
*NewOptions
= NULL
;
478 NewOptions
= StrDuplicate(Options
);
479 if ((InitrdPath
!= NULL
) && !StriSubCmp(L
"initrd=", Options
)) {
480 MergeStrings(&NewOptions
, L
"initrd=", L
' ');
481 MergeStrings(&NewOptions
, InitrdPath
, 0);
484 } // CHAR16 *AddInitrdToOptions()
486 // Prepare a REFIT_MENU_SCREEN data structure for a subscreen entry. This sets up
487 // the default entry that launches the boot loader using the same options as the
488 // main Entry does. Subsequent options can be added by the calling function.
489 // If a subscreen already exists in the Entry that's passed to this function,
490 // it's left unchanged and a pointer to it is returned.
491 // Returns a pointer to the new subscreen data structure, or NULL if there
492 // were problems allocating memory.
493 REFIT_MENU_SCREEN
*InitializeSubScreen(IN LOADER_ENTRY
*Entry
) {
494 CHAR16
*FileName
, *MainOptions
= NULL
;
495 REFIT_MENU_SCREEN
*SubScreen
= NULL
;
496 LOADER_ENTRY
*SubEntry
;
498 FileName
= Basename(Entry
->LoaderPath
);
499 if (Entry
->me
.SubScreen
== NULL
) { // No subscreen yet; initialize default entry....
500 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
501 if (SubScreen
!= NULL
) {
502 SubScreen
->Title
= AllocateZeroPool(sizeof(CHAR16
) * 256);
503 SPrint(SubScreen
->Title
, 255, L
"Boot Options for %s on %s",
504 (Entry
->Title
!= NULL
) ? Entry
->Title
: FileName
, Entry
->VolName
);
505 SubScreen
->TitleImage
= Entry
->me
.Image
;
507 SubEntry
= InitializeLoaderEntry(Entry
);
508 if (SubEntry
!= NULL
) {
509 SubEntry
->me
.Title
= StrDuplicate(L
"Boot using default options");
510 MainOptions
= SubEntry
->LoadOptions
;
511 SubEntry
->LoadOptions
= AddInitrdToOptions(MainOptions
, SubEntry
->InitrdPath
);
512 MyFreePool(MainOptions
);
513 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
514 } // if (SubEntry != NULL)
515 SubScreen
->Hint1
= StrDuplicate(SUBSCREEN_HINT1
);
516 if (GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_EDITOR
) {
517 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2_NO_EDITOR
);
519 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2
);
521 } // if (SubScreen != NULL)
522 } else { // existing subscreen; less initialization, and just add new entry later....
523 SubScreen
= Entry
->me
.SubScreen
;
526 } // REFIT_MENU_SCREEN *InitializeSubScreen()
528 VOID
GenerateSubScreen(LOADER_ENTRY
*Entry
, IN REFIT_VOLUME
*Volume
) {
529 REFIT_MENU_SCREEN
*SubScreen
;
530 LOADER_ENTRY
*SubEntry
;
532 CHAR16 DiagsFileName
[256];
537 // create the submenu
538 if (StrLen(Entry
->Title
) == 0) {
539 MyFreePool(Entry
->Title
);
542 SubScreen
= InitializeSubScreen(Entry
);
544 // loader-specific submenu entries
545 if (Entry
->OSType
== 'M') { // entries for Mac OS X
547 SubEntry
= InitializeLoaderEntry(Entry
);
548 if (SubEntry
!= NULL
) {
549 SubEntry
->me
.Title
= L
"Boot Mac OS X with a 64-bit kernel";
550 SubEntry
->LoadOptions
= L
"arch=x86_64";
551 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
552 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
555 SubEntry
= InitializeLoaderEntry(Entry
);
556 if (SubEntry
!= NULL
) {
557 SubEntry
->me
.Title
= L
"Boot Mac OS X with a 32-bit kernel";
558 SubEntry
->LoadOptions
= L
"arch=i386";
559 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
560 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
564 if (!(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_SINGLEUSER
)) {
565 SubEntry
= InitializeLoaderEntry(Entry
);
566 if (SubEntry
!= NULL
) {
567 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode";
568 SubEntry
->UseGraphicsMode
= FALSE
;
569 SubEntry
->LoadOptions
= L
"-v";
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 verbose mode (64-bit)";
577 SubEntry
->UseGraphicsMode
= FALSE
;
578 SubEntry
->LoadOptions
= L
"-v arch=x86_64";
579 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
582 SubEntry
= InitializeLoaderEntry(Entry
);
583 if (SubEntry
!= NULL
) {
584 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode (32-bit)";
585 SubEntry
->UseGraphicsMode
= FALSE
;
586 SubEntry
->LoadOptions
= L
"-v arch=i386";
587 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
591 SubEntry
= InitializeLoaderEntry(Entry
);
592 if (SubEntry
!= NULL
) {
593 SubEntry
->me
.Title
= L
"Boot Mac OS X in single user mode";
594 SubEntry
->UseGraphicsMode
= FALSE
;
595 SubEntry
->LoadOptions
= L
"-v -s";
596 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
600 // check for Apple hardware diagnostics
601 StrCpy(DiagsFileName
, L
"System\\Library\\CoreServices\\.diagnostics\\diags.efi");
602 if (FileExists(Volume
->RootDir
, DiagsFileName
) && !(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_HWTEST
)) {
603 SubEntry
= InitializeLoaderEntry(Entry
);
604 if (SubEntry
!= NULL
) {
605 SubEntry
->me
.Title
= L
"Run Apple Hardware Test";
606 MyFreePool(SubEntry
->LoaderPath
);
607 SubEntry
->LoaderPath
= StrDuplicate(DiagsFileName
);
608 SubEntry
->DevicePath
= FileDevicePath(Volume
->DeviceHandle
, SubEntry
->LoaderPath
);
609 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
610 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
612 } // if diagnostics entry found
614 } else if (Entry
->OSType
== 'L') { // entries for Linux kernels with EFI stub loaders
615 File
= ReadLinuxOptionsFile(Entry
->LoaderPath
, Volume
);
617 InitrdName
= FindInitrd(Entry
->LoaderPath
, Volume
);
618 TokenCount
= ReadTokenLine(File
, &TokenList
);
619 // first entry requires special processing, since it was initially set
620 // up with a default title but correct options by InitializeSubScreen(),
622 if ((SubScreen
->Entries
!= NULL
) && (SubScreen
->Entries
[0] != NULL
)) {
623 MyFreePool(SubScreen
->Entries
[0]->Title
);
624 SubScreen
->Entries
[0]->Title
= StrDuplicate(TokenList
[0]);
626 FreeTokenLine(&TokenList
, &TokenCount
);
627 while ((TokenCount
= ReadTokenLine(File
, &TokenList
)) > 1) {
628 SubEntry
= InitializeLoaderEntry(Entry
);
629 SubEntry
->me
.Title
= StrDuplicate(TokenList
[0]);
630 MyFreePool(SubEntry
->LoadOptions
);
631 SubEntry
->LoadOptions
= AddInitrdToOptions(TokenList
[1], InitrdName
);
632 FreeTokenLine(&TokenList
, &TokenCount
);
633 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_LINUX
;
634 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
636 MyFreePool(InitrdName
);
638 } // if Linux options file exists
640 } else if (Entry
->OSType
== 'E') { // entries for ELILO
641 SubEntry
= InitializeLoaderEntry(Entry
);
642 if (SubEntry
!= NULL
) {
643 SubEntry
->me
.Title
= L
"Run ELILO in interactive mode";
644 SubEntry
->LoadOptions
= L
"-p";
645 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
646 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
649 SubEntry
= InitializeLoaderEntry(Entry
);
650 if (SubEntry
!= NULL
) {
651 SubEntry
->me
.Title
= L
"Boot Linux for a 17\" iMac or a 15\" MacBook Pro (*)";
652 SubEntry
->UseGraphicsMode
= TRUE
;
653 SubEntry
->LoadOptions
= L
"-d 0 i17";
654 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
655 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
658 SubEntry
= InitializeLoaderEntry(Entry
);
659 if (SubEntry
!= NULL
) {
660 SubEntry
->me
.Title
= L
"Boot Linux for a 20\" iMac (*)";
661 SubEntry
->UseGraphicsMode
= TRUE
;
662 SubEntry
->LoadOptions
= L
"-d 0 i20";
663 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
664 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
667 SubEntry
= InitializeLoaderEntry(Entry
);
668 if (SubEntry
!= NULL
) {
669 SubEntry
->me
.Title
= L
"Boot Linux for a Mac Mini (*)";
670 SubEntry
->UseGraphicsMode
= TRUE
;
671 SubEntry
->LoadOptions
= L
"-d 0 mini";
672 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
673 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
676 AddMenuInfoLine(SubScreen
, L
"NOTE: This is an example. Entries");
677 AddMenuInfoLine(SubScreen
, L
"marked with (*) may not work.");
679 } else if (Entry
->OSType
== 'X') { // entries for xom.efi
680 // by default, skip the built-in selection and boot from hard disk only
681 Entry
->LoadOptions
= L
"-s -h";
683 SubEntry
= InitializeLoaderEntry(Entry
);
684 if (SubEntry
!= NULL
) {
685 SubEntry
->me
.Title
= L
"Boot Windows from Hard Disk";
686 SubEntry
->LoadOptions
= L
"-s -h";
687 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
688 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
691 SubEntry
= InitializeLoaderEntry(Entry
);
692 if (SubEntry
!= NULL
) {
693 SubEntry
->me
.Title
= L
"Boot Windows from CD-ROM";
694 SubEntry
->LoadOptions
= L
"-s -c";
695 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
696 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
699 SubEntry
= InitializeLoaderEntry(Entry
);
700 if (SubEntry
!= NULL
) {
701 SubEntry
->me
.Title
= L
"Run XOM in text mode";
702 SubEntry
->UseGraphicsMode
= FALSE
;
703 SubEntry
->LoadOptions
= L
"-v";
704 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
705 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
707 } // entries for xom.efi
708 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
709 Entry
->me
.SubScreen
= SubScreen
;
710 } // VOID GenerateSubScreen()
712 // Returns options for a Linux kernel. Reads them from an options file in the
713 // kernel's directory; and if present, adds an initrd= option for an initial
714 // RAM disk file with the same version number as the kernel file.
715 static CHAR16
* GetMainLinuxOptions(IN CHAR16
* LoaderPath
, IN REFIT_VOLUME
*Volume
) {
716 CHAR16
*Options
= NULL
, *InitrdName
, *FullOptions
= NULL
;
718 Options
= GetFirstOptionsFromFile(LoaderPath
, Volume
);
719 InitrdName
= FindInitrd(LoaderPath
, Volume
);
720 FullOptions
= AddInitrdToOptions(Options
, InitrdName
);
723 MyFreePool(InitrdName
);
724 return (FullOptions
);
725 } // static CHAR16 * GetMainLinuxOptions()
727 // Sets a few defaults for a loader entry -- mainly the icon, but also the OS type
728 // code and shortcut letter. For Linux EFI stub loaders, also sets kernel options
729 // that will (with luck) work fairly automatically.
730 VOID
SetLoaderDefaults(LOADER_ENTRY
*Entry
, CHAR16
*LoaderPath
, REFIT_VOLUME
*Volume
) {
731 CHAR16 IconFileName
[256];
732 CHAR16
*FileName
, *PathOnly
, *OSIconName
= NULL
, *Temp
, *SubString
;
733 CHAR16 ShortcutLetter
= 0;
736 FileName
= Basename(LoaderPath
);
737 PathOnly
= FindPath(LoaderPath
);
739 // locate a custom icon for the loader
740 // Anything found here takes precedence over the "hints" in the OSIconName variable
741 StrCpy(IconFileName
, LoaderPath
);
742 ReplaceEfiExtension(IconFileName
, L
".icns");
743 if (FileExists(Volume
->RootDir
, IconFileName
)) {
744 Entry
->me
.Image
= LoadIcns(Volume
->RootDir
, IconFileName
, 128);
745 } else if ((StrLen(PathOnly
) == 0) && (Volume
->VolIconImage
!= NULL
)) {
746 Entry
->me
.Image
= Volume
->VolIconImage
;
747 } // icon matched to loader or volume
749 // Begin creating icon "hints" by using last part of directory path leading
751 Temp
= FindLastDirName(LoaderPath
);
752 MergeStrings(&OSIconName
, Temp
, L
',');
755 if (OSIconName
!= NULL
) {
756 ShortcutLetter
= OSIconName
[0];
759 // Add every "word" in the volume label, delimited by spaces, dashes (-), or
760 // underscores (_), to the list of hints to be used in searching for OS
762 if ((Volume
->VolName
) && (StrLen(Volume
->VolName
) > 0)) {
763 Temp
= SubString
= StrDuplicate(Volume
->VolName
);
765 Length
= StrLen(Temp
);
766 for (i
= 0; i
< Length
; i
++) {
767 if ((Temp
[i
] == L
' ') || (Temp
[i
] == L
'_') || (Temp
[i
] == L
'-')) {
769 if (StrLen(SubString
) > 0)
770 MergeStrings(&OSIconName
, SubString
, L
',');
771 SubString
= Temp
+ i
+ 1;
774 MergeStrings(&OSIconName
, SubString
, L
',');
779 // detect specific loaders
780 if (StriSubCmp(L
"bzImage", LoaderPath
) || StriSubCmp(L
"vmlinuz", LoaderPath
)) {
781 MergeStrings(&OSIconName
, L
"linux", L
',');
783 if (ShortcutLetter
== 0)
784 ShortcutLetter
= 'L';
785 Entry
->LoadOptions
= GetMainLinuxOptions(LoaderPath
, Volume
);
786 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_LINUX
;
787 } else if (StriSubCmp(L
"refit", LoaderPath
)) {
788 MergeStrings(&OSIconName
, L
"refit", L
',');
790 ShortcutLetter
= 'R';
791 } else if (StriCmp(LoaderPath
, MACOSX_LOADER_PATH
) == 0) {
792 if (Volume
->VolIconImage
!= NULL
) { // custom icon file found
793 Entry
->me
.Image
= Volume
->VolIconImage
;
795 MergeStrings(&OSIconName
, L
"mac", L
',');
797 ShortcutLetter
= 'M';
798 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
799 } else if (StriCmp(FileName
, L
"diags.efi") == 0) {
800 MergeStrings(&OSIconName
, L
"hwtest", L
',');
801 } else if (StriCmp(FileName
, L
"e.efi") == 0 || StriCmp(FileName
, L
"elilo.efi") == 0 || StriSubCmp(L
"elilo", FileName
)) {
802 MergeStrings(&OSIconName
, L
"elilo,linux", L
',');
804 if (ShortcutLetter
== 0)
805 ShortcutLetter
= 'L';
806 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
807 } else if (StriSubCmp(L
"grub", FileName
)) {
809 ShortcutLetter
= 'G';
810 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_GRUB
;
811 } else if (StriCmp(FileName
, L
"cdboot.efi") == 0 ||
812 StriCmp(FileName
, L
"bootmgr.efi") == 0 ||
813 StriCmp(FileName
, L
"bootmgfw.efi") == 0) {
814 MergeStrings(&OSIconName
, L
"win", L
',');
816 ShortcutLetter
= 'W';
817 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
818 } else if (StriCmp(FileName
, L
"xom.efi") == 0) {
819 MergeStrings(&OSIconName
, L
"xom,win", L
',');
820 Entry
->UseGraphicsMode
= TRUE
;
822 ShortcutLetter
= 'W';
823 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
826 if ((ShortcutLetter
>= 'a') && (ShortcutLetter
<= 'z'))
827 ShortcutLetter
= ShortcutLetter
- 'a' + 'A'; // convert lowercase to uppercase
828 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
829 if (Entry
->me
.Image
== NULL
)
830 Entry
->me
.Image
= LoadOSIcon(OSIconName
, L
"unknown", FALSE
);
831 MyFreePool(PathOnly
);
832 } // VOID SetLoaderDefaults()
834 // Add a specified EFI boot loader to the list, using automatic settings
835 // for icons, options, etc.
836 LOADER_ENTRY
* AddLoaderEntry(IN CHAR16
*LoaderPath
, IN CHAR16
*LoaderTitle
, IN REFIT_VOLUME
*Volume
) {
839 CleanUpPathNameSlashes(LoaderPath
);
840 Entry
= InitializeLoaderEntry(NULL
);
842 Entry
->Title
= StrDuplicate((LoaderTitle
!= NULL
) ? LoaderTitle
: LoaderPath
);
843 Entry
->me
.Title
= AllocateZeroPool(sizeof(CHAR16
) * 256);
844 SPrint(Entry
->me
.Title
, 255, L
"Boot %s from %s", (LoaderTitle
!= NULL
) ? LoaderTitle
: LoaderPath
, Volume
->VolName
);
846 Entry
->me
.BadgeImage
= Volume
->VolBadgeImage
;
847 if ((LoaderPath
!= NULL
) && (LoaderPath
[0] != L
'\\')) {
848 Entry
->LoaderPath
= StrDuplicate(L
"\\");
850 Entry
->LoaderPath
= NULL
;
852 MergeStrings(&(Entry
->LoaderPath
), LoaderPath
, 0);
853 Entry
->VolName
= Volume
->VolName
;
854 Entry
->DevicePath
= FileDevicePath(Volume
->DeviceHandle
, Entry
->LoaderPath
);
855 SetLoaderDefaults(Entry
, LoaderPath
, Volume
);
856 GenerateSubScreen(Entry
, Volume
);
857 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
861 } // LOADER_ENTRY * AddLoaderEntry()
863 // Returns -1 if (Time1 < Time2), +1 if (Time1 > Time2), or 0 if
864 // (Time1 == Time2). Precision is only to the nearest second; since
865 // this is used for sorting boot loader entries, differences smaller
866 // than this are likely to be meaningless (and unlikely!).
867 INTN
TimeComp(IN EFI_TIME
*Time1
, IN EFI_TIME
*Time2
) {
868 INT64 Time1InSeconds
, Time2InSeconds
;
870 // Following values are overestimates; I'm assuming 31 days in every month.
871 // This is fine for the purpose of this function, which is limited
872 Time1InSeconds
= Time1
->Second
+ (Time1
->Minute
* 60) + (Time1
->Hour
* 3600) + (Time1
->Day
* 86400) +
873 (Time1
->Month
* 2678400) + ((Time1
->Year
- 1998) * 32140800);
874 Time2InSeconds
= Time2
->Second
+ (Time2
->Minute
* 60) + (Time2
->Hour
* 3600) + (Time2
->Day
* 86400) +
875 (Time2
->Month
* 2678400) + ((Time2
->Year
- 1998) * 32140800);
876 if (Time1InSeconds
< Time2InSeconds
)
878 else if (Time1InSeconds
> Time2InSeconds
)
884 // Adds a loader list element, keeping it sorted by date. Returns the new
885 // first element (the one with the most recent date).
886 static struct LOADER_LIST
* AddLoaderListEntry(struct LOADER_LIST
*LoaderList
, struct LOADER_LIST
*NewEntry
) {
887 struct LOADER_LIST
*LatestEntry
, *CurrentEntry
, *PrevEntry
= NULL
;
889 LatestEntry
= CurrentEntry
= LoaderList
;
890 if (LoaderList
== NULL
) {
891 LatestEntry
= NewEntry
;
893 while ((CurrentEntry
!= NULL
) && (TimeComp(&(NewEntry
->TimeStamp
), &(CurrentEntry
->TimeStamp
)) < 0)) {
894 PrevEntry
= CurrentEntry
;
895 CurrentEntry
= CurrentEntry
->NextEntry
;
897 NewEntry
->NextEntry
= CurrentEntry
;
898 if (PrevEntry
== NULL
) {
899 LatestEntry
= NewEntry
;
901 PrevEntry
->NextEntry
= NewEntry
;
904 return (LatestEntry
);
905 } // static VOID AddLoaderListEntry()
907 // Delete the LOADER_LIST linked list
908 static VOID
CleanUpLoaderList(struct LOADER_LIST
*LoaderList
) {
909 struct LOADER_LIST
*Temp
;
911 while (LoaderList
!= NULL
) {
913 LoaderList
= LoaderList
->NextEntry
;
914 MyFreePool(Temp
->FileName
);
917 } // static VOID CleanUpLoaderList()
919 // Scan an individual directory for EFI boot loader files and, if found,
920 // add them to the list. Sorts the entries within the loader directory
921 // so that the most recent one appears first in the list.
922 static VOID
ScanLoaderDir(IN REFIT_VOLUME
*Volume
, IN CHAR16
*Path
, IN CHAR16
*Pattern
)
925 REFIT_DIR_ITER DirIter
;
926 EFI_FILE_INFO
*DirEntry
;
927 CHAR16 FileName
[256], *Extension
;
928 struct LOADER_LIST
*LoaderList
= NULL
, *NewLoader
;
930 if ((!SelfDirPath
|| !Path
|| ((StriCmp(Path
, SelfDirPath
) == 0) && (Volume
->DeviceHandle
!= SelfVolume
->DeviceHandle
)) ||
931 (StriCmp(Path
, SelfDirPath
) != 0)) &&
932 (!IsIn(Path
, GlobalConfig
.DontScanDirs
)) &&
933 (!IsIn(Volume
->VolName
, GlobalConfig
.DontScanVolumes
))) {
934 // look through contents of the directory
935 DirIterOpen(Volume
->RootDir
, Path
, &DirIter
);
936 while (DirIterNext(&DirIter
, 2, Pattern
, &DirEntry
)) {
937 Extension
= FindExtension(DirEntry
->FileName
);
938 if (DirEntry
->FileName
[0] == '.' ||
939 StriCmp(Extension
, L
".icns") == 0 ||
940 StriSubCmp(L
"shell", DirEntry
->FileName
) ||
941 IsIn(DirEntry
->FileName
, GlobalConfig
.DontScanFiles
))
942 continue; // skip this
945 SPrint(FileName
, 255, L
"\\%s\\%s", Path
, DirEntry
->FileName
);
947 SPrint(FileName
, 255, L
"\\%s", DirEntry
->FileName
);
948 CleanUpPathNameSlashes(FileName
);
949 NewLoader
= AllocateZeroPool(sizeof(struct LOADER_LIST
));
950 if (NewLoader
!= NULL
) {
951 NewLoader
->FileName
= StrDuplicate(FileName
);
952 NewLoader
->TimeStamp
= DirEntry
->ModificationTime
;
953 LoaderList
= AddLoaderListEntry(LoaderList
, NewLoader
);
955 MyFreePool(Extension
);
957 NewLoader
= LoaderList
;
958 while (NewLoader
!= NULL
) {
959 AddLoaderEntry(NewLoader
->FileName
, NULL
, Volume
);
960 NewLoader
= NewLoader
->NextEntry
;
962 CleanUpLoaderList(LoaderList
);
963 Status
= DirIterClose(&DirIter
);
964 if (Status
!= EFI_NOT_FOUND
) {
966 SPrint(FileName
, 255, L
"while scanning the %s directory", Path
);
968 StrCpy(FileName
, L
"while scanning the root directory");
969 CheckError(Status
, FileName
);
970 } // if (Status != EFI_NOT_FOUND)
971 } // if not scanning our own directory
972 } /* static VOID ScanLoaderDir() */
974 static VOID
ScanEfiFiles(REFIT_VOLUME
*Volume
) {
976 REFIT_DIR_ITER EfiDirIter
;
977 EFI_FILE_INFO
*EfiDirEntry
;
978 CHAR16 FileName
[256], *Directory
, *MatchPatterns
;
981 MatchPatterns
= StrDuplicate(LOADER_MATCH_PATTERNS
);
982 if (GlobalConfig
.ScanAllLinux
)
983 MergeStrings(&MatchPatterns
, LINUX_MATCH_PATTERNS
, L
',');
985 if ((Volume
->RootDir
!= NULL
) && (Volume
->VolName
!= NULL
)) {
986 // check for Mac OS X boot loader
987 if (!IsIn(L
"System\\Library\\CoreServices", GlobalConfig
.DontScanDirs
)) {
988 StrCpy(FileName
, MACOSX_LOADER_PATH
);
989 if (FileExists(Volume
->RootDir
, FileName
) && !IsIn(L
"boot.efi", GlobalConfig
.DontScanFiles
)) {
990 AddLoaderEntry(FileName
, L
"Mac OS X", Volume
);
994 StrCpy(FileName
, L
"System\\Library\\CoreServices\\xom.efi");
995 if (FileExists(Volume
->RootDir
, FileName
) && !IsIn(L
"boot.efi", GlobalConfig
.DontScanFiles
)) {
996 AddLoaderEntry(FileName
, L
"Windows XP (XoM)", Volume
);
998 } // if Mac directory not in GlobalConfig.DontScanDirs list
1000 // check for Microsoft boot loader/menu
1001 StrCpy(FileName
, L
"EFI\\Microsoft\\Boot\\Bootmgfw.efi");
1002 if (FileExists(Volume
->RootDir
, FileName
) && !IsIn(L
"EFI\\Microsoft\\Boot", GlobalConfig
.DontScanDirs
) &&
1003 !IsIn(L
"bootmgfw.efi", GlobalConfig
.DontScanFiles
)) {
1004 AddLoaderEntry(FileName
, L
"Microsoft EFI boot", Volume
);
1007 // scan the root directory for EFI executables
1008 ScanLoaderDir(Volume
, L
"\\", MatchPatterns
);
1010 // scan subdirectories of the EFI directory (as per the standard)
1011 DirIterOpen(Volume
->RootDir
, L
"EFI", &EfiDirIter
);
1012 while (DirIterNext(&EfiDirIter
, 1, NULL
, &EfiDirEntry
)) {
1013 if (StriCmp(EfiDirEntry
->FileName
, L
"tools") == 0 || EfiDirEntry
->FileName
[0] == '.')
1014 continue; // skip this, doesn't contain boot loaders
1015 SPrint(FileName
, 255, L
"EFI\\%s", EfiDirEntry
->FileName
);
1016 ScanLoaderDir(Volume
, FileName
, MatchPatterns
);
1018 Status
= DirIterClose(&EfiDirIter
);
1019 if (Status
!= EFI_NOT_FOUND
)
1020 CheckError(Status
, L
"while scanning the EFI directory");
1022 // Scan user-specified (or additional default) directories....
1024 while ((Directory
= FindCommaDelimited(GlobalConfig
.AlsoScan
, i
++)) != NULL
) {
1025 CleanUpPathNameSlashes(Directory
);
1026 Length
= StrLen(Directory
);
1028 ScanLoaderDir(Volume
, Directory
, MatchPatterns
);
1029 MyFreePool(Directory
);
1032 } // static VOID ScanEfiFiles()
1034 // Scan internal disks for valid EFI boot loaders....
1035 static VOID
ScanInternal(VOID
) {
1038 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1039 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_INTERNAL
) {
1040 ScanEfiFiles(Volumes
[VolumeIndex
]);
1043 } // static VOID ScanInternal()
1045 // Scan external disks for valid EFI boot loaders....
1046 static VOID
ScanExternal(VOID
) {
1049 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1050 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_EXTERNAL
) {
1051 ScanEfiFiles(Volumes
[VolumeIndex
]);
1054 } // static VOID ScanExternal()
1056 // Scan internal disks for valid EFI boot loaders....
1057 static VOID
ScanOptical(VOID
) {
1060 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1061 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_OPTICAL
) {
1062 ScanEfiFiles(Volumes
[VolumeIndex
]);
1065 } // static VOID ScanOptical()
1068 // legacy boot functions
1071 static EFI_STATUS
ActivateMbrPartition(IN EFI_BLOCK_IO
*BlockIO
, IN UINTN PartitionIndex
)
1074 UINT8 SectorBuffer
[512];
1075 MBR_PARTITION_INFO
*MbrTable
, *EMbrTable
;
1076 UINT32 ExtBase
, ExtCurrent
, NextExtCurrent
;
1077 UINTN LogicalPartitionIndex
= 4;
1079 BOOLEAN HaveBootCode
;
1082 Status
= refit_call5_wrapper(BlockIO
->ReadBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, 0, 512, SectorBuffer
);
1083 if (EFI_ERROR(Status
))
1085 if (*((UINT16
*)(SectorBuffer
+ 510)) != 0xaa55)
1086 return EFI_NOT_FOUND
; // safety measure #1
1088 // add boot code if necessary
1089 HaveBootCode
= FALSE
;
1090 for (i
= 0; i
< MBR_BOOTCODE_SIZE
; i
++) {
1091 if (SectorBuffer
[i
] != 0) {
1092 HaveBootCode
= TRUE
;
1096 if (!HaveBootCode
) {
1097 // no boot code found in the MBR, add the syslinux MBR code
1098 SetMem(SectorBuffer
, MBR_BOOTCODE_SIZE
, 0);
1099 CopyMem(SectorBuffer
, syslinux_mbr
, SYSLINUX_MBR_SIZE
);
1102 // set the partition active
1103 MbrTable
= (MBR_PARTITION_INFO
*)(SectorBuffer
+ 446);
1105 for (i
= 0; i
< 4; i
++) {
1106 if (MbrTable
[i
].Flags
!= 0x00 && MbrTable
[i
].Flags
!= 0x80)
1107 return EFI_NOT_FOUND
; // safety measure #2
1108 if (i
== PartitionIndex
)
1109 MbrTable
[i
].Flags
= 0x80;
1110 else if (PartitionIndex
>= 4 && IS_EXTENDED_PART_TYPE(MbrTable
[i
].Type
)) {
1111 MbrTable
[i
].Flags
= 0x80;
1112 ExtBase
= MbrTable
[i
].StartLBA
;
1114 MbrTable
[i
].Flags
= 0x00;
1118 Status
= refit_call5_wrapper(BlockIO
->WriteBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, 0, 512, SectorBuffer
);
1119 if (EFI_ERROR(Status
))
1122 if (PartitionIndex
>= 4) {
1123 // we have to activate a logical partition, so walk the EMBR chain
1125 // NOTE: ExtBase was set above while looking at the MBR table
1126 for (ExtCurrent
= ExtBase
; ExtCurrent
; ExtCurrent
= NextExtCurrent
) {
1127 // read current EMBR
1128 Status
= refit_call5_wrapper(BlockIO
->ReadBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, ExtCurrent
, 512, SectorBuffer
);
1129 if (EFI_ERROR(Status
))
1131 if (*((UINT16
*)(SectorBuffer
+ 510)) != 0xaa55)
1132 return EFI_NOT_FOUND
; // safety measure #3
1134 // scan EMBR, set appropriate partition active
1135 EMbrTable
= (MBR_PARTITION_INFO
*)(SectorBuffer
+ 446);
1137 for (i
= 0; i
< 4; i
++) {
1138 if (EMbrTable
[i
].Flags
!= 0x00 && EMbrTable
[i
].Flags
!= 0x80)
1139 return EFI_NOT_FOUND
; // safety measure #4
1140 if (EMbrTable
[i
].StartLBA
== 0 || EMbrTable
[i
].Size
== 0)
1142 if (IS_EXTENDED_PART_TYPE(EMbrTable
[i
].Type
)) {
1143 // link to next EMBR
1144 NextExtCurrent
= ExtBase
+ EMbrTable
[i
].StartLBA
;
1145 EMbrTable
[i
].Flags
= (PartitionIndex
>= LogicalPartitionIndex
) ? 0x80 : 0x00;
1148 // logical partition
1149 EMbrTable
[i
].Flags
= (PartitionIndex
== LogicalPartitionIndex
) ? 0x80 : 0x00;
1150 LogicalPartitionIndex
++;
1154 // write current EMBR
1155 Status
= refit_call5_wrapper(BlockIO
->WriteBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, ExtCurrent
, 512, SectorBuffer
);
1156 if (EFI_ERROR(Status
))
1159 if (PartitionIndex
< LogicalPartitionIndex
)
1160 break; // stop the loop, no need to touch further EMBRs
1166 } /* static EFI_STATUS ActivateMbrPartition() */
1168 // early 2006 Core Duo / Core Solo models
1169 static UINT8 LegacyLoaderDevicePath1Data
[] = {
1170 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1171 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1172 0xFF, 0xFF, 0xF9, 0xFF, 0x00, 0x00, 0x00, 0x00,
1173 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1174 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1175 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1177 // mid-2006 Mac Pro (and probably other Core 2 models)
1178 static UINT8 LegacyLoaderDevicePath2Data
[] = {
1179 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1180 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1181 0xFF, 0xFF, 0xF7, 0xFF, 0x00, 0x00, 0x00, 0x00,
1182 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1183 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1184 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1186 // mid-2007 MBP ("Santa Rosa" based models)
1187 static UINT8 LegacyLoaderDevicePath3Data
[] = {
1188 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1189 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1190 0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
1191 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1192 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1193 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1196 static UINT8 LegacyLoaderDevicePath4Data
[] = {
1197 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1198 0x00, 0x00, 0xC0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1199 0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
1200 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1201 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1202 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1204 // late-2008 MB/MBP (NVidia chipset)
1205 static UINT8 LegacyLoaderDevicePath5Data
[] = {
1206 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1207 0x00, 0x40, 0xCB, 0xFF, 0x00, 0x00, 0x00, 0x00,
1208 0xFF, 0xBF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
1209 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1210 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1211 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1214 static EFI_DEVICE_PATH
*LegacyLoaderList
[] = {
1215 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath1Data
,
1216 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath2Data
,
1217 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath3Data
,
1218 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath4Data
,
1219 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath5Data
,
1223 #define MAX_DISCOVERED_PATHS (16)
1225 static VOID
StartLegacy(IN LEGACY_ENTRY
*Entry
)
1228 EG_IMAGE
*BootLogoImage
;
1229 UINTN ErrorInStep
= 0;
1230 EFI_DEVICE_PATH
*DiscoveredPathList
[MAX_DISCOVERED_PATHS
];
1232 BeginExternalScreen(TRUE
, L
"Booting Legacy OS (Mac mode)");
1234 BootLogoImage
= LoadOSIcon(Entry
->Volume
->OSIconName
, L
"legacy", TRUE
);
1235 if (BootLogoImage
!= NULL
)
1236 BltImageAlpha(BootLogoImage
,
1237 (UGAWidth
- BootLogoImage
->Width
) >> 1,
1238 (UGAHeight
- BootLogoImage
->Height
) >> 1,
1239 &StdBackgroundPixel
);
1241 if (Entry
->Volume
->IsMbrPartition
) {
1242 ActivateMbrPartition(Entry
->Volume
->WholeDiskBlockIO
, Entry
->Volume
->MbrPartitionIndex
);
1245 ExtractLegacyLoaderPaths(DiscoveredPathList
, MAX_DISCOVERED_PATHS
, LegacyLoaderList
);
1247 Status
= StartEFIImageList(DiscoveredPathList
, Entry
->LoadOptions
, NULL
, L
"legacy loader", 0, &ErrorInStep
, TRUE
);
1248 if (Status
== EFI_NOT_FOUND
) {
1249 if (ErrorInStep
== 1) {
1250 Print(L
"\nPlease make sure that you have the latest firmware update installed.\n");
1251 } else if (ErrorInStep
== 3) {
1252 Print(L
"\nThe firmware refused to boot from the selected volume. Note that external\n"
1253 L
"hard drives are not well-supported by Apple's firmware for legacy OS booting.\n");
1256 FinishExternalScreen();
1257 } /* static VOID StartLegacy() */
1259 // Start a device on a non-Mac using the EFI_LEGACY_BIOS_PROTOCOL
1260 #ifdef __MAKEWITH_TIANO
1261 static VOID
StartLegacyUEFI(IN LEGACY_ENTRY
*Entry
)
1263 BeginExternalScreen(TRUE
, L
"Booting Legacy OS (UEFI mode)");
1265 BdsLibConnectDevicePath (Entry
->BdsOption
->DevicePath
);
1266 BdsLibDoLegacyBoot(Entry
->BdsOption
);
1268 // If we get here, it means that there was a failure....
1269 Print(L
"Failure booting legacy (BIOS) OS.");
1271 FinishExternalScreen();
1272 } // static VOID StartLegacyUEFI()
1273 #endif // __MAKEWITH_TIANO
1275 static LEGACY_ENTRY
* AddLegacyEntry(IN CHAR16
*LoaderTitle
, IN REFIT_VOLUME
*Volume
)
1277 LEGACY_ENTRY
*Entry
, *SubEntry
;
1278 REFIT_MENU_SCREEN
*SubScreen
;
1280 CHAR16 ShortcutLetter
= 0;
1282 if (LoaderTitle
== NULL
) {
1283 if (Volume
->OSName
!= NULL
) {
1284 LoaderTitle
= Volume
->OSName
;
1285 if (LoaderTitle
[0] == 'W' || LoaderTitle
[0] == 'L')
1286 ShortcutLetter
= LoaderTitle
[0];
1288 LoaderTitle
= L
"Legacy OS";
1290 if (Volume
->VolName
!= NULL
)
1291 VolDesc
= Volume
->VolName
;
1293 VolDesc
= (Volume
->DiskKind
== DISK_KIND_OPTICAL
) ? L
"CD" : L
"HD";
1295 // prepare the menu entry
1296 Entry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1297 Entry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1298 SPrint(Entry
->me
.Title
, 255, L
"Boot %s from %s", LoaderTitle
, VolDesc
);
1299 Entry
->me
.Tag
= TAG_LEGACY
;
1301 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1302 Entry
->me
.Image
= LoadOSIcon(Volume
->OSIconName
, L
"legacy", FALSE
);
1303 Entry
->me
.BadgeImage
= Volume
->VolBadgeImage
;
1304 Entry
->Volume
= Volume
;
1305 Entry
->LoadOptions
= (Volume
->DiskKind
== DISK_KIND_OPTICAL
) ? L
"CD" :
1306 ((Volume
->DiskKind
== DISK_KIND_EXTERNAL
) ? L
"USB" : L
"HD");
1307 Entry
->Enabled
= TRUE
;
1309 // create the submenu
1310 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
1311 SubScreen
->Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1312 SPrint(SubScreen
->Title
, 255, L
"Boot Options for %s on %s", LoaderTitle
, VolDesc
);
1313 SubScreen
->TitleImage
= Entry
->me
.Image
;
1314 SubScreen
->Hint1
= StrDuplicate(SUBSCREEN_HINT1
);
1315 if (GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_EDITOR
) {
1316 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2_NO_EDITOR
);
1318 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2
);
1322 SubEntry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1323 SubEntry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1324 SPrint(SubEntry
->me
.Title
, 255, L
"Boot %s", LoaderTitle
);
1325 SubEntry
->me
.Tag
= TAG_LEGACY
;
1326 SubEntry
->Volume
= Entry
->Volume
;
1327 SubEntry
->LoadOptions
= Entry
->LoadOptions
;
1328 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
1330 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
1331 Entry
->me
.SubScreen
= SubScreen
;
1332 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1334 } /* static LEGACY_ENTRY * AddLegacyEntry() */
1337 #ifdef __MAKEWITH_TIANO
1338 // default volume badge icon based on disk kind
1339 static EG_IMAGE
* GetDiskBadge(IN UINTN DiskType
) {
1340 EG_IMAGE
* Badge
= NULL
;
1344 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_INTERNAL
);
1347 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_EXTERNAL
);
1350 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_OPTICAL
);
1354 } // static EG_IMAGE * GetDiskBadge()
1357 Create a rEFInd boot option from a Legacy BIOS protocol option.
1359 static LEGACY_ENTRY
* AddLegacyEntryUEFI(BDS_COMMON_OPTION
*BdsOption
, IN UINT16 DiskType
)
1361 LEGACY_ENTRY
*Entry
, *SubEntry
;
1362 REFIT_MENU_SCREEN
*SubScreen
;
1363 CHAR16 ShortcutLetter
= 0;
1364 CHAR16
*LegacyDescription
= BdsOption
->Description
;
1366 // ScanVolume(Volume);
1368 // prepare the menu entry
1369 Entry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1370 Entry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1371 SPrint(Entry
->me
.Title
, 255, L
"Boot legacy target %s", LegacyDescription
);
1372 Entry
->me
.Tag
= TAG_LEGACY_UEFI
;
1374 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1375 Entry
->me
.Image
= LoadOSIcon(L
"legacy", L
"legacy", TRUE
);
1376 Entry
->LoadOptions
= (DiskType
== BBS_CDROM
) ? L
"CD" :
1377 ((DiskType
== BBS_USB
) ? L
"USB" : L
"HD");
1378 Entry
->me
.BadgeImage
= GetDiskBadge(DiskType
);
1379 // Entry->me.BadgeImage = Volume->VolBadgeImage;
1380 Entry
->BdsOption
= BdsOption
;
1381 Entry
->Enabled
= TRUE
;
1383 // create the submenu
1384 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
1385 SubScreen
->Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1386 SPrint(SubScreen
->Title
, 255, L
"No boot options for legacy target");
1387 SubScreen
->TitleImage
= Entry
->me
.Image
;
1388 SubScreen
->Hint1
= StrDuplicate(SUBSCREEN_HINT1
);
1389 if (GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_EDITOR
) {
1390 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2_NO_EDITOR
);
1392 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2
);
1396 SubEntry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1397 SubEntry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1398 SPrint(SubEntry
->me
.Title
, 255, L
"Boot %s", LegacyDescription
);
1399 SubEntry
->me
.Tag
= TAG_LEGACY_UEFI
;
1400 Entry
->BdsOption
= BdsOption
;
1401 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
1403 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
1404 Entry
->me
.SubScreen
= SubScreen
;
1405 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1407 } /* static LEGACY_ENTRY * AddLegacyEntryUEFI() */
1410 Scan for legacy BIOS targets on machines that implement EFI_LEGACY_BIOS_PROTOCOL.
1411 In testing, protocol has not been implemented on Macs but has been
1412 implemented on several Dell PCs and an ASUS motherboard.
1413 Restricts output to disks of the specified DiskType.
1415 static VOID
ScanLegacyUEFI(IN UINTN DiskType
)
1418 EFI_LEGACY_BIOS_PROTOCOL
*LegacyBios
;
1419 UINT16
*BootOrder
= NULL
;
1421 CHAR16 BootOption
[10];
1422 UINTN BootOrderSize
= 0;
1424 BDS_COMMON_OPTION
*BdsOption
;
1425 LIST_ENTRY TempList
;
1426 BBS_BBS_DEVICE_PATH
* BbsDevicePath
= NULL
;
1428 InitializeListHead (&TempList
);
1429 ZeroMem (Buffer
, sizeof (Buffer
));
1431 // If LegacyBios protocol is not implemented on this platform, then
1432 //we do not support this type of legacy boot on this machine.
1433 Status
= gBS
->LocateProtocol(&gEfiLegacyBootProtocolGuid
, NULL
, (VOID
**) &LegacyBios
);
1434 if (EFI_ERROR (Status
))
1437 // Grab the boot order
1438 BootOrder
= BdsLibGetVariableAndSize(L
"BootOrder", &gEfiGlobalVariableGuid
, &BootOrderSize
);
1439 if (BootOrder
== NULL
) {
1444 while (Index
< BootOrderSize
/ sizeof (UINT16
))
1446 // Grab each boot option variable from the boot order, and convert
1447 // the variable into a BDS boot option
1448 UnicodeSPrint (BootOption
, sizeof (BootOption
), L
"Boot%04x", BootOrder
[Index
]);
1449 BdsOption
= BdsLibVariableToOption (&TempList
, BootOption
);
1451 if (BdsOption
!= NULL
) {
1452 BbsDevicePath
= (BBS_BBS_DEVICE_PATH
*)BdsOption
->DevicePath
;
1454 // Only add the entry if it is of a requested type (e.g. USB, HD)
1456 // Two checks necessary because some systems return EFI boot loaders
1457 // with a DeviceType value that would inappropriately include them
1458 // as legacy loaders....
1459 if ((BbsDevicePath
->DeviceType
== DiskType
) && (BdsOption
->DevicePath
->Type
== DEVICE_TYPE_BIOS
)) {
1460 AddLegacyEntryUEFI(BdsOption
, BbsDevicePath
->DeviceType
);
1465 } /* static VOID ScanLegacyUEFI() */
1467 static VOID
ScanLegacyUEFI(IN UINTN DiskType
){}
1468 #endif // __MAKEWITH_TIANO
1470 static VOID
ScanLegacyVolume(REFIT_VOLUME
*Volume
, UINTN VolumeIndex
) {
1472 BOOLEAN ShowVolume
, HideIfOthersFound
;
1475 HideIfOthersFound
= FALSE
;
1476 if (Volume
->IsAppleLegacy
) {
1478 HideIfOthersFound
= TRUE
;
1479 } else if (Volume
->HasBootCode
) {
1481 if (Volume
->BlockIO
== Volume
->WholeDiskBlockIO
&&
1482 Volume
->BlockIOOffset
== 0 &&
1483 Volume
->OSName
== NULL
)
1484 // this is a whole disk (MBR) entry; hide if we have entries for partitions
1485 HideIfOthersFound
= TRUE
;
1487 if (HideIfOthersFound
) {
1488 // check for other bootable entries on the same disk
1489 for (VolumeIndex2
= 0; VolumeIndex2
< VolumesCount
; VolumeIndex2
++) {
1490 if (VolumeIndex2
!= VolumeIndex
&& Volumes
[VolumeIndex2
]->HasBootCode
&&
1491 Volumes
[VolumeIndex2
]->WholeDiskBlockIO
== Volume
->WholeDiskBlockIO
)
1497 AddLegacyEntry(NULL
, Volume
);
1498 } // static VOID ScanLegacyVolume()
1500 // Scan attached optical discs for legacy (BIOS) boot code
1501 // and add anything found to the list....
1502 static VOID
ScanLegacyDisc(VOID
)
1505 REFIT_VOLUME
*Volume
;
1507 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
) {
1508 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1509 Volume
= Volumes
[VolumeIndex
];
1510 if (Volume
->DiskKind
== DISK_KIND_OPTICAL
)
1511 ScanLegacyVolume(Volume
, VolumeIndex
);
1513 } else if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
1514 ScanLegacyUEFI(BBS_CDROM
);
1516 } /* static VOID ScanLegacyDisc() */
1518 // Scan internal hard disks for legacy (BIOS) boot code
1519 // and add anything found to the list....
1520 static VOID
ScanLegacyInternal(VOID
)
1523 REFIT_VOLUME
*Volume
;
1525 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
) {
1526 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1527 Volume
= Volumes
[VolumeIndex
];
1528 if (Volume
->DiskKind
== DISK_KIND_INTERNAL
)
1529 ScanLegacyVolume(Volume
, VolumeIndex
);
1531 } else if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
1532 ScanLegacyUEFI(BBS_HARDDISK
);
1534 } /* static VOID ScanLegacyInternal() */
1536 // Scan external disks for legacy (BIOS) boot code
1537 // and add anything found to the list....
1538 static VOID
ScanLegacyExternal(VOID
)
1541 REFIT_VOLUME
*Volume
;
1543 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
) {
1544 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1545 Volume
= Volumes
[VolumeIndex
];
1546 if (Volume
->DiskKind
== DISK_KIND_EXTERNAL
)
1547 ScanLegacyVolume(Volume
, VolumeIndex
);
1549 } else if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
1550 ScanLegacyUEFI(BBS_USB
);
1552 } /* static VOID ScanLegacyExternal() */
1555 // pre-boot tool functions
1558 static VOID
StartTool(IN LOADER_ENTRY
*Entry
)
1560 BeginExternalScreen(Entry
->UseGraphicsMode
, Entry
->me
.Title
+ 6); // assumes "Start <title>" as assigned below
1561 StartEFIImage(Entry
->DevicePath
, Entry
->LoadOptions
, Basename(Entry
->LoaderPath
),
1562 Basename(Entry
->LoaderPath
), Entry
->OSType
, NULL
, TRUE
);
1563 FinishExternalScreen();
1564 } /* static VOID StartTool() */
1566 static LOADER_ENTRY
* AddToolEntry(EFI_HANDLE DeviceHandle
, IN CHAR16
*LoaderPath
, IN CHAR16
*LoaderTitle
, IN EG_IMAGE
*Image
,
1567 IN CHAR16 ShortcutLetter
, IN BOOLEAN UseGraphicsMode
)
1569 LOADER_ENTRY
*Entry
;
1570 CHAR16
*TitleStr
= NULL
;
1572 Entry
= AllocateZeroPool(sizeof(LOADER_ENTRY
));
1574 MergeStrings(&TitleStr
, L
"Start ", 0);
1575 MergeStrings(&TitleStr
, LoaderTitle
, 0);
1576 Entry
->me
.Title
= TitleStr
;
1577 Entry
->me
.Tag
= TAG_TOOL
;
1579 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1580 Entry
->me
.Image
= Image
;
1581 Entry
->LoaderPath
= (LoaderPath
) ? StrDuplicate(LoaderPath
) : NULL
;
1582 Entry
->DevicePath
= FileDevicePath(DeviceHandle
, Entry
->LoaderPath
);
1583 Entry
->UseGraphicsMode
= UseGraphicsMode
;
1585 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1587 } /* static LOADER_ENTRY * AddToolEntry() */
1590 // pre-boot driver functions
1593 static UINTN
ScanDriverDir(IN CHAR16
*Path
)
1596 REFIT_DIR_ITER DirIter
;
1598 EFI_FILE_INFO
*DirEntry
;
1599 CHAR16 FileName
[256];
1601 CleanUpPathNameSlashes(Path
);
1602 // look through contents of the directory
1603 DirIterOpen(SelfRootDir
, Path
, &DirIter
);
1604 while (DirIterNext(&DirIter
, 2, LOADER_MATCH_PATTERNS
, &DirEntry
)) {
1605 if (DirEntry
->FileName
[0] == '.')
1606 continue; // skip this
1608 SPrint(FileName
, 255, L
"%s\\%s", Path
, DirEntry
->FileName
);
1610 Status
= StartEFIImage(FileDevicePath(SelfLoadedImage
->DeviceHandle
, FileName
),
1611 L
"", DirEntry
->FileName
, DirEntry
->FileName
, 0, NULL
, FALSE
);
1613 Status
= DirIterClose(&DirIter
);
1614 if (Status
!= EFI_NOT_FOUND
) {
1615 SPrint(FileName
, 255, L
"while scanning the %s directory", Path
);
1616 CheckError(Status
, FileName
);
1621 #ifdef __MAKEWITH_GNUEFI
1622 static EFI_STATUS
ConnectAllDriversToAllControllers(VOID
)
1625 UINTN AllHandleCount
;
1626 EFI_HANDLE
*AllHandleBuffer
;
1629 EFI_HANDLE
*HandleBuffer
;
1635 Status
= LibLocateHandle(AllHandles
,
1640 if (EFI_ERROR(Status
))
1643 for (Index
= 0; Index
< AllHandleCount
; Index
++) {
1645 // Scan the handle database
1647 Status
= LibScanHandleDatabase(NULL
,
1649 AllHandleBuffer
[Index
],
1654 if (EFI_ERROR (Status
))
1658 if (HandleType
[Index
] & EFI_HANDLE_TYPE_DRIVER_BINDING_HANDLE
)
1660 if (HandleType
[Index
] & EFI_HANDLE_TYPE_IMAGE_HANDLE
)
1665 for (HandleIndex
= 0; HandleIndex
< HandleCount
; HandleIndex
++) {
1666 if (HandleType
[HandleIndex
] & EFI_HANDLE_TYPE_PARENT_HANDLE
)
1671 if (HandleType
[Index
] & EFI_HANDLE_TYPE_DEVICE_HANDLE
) {
1672 Status
= refit_call4_wrapper(BS
->ConnectController
,
1673 AllHandleBuffer
[Index
],
1681 MyFreePool (HandleBuffer
);
1682 MyFreePool (HandleType
);
1686 MyFreePool (AllHandleBuffer
);
1688 } /* EFI_STATUS ConnectAllDriversToAllControllers() */
1690 static EFI_STATUS
ConnectAllDriversToAllControllers(VOID
) {
1691 BdsLibConnectAllDriversToAllControllers();
1696 // Load all EFI drivers from rEFInd's "drivers" subdirectory and from the
1697 // directories specified by the user in the "scan_driver_dirs" configuration
1699 static VOID
LoadDrivers(VOID
)
1701 CHAR16
*Directory
, *SelfDirectory
;
1702 UINTN i
= 0, Length
, NumFound
= 0;
1704 // load drivers from the subdirectories of rEFInd's home directory specified
1705 // in the DRIVER_DIRS constant.
1706 while ((Directory
= FindCommaDelimited(DRIVER_DIRS
, i
++)) != NULL
) {
1707 SelfDirectory
= SelfDirPath
? StrDuplicate(SelfDirPath
) : NULL
;
1708 CleanUpPathNameSlashes(SelfDirectory
);
1709 MergeStrings(&SelfDirectory
, Directory
, L
'\\');
1710 NumFound
+= ScanDriverDir(SelfDirectory
);
1711 MyFreePool(Directory
);
1712 MyFreePool(SelfDirectory
);
1715 // Scan additional user-specified driver directories....
1717 while ((Directory
= FindCommaDelimited(GlobalConfig
.DriverDirs
, i
++)) != NULL
) {
1718 CleanUpPathNameSlashes(Directory
);
1719 Length
= StrLen(Directory
);
1721 NumFound
+= ScanDriverDir(Directory
);
1723 MyFreePool(Directory
);
1726 // connect all devices
1728 ConnectAllDriversToAllControllers();
1729 } /* static VOID LoadDrivers() */
1731 // Determine what (if any) type of legacy (BIOS) boot support is available
1732 static VOID
FindLegacyBootType(VOID
) {
1733 #ifdef __MAKEWITH_TIANO
1735 EFI_LEGACY_BIOS_PROTOCOL
*LegacyBios
;
1738 GlobalConfig
.LegacyType
= LEGACY_TYPE_NONE
;
1740 // UEFI-style legacy BIOS support is available only with the TianoCore EDK2
1741 // build environment, and then only with some implementations....
1742 #ifdef __MAKEWITH_TIANO
1743 Status
= gBS
->LocateProtocol (&gEfiLegacyBootProtocolGuid
, NULL
, (VOID
**) &LegacyBios
);
1744 if (!EFI_ERROR (Status
))
1745 GlobalConfig
.LegacyType
= LEGACY_TYPE_UEFI
;
1748 // Macs have their own system. If the firmware vendor code contains the
1749 // string "Apple", assume it's available. Note that this overrides the
1750 // UEFI type, and might yield false positives if the vendor string
1751 // contains "Apple" as part of something bigger, so this isn't 100%
1753 if (StriSubCmp(L
"Apple", ST
->FirmwareVendor
))
1754 GlobalConfig
.LegacyType
= LEGACY_TYPE_MAC
;
1755 } // static VOID FindLegacyBootType
1757 // Warn the user if legacy OS scans are enabled but the firmware or this
1758 // application can't support them....
1759 static VOID
WarnIfLegacyProblems() {
1760 BOOLEAN found
= FALSE
;
1763 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_NONE
) {
1765 if (GlobalConfig
.ScanFor
[i
] == 'h' || GlobalConfig
.ScanFor
[i
] == 'b' || GlobalConfig
.ScanFor
[i
] == 'c')
1768 } while ((i
< NUM_SCAN_OPTIONS
) && (!found
));
1770 Print(L
"NOTE: refind.conf's 'scanfor' line specifies scanning for one or more legacy\n");
1771 Print(L
"(BIOS) boot options; however, this is not possible because ");
1772 #ifdef __MAKEWITH_TIANO
1773 Print(L
"your computer lacks\n");
1774 Print(L
"the necessary Compatibility Support Module (CSM) support.\n");
1776 Print(L
"this program was\n");
1777 Print(L
"compiled without the necessary support. Please visit\n");
1778 Print(L
"http://www.rodsbooks.com/refind/getting.html and download and install a rEFInd\n");
1779 Print(L
"binary built with the TianoCore EDK2 to enable legacy boot support.\n");
1783 } // if no legacy support
1784 } // static VOID WarnIfLegacyProblems()
1786 // Locates boot loaders. NOTE: This assumes that GlobalConfig.LegacyType is set correctly.
1787 static VOID
ScanForBootloaders(VOID
) {
1792 // scan for loaders and tools, add them to the menu
1793 for (i
= 0; i
< NUM_SCAN_OPTIONS
; i
++) {
1794 switch(GlobalConfig
.ScanFor
[i
]) {
1799 ScanLegacyInternal();
1802 ScanLegacyExternal();
1805 ScanUserConfigured();
1819 // assign shortcut keys
1820 for (i
= 0; i
< MainMenu
.EntryCount
&& MainMenu
.Entries
[i
]->Row
== 0 && i
< 9; i
++)
1821 MainMenu
.Entries
[i
]->ShortcutDigit
= (CHAR16
)('1' + i
);
1823 // wait for user ACK when there were errors
1824 FinishTextScreen(FALSE
);
1825 } // static VOID ScanForBootloaders()
1827 // Add the second-row tags containing built-in and external tools (EFI shell,
1829 static VOID
ScanForTools(VOID
) {
1830 CHAR16
*FileName
= NULL
, Description
[256];
1831 REFIT_MENU_ENTRY
*TempMenuEntry
;
1832 UINTN i
, j
, VolumeIndex
;
1834 for (i
= 0; i
< NUM_TOOLS
; i
++) {
1835 switch(GlobalConfig
.ShowTools
[i
]) {
1837 TempMenuEntry
= CopyMenuEntry(&MenuEntryShutdown
);
1838 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_SHUTDOWN
);
1839 AddMenuEntry(&MainMenu
, TempMenuEntry
);
1842 TempMenuEntry
= CopyMenuEntry(&MenuEntryReset
);
1843 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_RESET
);
1844 AddMenuEntry(&MainMenu
, TempMenuEntry
);
1847 TempMenuEntry
= CopyMenuEntry(&MenuEntryAbout
);
1848 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT
);
1849 AddMenuEntry(&MainMenu
, TempMenuEntry
);
1852 TempMenuEntry
= CopyMenuEntry(&MenuEntryExit
);
1853 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_EXIT
);
1854 AddMenuEntry(&MainMenu
, TempMenuEntry
);
1858 MyFreePool(FileName
);
1859 while ((FileName
= FindCommaDelimited(SHELL_NAMES
, j
++)) != NULL
) {
1860 if (FileExists(SelfRootDir
, FileName
)) {
1861 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, L
"EFI Shell", BuiltinIcon(BUILTIN_ICON_TOOL_SHELL
),
1867 MyFreePool(FileName
);
1868 FileName
= StrDuplicate(L
"\\efi\\tools\\gptsync.efi");
1869 // MergeStrings(&FileName, L"\\efi\\tools\\gptsync.efi", 0);
1870 if (FileExists(SelfRootDir
, FileName
)) {
1871 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, L
"Make Hybrid MBR", BuiltinIcon(BUILTIN_ICON_TOOL_PART
), 'P', FALSE
);
1874 case TAG_APPLE_RECOVERY
:
1875 MyFreePool(FileName
);
1876 FileName
= StrDuplicate(L
"\\com.apple.recovery.boot\\boot.efi");
1877 // MergeStrings(&FileName, L"\\com.apple.recovery.boot\\boot.efi", 0);
1878 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1879 if ((Volumes
[VolumeIndex
]->RootDir
!= NULL
) && (FileExists(Volumes
[VolumeIndex
]->RootDir
, FileName
))) {
1880 SPrint(Description
, 255, L
"Apple Recovery on %s", Volumes
[VolumeIndex
]->VolName
);
1881 AddToolEntry(Volumes
[VolumeIndex
]->DeviceHandle
, FileName
, Description
,
1882 BuiltinIcon(BUILTIN_ICON_TOOL_APPLE_RESCUE
), 'R', TRUE
);
1888 MyFreePool(FileName
);
1889 while ((FileName
= FindCommaDelimited(MOK_NAMES
, j
++)) != NULL
) {
1890 if (FileExists(SelfRootDir
, FileName
)) {
1891 SPrint(Description
, 255, L
"MOK Key Manager at %s", FileName
);
1892 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, Description
,
1893 BuiltinIcon(BUILTIN_ICON_TOOL_MOK_TOOL
), 'S', FALSE
);
1896 if (FileExists(SelfDir
, L
"MokManager.efi")) {
1897 MyFreePool(FileName
);
1898 FileName
= StrDuplicate(SelfDirPath
);
1899 MergeStrings(&FileName
, L
"\\MokManager.efi", 0);
1900 SPrint(Description
, 255, L
"MOK Key Manager at %s", FileName
);
1901 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, Description
,
1902 BuiltinIcon(BUILTIN_ICON_TOOL_MOK_TOOL
), 'S', FALSE
);
1906 MyFreePool(FileName
);
1909 } // static VOID ScanForTools
1911 // Rescan for boot loaders
1912 VOID
RescanAll(VOID
) {
1919 egDisplayMessage(L
"Scanning for new boot loaders; please wait....", &BGColor
);
1920 FreeList((VOID
***) &(MainMenu
.Entries
), &MainMenu
.EntryCount
);
1921 MainMenu
.Entries
= NULL
;
1922 MainMenu
.EntryCount
= 0;
1923 ReadConfig(CONFIG_FILE_NAME
);
1924 ConnectAllDriversToAllControllers();
1926 ScanForBootloaders();
1929 } // VOID RescanAll()
1931 #ifndef __MAKEWITH_GNUEFI
1933 // Minimal initialization function
1934 static VOID
InitializeLib(IN EFI_HANDLE ImageHandle
, IN EFI_SYSTEM_TABLE
*SystemTable
) {
1936 // gImageHandle = ImageHandle;
1937 gBS
= SystemTable
->BootServices
;
1938 // gRS = SystemTable->RuntimeServices;
1939 gRT
= SystemTable
->RuntimeServices
; // Some BDS functions need gRT to be set
1940 EfiGetSystemConfigurationTable (&gEfiDxeServicesTableGuid
, (VOID
**) &gDS
);
1942 InitializeConsoleSim();
1952 efi_main (EFI_HANDLE ImageHandle
, EFI_SYSTEM_TABLE
*SystemTable
)
1955 BOOLEAN MainLoopRunning
= TRUE
;
1956 REFIT_MENU_ENTRY
*ChosenEntry
;
1962 InitializeLib(ImageHandle
, SystemTable
);
1963 Status
= InitRefitLib(ImageHandle
);
1964 if (EFI_ERROR(Status
))
1967 // read configuration
1968 CopyMem(GlobalConfig
.ScanFor
, "ieom ", NUM_SCAN_OPTIONS
);
1969 FindLegacyBootType();
1970 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
)
1971 CopyMem(GlobalConfig
.ScanFor
, "ihebocm ", NUM_SCAN_OPTIONS
);
1972 ReadConfig(CONFIG_FILE_NAME
);
1975 WarnIfLegacyProblems();
1976 MainMenu
.TimeoutSeconds
= GlobalConfig
.Timeout
;
1978 // disable EFI watchdog timer
1979 refit_call4_wrapper(BS
->SetWatchdogTimer
, 0x0000, 0x0000, 0x0000, NULL
);
1981 // further bootstrap (now with config available)
1985 ScanForBootloaders();
1988 if (GlobalConfig
.ScanDelay
> 0) {
1993 egDisplayMessage(L
"Pausing before disk scan; please wait....", &BGColor
);
1994 for (i
= 0; i
< GlobalConfig
.ScanDelay
; i
++)
1995 refit_call1_wrapper(BS
->Stall
, 1000000);
1999 Selection
= StrDuplicate(GlobalConfig
.DefaultSelection
);
2000 while (MainLoopRunning
) {
2001 MenuExit
= RunMainMenu(&MainMenu
, Selection
, &ChosenEntry
);
2003 // The Escape key triggers a re-scan operation....
2004 if (MenuExit
== MENU_EXIT_ESCAPE
) {
2009 switch (ChosenEntry
->Tag
) {
2011 case TAG_REBOOT
: // Reboot
2013 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);
2014 MainLoopRunning
= FALSE
; // just in case we get this far
2017 case TAG_SHUTDOWN
: // Shut Down
2019 refit_call4_wrapper(RT
->ResetSystem
, EfiResetShutdown
, EFI_SUCCESS
, 0, NULL
);
2020 MainLoopRunning
= FALSE
; // just in case we get this far
2023 case TAG_ABOUT
: // About rEFInd
2027 case TAG_LOADER
: // Boot OS via .EFI loader
2028 StartLoader((LOADER_ENTRY
*)ChosenEntry
);
2031 case TAG_LEGACY
: // Boot legacy OS
2032 StartLegacy((LEGACY_ENTRY
*)ChosenEntry
);
2035 #ifdef __MAKEWITH_TIANO
2036 case TAG_LEGACY_UEFI
: // Boot a legacy OS on a non-Mac
2037 StartLegacyUEFI((LEGACY_ENTRY
*)ChosenEntry
);
2039 #endif // __MAKEWITH_TIANO
2041 case TAG_TOOL
: // Start a EFI tool
2042 StartTool((LOADER_ENTRY
*)ChosenEntry
);
2045 case TAG_EXIT
: // Terminate rEFInd
2046 BeginTextScreen(L
" ");
2051 MyFreePool(Selection
);
2052 Selection
= (ChosenEntry
->Title
) ? StrDuplicate(ChosenEntry
->Title
) : NULL
;
2055 // If we end up here, things have gone wrong. Try to reboot, and if that
2056 // fails, go into an endless loop.
2057 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);