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 "security_policy.h"
53 #include "../include/Handle.h"
54 #include "../include/refit_call_wrapper.h"
55 #include "driver_support.h"
56 #include "../include/syslinux_mbr.h"
58 #ifdef __MAKEWITH_GNUEFI
59 #define EFI_SECURITY_VIOLATION EFIERR (26)
61 #include "../EfiLib/BdsHelper.h"
62 #endif // __MAKEWITH_GNUEFI
67 #define MACOSX_LOADER_PATH L"System\\Library\\CoreServices\\boot.efi"
69 #define SHELL_NAMES L"\\EFI\\tools\\shell.efi,\\EFI\\tools\\shellx64.efi,\\shellx64.efi"
70 #define DRIVER_DIRS L"drivers,drivers_x64"
71 #define FALLBACK_FULLNAME L"EFI\\BOOT\\bootx64.efi"
72 #define FALLBACK_BASENAME L"bootx64.efi"
74 #define SHELL_NAMES L"\\EFI\\tools\\shell.efi,\\EFI\\tools\\shellia32.efi,\\shellia32.efi"
75 #define DRIVER_DIRS L"drivers,drivers_ia32"
76 #define FALLBACK_FULLNAME L"EFI\\BOOT\\bootia32.efi"
77 #define FALLBACK_BASENAME L"bootia32.efi"
79 #define SHELL_NAMES L"\\EFI\\tools\\shell.efi"
80 #define DRIVER_DIRS L"drivers"
81 #define FALLBACK_FULLNAME L"EFI\\BOOT\\boot.efi" /* Not really correct */
82 #define FALLBACK_BASENAME L"boot.efi" /* Not really correct */
85 #define MOK_NAMES L"\\EFI\\tools\\MokManager.efi,\\EFI\\fedora\\MokManager.efi,\\EFI\\redhat\\MokManager.efi,\\EFI\\ubuntu\\MokManager.efi,\\EFI\\suse\\MokManager"
87 // Filename patterns that identify EFI boot loaders. Note that a single case (either L"*.efi" or
88 // L"*.EFI") is fine for most systems; but Gigabyte's buggy Hybrid EFI does a case-sensitive
89 // comparison when it should do a case-insensitive comparison, so I'm doubling this up. It does
90 // no harm on other computers, AFAIK. In theory, every case variation should be done for
91 // completeness, but that's ridiculous....
92 #define LOADER_MATCH_PATTERNS L"*.efi,*.EFI"
94 // Patterns that identify Linux kernels. Added to the loader match pattern when the
95 // scan_all_linux_kernels option is set in the configuration file. Causes kernels WITHOUT
96 // a ".efi" extension to be found when scanning for boot loaders.
97 #define LINUX_MATCH_PATTERNS L"vmlinuz*,bzImage*"
99 // Default hint text for program-launch submenus
100 #define SUBSCREEN_HINT1 L"Use arrow keys to move cursor; Enter to boot;"
101 #define SUBSCREEN_HINT2 L"Insert or F2 to edit options; Esc to return to main menu"
102 #define SUBSCREEN_HINT2_NO_EDITOR L"Esc to return to main menu"
104 static REFIT_MENU_ENTRY MenuEntryAbout
= { L
"About rEFInd", TAG_ABOUT
, 1, 0, 'A', NULL
, NULL
, NULL
};
105 static REFIT_MENU_ENTRY MenuEntryReset
= { L
"Reboot Computer", TAG_REBOOT
, 1, 0, 'R', NULL
, NULL
, NULL
};
106 static REFIT_MENU_ENTRY MenuEntryShutdown
= { L
"Shut Down Computer", TAG_SHUTDOWN
, 1, 0, 'U', NULL
, NULL
, NULL
};
107 static REFIT_MENU_ENTRY MenuEntryReturn
= { L
"Return to Main Menu", TAG_RETURN
, 0, 0, 0, NULL
, NULL
, NULL
};
108 static REFIT_MENU_ENTRY MenuEntryExit
= { L
"Exit rEFInd", TAG_EXIT
, 1, 0, 0, NULL
, NULL
, NULL
};
110 static REFIT_MENU_SCREEN MainMenu
= { L
"Main Menu", NULL
, 0, NULL
, 0, NULL
, 0, L
"Automatic boot",
111 L
"Use arrow keys to move cursor; Enter to boot;",
112 L
"Insert or F2 for more options; Esc to refresh" };
113 static REFIT_MENU_SCREEN AboutMenu
= { L
"About", NULL
, 0, NULL
, 0, NULL
, 0, NULL
, L
"Press Enter to return to main menu", L
"" };
115 REFIT_CONFIG GlobalConfig
= { FALSE
, FALSE
, 0, 0, 0, DONT_CHANGE_TEXT_MODE
, 20, 0, 0, GRAPHICS_FOR_OSX
, LEGACY_TYPE_MAC
, 0,
116 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
117 {TAG_SHELL
, TAG_APPLE_RECOVERY
, TAG_MOK_TOOL
, TAG_ABOUT
, TAG_SHUTDOWN
, TAG_REBOOT
, 0, 0, 0, 0, 0 }};
119 // Structure used to hold boot loader filenames and time stamps in
120 // a linked list; used to sort entries within a directory.
124 struct LOADER_LIST
*NextEntry
;
131 static VOID
AboutrEFInd(VOID
)
133 CHAR16
*TempStr
; // Note: Don't deallocate; moved to menu structure
135 if (AboutMenu
.EntryCount
== 0) {
136 AboutMenu
.TitleImage
= BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT
);
137 AddMenuInfoLine(&AboutMenu
, L
"rEFInd Version 0.6.6.6");
138 AddMenuInfoLine(&AboutMenu
, L
"");
139 AddMenuInfoLine(&AboutMenu
, L
"Copyright (c) 2006-2010 Christoph Pfisterer");
140 AddMenuInfoLine(&AboutMenu
, L
"Copyright (c) 2012 Roderick W. Smith");
141 AddMenuInfoLine(&AboutMenu
, L
"Portions Copyright (c) Intel Corporation and others");
142 AddMenuInfoLine(&AboutMenu
, L
"Distributed under the terms of the GNU GPLv3 license");
143 AddMenuInfoLine(&AboutMenu
, L
"");
144 AddMenuInfoLine(&AboutMenu
, L
"Running on:");
145 TempStr
= AllocateZeroPool(256 * sizeof(CHAR16
));
146 SPrint(TempStr
, 255, L
" EFI Revision %d.%02d", ST
->Hdr
.Revision
>> 16, ST
->Hdr
.Revision
& ((1 << 16) - 1));
147 AddMenuInfoLine(&AboutMenu
, TempStr
);
149 AddMenuInfoLine(&AboutMenu
, L
" Platform: x86 (32 bit)");
150 #elif defined(EFIX64)
151 TempStr
= AllocateZeroPool(256 * sizeof(CHAR16
));
152 SPrint(TempStr
, 255, L
" Platform: x86_64 (64 bit); Secure Boot %s", secure_mode() ? L
"active" : L
"inactive");
153 AddMenuInfoLine(&AboutMenu
, TempStr
);
155 AddMenuInfoLine(&AboutMenu
, L
" Platform: unknown");
157 TempStr
= AllocateZeroPool(256 * sizeof(CHAR16
));
158 SPrint(TempStr
, 255, L
" Firmware: %s %d.%02d",
159 ST
->FirmwareVendor
, ST
->FirmwareRevision
>> 16, ST
->FirmwareRevision
& ((1 << 16) - 1));
160 AddMenuInfoLine(&AboutMenu
, TempStr
);
161 TempStr
= AllocateZeroPool(256 * sizeof(CHAR16
));
162 SPrint(TempStr
, 255, L
" Screen Output: %s", egScreenDescription());
163 AddMenuInfoLine(&AboutMenu
, TempStr
);
164 AddMenuInfoLine(&AboutMenu
, L
"");
165 #if defined(__MAKEWITH_GNUEFI)
166 AddMenuInfoLine(&AboutMenu
, L
"Built with GNU-EFI");
168 AddMenuInfoLine(&AboutMenu
, L
"Built with TianoCore EDK2");
170 AddMenuInfoLine(&AboutMenu
, L
"");
171 AddMenuInfoLine(&AboutMenu
, L
"For more information, see the rEFInd Web site:");
172 AddMenuInfoLine(&AboutMenu
, L
"http://www.rodsbooks.com/refind/");
173 AddMenuEntry(&AboutMenu
, &MenuEntryReturn
);
176 RunMenu(&AboutMenu
, NULL
);
177 } /* VOID AboutrEFInd() */
179 // Launch an EFI binary.
180 static EFI_STATUS
StartEFIImageList(IN EFI_DEVICE_PATH
**DevicePaths
,
181 IN CHAR16
*LoadOptions
, IN CHAR16
*LoadOptionsPrefix
,
182 IN CHAR16
*ImageTitle
, IN CHAR8 OSType
,
183 OUT UINTN
*ErrorInStep
,
186 EFI_STATUS Status
, ReturnStatus
;
187 EFI_HANDLE ChildImageHandle
;
188 EFI_LOADED_IMAGE
*ChildLoadedImage
= NULL
;
189 UINTN DevicePathIndex
;
190 CHAR16 ErrorInfo
[256];
191 CHAR16
*FullLoadOptions
= NULL
;
193 if (ErrorInStep
!= NULL
)
197 if (LoadOptions
!= NULL
) {
198 if (LoadOptionsPrefix
!= NULL
) {
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 file were read ahead of
221 // time and passed as a pre-loaded image to LoadImage(), 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 (ReturnStatus
!= EFI_NOT_FOUND
) {
233 SPrint(ErrorInfo
, 255, L
"while loading %s", ImageTitle
);
234 if (CheckError(Status
, ErrorInfo
)) {
235 if (ErrorInStep
!= NULL
)
240 ReturnStatus
= Status
= refit_call3_wrapper(BS
->HandleProtocol
, ChildImageHandle
, &LoadedImageProtocol
,
241 (VOID
**) &ChildLoadedImage
);
242 if (CheckError(Status
, L
"while getting a LoadedImageProtocol handle")) {
243 if (ErrorInStep
!= NULL
)
247 ChildLoadedImage
->LoadOptions
= (VOID
*)FullLoadOptions
;
248 ChildLoadedImage
->LoadOptionsSize
= ((UINT32
)StrLen(FullLoadOptions
) + 1) * sizeof(CHAR16
);
249 // turn control over to the image
250 // TODO: (optionally) re-enable the EFI watchdog timer!
252 // close open file handles
254 ReturnStatus
= Status
= refit_call3_wrapper(BS
->StartImage
, ChildImageHandle
, NULL
, NULL
);
256 // control returns here when the child image calls Exit()
257 SPrint(ErrorInfo
, 255, L
"returned from %s", ImageTitle
);
258 if (CheckError(Status
, ErrorInfo
)) {
259 if (ErrorInStep
!= NULL
)
263 // re-open file handles
267 // unload the image, we don't care if it works or not...
268 Status
= refit_call1_wrapper(BS
->UnloadImage
, ChildImageHandle
);
271 MyFreePool(FullLoadOptions
);
273 } /* static EFI_STATUS StartEFIImageList() */
275 static EFI_STATUS
StartEFIImage(IN EFI_DEVICE_PATH
*DevicePath
,
276 IN CHAR16
*LoadOptions
, IN CHAR16
*LoadOptionsPrefix
,
277 IN CHAR16
*ImageTitle
, IN CHAR8 OSType
,
278 OUT UINTN
*ErrorInStep
,
281 EFI_DEVICE_PATH
*DevicePaths
[2];
283 DevicePaths
[0] = DevicePath
;
284 DevicePaths
[1] = NULL
;
285 return StartEFIImageList(DevicePaths
, LoadOptions
, LoadOptionsPrefix
, ImageTitle
, OSType
, ErrorInStep
, Verbose
);
286 } /* static EFI_STATUS StartEFIImage() */
289 // EFI OS loader functions
292 static VOID
StartLoader(IN LOADER_ENTRY
*Entry
)
294 UINTN ErrorInStep
= 0;
296 BeginExternalScreen(Entry
->UseGraphicsMode
, L
"Booting OS");
297 StartEFIImage(Entry
->DevicePath
, Entry
->LoadOptions
, Basename(Entry
->LoaderPath
),
298 Basename(Entry
->LoaderPath
), Entry
->OSType
, &ErrorInStep
, !Entry
->UseGraphicsMode
);
299 FinishExternalScreen();
302 // Locate an initrd or initramfs file that matches the kernel specified by LoaderPath.
303 // The matching file has a name that begins with "init" and includes the same version
304 // number string as is found in LoaderPath -- but not a longer version number string.
305 // For instance, if LoaderPath is \EFI\kernels\bzImage-3.3.0.efi, and if \EFI\kernels
306 // has a file called initramfs-3.3.0.img, this function will return the string
307 // '\EFI\kernels\initramfs-3.3.0.img'. If the directory ALSO contains the file
308 // initramfs-3.3.0-rc7.img or initramfs-13.3.0.img, those files will NOT match;
309 // however, initmine-3.3.0.img might match. (FindInitrd() returns the first match it
310 // finds). Thus, care should be taken to avoid placing duplicate matching files in
311 // the kernel's directory.
312 // If no matching init file can be found, returns NULL.
313 static CHAR16
* FindInitrd(IN CHAR16
*LoaderPath
, IN REFIT_VOLUME
*Volume
) {
314 CHAR16
*InitrdName
= NULL
, *FileName
, *KernelVersion
, *InitrdVersion
, *Path
;
315 REFIT_DIR_ITER DirIter
;
316 EFI_FILE_INFO
*DirEntry
;
318 FileName
= Basename(LoaderPath
);
319 KernelVersion
= FindNumbers(FileName
);
320 Path
= FindPath(LoaderPath
);
322 // Add trailing backslash for root directory; necessary on some systems, but must
323 // NOT be added to all directories, since on other systems, a trailing backslash on
324 // anything but the root directory causes them to flake out!
325 if (StrLen(Path
) == 0) {
326 MergeStrings(&Path
, L
"\\", 0);
328 DirIterOpen(Volume
->RootDir
, Path
, &DirIter
);
329 // Now add a trailing backslash if it was NOT added earlier, for consistency in
330 // building the InitrdName later....
331 if ((StrLen(Path
) > 0) && (Path
[StrLen(Path
) - 1] != L
'\\'))
332 MergeStrings(&Path
, L
"\\", 0);
333 while ((DirIterNext(&DirIter
, 2, L
"init*", &DirEntry
)) && (InitrdName
== NULL
)) {
334 InitrdVersion
= FindNumbers(DirEntry
->FileName
);
335 if (KernelVersion
!= NULL
) {
336 if (StriCmp(InitrdVersion
, KernelVersion
) == 0) {
337 MergeStrings(&InitrdName
, Path
, 0);
338 MergeStrings(&InitrdName
, DirEntry
->FileName
, 0);
341 if (InitrdVersion
== NULL
) {
342 MergeStrings(&InitrdName
, Path
, 0);
343 MergeStrings(&InitrdName
, DirEntry
->FileName
, 0);
346 MyFreePool(InitrdVersion
);
348 DirIterClose(&DirIter
);
350 // Note: Don't FreePool(FileName), since Basename returns a pointer WITHIN the string it's passed.
351 MyFreePool(KernelVersion
);
354 } // static CHAR16 * FindInitrd()
356 LOADER_ENTRY
* AddPreparedLoaderEntry(LOADER_ENTRY
*Entry
) {
357 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
360 } // LOADER_ENTRY * AddPreparedLoaderEntry()
362 // Creates a copy of a menu screen.
363 // Returns a pointer to the copy of the menu screen.
364 static REFIT_MENU_SCREEN
* CopyMenuScreen(REFIT_MENU_SCREEN
*Entry
) {
365 REFIT_MENU_SCREEN
*NewEntry
;
368 NewEntry
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
369 if ((Entry
!= NULL
) && (NewEntry
!= NULL
)) {
370 CopyMem(NewEntry
, Entry
, sizeof(REFIT_MENU_SCREEN
));
371 NewEntry
->Title
= (Entry
->Title
) ? StrDuplicate(Entry
->Title
) : NULL
;
372 NewEntry
->TimeoutText
= (Entry
->TimeoutText
) ? StrDuplicate(Entry
->TimeoutText
) : NULL
;
373 if (Entry
->TitleImage
!= NULL
) {
374 NewEntry
->TitleImage
= AllocatePool(sizeof(EG_IMAGE
));
375 if (NewEntry
->TitleImage
!= NULL
)
376 CopyMem(NewEntry
->TitleImage
, Entry
->TitleImage
, sizeof(EG_IMAGE
));
378 NewEntry
->InfoLines
= (CHAR16
**) AllocateZeroPool(Entry
->InfoLineCount
* (sizeof(CHAR16
*)));
379 for (i
= 0; i
< Entry
->InfoLineCount
&& NewEntry
->InfoLines
; i
++) {
380 NewEntry
->InfoLines
[i
] = (Entry
->InfoLines
[i
]) ? StrDuplicate(Entry
->InfoLines
[i
]) : NULL
;
382 NewEntry
->Entries
= (REFIT_MENU_ENTRY
**) AllocateZeroPool(Entry
->EntryCount
* (sizeof (REFIT_MENU_ENTRY
*)));
383 for (i
= 0; i
< Entry
->EntryCount
&& NewEntry
->Entries
; i
++) {
384 AddMenuEntry(NewEntry
, Entry
->Entries
[i
]);
386 NewEntry
->Hint1
= (Entry
->Hint1
) ? StrDuplicate(Entry
->Hint1
) : NULL
;
387 NewEntry
->Hint2
= (Entry
->Hint2
) ? StrDuplicate(Entry
->Hint2
) : NULL
;
390 } // static REFIT_MENU_SCREEN* CopyMenuScreen()
392 // Creates a copy of a menu entry. Intended to enable moving a stack-based
393 // menu entry (such as the ones for the "reboot" and "exit" functions) to
394 // to the heap. This enables easier deletion of the whole set of menu
395 // entries when re-scanning.
396 // Returns a pointer to the copy of the menu entry.
397 static REFIT_MENU_ENTRY
* CopyMenuEntry(REFIT_MENU_ENTRY
*Entry
) {
398 REFIT_MENU_ENTRY
*NewEntry
;
400 NewEntry
= AllocateZeroPool(sizeof(REFIT_MENU_ENTRY
));
401 if ((Entry
!= NULL
) && (NewEntry
!= NULL
)) {
402 CopyMem(NewEntry
, Entry
, sizeof(REFIT_MENU_ENTRY
));
403 NewEntry
->Title
= (Entry
->Title
) ? StrDuplicate(Entry
->Title
) : NULL
;
404 if (Entry
->BadgeImage
!= NULL
) {
405 NewEntry
->BadgeImage
= AllocatePool(sizeof(EG_IMAGE
));
406 if (NewEntry
->BadgeImage
!= NULL
)
407 CopyMem(NewEntry
->BadgeImage
, Entry
->BadgeImage
, sizeof(EG_IMAGE
));
409 if (Entry
->Image
!= NULL
) {
410 NewEntry
->Image
= AllocatePool(sizeof(EG_IMAGE
));
411 if (NewEntry
->Image
!= NULL
)
412 CopyMem(NewEntry
->Image
, Entry
->Image
, sizeof(EG_IMAGE
));
414 if (Entry
->SubScreen
!= NULL
) {
415 NewEntry
->SubScreen
= CopyMenuScreen(Entry
->SubScreen
);
419 } // REFIT_MENU_ENTRY* CopyMenuEntry()
421 // Creates a new LOADER_ENTRY data structure and populates it with
422 // default values from the specified Entry, or NULL values if Entry
423 // is unspecified (NULL).
424 // Returns a pointer to the new data structure, or NULL if it
425 // couldn't be allocated
426 LOADER_ENTRY
*InitializeLoaderEntry(IN LOADER_ENTRY
*Entry
) {
427 LOADER_ENTRY
*NewEntry
= NULL
;
429 NewEntry
= AllocateZeroPool(sizeof(LOADER_ENTRY
));
430 if (NewEntry
!= NULL
) {
431 NewEntry
->me
.Title
= NULL
;
432 NewEntry
->me
.Tag
= TAG_LOADER
;
433 NewEntry
->Enabled
= TRUE
;
434 NewEntry
->UseGraphicsMode
= FALSE
;
435 NewEntry
->OSType
= 0;
437 NewEntry
->LoaderPath
= (Entry
->LoaderPath
) ? StrDuplicate(Entry
->LoaderPath
) : NULL
;
438 NewEntry
->VolName
= (Entry
->VolName
) ? StrDuplicate(Entry
->VolName
) : NULL
;
439 NewEntry
->DevicePath
= Entry
->DevicePath
;
440 NewEntry
->UseGraphicsMode
= Entry
->UseGraphicsMode
;
441 NewEntry
->LoadOptions
= (Entry
->LoadOptions
) ? StrDuplicate(Entry
->LoadOptions
) : NULL
;
442 NewEntry
->InitrdPath
= (Entry
->InitrdPath
) ? StrDuplicate(Entry
->InitrdPath
) : NULL
;
446 } // LOADER_ENTRY *InitializeLoaderEntry()
448 // Adds InitrdPath to Options, but only if Options doesn't already include an
449 // initrd= line. Done to enable overriding the default initrd selection in a
450 // refind_linux.conf file's options list.
451 // Returns a pointer to a new string. The calling function is responsible for
452 // freeing its memory.
453 static CHAR16
*AddInitrdToOptions(CHAR16
*Options
, CHAR16
*InitrdPath
) {
454 CHAR16
*NewOptions
= NULL
;
457 NewOptions
= StrDuplicate(Options
);
458 if ((InitrdPath
!= NULL
) && !StriSubCmp(L
"initrd=", Options
)) {
459 MergeStrings(&NewOptions
, L
"initrd=", L
' ');
460 MergeStrings(&NewOptions
, InitrdPath
, 0);
463 } // CHAR16 *AddInitrdToOptions()
465 // Prepare a REFIT_MENU_SCREEN data structure for a subscreen entry. This sets up
466 // the default entry that launches the boot loader using the same options as the
467 // main Entry does. Subsequent options can be added by the calling function.
468 // If a subscreen already exists in the Entry that's passed to this function,
469 // it's left unchanged and a pointer to it is returned.
470 // Returns a pointer to the new subscreen data structure, or NULL if there
471 // were problems allocating memory.
472 REFIT_MENU_SCREEN
*InitializeSubScreen(IN LOADER_ENTRY
*Entry
) {
473 CHAR16
*FileName
, *MainOptions
= NULL
;
474 REFIT_MENU_SCREEN
*SubScreen
= NULL
;
475 LOADER_ENTRY
*SubEntry
;
477 FileName
= Basename(Entry
->LoaderPath
);
478 if (Entry
->me
.SubScreen
== NULL
) { // No subscreen yet; initialize default entry....
479 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
480 if (SubScreen
!= NULL
) {
481 SubScreen
->Title
= AllocateZeroPool(sizeof(CHAR16
) * 256);
482 SPrint(SubScreen
->Title
, 255, L
"Boot Options for %s on %s",
483 (Entry
->Title
!= NULL
) ? Entry
->Title
: FileName
, Entry
->VolName
);
484 SubScreen
->TitleImage
= Entry
->me
.Image
;
486 SubEntry
= InitializeLoaderEntry(Entry
);
487 if (SubEntry
!= NULL
) {
488 SubEntry
->me
.Title
= StrDuplicate(L
"Boot using default options");
489 MainOptions
= SubEntry
->LoadOptions
;
490 SubEntry
->LoadOptions
= AddInitrdToOptions(MainOptions
, SubEntry
->InitrdPath
);
491 MyFreePool(MainOptions
);
492 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
493 } // if (SubEntry != NULL)
494 SubScreen
->Hint1
= StrDuplicate(SUBSCREEN_HINT1
);
495 if (GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_EDITOR
) {
496 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2_NO_EDITOR
);
498 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2
);
500 } // if (SubScreen != NULL)
501 } else { // existing subscreen; less initialization, and just add new entry later....
502 SubScreen
= Entry
->me
.SubScreen
;
505 } // REFIT_MENU_SCREEN *InitializeSubScreen()
507 VOID
GenerateSubScreen(LOADER_ENTRY
*Entry
, IN REFIT_VOLUME
*Volume
) {
508 REFIT_MENU_SCREEN
*SubScreen
;
509 LOADER_ENTRY
*SubEntry
;
511 CHAR16 DiagsFileName
[256];
516 // create the submenu
517 if (StrLen(Entry
->Title
) == 0) {
518 MyFreePool(Entry
->Title
);
521 SubScreen
= InitializeSubScreen(Entry
);
523 // loader-specific submenu entries
524 if (Entry
->OSType
== 'M') { // entries for Mac OS X
526 SubEntry
= InitializeLoaderEntry(Entry
);
527 if (SubEntry
!= NULL
) {
528 SubEntry
->me
.Title
= L
"Boot Mac OS X with a 64-bit kernel";
529 SubEntry
->LoadOptions
= L
"arch=x86_64";
530 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
531 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
534 SubEntry
= InitializeLoaderEntry(Entry
);
535 if (SubEntry
!= NULL
) {
536 SubEntry
->me
.Title
= L
"Boot Mac OS X with a 32-bit kernel";
537 SubEntry
->LoadOptions
= L
"arch=i386";
538 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
539 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
543 if (!(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_SINGLEUSER
)) {
544 SubEntry
= InitializeLoaderEntry(Entry
);
545 if (SubEntry
!= NULL
) {
546 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode";
547 SubEntry
->UseGraphicsMode
= FALSE
;
548 SubEntry
->LoadOptions
= L
"-v";
549 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
553 SubEntry
= InitializeLoaderEntry(Entry
);
554 if (SubEntry
!= NULL
) {
555 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode (64-bit)";
556 SubEntry
->UseGraphicsMode
= FALSE
;
557 SubEntry
->LoadOptions
= L
"-v arch=x86_64";
558 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
561 SubEntry
= InitializeLoaderEntry(Entry
);
562 if (SubEntry
!= NULL
) {
563 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode (32-bit)";
564 SubEntry
->UseGraphicsMode
= FALSE
;
565 SubEntry
->LoadOptions
= L
"-v arch=i386";
566 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
570 SubEntry
= InitializeLoaderEntry(Entry
);
571 if (SubEntry
!= NULL
) {
572 SubEntry
->me
.Title
= L
"Boot Mac OS X in single user mode";
573 SubEntry
->UseGraphicsMode
= FALSE
;
574 SubEntry
->LoadOptions
= L
"-v -s";
575 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
577 } // single-user mode allowed
579 if (!(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_SAFEMODE
)) {
580 SubEntry
= InitializeLoaderEntry(Entry
);
581 if (SubEntry
!= NULL
) {
582 SubEntry
->me
.Title
= L
"Boot Mac OS X in safe mode";
583 SubEntry
->UseGraphicsMode
= FALSE
;
584 SubEntry
->LoadOptions
= L
"-v -x";
585 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
587 } // safe mode allowed
589 // check for Apple hardware diagnostics
590 StrCpy(DiagsFileName
, L
"System\\Library\\CoreServices\\.diagnostics\\diags.efi");
591 if (FileExists(Volume
->RootDir
, DiagsFileName
) && !(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_HWTEST
)) {
592 SubEntry
= InitializeLoaderEntry(Entry
);
593 if (SubEntry
!= NULL
) {
594 SubEntry
->me
.Title
= L
"Run Apple Hardware Test";
595 MyFreePool(SubEntry
->LoaderPath
);
596 SubEntry
->LoaderPath
= StrDuplicate(DiagsFileName
);
597 SubEntry
->DevicePath
= FileDevicePath(Volume
->DeviceHandle
, SubEntry
->LoaderPath
);
598 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
599 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
601 } // if diagnostics entry found
603 } else if (Entry
->OSType
== 'L') { // entries for Linux kernels with EFI stub loaders
604 File
= ReadLinuxOptionsFile(Entry
->LoaderPath
, Volume
);
606 InitrdName
= FindInitrd(Entry
->LoaderPath
, Volume
);
607 TokenCount
= ReadTokenLine(File
, &TokenList
);
608 // first entry requires special processing, since it was initially set
609 // up with a default title but correct options by InitializeSubScreen(),
611 if ((SubScreen
->Entries
!= NULL
) && (SubScreen
->Entries
[0] != NULL
)) {
612 MyFreePool(SubScreen
->Entries
[0]->Title
);
613 SubScreen
->Entries
[0]->Title
= TokenList
[0] ? StrDuplicate(TokenList
[0]) : StrDuplicate(L
"Boot Linux");
615 FreeTokenLine(&TokenList
, &TokenCount
);
616 while ((TokenCount
= ReadTokenLine(File
, &TokenList
)) > 1) {
617 SubEntry
= InitializeLoaderEntry(Entry
);
618 SubEntry
->me
.Title
= TokenList
[0] ? StrDuplicate(TokenList
[0]) : StrDuplicate(L
"Boot Linux");
619 MyFreePool(SubEntry
->LoadOptions
);
620 SubEntry
->LoadOptions
= AddInitrdToOptions(TokenList
[1], InitrdName
);
621 FreeTokenLine(&TokenList
, &TokenCount
);
622 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_LINUX
;
623 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
625 MyFreePool(InitrdName
);
627 } // if Linux options file exists
629 } else if (Entry
->OSType
== 'E') { // entries for ELILO
630 SubEntry
= InitializeLoaderEntry(Entry
);
631 if (SubEntry
!= NULL
) {
632 SubEntry
->me
.Title
= L
"Run ELILO in interactive mode";
633 SubEntry
->LoadOptions
= L
"-p";
634 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
635 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
638 SubEntry
= InitializeLoaderEntry(Entry
);
639 if (SubEntry
!= NULL
) {
640 SubEntry
->me
.Title
= L
"Boot Linux for a 17\" iMac or a 15\" MacBook Pro (*)";
641 SubEntry
->UseGraphicsMode
= TRUE
;
642 SubEntry
->LoadOptions
= L
"-d 0 i17";
643 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
644 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
647 SubEntry
= InitializeLoaderEntry(Entry
);
648 if (SubEntry
!= NULL
) {
649 SubEntry
->me
.Title
= L
"Boot Linux for a 20\" iMac (*)";
650 SubEntry
->UseGraphicsMode
= TRUE
;
651 SubEntry
->LoadOptions
= L
"-d 0 i20";
652 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
653 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
656 SubEntry
= InitializeLoaderEntry(Entry
);
657 if (SubEntry
!= NULL
) {
658 SubEntry
->me
.Title
= L
"Boot Linux for a Mac Mini (*)";
659 SubEntry
->UseGraphicsMode
= TRUE
;
660 SubEntry
->LoadOptions
= L
"-d 0 mini";
661 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
662 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
665 AddMenuInfoLine(SubScreen
, L
"NOTE: This is an example. Entries");
666 AddMenuInfoLine(SubScreen
, L
"marked with (*) may not work.");
668 } else if (Entry
->OSType
== 'X') { // entries for xom.efi
669 // by default, skip the built-in selection and boot from hard disk only
670 Entry
->LoadOptions
= L
"-s -h";
672 SubEntry
= InitializeLoaderEntry(Entry
);
673 if (SubEntry
!= NULL
) {
674 SubEntry
->me
.Title
= L
"Boot Windows from Hard Disk";
675 SubEntry
->LoadOptions
= L
"-s -h";
676 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
677 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
680 SubEntry
= InitializeLoaderEntry(Entry
);
681 if (SubEntry
!= NULL
) {
682 SubEntry
->me
.Title
= L
"Boot Windows from CD-ROM";
683 SubEntry
->LoadOptions
= L
"-s -c";
684 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
685 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
688 SubEntry
= InitializeLoaderEntry(Entry
);
689 if (SubEntry
!= NULL
) {
690 SubEntry
->me
.Title
= L
"Run XOM in text mode";
691 SubEntry
->UseGraphicsMode
= FALSE
;
692 SubEntry
->LoadOptions
= L
"-v";
693 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
694 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
696 } // entries for xom.efi
697 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
698 Entry
->me
.SubScreen
= SubScreen
;
699 } // VOID GenerateSubScreen()
701 // Returns options for a Linux kernel. Reads them from an options file in the
702 // kernel's directory; and if present, adds an initrd= option for an initial
703 // RAM disk file with the same version number as the kernel file.
704 static CHAR16
* GetMainLinuxOptions(IN CHAR16
* LoaderPath
, IN REFIT_VOLUME
*Volume
) {
705 CHAR16
*Options
= NULL
, *InitrdName
, *FullOptions
= NULL
;
707 Options
= GetFirstOptionsFromFile(LoaderPath
, Volume
);
708 InitrdName
= FindInitrd(LoaderPath
, Volume
);
709 FullOptions
= AddInitrdToOptions(Options
, InitrdName
);
712 MyFreePool(InitrdName
);
713 return (FullOptions
);
714 } // static CHAR16 * GetMainLinuxOptions()
716 // Sets a few defaults for a loader entry -- mainly the icon, but also the OS type
717 // code and shortcut letter. For Linux EFI stub loaders, also sets kernel options
718 // that will (with luck) work fairly automatically.
719 VOID
SetLoaderDefaults(LOADER_ENTRY
*Entry
, CHAR16
*LoaderPath
, REFIT_VOLUME
*Volume
) {
720 CHAR16
*FileName
, *PathOnly
, *NoExtension
, *OSIconName
= NULL
, *Temp
, *SubString
;
721 CHAR16 ShortcutLetter
= 0;
724 FileName
= Basename(LoaderPath
);
725 PathOnly
= FindPath(LoaderPath
);
726 NoExtension
= StripEfiExtension(FileName
);
728 // locate a custom icon for the loader
729 // Anything found here takes precedence over the "hints" in the OSIconName variable
730 if (!Entry
->me
.Image
)
731 Entry
->me
.Image
= egFindIcon(NoExtension
, 128);
732 if (!Entry
->me
.Image
)
733 Entry
->me
.Image
= Volume
->VolIconImage
;
735 // Begin creating icon "hints" by using last part of directory path leading
737 Temp
= FindLastDirName(LoaderPath
);
738 MergeStrings(&OSIconName
, Temp
, L
',');
741 if (OSIconName
!= NULL
) {
742 ShortcutLetter
= OSIconName
[0];
745 // Add every "word" in the volume label, delimited by spaces, dashes (-), or
746 // underscores (_), to the list of hints to be used in searching for OS
748 if ((Volume
->VolName
) && (StrLen(Volume
->VolName
) > 0)) {
749 Temp
= SubString
= StrDuplicate(Volume
->VolName
);
751 Length
= StrLen(Temp
);
752 for (i
= 0; i
< Length
; i
++) {
753 if ((Temp
[i
] == L
' ') || (Temp
[i
] == L
'_') || (Temp
[i
] == L
'-')) {
755 if (StrLen(SubString
) > 0)
756 MergeStrings(&OSIconName
, SubString
, L
',');
757 SubString
= Temp
+ i
+ 1;
760 MergeStrings(&OSIconName
, SubString
, L
',');
765 // detect specific loaders
766 if (StriSubCmp(L
"bzImage", LoaderPath
) || StriSubCmp(L
"vmlinuz", LoaderPath
)) {
767 MergeStrings(&OSIconName
, L
"linux", L
',');
769 if (ShortcutLetter
== 0)
770 ShortcutLetter
= 'L';
771 Entry
->LoadOptions
= GetMainLinuxOptions(LoaderPath
, Volume
);
772 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_LINUX
;
773 } else if (StriSubCmp(L
"refit", LoaderPath
)) {
774 MergeStrings(&OSIconName
, L
"refit", L
',');
776 ShortcutLetter
= 'R';
777 } else if (StriCmp(LoaderPath
, MACOSX_LOADER_PATH
) == 0) {
778 if (Volume
->VolIconImage
!= NULL
) { // custom icon file found
779 Entry
->me
.Image
= Volume
->VolIconImage
;
781 MergeStrings(&OSIconName
, L
"mac", L
',');
783 ShortcutLetter
= 'M';
784 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
785 } else if (StriCmp(FileName
, L
"diags.efi") == 0) {
786 MergeStrings(&OSIconName
, L
"hwtest", L
',');
787 } else if (StriCmp(FileName
, L
"e.efi") == 0 || StriCmp(FileName
, L
"elilo.efi") == 0 || StriSubCmp(L
"elilo", FileName
)) {
788 MergeStrings(&OSIconName
, L
"elilo,linux", L
',');
790 if (ShortcutLetter
== 0)
791 ShortcutLetter
= 'L';
792 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
793 } else if (StriSubCmp(L
"grub", FileName
)) {
795 ShortcutLetter
= 'G';
796 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_GRUB
;
797 } else if (StriCmp(FileName
, L
"cdboot.efi") == 0 ||
798 StriCmp(FileName
, L
"bootmgr.efi") == 0 ||
799 StriCmp(FileName
, L
"bootmgfw.efi") == 0) {
800 MergeStrings(&OSIconName
, L
"win", L
',');
802 ShortcutLetter
= 'W';
803 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
804 } else if (StriCmp(FileName
, L
"xom.efi") == 0) {
805 MergeStrings(&OSIconName
, L
"xom,win", L
',');
806 Entry
->UseGraphicsMode
= TRUE
;
808 ShortcutLetter
= 'W';
809 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
812 if ((ShortcutLetter
>= 'a') && (ShortcutLetter
<= 'z'))
813 ShortcutLetter
= ShortcutLetter
- 'a' + 'A'; // convert lowercase to uppercase
814 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
815 if (Entry
->me
.Image
== NULL
)
816 Entry
->me
.Image
= LoadOSIcon(OSIconName
, L
"unknown", FALSE
);
817 MyFreePool(PathOnly
);
818 } // VOID SetLoaderDefaults()
820 // Add a specified EFI boot loader to the list, using automatic settings
821 // for icons, options, etc.
822 LOADER_ENTRY
* AddLoaderEntry(IN CHAR16
*LoaderPath
, IN CHAR16
*LoaderTitle
, IN REFIT_VOLUME
*Volume
) {
825 CleanUpPathNameSlashes(LoaderPath
);
826 Entry
= InitializeLoaderEntry(NULL
);
828 Entry
->Title
= StrDuplicate((LoaderTitle
!= NULL
) ? LoaderTitle
: LoaderPath
);
829 Entry
->me
.Title
= AllocateZeroPool(sizeof(CHAR16
) * 256);
830 SPrint(Entry
->me
.Title
, 255, L
"Boot %s from %s", (LoaderTitle
!= NULL
) ? LoaderTitle
: LoaderPath
, Volume
->VolName
);
832 Entry
->me
.BadgeImage
= Volume
->VolBadgeImage
;
833 if ((LoaderPath
!= NULL
) && (LoaderPath
[0] != L
'\\')) {
834 Entry
->LoaderPath
= StrDuplicate(L
"\\");
836 Entry
->LoaderPath
= NULL
;
838 MergeStrings(&(Entry
->LoaderPath
), LoaderPath
, 0);
839 Entry
->VolName
= Volume
->VolName
;
840 Entry
->DevicePath
= FileDevicePath(Volume
->DeviceHandle
, Entry
->LoaderPath
);
841 SetLoaderDefaults(Entry
, LoaderPath
, Volume
);
842 GenerateSubScreen(Entry
, Volume
);
843 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
847 } // LOADER_ENTRY * AddLoaderEntry()
849 // Returns -1 if (Time1 < Time2), +1 if (Time1 > Time2), or 0 if
850 // (Time1 == Time2). Precision is only to the nearest second; since
851 // this is used for sorting boot loader entries, differences smaller
852 // than this are likely to be meaningless (and unlikely!).
853 INTN
TimeComp(IN EFI_TIME
*Time1
, IN EFI_TIME
*Time2
) {
854 INT64 Time1InSeconds
, Time2InSeconds
;
856 // Following values are overestimates; I'm assuming 31 days in every month.
857 // This is fine for the purpose of this function, which is limited
858 Time1InSeconds
= Time1
->Second
+ (Time1
->Minute
* 60) + (Time1
->Hour
* 3600) + (Time1
->Day
* 86400) +
859 (Time1
->Month
* 2678400) + ((Time1
->Year
- 1998) * 32140800);
860 Time2InSeconds
= Time2
->Second
+ (Time2
->Minute
* 60) + (Time2
->Hour
* 3600) + (Time2
->Day
* 86400) +
861 (Time2
->Month
* 2678400) + ((Time2
->Year
- 1998) * 32140800);
862 if (Time1InSeconds
< Time2InSeconds
)
864 else if (Time1InSeconds
> Time2InSeconds
)
870 // Adds a loader list element, keeping it sorted by date. Returns the new
871 // first element (the one with the most recent date).
872 static struct LOADER_LIST
* AddLoaderListEntry(struct LOADER_LIST
*LoaderList
, struct LOADER_LIST
*NewEntry
) {
873 struct LOADER_LIST
*LatestEntry
, *CurrentEntry
, *PrevEntry
= NULL
;
875 LatestEntry
= CurrentEntry
= LoaderList
;
876 if (LoaderList
== NULL
) {
877 LatestEntry
= NewEntry
;
879 while ((CurrentEntry
!= NULL
) && (TimeComp(&(NewEntry
->TimeStamp
), &(CurrentEntry
->TimeStamp
)) < 0)) {
880 PrevEntry
= CurrentEntry
;
881 CurrentEntry
= CurrentEntry
->NextEntry
;
883 NewEntry
->NextEntry
= CurrentEntry
;
884 if (PrevEntry
== NULL
) {
885 LatestEntry
= NewEntry
;
887 PrevEntry
->NextEntry
= NewEntry
;
890 return (LatestEntry
);
891 } // static VOID AddLoaderListEntry()
893 // Delete the LOADER_LIST linked list
894 static VOID
CleanUpLoaderList(struct LOADER_LIST
*LoaderList
) {
895 struct LOADER_LIST
*Temp
;
897 while (LoaderList
!= NULL
) {
899 LoaderList
= LoaderList
->NextEntry
;
900 MyFreePool(Temp
->FileName
);
903 } // static VOID CleanUpLoaderList()
905 // Returns FALSE if the specified file/volume matches the GlobalConfig.DontScanDirs
906 // or GlobalConfig.DontScanVolumes specification, or if Path points to a volume
907 // other than the one specified by Volume. Returns TRUE if none of these conditions
908 // is met -- that is, if the path is eligible for scanning. Also reduces *Path to a
909 // path alone, with no volume specification.
910 static BOOLEAN
ShouldScan(REFIT_VOLUME
*Volume
, CHAR16
*Path
) {
911 CHAR16
*VolName
= NULL
, *DontScanDir
;
913 BOOLEAN ScanIt
= TRUE
;
915 if (IsIn(Volume
->VolName
, GlobalConfig
.DontScanVolumes
))
918 while ((DontScanDir
= FindCommaDelimited(GlobalConfig
.DontScanDirs
, i
++)) && ScanIt
) {
919 SplitVolumeAndFilename(&DontScanDir
, &VolName
);
920 CleanUpPathNameSlashes(DontScanDir
);
921 if (VolName
!= NULL
) {
922 if ((StriCmp(VolName
, Volume
->VolName
) == 0) && (StriCmp(DontScanDir
, Path
) == 0))
924 if ((StrLen(VolName
) > 2) && (VolName
[0] == L
'f') && (VolName
[1] == L
's') && (VolName
[2] >= L
'0') && (VolName
[2] <= L
'9')) {
925 VolNum
= Atoi(VolName
+ 2);
926 if ((VolNum
== Volume
->VolNumber
) && (StriCmp(DontScanDir
, Path
) == 0))
930 if (StriCmp(DontScanDir
, Path
) == 0)
933 MyFreePool(DontScanDir
);
937 } // BOOLEAN ShouldScan()
939 // Returns TRUE if the file is byte-for-byte identical with the fallback file
940 // on the volume AND if the file is not itself the fallback file; returns
941 // FALSE if the file is not identical to the fallback file OR if the file
942 // IS the fallback file. Intended for use in excluding the fallback boot
943 // loader when it's a duplicate of another boot loader.
944 BOOLEAN
DuplicatesFallback(IN REFIT_VOLUME
*Volume
, IN CHAR16
*FileName
) {
945 CHAR8
*FileContents
, *FallbackContents
;
946 EFI_FILE_HANDLE FileHandle
, FallbackHandle
;
947 EFI_FILE_INFO
*FileInfo
, *FallbackInfo
;
948 UINTN FileSize
= 0, FallbackSize
= 0;
950 BOOLEAN AreIdentical
= FALSE
;
952 CleanUpPathNameSlashes(FileName
);
954 if (StriCmp(FileName
, FALLBACK_FULLNAME
) == 0)
955 return FALSE
; // identical filenames, so not a duplicate....
957 Status
= refit_call5_wrapper(Volume
->RootDir
->Open
, Volume
->RootDir
, &FileHandle
, FileName
, EFI_FILE_MODE_READ
, 0);
958 if (Status
== EFI_SUCCESS
) {
959 FileInfo
= LibFileInfo(FileHandle
);
960 FileSize
= FileInfo
->FileSize
;
965 Status
= refit_call5_wrapper(Volume
->RootDir
->Open
, Volume
->RootDir
, &FallbackHandle
, FALLBACK_FULLNAME
, EFI_FILE_MODE_READ
, 0);
966 if (Status
== EFI_SUCCESS
) {
967 FallbackInfo
= LibFileInfo(FallbackHandle
);
968 FallbackSize
= FallbackInfo
->FileSize
;
970 refit_call1_wrapper(FileHandle
->Close
, FileHandle
);
974 if (FallbackSize
!= FileSize
) { // not same size, so can't be identical
975 AreIdentical
= FALSE
;
976 } else { // could be identical; do full check....
977 FileContents
= AllocatePool(FileSize
);
978 FallbackContents
= AllocatePool(FallbackSize
);
979 if (FileContents
&& FallbackContents
) {
980 Status
= refit_call3_wrapper(FileHandle
->Read
, FileHandle
, &FileSize
, FileContents
);
981 if (Status
== EFI_SUCCESS
)
982 Status
= refit_call3_wrapper(FallbackHandle
->Read
, FallbackHandle
, &FallbackSize
, FallbackContents
);
983 if (Status
== EFI_SUCCESS
) {
984 AreIdentical
= (CompareMem(FileContents
, FallbackContents
, FileSize
) == 0);
987 MyFreePool(FileContents
);
988 MyFreePool(FallbackContents
);
991 refit_call1_wrapper(FileHandle
->Close
, FileHandle
);
992 refit_call1_wrapper(FileHandle
->Close
, FallbackHandle
);
995 } // BOOLEAN DuplicatesFallback()
997 // Scan an individual directory for EFI boot loader files and, if found,
998 // add them to the list. Exception: Ignores FALLBACK_FULLNAME, which is picked
999 // up in ScanEfiFiles(). Sorts the entries within the loader directory so that
1000 // the most recent one appears first in the list.
1001 // Returns TRUE if a duplicate for FALLBACK_FILENAME was found, FALSE if not.
1002 static BOOLEAN
ScanLoaderDir(IN REFIT_VOLUME
*Volume
, IN CHAR16
*Path
, IN CHAR16
*Pattern
)
1005 REFIT_DIR_ITER DirIter
;
1006 EFI_FILE_INFO
*DirEntry
;
1007 CHAR16 FileName
[256], *Extension
;
1008 struct LOADER_LIST
*LoaderList
= NULL
, *NewLoader
;
1009 BOOLEAN FoundFallbackDuplicate
= FALSE
;
1011 if ((!SelfDirPath
|| !Path
|| ((StriCmp(Path
, SelfDirPath
) == 0) && (Volume
->DeviceHandle
!= SelfVolume
->DeviceHandle
)) ||
1012 (StriCmp(Path
, SelfDirPath
) != 0)) &&
1013 (ShouldScan(Volume
, Path
))) {
1014 // look through contents of the directory
1015 DirIterOpen(Volume
->RootDir
, Path
, &DirIter
);
1016 while (DirIterNext(&DirIter
, 2, Pattern
, &DirEntry
)) {
1017 Extension
= FindExtension(DirEntry
->FileName
);
1018 if (DirEntry
->FileName
[0] == '.' ||
1019 StriCmp(Extension
, L
".icns") == 0 ||
1020 StriCmp(Extension
, L
".png") == 0 ||
1021 (StriCmp(DirEntry
->FileName
, FALLBACK_BASENAME
) == 0 && (StriCmp(Path
, L
"EFI\\BOOT") == 0)) ||
1022 StriSubCmp(L
"shell", DirEntry
->FileName
) ||
1023 IsIn(DirEntry
->FileName
, GlobalConfig
.DontScanFiles
))
1024 continue; // skip this
1027 SPrint(FileName
, 255, L
"\\%s\\%s", Path
, DirEntry
->FileName
);
1029 SPrint(FileName
, 255, L
"\\%s", DirEntry
->FileName
);
1030 CleanUpPathNameSlashes(FileName
);
1031 NewLoader
= AllocateZeroPool(sizeof(struct LOADER_LIST
));
1032 if (NewLoader
!= NULL
) {
1033 NewLoader
->FileName
= StrDuplicate(FileName
);
1034 NewLoader
->TimeStamp
= DirEntry
->ModificationTime
;
1035 LoaderList
= AddLoaderListEntry(LoaderList
, NewLoader
);
1036 if (DuplicatesFallback(Volume
, FileName
))
1037 FoundFallbackDuplicate
= TRUE
;
1039 MyFreePool(Extension
);
1041 NewLoader
= LoaderList
;
1042 while (NewLoader
!= NULL
) {
1043 AddLoaderEntry(NewLoader
->FileName
, NULL
, Volume
);
1044 NewLoader
= NewLoader
->NextEntry
;
1046 CleanUpLoaderList(LoaderList
);
1047 Status
= DirIterClose(&DirIter
);
1048 if (Status
!= EFI_NOT_FOUND
) {
1050 SPrint(FileName
, 255, L
"while scanning the %s directory", Path
);
1052 StrCpy(FileName
, L
"while scanning the root directory");
1053 CheckError(Status
, FileName
);
1054 } // if (Status != EFI_NOT_FOUND)
1055 } // if not scanning our own directory
1057 return FoundFallbackDuplicate
;
1058 } /* static VOID ScanLoaderDir() */
1060 static VOID
ScanEfiFiles(REFIT_VOLUME
*Volume
) {
1062 REFIT_DIR_ITER EfiDirIter
;
1063 EFI_FILE_INFO
*EfiDirEntry
;
1064 CHAR16 FileName
[256], *Directory
, *MatchPatterns
, *VolName
= NULL
;
1065 UINTN i
, Length
, VolNum
;
1066 BOOLEAN ScanFallbackLoader
= TRUE
;
1068 // Print(L"Entering ScanEfiFiles(), GlobalConfig.ScanAllLinux = %s\n", GlobalConfig.ScanAllLinux ? L"TRUE" : L"FALSE");
1069 MatchPatterns
= StrDuplicate(LOADER_MATCH_PATTERNS
);
1070 if (GlobalConfig
.ScanAllLinux
)
1071 MergeStrings(&MatchPatterns
, LINUX_MATCH_PATTERNS
, L
',');
1072 // Print(L"MatchPatterns = '%s'\n", MatchPatterns);
1074 if ((Volume
->RootDir
!= NULL
) && (Volume
->VolName
!= NULL
)) {
1075 // check for Mac OS X boot loader
1076 if (!IsIn(L
"System\\Library\\CoreServices", GlobalConfig
.DontScanDirs
)) {
1077 StrCpy(FileName
, MACOSX_LOADER_PATH
);
1078 if (FileExists(Volume
->RootDir
, FileName
) && !IsIn(L
"boot.efi", GlobalConfig
.DontScanFiles
)) {
1079 AddLoaderEntry(FileName
, L
"Mac OS X", Volume
);
1080 if (DuplicatesFallback(Volume
, FileName
))
1081 ScanFallbackLoader
= FALSE
;
1085 StrCpy(FileName
, L
"System\\Library\\CoreServices\\xom.efi");
1086 if (FileExists(Volume
->RootDir
, FileName
) && !IsIn(L
"boot.efi", GlobalConfig
.DontScanFiles
)) {
1087 AddLoaderEntry(FileName
, L
"Windows XP (XoM)", Volume
);
1088 if (DuplicatesFallback(Volume
, FileName
))
1089 ScanFallbackLoader
= FALSE
;
1091 } // if Mac directory not in GlobalConfig.DontScanDirs list
1093 // check for Microsoft boot loader/menu
1094 StrCpy(FileName
, L
"EFI\\Microsoft\\Boot\\Bootmgfw.efi");
1095 if (FileExists(Volume
->RootDir
, FileName
) && !IsIn(L
"EFI\\Microsoft\\Boot", GlobalConfig
.DontScanDirs
) &&
1096 !IsIn(L
"bootmgfw.efi", GlobalConfig
.DontScanFiles
)) {
1097 AddLoaderEntry(FileName
, L
"Microsoft EFI boot", Volume
);
1098 if (DuplicatesFallback(Volume
, FileName
))
1099 ScanFallbackLoader
= FALSE
;
1102 // scan the root directory for EFI executables
1103 if (ScanLoaderDir(Volume
, L
"\\", MatchPatterns
))
1104 ScanFallbackLoader
= FALSE
;
1106 // scan subdirectories of the EFI directory (as per the standard)
1107 DirIterOpen(Volume
->RootDir
, L
"EFI", &EfiDirIter
);
1108 while (DirIterNext(&EfiDirIter
, 1, NULL
, &EfiDirEntry
)) {
1109 if (StriCmp(EfiDirEntry
->FileName
, L
"tools") == 0 || EfiDirEntry
->FileName
[0] == '.')
1110 continue; // skip this, doesn't contain boot loaders or is scanned later
1111 SPrint(FileName
, 255, L
"EFI\\%s", EfiDirEntry
->FileName
);
1112 if (ScanLoaderDir(Volume
, FileName
, MatchPatterns
))
1113 ScanFallbackLoader
= FALSE
;
1115 Status
= DirIterClose(&EfiDirIter
);
1116 if (Status
!= EFI_NOT_FOUND
)
1117 CheckError(Status
, L
"while scanning the EFI directory");
1119 // Scan user-specified (or additional default) directories....
1121 while ((Directory
= FindCommaDelimited(GlobalConfig
.AlsoScan
, i
++)) != NULL
) {
1122 VolNum
= VOL_DONTSCAN
;
1123 SplitVolumeAndFilename(&Directory
, &VolName
);
1124 CleanUpPathNameSlashes(Directory
);
1125 Length
= StrLen(Directory
);
1126 if (VolName
&& (Length
> 0) && (StrLen(VolName
) > 2) && (VolName
[0] == L
'f') && (VolName
[1] == L
's') &&
1127 (VolName
[2] >= L
'0') && (VolName
[2] <= L
'9'))
1128 VolNum
= Atoi(VolName
+ 2);
1129 if ((Length
> 0) && ((VolName
== NULL
) || (StriCmp(VolName
, Volume
->VolName
) == 0) || (Volume
->VolNumber
== VolNum
)))
1130 if (ScanLoaderDir(Volume
, Directory
, MatchPatterns
))
1131 ScanFallbackLoader
= FALSE
;
1132 MyFreePool(Directory
);
1133 MyFreePool(VolName
);
1136 // If not a duplicate & if it exists & if it's not us, create an entry
1137 // for the fallback boot loader
1138 if (ScanFallbackLoader
&& FileExists(Volume
->RootDir
, FALLBACK_FULLNAME
) &&
1139 ((StriCmp(SelfDirPath
, L
"EFI\\BOOT") != 0) || (Volume
->DeviceHandle
!= SelfVolume
->DeviceHandle
))) {
1140 AddLoaderEntry(FALLBACK_FULLNAME
, L
"Fallback boot loader", Volume
);
1143 } // static VOID ScanEfiFiles()
1145 // Scan internal disks for valid EFI boot loaders....
1146 static VOID
ScanInternal(VOID
) {
1149 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1150 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_INTERNAL
) {
1151 ScanEfiFiles(Volumes
[VolumeIndex
]);
1154 } // static VOID ScanInternal()
1156 // Scan external disks for valid EFI boot loaders....
1157 static VOID
ScanExternal(VOID
) {
1160 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1161 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_EXTERNAL
) {
1162 ScanEfiFiles(Volumes
[VolumeIndex
]);
1165 } // static VOID ScanExternal()
1167 // Scan internal disks for valid EFI boot loaders....
1168 static VOID
ScanOptical(VOID
) {
1171 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1172 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_OPTICAL
) {
1173 ScanEfiFiles(Volumes
[VolumeIndex
]);
1176 } // static VOID ScanOptical()
1179 // legacy boot functions
1182 static EFI_STATUS
ActivateMbrPartition(IN EFI_BLOCK_IO
*BlockIO
, IN UINTN PartitionIndex
)
1185 UINT8 SectorBuffer
[512];
1186 MBR_PARTITION_INFO
*MbrTable
, *EMbrTable
;
1187 UINT32 ExtBase
, ExtCurrent
, NextExtCurrent
;
1188 UINTN LogicalPartitionIndex
= 4;
1190 BOOLEAN HaveBootCode
;
1193 Status
= refit_call5_wrapper(BlockIO
->ReadBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, 0, 512, SectorBuffer
);
1194 if (EFI_ERROR(Status
))
1196 if (*((UINT16
*)(SectorBuffer
+ 510)) != 0xaa55)
1197 return EFI_NOT_FOUND
; // safety measure #1
1199 // add boot code if necessary
1200 HaveBootCode
= FALSE
;
1201 for (i
= 0; i
< MBR_BOOTCODE_SIZE
; i
++) {
1202 if (SectorBuffer
[i
] != 0) {
1203 HaveBootCode
= TRUE
;
1207 if (!HaveBootCode
) {
1208 // no boot code found in the MBR, add the syslinux MBR code
1209 SetMem(SectorBuffer
, MBR_BOOTCODE_SIZE
, 0);
1210 CopyMem(SectorBuffer
, syslinux_mbr
, SYSLINUX_MBR_SIZE
);
1213 // set the partition active
1214 MbrTable
= (MBR_PARTITION_INFO
*)(SectorBuffer
+ 446);
1216 for (i
= 0; i
< 4; i
++) {
1217 if (MbrTable
[i
].Flags
!= 0x00 && MbrTable
[i
].Flags
!= 0x80)
1218 return EFI_NOT_FOUND
; // safety measure #2
1219 if (i
== PartitionIndex
)
1220 MbrTable
[i
].Flags
= 0x80;
1221 else if (PartitionIndex
>= 4 && IS_EXTENDED_PART_TYPE(MbrTable
[i
].Type
)) {
1222 MbrTable
[i
].Flags
= 0x80;
1223 ExtBase
= MbrTable
[i
].StartLBA
;
1225 MbrTable
[i
].Flags
= 0x00;
1229 Status
= refit_call5_wrapper(BlockIO
->WriteBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, 0, 512, SectorBuffer
);
1230 if (EFI_ERROR(Status
))
1233 if (PartitionIndex
>= 4) {
1234 // we have to activate a logical partition, so walk the EMBR chain
1236 // NOTE: ExtBase was set above while looking at the MBR table
1237 for (ExtCurrent
= ExtBase
; ExtCurrent
; ExtCurrent
= NextExtCurrent
) {
1238 // read current EMBR
1239 Status
= refit_call5_wrapper(BlockIO
->ReadBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, ExtCurrent
, 512, SectorBuffer
);
1240 if (EFI_ERROR(Status
))
1242 if (*((UINT16
*)(SectorBuffer
+ 510)) != 0xaa55)
1243 return EFI_NOT_FOUND
; // safety measure #3
1245 // scan EMBR, set appropriate partition active
1246 EMbrTable
= (MBR_PARTITION_INFO
*)(SectorBuffer
+ 446);
1248 for (i
= 0; i
< 4; i
++) {
1249 if (EMbrTable
[i
].Flags
!= 0x00 && EMbrTable
[i
].Flags
!= 0x80)
1250 return EFI_NOT_FOUND
; // safety measure #4
1251 if (EMbrTable
[i
].StartLBA
== 0 || EMbrTable
[i
].Size
== 0)
1253 if (IS_EXTENDED_PART_TYPE(EMbrTable
[i
].Type
)) {
1254 // link to next EMBR
1255 NextExtCurrent
= ExtBase
+ EMbrTable
[i
].StartLBA
;
1256 EMbrTable
[i
].Flags
= (PartitionIndex
>= LogicalPartitionIndex
) ? 0x80 : 0x00;
1259 // logical partition
1260 EMbrTable
[i
].Flags
= (PartitionIndex
== LogicalPartitionIndex
) ? 0x80 : 0x00;
1261 LogicalPartitionIndex
++;
1265 // write current EMBR
1266 Status
= refit_call5_wrapper(BlockIO
->WriteBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, ExtCurrent
, 512, SectorBuffer
);
1267 if (EFI_ERROR(Status
))
1270 if (PartitionIndex
< LogicalPartitionIndex
)
1271 break; // stop the loop, no need to touch further EMBRs
1277 } /* static EFI_STATUS ActivateMbrPartition() */
1279 // early 2006 Core Duo / Core Solo models
1280 static UINT8 LegacyLoaderDevicePath1Data
[] = {
1281 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1282 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1283 0xFF, 0xFF, 0xF9, 0xFF, 0x00, 0x00, 0x00, 0x00,
1284 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1285 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1286 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1288 // mid-2006 Mac Pro (and probably other Core 2 models)
1289 static UINT8 LegacyLoaderDevicePath2Data
[] = {
1290 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1291 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1292 0xFF, 0xFF, 0xF7, 0xFF, 0x00, 0x00, 0x00, 0x00,
1293 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1294 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1295 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1297 // mid-2007 MBP ("Santa Rosa" based models)
1298 static UINT8 LegacyLoaderDevicePath3Data
[] = {
1299 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1300 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1301 0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
1302 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1303 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1304 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1307 static UINT8 LegacyLoaderDevicePath4Data
[] = {
1308 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1309 0x00, 0x00, 0xC0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1310 0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
1311 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1312 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1313 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1315 // late-2008 MB/MBP (NVidia chipset)
1316 static UINT8 LegacyLoaderDevicePath5Data
[] = {
1317 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1318 0x00, 0x40, 0xCB, 0xFF, 0x00, 0x00, 0x00, 0x00,
1319 0xFF, 0xBF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
1320 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1321 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1322 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1325 static EFI_DEVICE_PATH
*LegacyLoaderList
[] = {
1326 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath1Data
,
1327 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath2Data
,
1328 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath3Data
,
1329 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath4Data
,
1330 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath5Data
,
1334 #define MAX_DISCOVERED_PATHS (16)
1336 static VOID
StartLegacy(IN LEGACY_ENTRY
*Entry
)
1339 EG_IMAGE
*BootLogoImage
;
1340 UINTN ErrorInStep
= 0;
1341 EFI_DEVICE_PATH
*DiscoveredPathList
[MAX_DISCOVERED_PATHS
];
1343 BeginExternalScreen(TRUE
, L
"Booting Legacy OS (Mac mode)");
1345 BootLogoImage
= LoadOSIcon(Entry
->Volume
->OSIconName
, L
"legacy", TRUE
);
1346 if (BootLogoImage
!= NULL
)
1347 BltImageAlpha(BootLogoImage
,
1348 (UGAWidth
- BootLogoImage
->Width
) >> 1,
1349 (UGAHeight
- BootLogoImage
->Height
) >> 1,
1350 &StdBackgroundPixel
);
1352 if (Entry
->Volume
->IsMbrPartition
) {
1353 ActivateMbrPartition(Entry
->Volume
->WholeDiskBlockIO
, Entry
->Volume
->MbrPartitionIndex
);
1356 ExtractLegacyLoaderPaths(DiscoveredPathList
, MAX_DISCOVERED_PATHS
, LegacyLoaderList
);
1358 Status
= StartEFIImageList(DiscoveredPathList
, Entry
->LoadOptions
, NULL
, L
"legacy loader", 0, &ErrorInStep
, TRUE
);
1359 if (Status
== EFI_NOT_FOUND
) {
1360 if (ErrorInStep
== 1) {
1361 Print(L
"\nPlease make sure that you have the latest firmware update installed.\n");
1362 } else if (ErrorInStep
== 3) {
1363 Print(L
"\nThe firmware refused to boot from the selected volume. Note that external\n"
1364 L
"hard drives are not well-supported by Apple's firmware for legacy OS booting.\n");
1367 FinishExternalScreen();
1368 } /* static VOID StartLegacy() */
1370 // Start a device on a non-Mac using the EFI_LEGACY_BIOS_PROTOCOL
1371 #ifdef __MAKEWITH_TIANO
1372 static VOID
StartLegacyUEFI(IN LEGACY_ENTRY
*Entry
)
1374 BeginExternalScreen(TRUE
, L
"Booting Legacy OS (UEFI mode)");
1376 BdsLibConnectDevicePath (Entry
->BdsOption
->DevicePath
);
1377 BdsLibDoLegacyBoot(Entry
->BdsOption
);
1379 // If we get here, it means that there was a failure....
1380 Print(L
"Failure booting legacy (BIOS) OS.");
1382 FinishExternalScreen();
1383 } // static VOID StartLegacyUEFI()
1384 #endif // __MAKEWITH_TIANO
1386 static LEGACY_ENTRY
* AddLegacyEntry(IN CHAR16
*LoaderTitle
, IN REFIT_VOLUME
*Volume
)
1388 LEGACY_ENTRY
*Entry
, *SubEntry
;
1389 REFIT_MENU_SCREEN
*SubScreen
;
1391 CHAR16 ShortcutLetter
= 0;
1393 if (LoaderTitle
== NULL
) {
1394 if (Volume
->OSName
!= NULL
) {
1395 LoaderTitle
= Volume
->OSName
;
1396 if (LoaderTitle
[0] == 'W' || LoaderTitle
[0] == 'L')
1397 ShortcutLetter
= LoaderTitle
[0];
1399 LoaderTitle
= L
"Legacy OS";
1401 if (Volume
->VolName
!= NULL
)
1402 VolDesc
= Volume
->VolName
;
1404 VolDesc
= (Volume
->DiskKind
== DISK_KIND_OPTICAL
) ? L
"CD" : L
"HD";
1406 // prepare the menu entry
1407 Entry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1408 Entry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1409 SPrint(Entry
->me
.Title
, 255, L
"Boot %s from %s", LoaderTitle
, VolDesc
);
1410 Entry
->me
.Tag
= TAG_LEGACY
;
1412 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1413 Entry
->me
.Image
= LoadOSIcon(Volume
->OSIconName
, L
"legacy", FALSE
);
1414 Entry
->me
.BadgeImage
= Volume
->VolBadgeImage
;
1415 Entry
->Volume
= Volume
;
1416 Entry
->LoadOptions
= (Volume
->DiskKind
== DISK_KIND_OPTICAL
) ? L
"CD" :
1417 ((Volume
->DiskKind
== DISK_KIND_EXTERNAL
) ? L
"USB" : L
"HD");
1418 Entry
->Enabled
= TRUE
;
1420 // create the submenu
1421 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
1422 SubScreen
->Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1423 SPrint(SubScreen
->Title
, 255, L
"Boot Options for %s on %s", LoaderTitle
, VolDesc
);
1424 SubScreen
->TitleImage
= Entry
->me
.Image
;
1425 SubScreen
->Hint1
= StrDuplicate(SUBSCREEN_HINT1
);
1426 if (GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_EDITOR
) {
1427 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2_NO_EDITOR
);
1429 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2
);
1433 SubEntry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1434 SubEntry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1435 SPrint(SubEntry
->me
.Title
, 255, L
"Boot %s", LoaderTitle
);
1436 SubEntry
->me
.Tag
= TAG_LEGACY
;
1437 SubEntry
->Volume
= Entry
->Volume
;
1438 SubEntry
->LoadOptions
= Entry
->LoadOptions
;
1439 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
1441 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
1442 Entry
->me
.SubScreen
= SubScreen
;
1443 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1445 } /* static LEGACY_ENTRY * AddLegacyEntry() */
1448 #ifdef __MAKEWITH_GNUEFI
1449 static VOID
ScanLegacyUEFI(IN UINTN DiskType
){}
1451 // default volume badge icon based on disk kind
1452 static EG_IMAGE
* GetDiskBadge(IN UINTN DiskType
) {
1453 EG_IMAGE
* Badge
= NULL
;
1457 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_INTERNAL
);
1460 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_EXTERNAL
);
1463 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_OPTICAL
);
1467 } // static EG_IMAGE * GetDiskBadge()
1470 Create a rEFInd boot option from a Legacy BIOS protocol option.
1472 static LEGACY_ENTRY
* AddLegacyEntryUEFI(BDS_COMMON_OPTION
*BdsOption
, IN UINT16 DiskType
)
1474 LEGACY_ENTRY
*Entry
, *SubEntry
;
1475 REFIT_MENU_SCREEN
*SubScreen
;
1476 CHAR16 ShortcutLetter
= 0;
1477 CHAR16
*LegacyDescription
= BdsOption
->Description
;
1479 // prepare the menu entry
1480 Entry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1481 Entry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1482 SPrint(Entry
->me
.Title
, 255, L
"Boot legacy target %s", LegacyDescription
);
1483 Entry
->me
.Tag
= TAG_LEGACY_UEFI
;
1485 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1486 Entry
->me
.Image
= LoadOSIcon(L
"legacy", L
"legacy", TRUE
);
1487 Entry
->LoadOptions
= (DiskType
== BBS_CDROM
) ? L
"CD" :
1488 ((DiskType
== BBS_USB
) ? L
"USB" : L
"HD");
1489 Entry
->me
.BadgeImage
= GetDiskBadge(DiskType
);
1490 Entry
->BdsOption
= BdsOption
;
1491 Entry
->Enabled
= TRUE
;
1493 // create the submenu
1494 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
1495 SubScreen
->Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1496 SPrint(SubScreen
->Title
, 255, L
"No boot options for legacy target");
1497 SubScreen
->TitleImage
= Entry
->me
.Image
;
1498 SubScreen
->Hint1
= StrDuplicate(SUBSCREEN_HINT1
);
1499 if (GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_EDITOR
) {
1500 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2_NO_EDITOR
);
1502 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2
);
1506 SubEntry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1507 SubEntry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1508 SPrint(SubEntry
->me
.Title
, 255, L
"Boot %s", LegacyDescription
);
1509 SubEntry
->me
.Tag
= TAG_LEGACY_UEFI
;
1510 Entry
->BdsOption
= BdsOption
;
1511 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
1513 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
1514 Entry
->me
.SubScreen
= SubScreen
;
1515 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1517 } /* static LEGACY_ENTRY * AddLegacyEntryUEFI() */
1520 Scan for legacy BIOS targets on machines that implement EFI_LEGACY_BIOS_PROTOCOL.
1521 In testing, protocol has not been implemented on Macs but has been
1522 implemented on several Dell PCs and an ASUS motherboard.
1523 Restricts output to disks of the specified DiskType.
1525 static VOID
ScanLegacyUEFI(IN UINTN DiskType
)
1528 EFI_LEGACY_BIOS_PROTOCOL
*LegacyBios
;
1529 UINT16
*BootOrder
= NULL
;
1531 CHAR16 BootOption
[10];
1532 UINTN BootOrderSize
= 0;
1534 BDS_COMMON_OPTION
*BdsOption
;
1535 LIST_ENTRY TempList
;
1536 BBS_BBS_DEVICE_PATH
* BbsDevicePath
= NULL
;
1538 InitializeListHead (&TempList
);
1539 ZeroMem (Buffer
, sizeof (Buffer
));
1541 // If LegacyBios protocol is not implemented on this platform, then
1542 //we do not support this type of legacy boot on this machine.
1543 Status
= gBS
->LocateProtocol(&gEfiLegacyBootProtocolGuid
, NULL
, (VOID
**) &LegacyBios
);
1544 if (EFI_ERROR (Status
))
1547 // Grab the boot order
1548 BootOrder
= BdsLibGetVariableAndSize(L
"BootOrder", &gEfiGlobalVariableGuid
, &BootOrderSize
);
1549 if (BootOrder
== NULL
) {
1554 while (Index
< BootOrderSize
/ sizeof (UINT16
))
1556 // Grab each boot option variable from the boot order, and convert
1557 // the variable into a BDS boot option
1558 UnicodeSPrint (BootOption
, sizeof (BootOption
), L
"Boot%04x", BootOrder
[Index
]);
1559 BdsOption
= BdsLibVariableToOption (&TempList
, BootOption
);
1561 if (BdsOption
!= NULL
) {
1562 BbsDevicePath
= (BBS_BBS_DEVICE_PATH
*)BdsOption
->DevicePath
;
1564 // Only add the entry if it is of a requested type (e.g. USB, HD)
1566 // Two checks necessary because some systems return EFI boot loaders
1567 // with a DeviceType value that would inappropriately include them
1568 // as legacy loaders....
1569 if ((BbsDevicePath
->DeviceType
== DiskType
) && (BdsOption
->DevicePath
->Type
== DEVICE_TYPE_BIOS
)) {
1570 AddLegacyEntryUEFI(BdsOption
, BbsDevicePath
->DeviceType
);
1575 } /* static VOID ScanLegacyUEFI() */
1576 #endif // __MAKEWITH_GNUEFI
1578 static VOID
ScanLegacyVolume(REFIT_VOLUME
*Volume
, UINTN VolumeIndex
) {
1580 BOOLEAN ShowVolume
, HideIfOthersFound
;
1583 HideIfOthersFound
= FALSE
;
1584 if (Volume
->IsAppleLegacy
) {
1586 HideIfOthersFound
= TRUE
;
1587 } else if (Volume
->HasBootCode
) {
1589 if (Volume
->BlockIO
== Volume
->WholeDiskBlockIO
&&
1590 Volume
->BlockIOOffset
== 0 &&
1591 Volume
->OSName
== NULL
)
1592 // this is a whole disk (MBR) entry; hide if we have entries for partitions
1593 HideIfOthersFound
= TRUE
;
1595 if (HideIfOthersFound
) {
1596 // check for other bootable entries on the same disk
1597 for (VolumeIndex2
= 0; VolumeIndex2
< VolumesCount
; VolumeIndex2
++) {
1598 if (VolumeIndex2
!= VolumeIndex
&& Volumes
[VolumeIndex2
]->HasBootCode
&&
1599 Volumes
[VolumeIndex2
]->WholeDiskBlockIO
== Volume
->WholeDiskBlockIO
)
1605 AddLegacyEntry(NULL
, Volume
);
1606 } // static VOID ScanLegacyVolume()
1608 // Scan attached optical discs for legacy (BIOS) boot code
1609 // and add anything found to the list....
1610 static VOID
ScanLegacyDisc(VOID
)
1613 REFIT_VOLUME
*Volume
;
1615 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
) {
1616 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1617 Volume
= Volumes
[VolumeIndex
];
1618 if (Volume
->DiskKind
== DISK_KIND_OPTICAL
)
1619 ScanLegacyVolume(Volume
, VolumeIndex
);
1621 } else if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
1622 ScanLegacyUEFI(BBS_CDROM
);
1624 } /* static VOID ScanLegacyDisc() */
1626 // Scan internal hard disks for legacy (BIOS) boot code
1627 // and add anything found to the list....
1628 static VOID
ScanLegacyInternal(VOID
)
1631 REFIT_VOLUME
*Volume
;
1633 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
) {
1634 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1635 Volume
= Volumes
[VolumeIndex
];
1636 if (Volume
->DiskKind
== DISK_KIND_INTERNAL
)
1637 ScanLegacyVolume(Volume
, VolumeIndex
);
1639 } else if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
1640 ScanLegacyUEFI(BBS_HARDDISK
);
1642 } /* static VOID ScanLegacyInternal() */
1644 // Scan external disks for legacy (BIOS) boot code
1645 // and add anything found to the list....
1646 static VOID
ScanLegacyExternal(VOID
)
1649 REFIT_VOLUME
*Volume
;
1651 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
) {
1652 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1653 Volume
= Volumes
[VolumeIndex
];
1654 if (Volume
->DiskKind
== DISK_KIND_EXTERNAL
)
1655 ScanLegacyVolume(Volume
, VolumeIndex
);
1657 } else if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
1658 ScanLegacyUEFI(BBS_USB
);
1660 } /* static VOID ScanLegacyExternal() */
1663 // pre-boot tool functions
1666 static VOID
StartTool(IN LOADER_ENTRY
*Entry
)
1668 BeginExternalScreen(Entry
->UseGraphicsMode
, Entry
->me
.Title
+ 6); // assumes "Start <title>" as assigned below
1669 StartEFIImage(Entry
->DevicePath
, Entry
->LoadOptions
, Basename(Entry
->LoaderPath
),
1670 Basename(Entry
->LoaderPath
), Entry
->OSType
, NULL
, TRUE
);
1671 FinishExternalScreen();
1672 } /* static VOID StartTool() */
1674 static LOADER_ENTRY
* AddToolEntry(EFI_HANDLE DeviceHandle
, IN CHAR16
*LoaderPath
, IN CHAR16
*LoaderTitle
, IN EG_IMAGE
*Image
,
1675 IN CHAR16 ShortcutLetter
, IN BOOLEAN UseGraphicsMode
)
1677 LOADER_ENTRY
*Entry
;
1678 CHAR16
*TitleStr
= NULL
;
1680 Entry
= AllocateZeroPool(sizeof(LOADER_ENTRY
));
1682 MergeStrings(&TitleStr
, L
"Start ", 0);
1683 MergeStrings(&TitleStr
, LoaderTitle
, 0);
1684 Entry
->me
.Title
= TitleStr
;
1685 Entry
->me
.Tag
= TAG_TOOL
;
1687 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1688 Entry
->me
.Image
= Image
;
1689 Entry
->LoaderPath
= (LoaderPath
) ? StrDuplicate(LoaderPath
) : NULL
;
1690 Entry
->DevicePath
= FileDevicePath(DeviceHandle
, Entry
->LoaderPath
);
1691 Entry
->UseGraphicsMode
= UseGraphicsMode
;
1693 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1695 } /* static LOADER_ENTRY * AddToolEntry() */
1698 // pre-boot driver functions
1701 static UINTN
ScanDriverDir(IN CHAR16
*Path
)
1704 REFIT_DIR_ITER DirIter
;
1706 EFI_FILE_INFO
*DirEntry
;
1707 CHAR16 FileName
[256];
1709 CleanUpPathNameSlashes(Path
);
1710 // look through contents of the directory
1711 DirIterOpen(SelfRootDir
, Path
, &DirIter
);
1712 while (DirIterNext(&DirIter
, 2, LOADER_MATCH_PATTERNS
, &DirEntry
)) {
1713 if (DirEntry
->FileName
[0] == '.')
1714 continue; // skip this
1716 SPrint(FileName
, 255, L
"%s\\%s", Path
, DirEntry
->FileName
);
1718 Status
= StartEFIImage(FileDevicePath(SelfLoadedImage
->DeviceHandle
, FileName
),
1719 L
"", DirEntry
->FileName
, DirEntry
->FileName
, 0, NULL
, FALSE
);
1721 Status
= DirIterClose(&DirIter
);
1722 if (Status
!= EFI_NOT_FOUND
) {
1723 SPrint(FileName
, 255, L
"while scanning the %s directory", Path
);
1724 CheckError(Status
, FileName
);
1729 #ifdef __MAKEWITH_GNUEFI
1730 static EFI_STATUS
ConnectAllDriversToAllControllers(VOID
)
1733 UINTN AllHandleCount
;
1734 EFI_HANDLE
*AllHandleBuffer
;
1737 EFI_HANDLE
*HandleBuffer
;
1743 Status
= LibLocateHandle(AllHandles
,
1748 if (EFI_ERROR(Status
))
1751 for (Index
= 0; Index
< AllHandleCount
; Index
++) {
1753 // Scan the handle database
1755 Status
= LibScanHandleDatabase(NULL
,
1757 AllHandleBuffer
[Index
],
1762 if (EFI_ERROR (Status
))
1766 if (HandleType
[Index
] & EFI_HANDLE_TYPE_DRIVER_BINDING_HANDLE
)
1768 if (HandleType
[Index
] & EFI_HANDLE_TYPE_IMAGE_HANDLE
)
1773 for (HandleIndex
= 0; HandleIndex
< HandleCount
; HandleIndex
++) {
1774 if (HandleType
[HandleIndex
] & EFI_HANDLE_TYPE_PARENT_HANDLE
)
1779 if (HandleType
[Index
] & EFI_HANDLE_TYPE_DEVICE_HANDLE
) {
1780 Status
= refit_call4_wrapper(BS
->ConnectController
,
1781 AllHandleBuffer
[Index
],
1789 MyFreePool (HandleBuffer
);
1790 MyFreePool (HandleType
);
1794 MyFreePool (AllHandleBuffer
);
1796 } /* EFI_STATUS ConnectAllDriversToAllControllers() */
1798 static EFI_STATUS
ConnectAllDriversToAllControllers(VOID
) {
1799 BdsLibConnectAllDriversToAllControllers();
1804 // Load all EFI drivers from rEFInd's "drivers" subdirectory and from the
1805 // directories specified by the user in the "scan_driver_dirs" configuration
1807 static VOID
LoadDrivers(VOID
)
1809 CHAR16
*Directory
, *SelfDirectory
;
1810 UINTN i
= 0, Length
, NumFound
= 0;
1812 // load drivers from the subdirectories of rEFInd's home directory specified
1813 // in the DRIVER_DIRS constant.
1814 while ((Directory
= FindCommaDelimited(DRIVER_DIRS
, i
++)) != NULL
) {
1815 SelfDirectory
= SelfDirPath
? StrDuplicate(SelfDirPath
) : NULL
;
1816 CleanUpPathNameSlashes(SelfDirectory
);
1817 MergeStrings(&SelfDirectory
, Directory
, L
'\\');
1818 NumFound
+= ScanDriverDir(SelfDirectory
);
1819 MyFreePool(Directory
);
1820 MyFreePool(SelfDirectory
);
1823 // Scan additional user-specified driver directories....
1825 while ((Directory
= FindCommaDelimited(GlobalConfig
.DriverDirs
, i
++)) != NULL
) {
1826 CleanUpPathNameSlashes(Directory
);
1827 Length
= StrLen(Directory
);
1829 NumFound
+= ScanDriverDir(Directory
);
1831 MyFreePool(Directory
);
1834 // connect all devices
1836 ConnectAllDriversToAllControllers();
1837 } /* static VOID LoadDrivers() */
1839 // Determine what (if any) type of legacy (BIOS) boot support is available
1840 static VOID
FindLegacyBootType(VOID
) {
1841 #ifdef __MAKEWITH_TIANO
1843 EFI_LEGACY_BIOS_PROTOCOL
*LegacyBios
;
1846 GlobalConfig
.LegacyType
= LEGACY_TYPE_NONE
;
1848 // UEFI-style legacy BIOS support is available only with the TianoCore EDK2
1849 // build environment, and then only with some EFI implementations....
1850 #ifdef __MAKEWITH_TIANO
1851 Status
= gBS
->LocateProtocol (&gEfiLegacyBootProtocolGuid
, NULL
, (VOID
**) &LegacyBios
);
1852 if (!EFI_ERROR (Status
))
1853 GlobalConfig
.LegacyType
= LEGACY_TYPE_UEFI
;
1856 // Macs have their own system. If the firmware vendor code contains the
1857 // string "Apple", assume it's available. Note that this overrides the
1858 // UEFI type, and might yield false positives if the vendor string
1859 // contains "Apple" as part of something bigger, so this isn't 100%
1861 if (StriSubCmp(L
"Apple", ST
->FirmwareVendor
))
1862 GlobalConfig
.LegacyType
= LEGACY_TYPE_MAC
;
1863 } // static VOID FindLegacyBootType
1865 // Warn the user if legacy OS scans are enabled but the firmware or this
1866 // application can't support them....
1867 static VOID
WarnIfLegacyProblems() {
1868 BOOLEAN found
= FALSE
;
1871 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_NONE
) {
1873 if (GlobalConfig
.ScanFor
[i
] == 'h' || GlobalConfig
.ScanFor
[i
] == 'b' || GlobalConfig
.ScanFor
[i
] == 'c')
1876 } while ((i
< NUM_SCAN_OPTIONS
) && (!found
));
1878 Print(L
"NOTE: refind.conf's 'scanfor' line specifies scanning for one or more legacy\n");
1879 Print(L
"(BIOS) boot options; however, this is not possible because ");
1880 #ifdef __MAKEWITH_TIANO
1881 Print(L
"your computer lacks\n");
1882 Print(L
"the necessary Compatibility Support Module (CSM) support.\n");
1884 Print(L
"this program was\n");
1885 Print(L
"compiled without the necessary support. Please visit\n");
1886 Print(L
"http://www.rodsbooks.com/refind/getting.html and download and install a rEFInd\n");
1887 Print(L
"binary built with the TianoCore EDK2 to enable legacy boot support.\n");
1891 } // if no legacy support
1892 } // static VOID WarnIfLegacyProblems()
1894 // Locates boot loaders. NOTE: This assumes that GlobalConfig.LegacyType is set correctly.
1895 static VOID
ScanForBootloaders(VOID
) {
1900 // scan for loaders and tools, add them to the menu
1901 for (i
= 0; i
< NUM_SCAN_OPTIONS
; i
++) {
1902 switch(GlobalConfig
.ScanFor
[i
]) {
1907 ScanLegacyInternal();
1910 ScanLegacyExternal();
1913 ScanUserConfigured(CONFIG_FILE_NAME
);
1927 // assign shortcut keys
1928 for (i
= 0; i
< MainMenu
.EntryCount
&& MainMenu
.Entries
[i
]->Row
== 0 && i
< 9; i
++)
1929 MainMenu
.Entries
[i
]->ShortcutDigit
= (CHAR16
)('1' + i
);
1931 // wait for user ACK when there were errors
1932 FinishTextScreen(FALSE
);
1933 } // static VOID ScanForBootloaders()
1935 // Add the second-row tags containing built-in and external tools (EFI shell,
1937 static VOID
ScanForTools(VOID
) {
1938 CHAR16
*FileName
= NULL
, Description
[256];
1939 REFIT_MENU_ENTRY
*TempMenuEntry
;
1940 UINTN i
, j
, VolumeIndex
;
1942 for (i
= 0; i
< NUM_TOOLS
; i
++) {
1943 switch(GlobalConfig
.ShowTools
[i
]) {
1945 TempMenuEntry
= CopyMenuEntry(&MenuEntryShutdown
);
1946 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_SHUTDOWN
);
1947 AddMenuEntry(&MainMenu
, TempMenuEntry
);
1950 TempMenuEntry
= CopyMenuEntry(&MenuEntryReset
);
1951 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_RESET
);
1952 AddMenuEntry(&MainMenu
, TempMenuEntry
);
1955 TempMenuEntry
= CopyMenuEntry(&MenuEntryAbout
);
1956 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT
);
1957 AddMenuEntry(&MainMenu
, TempMenuEntry
);
1960 TempMenuEntry
= CopyMenuEntry(&MenuEntryExit
);
1961 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_EXIT
);
1962 AddMenuEntry(&MainMenu
, TempMenuEntry
);
1966 MyFreePool(FileName
);
1967 while ((FileName
= FindCommaDelimited(SHELL_NAMES
, j
++)) != NULL
) {
1968 if (FileExists(SelfRootDir
, FileName
)) {
1969 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, L
"EFI Shell", BuiltinIcon(BUILTIN_ICON_TOOL_SHELL
),
1975 MyFreePool(FileName
);
1976 FileName
= StrDuplicate(L
"\\efi\\tools\\gptsync.efi");
1977 if (FileExists(SelfRootDir
, FileName
)) {
1978 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, L
"Make Hybrid MBR", BuiltinIcon(BUILTIN_ICON_TOOL_PART
), 'P', FALSE
);
1981 case TAG_APPLE_RECOVERY
:
1982 MyFreePool(FileName
);
1983 FileName
= StrDuplicate(L
"\\com.apple.recovery.boot\\boot.efi");
1984 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1985 if ((Volumes
[VolumeIndex
]->RootDir
!= NULL
) && (FileExists(Volumes
[VolumeIndex
]->RootDir
, FileName
))) {
1986 SPrint(Description
, 255, L
"Apple Recovery on %s", Volumes
[VolumeIndex
]->VolName
);
1987 AddToolEntry(Volumes
[VolumeIndex
]->DeviceHandle
, FileName
, Description
,
1988 BuiltinIcon(BUILTIN_ICON_TOOL_APPLE_RESCUE
), 'R', TRUE
);
1994 MyFreePool(FileName
);
1995 while ((FileName
= FindCommaDelimited(MOK_NAMES
, j
++)) != NULL
) {
1996 if (FileExists(SelfRootDir
, FileName
)) {
1997 SPrint(Description
, 255, L
"MOK Key Manager at %s", FileName
);
1998 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, Description
,
1999 BuiltinIcon(BUILTIN_ICON_TOOL_MOK_TOOL
), 'S', FALSE
);
2002 if (FileExists(SelfDir
, L
"MokManager.efi")) {
2003 MyFreePool(FileName
);
2004 FileName
= SelfDirPath
? StrDuplicate(SelfDirPath
) : NULL
;
2005 MergeStrings(&FileName
, L
"\\MokManager.efi", 0);
2006 SPrint(Description
, 255, L
"MOK Key Manager at %s", FileName
);
2007 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, Description
,
2008 BuiltinIcon(BUILTIN_ICON_TOOL_MOK_TOOL
), 'S', FALSE
);
2012 MyFreePool(FileName
);
2015 } // static VOID ScanForTools
2017 // Rescan for boot loaders
2018 VOID
RescanAll(VOID
) {
2025 egDisplayMessage(L
"Scanning for new boot loaders; please wait....", &BGColor
);
2026 FreeList((VOID
***) &(MainMenu
.Entries
), &MainMenu
.EntryCount
);
2027 MainMenu
.Entries
= NULL
;
2028 MainMenu
.EntryCount
= 0;
2029 ReadConfig(CONFIG_FILE_NAME
);
2030 ConnectAllDriversToAllControllers();
2032 ScanForBootloaders();
2035 } // VOID RescanAll()
2037 #ifdef __MAKEWITH_TIANO
2039 // Minimal initialization function
2040 static VOID
InitializeLib(IN EFI_HANDLE ImageHandle
, IN EFI_SYSTEM_TABLE
*SystemTable
) {
2042 // gImageHandle = ImageHandle;
2043 gBS
= SystemTable
->BootServices
;
2044 // gRS = SystemTable->RuntimeServices;
2045 gRT
= SystemTable
->RuntimeServices
; // Some BDS functions need gRT to be set
2046 EfiGetSystemConfigurationTable (&gEfiDxeServicesTableGuid
, (VOID
**) &gDS
);
2048 InitializeConsoleSim();
2053 // Set up our own Secure Boot extensions....
2054 // Returns TRUE on success, FALSE otherwise
2055 static BOOLEAN
SecureBootSetup(VOID
) {
2057 BOOLEAN Success
= FALSE
;
2059 if (secure_mode() && ShimLoaded()) {
2060 Status
= security_policy_install();
2061 if (Status
== EFI_SUCCESS
) {
2064 Print(L
"Failed to install MOK Secure Boot extensions");
2068 } // VOID SecureBootSetup()
2070 // Remove our own Secure Boot extensions....
2071 // Returns TRUE on success, FALSE otherwise
2072 static BOOLEAN
SecureBootUninstall(VOID
) {
2074 BOOLEAN Success
= TRUE
;
2076 if (secure_mode()) {
2077 Status
= security_policy_uninstall();
2078 if (Status
!= EFI_SUCCESS
) {
2080 BeginTextScreen(L
"Secure Boot Policy Failure");
2081 Print(L
"Failed to uninstall MOK Secure Boot extensions; forcing a reboot.");
2083 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);
2087 } // VOID SecureBootUninstall
2094 efi_main (EFI_HANDLE ImageHandle
, EFI_SYSTEM_TABLE
*SystemTable
)
2097 BOOLEAN MainLoopRunning
= TRUE
;
2098 BOOLEAN MokProtocol
;
2099 REFIT_MENU_ENTRY
*ChosenEntry
;
2101 CHAR16
*Selection
= NULL
;
2105 InitializeLib(ImageHandle
, SystemTable
);
2106 Status
= InitRefitLib(ImageHandle
);
2107 if (EFI_ERROR(Status
))
2110 // read configuration
2111 CopyMem(GlobalConfig
.ScanFor
, "ieom ", NUM_SCAN_OPTIONS
);
2112 FindLegacyBootType();
2113 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
)
2114 CopyMem(GlobalConfig
.ScanFor
, "ihebocm ", NUM_SCAN_OPTIONS
);
2116 ReadConfig(CONFIG_FILE_NAME
);
2119 WarnIfLegacyProblems();
2120 MainMenu
.TimeoutSeconds
= GlobalConfig
.Timeout
;
2122 // disable EFI watchdog timer
2123 refit_call4_wrapper(BS
->SetWatchdogTimer
, 0x0000, 0x0000, 0x0000, NULL
);
2125 // further bootstrap (now with config available)
2126 MokProtocol
= SecureBootSetup();
2128 ScanForBootloaders();
2132 if (GlobalConfig
.ScanDelay
> 0) {
2137 egDisplayMessage(L
"Pausing before disk scan; please wait....", &BGColor
);
2138 for (i
= 0; i
< GlobalConfig
.ScanDelay
; i
++)
2139 refit_call1_wrapper(BS
->Stall
, 1000000);
2143 if (GlobalConfig
.DefaultSelection
)
2144 Selection
= StrDuplicate(GlobalConfig
.DefaultSelection
);
2146 while (MainLoopRunning
) {
2147 MenuExit
= RunMainMenu(&MainMenu
, Selection
, &ChosenEntry
);
2149 // The Escape key triggers a re-scan operation....
2150 if (MenuExit
== MENU_EXIT_ESCAPE
) {
2155 switch (ChosenEntry
->Tag
) {
2157 case TAG_REBOOT
: // Reboot
2159 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);
2160 MainLoopRunning
= FALSE
; // just in case we get this far
2163 case TAG_SHUTDOWN
: // Shut Down
2165 refit_call4_wrapper(RT
->ResetSystem
, EfiResetShutdown
, EFI_SUCCESS
, 0, NULL
);
2166 MainLoopRunning
= FALSE
; // just in case we get this far
2169 case TAG_ABOUT
: // About rEFInd
2173 case TAG_LOADER
: // Boot OS via .EFI loader
2174 StartLoader((LOADER_ENTRY
*)ChosenEntry
);
2177 case TAG_LEGACY
: // Boot legacy OS
2178 StartLegacy((LEGACY_ENTRY
*)ChosenEntry
);
2181 #ifdef __MAKEWITH_TIANO
2182 case TAG_LEGACY_UEFI
: // Boot a legacy OS on a non-Mac
2183 StartLegacyUEFI((LEGACY_ENTRY
*)ChosenEntry
);
2187 case TAG_TOOL
: // Start a EFI tool
2188 StartTool((LOADER_ENTRY
*)ChosenEntry
);
2191 case TAG_EXIT
: // Terminate rEFInd
2192 if ((MokProtocol
) && !SecureBootUninstall()) {
2193 MainLoopRunning
= FALSE
; // just in case we get this far
2195 BeginTextScreen(L
" ");
2201 MyFreePool(Selection
);
2202 Selection
= (ChosenEntry
->Title
) ? StrDuplicate(ChosenEntry
->Title
) : NULL
;
2205 // If we end up here, things have gone wrong. Try to reboot, and if that
2206 // fails, go into an endless loop.
2207 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);