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"
72 #define SHELL_NAMES L"\\EFI\\tools\\shell.efi,\\EFI\\tools\\shellia32.efi,\\shellia32.efi"
73 #define DRIVER_DIRS L"drivers,drivers_ia32"
75 #define SHELL_NAMES L"\\EFI\\tools\\shell.efi"
76 #define DRIVER_DIRS L"drivers"
79 #define MOK_NAMES L"\\EFI\\tools\\MokManager.efi,\\EFI\\redhat\\MokManager.efi,\\EFI\\ubuntu\\MokManager.efi,\\EFI\\suse\\MokManager"
81 // Filename patterns that identify EFI boot loaders. Note that a single case (either L"*.efi" or
82 // L"*.EFI") is fine for most systems; but Gigabyte's buggy Hybrid EFI does a case-sensitive
83 // comparison when it should do a case-insensitive comparison, so I'm doubling this up. It does
84 // no harm on other computers, AFAIK. In theory, every case variation should be done for
85 // completeness, but that's ridiculous....
86 #define LOADER_MATCH_PATTERNS L"*.efi,*.EFI"
88 // Patterns that identify Linux kernels. Added to the loader match pattern when the
89 // scan_all_linux_kernels option is set in the configuration file. Causes kernels WITHOUT
90 // a ".efi" extension to be found when scanning for boot loaders.
91 #define LINUX_MATCH_PATTERNS L"vmlinuz*,bzImage*"
93 // Default hint text for program-launch submenus
94 #define SUBSCREEN_HINT1 L"Use arrow keys to move cursor; Enter to boot;"
95 #define SUBSCREEN_HINT2 L"Insert or F2 to edit options; Esc to return to main menu"
96 #define SUBSCREEN_HINT2_NO_EDITOR L"Esc to return to main menu"
98 static REFIT_MENU_ENTRY MenuEntryAbout
= { L
"About rEFInd", TAG_ABOUT
, 1, 0, 'A', NULL
, NULL
, NULL
};
99 static REFIT_MENU_ENTRY MenuEntryReset
= { L
"Reboot Computer", TAG_REBOOT
, 1, 0, 'R', NULL
, NULL
, NULL
};
100 static REFIT_MENU_ENTRY MenuEntryShutdown
= { L
"Shut Down Computer", TAG_SHUTDOWN
, 1, 0, 'U', NULL
, NULL
, NULL
};
101 static REFIT_MENU_ENTRY MenuEntryReturn
= { L
"Return to Main Menu", TAG_RETURN
, 0, 0, 0, NULL
, NULL
, NULL
};
102 static REFIT_MENU_ENTRY MenuEntryExit
= { L
"Exit rEFInd", TAG_EXIT
, 1, 0, 0, NULL
, NULL
, NULL
};
104 static REFIT_MENU_SCREEN MainMenu
= { L
"Main Menu", NULL
, 0, NULL
, 0, NULL
, 0, L
"Automatic boot",
105 L
"Use arrow keys to move cursor; Enter to boot;",
106 L
"Insert or F2 for more options; Esc to refresh" };
107 static REFIT_MENU_SCREEN AboutMenu
= { L
"About", NULL
, 0, NULL
, 0, NULL
, 0, NULL
, L
"Press Enter to return to main menu", L
"" };
109 REFIT_CONFIG GlobalConfig
= { FALSE
, FALSE
, 0, 0, DONT_CHANGE_TEXT_MODE
, 20, 0, 0, GRAPHICS_FOR_OSX
, LEGACY_TYPE_MAC
, 0,
110 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
111 {TAG_SHELL
, TAG_APPLE_RECOVERY
, TAG_MOK_TOOL
, TAG_ABOUT
, TAG_SHUTDOWN
, TAG_REBOOT
, 0, 0, 0, 0, 0 }};
113 // Structure used to hold boot loader filenames and time stamps in
114 // a linked list; used to sort entries within a directory.
118 struct LOADER_LIST
*NextEntry
;
125 static VOID
AboutrEFInd(VOID
)
127 CHAR16
*TempStr
; // Note: Don't deallocate; moved to menu structure
129 if (AboutMenu
.EntryCount
== 0) {
130 AboutMenu
.TitleImage
= BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT
);
131 AddMenuInfoLine(&AboutMenu
, L
"rEFInd Version 0.6.3");
132 AddMenuInfoLine(&AboutMenu
, L
"");
133 AddMenuInfoLine(&AboutMenu
, L
"Copyright (c) 2006-2010 Christoph Pfisterer");
134 AddMenuInfoLine(&AboutMenu
, L
"Copyright (c) 2012 Roderick W. Smith");
135 AddMenuInfoLine(&AboutMenu
, L
"Portions Copyright (c) Intel Corporation and others");
136 AddMenuInfoLine(&AboutMenu
, L
"Distributed under the terms of the GNU GPLv3 license");
137 AddMenuInfoLine(&AboutMenu
, L
"");
138 AddMenuInfoLine(&AboutMenu
, L
"Running on:");
139 TempStr
= AllocateZeroPool(256 * sizeof(CHAR16
));
140 SPrint(TempStr
, 255, L
" EFI Revision %d.%02d", ST
->Hdr
.Revision
>> 16, ST
->Hdr
.Revision
& ((1 << 16) - 1));
141 AddMenuInfoLine(&AboutMenu
, TempStr
);
143 AddMenuInfoLine(&AboutMenu
, L
" Platform: x86 (32 bit)");
144 #elif defined(EFIX64)
145 TempStr
= AllocateZeroPool(256 * sizeof(CHAR16
));
146 SPrint(TempStr
, 255, L
" Platform: x86_64 (64 bit); Secure Boot %s", secure_mode() ? L
"active" : L
"inactive");
147 AddMenuInfoLine(&AboutMenu
, TempStr
);
149 AddMenuInfoLine(&AboutMenu
, L
" Platform: unknown");
151 TempStr
= AllocateZeroPool(256 * sizeof(CHAR16
));
152 SPrint(TempStr
, 255, L
" Firmware: %s %d.%02d",
153 ST
->FirmwareVendor
, ST
->FirmwareRevision
>> 16, ST
->FirmwareRevision
& ((1 << 16) - 1));
154 AddMenuInfoLine(&AboutMenu
, TempStr
);
155 TempStr
= AllocateZeroPool(256 * sizeof(CHAR16
));
156 SPrint(TempStr
, 255, L
" Screen Output: %s", egScreenDescription());
157 AddMenuInfoLine(&AboutMenu
, TempStr
);
158 AddMenuInfoLine(&AboutMenu
, L
"");
159 #if defined(__MAKEWITH_GNUEFI)
160 AddMenuInfoLine(&AboutMenu
, L
"Built with GNU-EFI");
162 AddMenuInfoLine(&AboutMenu
, L
"Built with TianoCore EDK2");
164 AddMenuInfoLine(&AboutMenu
, L
"");
165 AddMenuInfoLine(&AboutMenu
, L
"For more information, see the rEFInd Web site:");
166 AddMenuInfoLine(&AboutMenu
, L
"http://www.rodsbooks.com/refind/");
167 AddMenuEntry(&AboutMenu
, &MenuEntryReturn
);
170 RunMenu(&AboutMenu
, NULL
);
171 } /* VOID AboutrEFInd() */
173 // Launch an EFI binary.
174 static EFI_STATUS
StartEFIImageList(IN EFI_DEVICE_PATH
**DevicePaths
,
175 IN CHAR16
*LoadOptions
, IN CHAR16
*LoadOptionsPrefix
,
176 IN CHAR16
*ImageTitle
, IN CHAR8 OSType
,
177 OUT UINTN
*ErrorInStep
,
180 EFI_STATUS Status
, ReturnStatus
;
181 EFI_HANDLE ChildImageHandle
;
182 EFI_LOADED_IMAGE
*ChildLoadedImage
= NULL
;
183 UINTN DevicePathIndex
;
184 CHAR16 ErrorInfo
[256];
185 CHAR16
*FullLoadOptions
= NULL
;
187 if (ErrorInStep
!= NULL
)
191 if (LoadOptions
!= NULL
) {
192 if (LoadOptionsPrefix
!= NULL
) {
193 MergeStrings(&FullLoadOptions
, LoadOptions
, L
' ');
195 MergeStrings(&FullLoadOptions
, L
" ", 0);
196 // NOTE: That last space is also added by the EFI shell and seems to be significant
197 // when passing options to Apple's boot.efi...
200 MergeStrings(&FullLoadOptions
, LoadOptions
, 0);
202 } else { // LoadOptions == NULL
203 // NOTE: We provide a non-null string when no options are specified for safety;
204 // some systems (at least DUET) can hang when launching some programs (such as
205 // an EFI shell) without this.
206 FullLoadOptions
= StrDuplicate(L
" ");
209 Print(L
"Starting %s\nUsing load options '%s'\n", ImageTitle
, FullLoadOptions
);
211 // load the image into memory (and execute it, in the case of a shim/MOK image).
212 ReturnStatus
= Status
= EFI_NOT_FOUND
; // in case the list is empty
213 for (DevicePathIndex
= 0; DevicePaths
[DevicePathIndex
] != NULL
; DevicePathIndex
++) {
214 // NOTE: Below commented-out line could be more efficient if file were read ahead of
215 // time and passed as a pre-loaded image to LoadImage(), but it doesn't work on my
216 // 32-bit Mac Mini or my 64-bit Intel box when launching a Linux kernel; the
217 // kernel returns a "Failed to handle fs_proto" error message.
218 // TODO: Track down the cause of this error and fix it, if possible.
219 // ReturnStatus = Status = refit_call6_wrapper(BS->LoadImage, FALSE, SelfImageHandle, DevicePaths[DevicePathIndex],
220 // ImageData, ImageSize, &ChildImageHandle);
221 ReturnStatus
= Status
= refit_call6_wrapper(BS
->LoadImage
, FALSE
, SelfImageHandle
, DevicePaths
[DevicePathIndex
],
222 NULL
, 0, &ChildImageHandle
);
223 if (ReturnStatus
!= EFI_NOT_FOUND
) {
227 SPrint(ErrorInfo
, 255, L
"while loading %s", ImageTitle
);
228 if (CheckError(Status
, ErrorInfo
)) {
229 if (ErrorInStep
!= NULL
)
234 ReturnStatus
= Status
= refit_call3_wrapper(BS
->HandleProtocol
, ChildImageHandle
, &LoadedImageProtocol
,
235 (VOID
**) &ChildLoadedImage
);
236 if (CheckError(Status
, L
"while getting a LoadedImageProtocol handle")) {
237 if (ErrorInStep
!= NULL
)
241 ChildLoadedImage
->LoadOptions
= (VOID
*)FullLoadOptions
;
242 ChildLoadedImage
->LoadOptionsSize
= ((UINT32
)StrLen(FullLoadOptions
) + 1) * sizeof(CHAR16
);
243 // turn control over to the image
244 // TODO: (optionally) re-enable the EFI watchdog timer!
246 // close open file handles
248 ReturnStatus
= Status
= refit_call3_wrapper(BS
->StartImage
, ChildImageHandle
, NULL
, NULL
);
250 // control returns here when the child image calls Exit()
251 SPrint(ErrorInfo
, 255, L
"returned from %s", ImageTitle
);
252 if (CheckError(Status
, ErrorInfo
)) {
253 if (ErrorInStep
!= NULL
)
257 // re-open file handles
261 // unload the image, we don't care if it works or not...
262 Status
= refit_call1_wrapper(BS
->UnloadImage
, ChildImageHandle
);
265 MyFreePool(FullLoadOptions
);
267 } /* static EFI_STATUS StartEFIImageList() */
269 static EFI_STATUS
StartEFIImage(IN EFI_DEVICE_PATH
*DevicePath
,
270 IN CHAR16
*LoadOptions
, IN CHAR16
*LoadOptionsPrefix
,
271 IN CHAR16
*ImageTitle
, IN CHAR8 OSType
,
272 OUT UINTN
*ErrorInStep
,
275 EFI_DEVICE_PATH
*DevicePaths
[2];
277 DevicePaths
[0] = DevicePath
;
278 DevicePaths
[1] = NULL
;
279 return StartEFIImageList(DevicePaths
, LoadOptions
, LoadOptionsPrefix
, ImageTitle
, OSType
, ErrorInStep
, Verbose
);
280 } /* static EFI_STATUS StartEFIImage() */
283 // EFI OS loader functions
286 static VOID
StartLoader(IN LOADER_ENTRY
*Entry
)
288 UINTN ErrorInStep
= 0;
290 BeginExternalScreen(Entry
->UseGraphicsMode
, L
"Booting OS");
291 StartEFIImage(Entry
->DevicePath
, Entry
->LoadOptions
, Basename(Entry
->LoaderPath
),
292 Basename(Entry
->LoaderPath
), Entry
->OSType
, &ErrorInStep
, !Entry
->UseGraphicsMode
);
293 FinishExternalScreen();
296 // Locate an initrd or initramfs file that matches the kernel specified by LoaderPath.
297 // The matching file has a name that begins with "init" and includes the same version
298 // number string as is found in LoaderPath -- but not a longer version number string.
299 // For instance, if LoaderPath is \EFI\kernels\bzImage-3.3.0.efi, and if \EFI\kernels
300 // has a file called initramfs-3.3.0.img, this function will return the string
301 // '\EFI\kernels\initramfs-3.3.0.img'. If the directory ALSO contains the file
302 // initramfs-3.3.0-rc7.img or initramfs-13.3.0.img, those files will NOT match;
303 // however, initmine-3.3.0.img might match. (FindInitrd() returns the first match it
304 // finds). Thus, care should be taken to avoid placing duplicate matching files in
305 // the kernel's directory.
306 // If no matching init file can be found, returns NULL.
307 static CHAR16
* FindInitrd(IN CHAR16
*LoaderPath
, IN REFIT_VOLUME
*Volume
) {
308 CHAR16
*InitrdName
= NULL
, *FileName
, *KernelVersion
, *InitrdVersion
, *Path
;
309 REFIT_DIR_ITER DirIter
;
310 EFI_FILE_INFO
*DirEntry
;
312 FileName
= Basename(LoaderPath
);
313 KernelVersion
= FindNumbers(FileName
);
314 Path
= FindPath(LoaderPath
);
316 // Add trailing backslash for root directory; necessary on some systems, but must
317 // NOT be added to all directories, since on other systems, a trailing backslash on
318 // anything but the root directory causes them to flake out!
319 if (StrLen(Path
) == 0) {
320 MergeStrings(&Path
, L
"\\", 0);
322 DirIterOpen(Volume
->RootDir
, Path
, &DirIter
);
323 // Now add a trailing backslash if it was NOT added earlier, for consistency in
324 // building the InitrdName later....
325 if ((StrLen(Path
) > 0) && (Path
[StrLen(Path
) - 1] != L
'\\'))
326 MergeStrings(&Path
, L
"\\", 0);
327 while ((DirIterNext(&DirIter
, 2, L
"init*", &DirEntry
)) && (InitrdName
== NULL
)) {
328 InitrdVersion
= FindNumbers(DirEntry
->FileName
);
329 if (KernelVersion
!= NULL
) {
330 if (StriCmp(InitrdVersion
, KernelVersion
) == 0) {
331 MergeStrings(&InitrdName
, Path
, 0);
332 MergeStrings(&InitrdName
, DirEntry
->FileName
, 0);
335 if (InitrdVersion
== NULL
) {
336 MergeStrings(&InitrdName
, Path
, 0);
337 MergeStrings(&InitrdName
, DirEntry
->FileName
, 0);
340 MyFreePool(InitrdVersion
);
342 DirIterClose(&DirIter
);
344 // Note: Don't FreePool(FileName), since Basename returns a pointer WITHIN the string it's passed.
345 MyFreePool(KernelVersion
);
348 } // static CHAR16 * FindInitrd()
350 LOADER_ENTRY
* AddPreparedLoaderEntry(LOADER_ENTRY
*Entry
) {
351 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
354 } // LOADER_ENTRY * AddPreparedLoaderEntry()
356 // Creates a copy of a menu screen.
357 // Returns a pointer to the copy of the menu screen.
358 static REFIT_MENU_SCREEN
* CopyMenuScreen(REFIT_MENU_SCREEN
*Entry
) {
359 REFIT_MENU_SCREEN
*NewEntry
;
362 NewEntry
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
363 if ((Entry
!= NULL
) && (NewEntry
!= NULL
)) {
364 CopyMem(NewEntry
, Entry
, sizeof(REFIT_MENU_SCREEN
));
365 NewEntry
->Title
= (Entry
->Title
) ? StrDuplicate(Entry
->Title
) : NULL
;
366 NewEntry
->TimeoutText
= (Entry
->TimeoutText
) ? StrDuplicate(Entry
->TimeoutText
) : NULL
;
367 if (Entry
->TitleImage
!= NULL
) {
368 NewEntry
->TitleImage
= AllocatePool(sizeof(EG_IMAGE
));
369 if (NewEntry
->TitleImage
!= NULL
)
370 CopyMem(NewEntry
->TitleImage
, Entry
->TitleImage
, sizeof(EG_IMAGE
));
372 NewEntry
->InfoLines
= (CHAR16
**) AllocateZeroPool(Entry
->InfoLineCount
* (sizeof(CHAR16
*)));
373 for (i
= 0; i
< Entry
->InfoLineCount
&& NewEntry
->InfoLines
; i
++) {
374 NewEntry
->InfoLines
[i
] = (Entry
->InfoLines
[i
]) ? StrDuplicate(Entry
->InfoLines
[i
]) : NULL
;
376 NewEntry
->Entries
= (REFIT_MENU_ENTRY
**) AllocateZeroPool(Entry
->EntryCount
* (sizeof (REFIT_MENU_ENTRY
*)));
377 for (i
= 0; i
< Entry
->EntryCount
&& NewEntry
->Entries
; i
++) {
378 AddMenuEntry(NewEntry
, Entry
->Entries
[i
]);
380 NewEntry
->Hint1
= (Entry
->Hint1
) ? StrDuplicate(Entry
->Hint1
) : NULL
;
381 NewEntry
->Hint2
= (Entry
->Hint2
) ? StrDuplicate(Entry
->Hint2
) : NULL
;
384 } // static REFIT_MENU_SCREEN* CopyMenuScreen()
386 // Creates a copy of a menu entry. Intended to enable moving a stack-based
387 // menu entry (such as the ones for the "reboot" and "exit" functions) to
388 // to the heap. This enables easier deletion of the whole set of menu
389 // entries when re-scanning.
390 // Returns a pointer to the copy of the menu entry.
391 static REFIT_MENU_ENTRY
* CopyMenuEntry(REFIT_MENU_ENTRY
*Entry
) {
392 REFIT_MENU_ENTRY
*NewEntry
;
394 NewEntry
= AllocateZeroPool(sizeof(REFIT_MENU_ENTRY
));
395 if ((Entry
!= NULL
) && (NewEntry
!= NULL
)) {
396 CopyMem(NewEntry
, Entry
, sizeof(REFIT_MENU_ENTRY
));
397 NewEntry
->Title
= (Entry
->Title
) ? StrDuplicate(Entry
->Title
) : NULL
;
398 if (Entry
->BadgeImage
!= NULL
) {
399 NewEntry
->BadgeImage
= AllocatePool(sizeof(EG_IMAGE
));
400 if (NewEntry
->BadgeImage
!= NULL
)
401 CopyMem(NewEntry
->BadgeImage
, Entry
->BadgeImage
, sizeof(EG_IMAGE
));
403 if (Entry
->Image
!= NULL
) {
404 NewEntry
->Image
= AllocatePool(sizeof(EG_IMAGE
));
405 if (NewEntry
->Image
!= NULL
)
406 CopyMem(NewEntry
->Image
, Entry
->Image
, sizeof(EG_IMAGE
));
408 if (Entry
->SubScreen
!= NULL
) {
409 NewEntry
->SubScreen
= CopyMenuScreen(Entry
->SubScreen
);
413 } // REFIT_MENU_ENTRY* CopyMenuEntry()
415 // Creates a new LOADER_ENTRY data structure and populates it with
416 // default values from the specified Entry, or NULL values if Entry
417 // is unspecified (NULL).
418 // Returns a pointer to the new data structure, or NULL if it
419 // couldn't be allocated
420 LOADER_ENTRY
*InitializeLoaderEntry(IN LOADER_ENTRY
*Entry
) {
421 LOADER_ENTRY
*NewEntry
= NULL
;
423 NewEntry
= AllocateZeroPool(sizeof(LOADER_ENTRY
));
424 if (NewEntry
!= NULL
) {
425 NewEntry
->me
.Title
= NULL
;
426 NewEntry
->me
.Tag
= TAG_LOADER
;
427 NewEntry
->Enabled
= TRUE
;
428 NewEntry
->UseGraphicsMode
= FALSE
;
429 NewEntry
->OSType
= 0;
431 NewEntry
->LoaderPath
= (Entry
->LoaderPath
) ? StrDuplicate(Entry
->LoaderPath
) : NULL
;
432 NewEntry
->VolName
= (Entry
->VolName
) ? StrDuplicate(Entry
->VolName
) : NULL
;
433 NewEntry
->DevicePath
= Entry
->DevicePath
;
434 NewEntry
->UseGraphicsMode
= Entry
->UseGraphicsMode
;
435 NewEntry
->LoadOptions
= (Entry
->LoadOptions
) ? StrDuplicate(Entry
->LoadOptions
) : NULL
;
436 NewEntry
->InitrdPath
= (Entry
->InitrdPath
) ? StrDuplicate(Entry
->InitrdPath
) : NULL
;
440 } // LOADER_ENTRY *InitializeLoaderEntry()
442 // Adds InitrdPath to Options, but only if Options doesn't already include an
443 // initrd= line. Done to enable overriding the default initrd selection in a
444 // refind_linux.conf file's options list.
445 // Returns a pointer to a new string. The calling function is responsible for
446 // freeing its memory.
447 static CHAR16
*AddInitrdToOptions(CHAR16
*Options
, CHAR16
*InitrdPath
) {
448 CHAR16
*NewOptions
= NULL
;
451 NewOptions
= StrDuplicate(Options
);
452 if ((InitrdPath
!= NULL
) && !StriSubCmp(L
"initrd=", Options
)) {
453 MergeStrings(&NewOptions
, L
"initrd=", L
' ');
454 MergeStrings(&NewOptions
, InitrdPath
, 0);
457 } // CHAR16 *AddInitrdToOptions()
459 // Prepare a REFIT_MENU_SCREEN data structure for a subscreen entry. This sets up
460 // the default entry that launches the boot loader using the same options as the
461 // main Entry does. Subsequent options can be added by the calling function.
462 // If a subscreen already exists in the Entry that's passed to this function,
463 // it's left unchanged and a pointer to it is returned.
464 // Returns a pointer to the new subscreen data structure, or NULL if there
465 // were problems allocating memory.
466 REFIT_MENU_SCREEN
*InitializeSubScreen(IN LOADER_ENTRY
*Entry
) {
467 CHAR16
*FileName
, *MainOptions
= NULL
;
468 REFIT_MENU_SCREEN
*SubScreen
= NULL
;
469 LOADER_ENTRY
*SubEntry
;
471 FileName
= Basename(Entry
->LoaderPath
);
472 if (Entry
->me
.SubScreen
== NULL
) { // No subscreen yet; initialize default entry....
473 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
474 if (SubScreen
!= NULL
) {
475 SubScreen
->Title
= AllocateZeroPool(sizeof(CHAR16
) * 256);
476 SPrint(SubScreen
->Title
, 255, L
"Boot Options for %s on %s",
477 (Entry
->Title
!= NULL
) ? Entry
->Title
: FileName
, Entry
->VolName
);
478 SubScreen
->TitleImage
= Entry
->me
.Image
;
480 SubEntry
= InitializeLoaderEntry(Entry
);
481 if (SubEntry
!= NULL
) {
482 SubEntry
->me
.Title
= StrDuplicate(L
"Boot using default options");
483 MainOptions
= SubEntry
->LoadOptions
;
484 SubEntry
->LoadOptions
= AddInitrdToOptions(MainOptions
, SubEntry
->InitrdPath
);
485 MyFreePool(MainOptions
);
486 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
487 } // if (SubEntry != NULL)
488 SubScreen
->Hint1
= StrDuplicate(SUBSCREEN_HINT1
);
489 if (GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_EDITOR
) {
490 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2_NO_EDITOR
);
492 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2
);
494 } // if (SubScreen != NULL)
495 } else { // existing subscreen; less initialization, and just add new entry later....
496 SubScreen
= Entry
->me
.SubScreen
;
499 } // REFIT_MENU_SCREEN *InitializeSubScreen()
501 VOID
GenerateSubScreen(LOADER_ENTRY
*Entry
, IN REFIT_VOLUME
*Volume
) {
502 REFIT_MENU_SCREEN
*SubScreen
;
503 LOADER_ENTRY
*SubEntry
;
505 CHAR16 DiagsFileName
[256];
510 // create the submenu
511 if (StrLen(Entry
->Title
) == 0) {
512 MyFreePool(Entry
->Title
);
515 SubScreen
= InitializeSubScreen(Entry
);
517 // loader-specific submenu entries
518 if (Entry
->OSType
== 'M') { // entries for Mac OS X
520 SubEntry
= InitializeLoaderEntry(Entry
);
521 if (SubEntry
!= NULL
) {
522 SubEntry
->me
.Title
= L
"Boot Mac OS X with a 64-bit kernel";
523 SubEntry
->LoadOptions
= L
"arch=x86_64";
524 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
525 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
528 SubEntry
= InitializeLoaderEntry(Entry
);
529 if (SubEntry
!= NULL
) {
530 SubEntry
->me
.Title
= L
"Boot Mac OS X with a 32-bit kernel";
531 SubEntry
->LoadOptions
= L
"arch=i386";
532 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
533 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
537 if (!(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_SINGLEUSER
)) {
538 SubEntry
= InitializeLoaderEntry(Entry
);
539 if (SubEntry
!= NULL
) {
540 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode";
541 SubEntry
->UseGraphicsMode
= FALSE
;
542 SubEntry
->LoadOptions
= L
"-v";
543 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
547 SubEntry
= InitializeLoaderEntry(Entry
);
548 if (SubEntry
!= NULL
) {
549 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode (64-bit)";
550 SubEntry
->UseGraphicsMode
= FALSE
;
551 SubEntry
->LoadOptions
= L
"-v arch=x86_64";
552 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
555 SubEntry
= InitializeLoaderEntry(Entry
);
556 if (SubEntry
!= NULL
) {
557 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode (32-bit)";
558 SubEntry
->UseGraphicsMode
= FALSE
;
559 SubEntry
->LoadOptions
= L
"-v arch=i386";
560 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
564 SubEntry
= InitializeLoaderEntry(Entry
);
565 if (SubEntry
!= NULL
) {
566 SubEntry
->me
.Title
= L
"Boot Mac OS X in single user mode";
567 SubEntry
->UseGraphicsMode
= FALSE
;
568 SubEntry
->LoadOptions
= L
"-v -s";
569 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
573 // check for Apple hardware diagnostics
574 StrCpy(DiagsFileName
, L
"System\\Library\\CoreServices\\.diagnostics\\diags.efi");
575 if (FileExists(Volume
->RootDir
, DiagsFileName
) && !(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_HWTEST
)) {
576 SubEntry
= InitializeLoaderEntry(Entry
);
577 if (SubEntry
!= NULL
) {
578 SubEntry
->me
.Title
= L
"Run Apple Hardware Test";
579 MyFreePool(SubEntry
->LoaderPath
);
580 SubEntry
->LoaderPath
= StrDuplicate(DiagsFileName
);
581 SubEntry
->DevicePath
= FileDevicePath(Volume
->DeviceHandle
, SubEntry
->LoaderPath
);
582 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
583 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
585 } // if diagnostics entry found
587 } else if (Entry
->OSType
== 'L') { // entries for Linux kernels with EFI stub loaders
588 File
= ReadLinuxOptionsFile(Entry
->LoaderPath
, Volume
);
590 InitrdName
= FindInitrd(Entry
->LoaderPath
, Volume
);
591 TokenCount
= ReadTokenLine(File
, &TokenList
);
592 // first entry requires special processing, since it was initially set
593 // up with a default title but correct options by InitializeSubScreen(),
595 if ((SubScreen
->Entries
!= NULL
) && (SubScreen
->Entries
[0] != NULL
)) {
596 MyFreePool(SubScreen
->Entries
[0]->Title
);
597 SubScreen
->Entries
[0]->Title
= StrDuplicate(TokenList
[0]);
599 FreeTokenLine(&TokenList
, &TokenCount
);
600 while ((TokenCount
= ReadTokenLine(File
, &TokenList
)) > 1) {
601 SubEntry
= InitializeLoaderEntry(Entry
);
602 SubEntry
->me
.Title
= StrDuplicate(TokenList
[0]);
603 MyFreePool(SubEntry
->LoadOptions
);
604 SubEntry
->LoadOptions
= AddInitrdToOptions(TokenList
[1], InitrdName
);
605 FreeTokenLine(&TokenList
, &TokenCount
);
606 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_LINUX
;
607 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
609 MyFreePool(InitrdName
);
611 } // if Linux options file exists
613 } else if (Entry
->OSType
== 'E') { // entries for ELILO
614 SubEntry
= InitializeLoaderEntry(Entry
);
615 if (SubEntry
!= NULL
) {
616 SubEntry
->me
.Title
= L
"Run ELILO in interactive mode";
617 SubEntry
->LoadOptions
= L
"-p";
618 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
619 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
622 SubEntry
= InitializeLoaderEntry(Entry
);
623 if (SubEntry
!= NULL
) {
624 SubEntry
->me
.Title
= L
"Boot Linux for a 17\" iMac or a 15\" MacBook Pro (*)";
625 SubEntry
->UseGraphicsMode
= TRUE
;
626 SubEntry
->LoadOptions
= L
"-d 0 i17";
627 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
628 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
631 SubEntry
= InitializeLoaderEntry(Entry
);
632 if (SubEntry
!= NULL
) {
633 SubEntry
->me
.Title
= L
"Boot Linux for a 20\" iMac (*)";
634 SubEntry
->UseGraphicsMode
= TRUE
;
635 SubEntry
->LoadOptions
= L
"-d 0 i20";
636 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
637 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
640 SubEntry
= InitializeLoaderEntry(Entry
);
641 if (SubEntry
!= NULL
) {
642 SubEntry
->me
.Title
= L
"Boot Linux for a Mac Mini (*)";
643 SubEntry
->UseGraphicsMode
= TRUE
;
644 SubEntry
->LoadOptions
= L
"-d 0 mini";
645 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
646 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
649 AddMenuInfoLine(SubScreen
, L
"NOTE: This is an example. Entries");
650 AddMenuInfoLine(SubScreen
, L
"marked with (*) may not work.");
652 } else if (Entry
->OSType
== 'X') { // entries for xom.efi
653 // by default, skip the built-in selection and boot from hard disk only
654 Entry
->LoadOptions
= L
"-s -h";
656 SubEntry
= InitializeLoaderEntry(Entry
);
657 if (SubEntry
!= NULL
) {
658 SubEntry
->me
.Title
= L
"Boot Windows from Hard Disk";
659 SubEntry
->LoadOptions
= L
"-s -h";
660 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
661 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
664 SubEntry
= InitializeLoaderEntry(Entry
);
665 if (SubEntry
!= NULL
) {
666 SubEntry
->me
.Title
= L
"Boot Windows from CD-ROM";
667 SubEntry
->LoadOptions
= L
"-s -c";
668 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
669 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
672 SubEntry
= InitializeLoaderEntry(Entry
);
673 if (SubEntry
!= NULL
) {
674 SubEntry
->me
.Title
= L
"Run XOM in text mode";
675 SubEntry
->UseGraphicsMode
= FALSE
;
676 SubEntry
->LoadOptions
= L
"-v";
677 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
678 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
680 } // entries for xom.efi
681 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
682 Entry
->me
.SubScreen
= SubScreen
;
683 } // VOID GenerateSubScreen()
685 // Returns options for a Linux kernel. Reads them from an options file in the
686 // kernel's directory; and if present, adds an initrd= option for an initial
687 // RAM disk file with the same version number as the kernel file.
688 static CHAR16
* GetMainLinuxOptions(IN CHAR16
* LoaderPath
, IN REFIT_VOLUME
*Volume
) {
689 CHAR16
*Options
= NULL
, *InitrdName
, *FullOptions
= NULL
;
691 Options
= GetFirstOptionsFromFile(LoaderPath
, Volume
);
692 InitrdName
= FindInitrd(LoaderPath
, Volume
);
693 FullOptions
= AddInitrdToOptions(Options
, InitrdName
);
696 MyFreePool(InitrdName
);
697 return (FullOptions
);
698 } // static CHAR16 * GetMainLinuxOptions()
700 // Sets a few defaults for a loader entry -- mainly the icon, but also the OS type
701 // code and shortcut letter. For Linux EFI stub loaders, also sets kernel options
702 // that will (with luck) work fairly automatically.
703 VOID
SetLoaderDefaults(LOADER_ENTRY
*Entry
, CHAR16
*LoaderPath
, REFIT_VOLUME
*Volume
) {
704 CHAR16 IconFileName
[256];
705 CHAR16
*FileName
, *PathOnly
, *OSIconName
= NULL
, *Temp
, *SubString
;
706 CHAR16 ShortcutLetter
= 0;
709 FileName
= Basename(LoaderPath
);
710 PathOnly
= FindPath(LoaderPath
);
712 // locate a custom icon for the loader
713 // Anything found here takes precedence over the "hints" in the OSIconName variable
714 StrCpy(IconFileName
, LoaderPath
);
715 ReplaceEfiExtension(IconFileName
, L
".icns");
716 if (FileExists(Volume
->RootDir
, IconFileName
)) {
717 Entry
->me
.Image
= LoadIcns(Volume
->RootDir
, IconFileName
, 128);
718 } else if ((StrLen(PathOnly
) == 0) && (Volume
->VolIconImage
!= NULL
)) {
719 Entry
->me
.Image
= Volume
->VolIconImage
;
720 } // icon matched to loader or volume
722 // Begin creating icon "hints" by using last part of directory path leading
724 Temp
= FindLastDirName(LoaderPath
);
725 MergeStrings(&OSIconName
, Temp
, L
',');
728 if (OSIconName
!= NULL
) {
729 ShortcutLetter
= OSIconName
[0];
732 // Add every "word" in the volume label, delimited by spaces, dashes (-), or
733 // underscores (_), to the list of hints to be used in searching for OS
735 if ((Volume
->VolName
) && (StrLen(Volume
->VolName
) > 0)) {
736 Temp
= SubString
= StrDuplicate(Volume
->VolName
);
738 Length
= StrLen(Temp
);
739 for (i
= 0; i
< Length
; i
++) {
740 if ((Temp
[i
] == L
' ') || (Temp
[i
] == L
'_') || (Temp
[i
] == L
'-')) {
742 if (StrLen(SubString
) > 0)
743 MergeStrings(&OSIconName
, SubString
, L
',');
744 SubString
= Temp
+ i
+ 1;
747 MergeStrings(&OSIconName
, SubString
, L
',');
752 // detect specific loaders
753 if (StriSubCmp(L
"bzImage", LoaderPath
) || StriSubCmp(L
"vmlinuz", LoaderPath
)) {
754 MergeStrings(&OSIconName
, L
"linux", L
',');
756 if (ShortcutLetter
== 0)
757 ShortcutLetter
= 'L';
758 Entry
->LoadOptions
= GetMainLinuxOptions(LoaderPath
, Volume
);
759 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_LINUX
;
760 } else if (StriSubCmp(L
"refit", LoaderPath
)) {
761 MergeStrings(&OSIconName
, L
"refit", L
',');
763 ShortcutLetter
= 'R';
764 } else if (StriCmp(LoaderPath
, MACOSX_LOADER_PATH
) == 0) {
765 if (Volume
->VolIconImage
!= NULL
) { // custom icon file found
766 Entry
->me
.Image
= Volume
->VolIconImage
;
768 MergeStrings(&OSIconName
, L
"mac", L
',');
770 ShortcutLetter
= 'M';
771 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
772 } else if (StriCmp(FileName
, L
"diags.efi") == 0) {
773 MergeStrings(&OSIconName
, L
"hwtest", L
',');
774 } else if (StriCmp(FileName
, L
"e.efi") == 0 || StriCmp(FileName
, L
"elilo.efi") == 0 || StriSubCmp(L
"elilo", FileName
)) {
775 MergeStrings(&OSIconName
, L
"elilo,linux", L
',');
777 if (ShortcutLetter
== 0)
778 ShortcutLetter
= 'L';
779 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
780 } else if (StriSubCmp(L
"grub", FileName
)) {
782 ShortcutLetter
= 'G';
783 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_GRUB
;
784 } else if (StriCmp(FileName
, L
"cdboot.efi") == 0 ||
785 StriCmp(FileName
, L
"bootmgr.efi") == 0 ||
786 StriCmp(FileName
, L
"bootmgfw.efi") == 0) {
787 MergeStrings(&OSIconName
, L
"win", L
',');
789 ShortcutLetter
= 'W';
790 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
791 } else if (StriCmp(FileName
, L
"xom.efi") == 0) {
792 MergeStrings(&OSIconName
, L
"xom,win", L
',');
793 Entry
->UseGraphicsMode
= TRUE
;
795 ShortcutLetter
= 'W';
796 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
799 if ((ShortcutLetter
>= 'a') && (ShortcutLetter
<= 'z'))
800 ShortcutLetter
= ShortcutLetter
- 'a' + 'A'; // convert lowercase to uppercase
801 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
802 if (Entry
->me
.Image
== NULL
)
803 Entry
->me
.Image
= LoadOSIcon(OSIconName
, L
"unknown", FALSE
);
804 MyFreePool(PathOnly
);
805 } // VOID SetLoaderDefaults()
807 // Add a specified EFI boot loader to the list, using automatic settings
808 // for icons, options, etc.
809 LOADER_ENTRY
* AddLoaderEntry(IN CHAR16
*LoaderPath
, IN CHAR16
*LoaderTitle
, IN REFIT_VOLUME
*Volume
) {
812 CleanUpPathNameSlashes(LoaderPath
);
813 Entry
= InitializeLoaderEntry(NULL
);
815 Entry
->Title
= StrDuplicate((LoaderTitle
!= NULL
) ? LoaderTitle
: LoaderPath
);
816 Entry
->me
.Title
= AllocateZeroPool(sizeof(CHAR16
) * 256);
817 SPrint(Entry
->me
.Title
, 255, L
"Boot %s from %s", (LoaderTitle
!= NULL
) ? LoaderTitle
: LoaderPath
, Volume
->VolName
);
819 Entry
->me
.BadgeImage
= Volume
->VolBadgeImage
;
820 if ((LoaderPath
!= NULL
) && (LoaderPath
[0] != L
'\\')) {
821 Entry
->LoaderPath
= StrDuplicate(L
"\\");
823 Entry
->LoaderPath
= NULL
;
825 MergeStrings(&(Entry
->LoaderPath
), LoaderPath
, 0);
826 Entry
->VolName
= Volume
->VolName
;
827 Entry
->DevicePath
= FileDevicePath(Volume
->DeviceHandle
, Entry
->LoaderPath
);
828 SetLoaderDefaults(Entry
, LoaderPath
, Volume
);
829 GenerateSubScreen(Entry
, Volume
);
830 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
834 } // LOADER_ENTRY * AddLoaderEntry()
836 // Returns -1 if (Time1 < Time2), +1 if (Time1 > Time2), or 0 if
837 // (Time1 == Time2). Precision is only to the nearest second; since
838 // this is used for sorting boot loader entries, differences smaller
839 // than this are likely to be meaningless (and unlikely!).
840 INTN
TimeComp(IN EFI_TIME
*Time1
, IN EFI_TIME
*Time2
) {
841 INT64 Time1InSeconds
, Time2InSeconds
;
843 // Following values are overestimates; I'm assuming 31 days in every month.
844 // This is fine for the purpose of this function, which is limited
845 Time1InSeconds
= Time1
->Second
+ (Time1
->Minute
* 60) + (Time1
->Hour
* 3600) + (Time1
->Day
* 86400) +
846 (Time1
->Month
* 2678400) + ((Time1
->Year
- 1998) * 32140800);
847 Time2InSeconds
= Time2
->Second
+ (Time2
->Minute
* 60) + (Time2
->Hour
* 3600) + (Time2
->Day
* 86400) +
848 (Time2
->Month
* 2678400) + ((Time2
->Year
- 1998) * 32140800);
849 if (Time1InSeconds
< Time2InSeconds
)
851 else if (Time1InSeconds
> Time2InSeconds
)
857 // Adds a loader list element, keeping it sorted by date. Returns the new
858 // first element (the one with the most recent date).
859 static struct LOADER_LIST
* AddLoaderListEntry(struct LOADER_LIST
*LoaderList
, struct LOADER_LIST
*NewEntry
) {
860 struct LOADER_LIST
*LatestEntry
, *CurrentEntry
, *PrevEntry
= NULL
;
862 LatestEntry
= CurrentEntry
= LoaderList
;
863 if (LoaderList
== NULL
) {
864 LatestEntry
= NewEntry
;
866 while ((CurrentEntry
!= NULL
) && (TimeComp(&(NewEntry
->TimeStamp
), &(CurrentEntry
->TimeStamp
)) < 0)) {
867 PrevEntry
= CurrentEntry
;
868 CurrentEntry
= CurrentEntry
->NextEntry
;
870 NewEntry
->NextEntry
= CurrentEntry
;
871 if (PrevEntry
== NULL
) {
872 LatestEntry
= NewEntry
;
874 PrevEntry
->NextEntry
= NewEntry
;
877 return (LatestEntry
);
878 } // static VOID AddLoaderListEntry()
880 // Delete the LOADER_LIST linked list
881 static VOID
CleanUpLoaderList(struct LOADER_LIST
*LoaderList
) {
882 struct LOADER_LIST
*Temp
;
884 while (LoaderList
!= NULL
) {
886 LoaderList
= LoaderList
->NextEntry
;
887 MyFreePool(Temp
->FileName
);
890 } // static VOID CleanUpLoaderList()
892 // Returns FALSE if the specified file/volume matches the GlobalConfig.DontScanDirs
893 // or GlobalConfig.DontScanVolumes specification, or if Path points to a volume
894 // other than the one specified by Volume. Returns TRUE if none of these conditions
895 // is met -- that is, if the path is eligible for scanning. Also reduces *Path to a
896 // path alone, with no volume specification.
897 static BOOLEAN
ShouldScan(REFIT_VOLUME
*Volume
, CHAR16
*Path
) {
898 CHAR16
*VolName
= NULL
, *DontScanDir
;
900 BOOLEAN ScanIt
= TRUE
;
902 if (IsIn(Volume
->VolName
, GlobalConfig
.DontScanVolumes
))
905 while ((DontScanDir
= FindCommaDelimited(GlobalConfig
.DontScanDirs
, i
++)) && ScanIt
) {
906 SplitVolumeAndFilename(&DontScanDir
, &VolName
);
907 CleanUpPathNameSlashes(DontScanDir
);
908 if (VolName
!= NULL
) {
909 if ((StriCmp(VolName
, Volume
->VolName
) == 0) && (StriCmp(DontScanDir
, Path
) == 0))
911 if ((VolName
[0] == L
'f') && (VolName
[1] == L
's') && (VolName
[2] >= L
'0') && (VolName
[2] <= L
'9')) {
912 VolNum
= Atoi(VolName
+ 2);
913 if ((VolNum
== Volume
->VolNumber
) && (StriCmp(DontScanDir
, Path
) == 0))
917 if (StriCmp(DontScanDir
, Path
) == 0)
920 MyFreePool(DontScanDir
);
924 } // BOOLEAN ShouldScan()
926 // Scan an individual directory for EFI boot loader files and, if found,
927 // add them to the list. Sorts the entries within the loader directory
928 // so that the most recent one appears first in the list.
929 static VOID
ScanLoaderDir(IN REFIT_VOLUME
*Volume
, IN CHAR16
*Path
, IN CHAR16
*Pattern
)
932 REFIT_DIR_ITER DirIter
;
933 EFI_FILE_INFO
*DirEntry
;
934 CHAR16 FileName
[256], *Extension
;
935 struct LOADER_LIST
*LoaderList
= NULL
, *NewLoader
;
937 if ((!SelfDirPath
|| !Path
|| ((StriCmp(Path
, SelfDirPath
) == 0) && (Volume
->DeviceHandle
!= SelfVolume
->DeviceHandle
)) ||
938 (StriCmp(Path
, SelfDirPath
) != 0)) &&
939 (ShouldScan(Volume
, Path
))) {
940 // look through contents of the directory
941 DirIterOpen(Volume
->RootDir
, Path
, &DirIter
);
942 while (DirIterNext(&DirIter
, 2, Pattern
, &DirEntry
)) {
943 Extension
= FindExtension(DirEntry
->FileName
);
944 if (DirEntry
->FileName
[0] == '.' ||
945 StriCmp(Extension
, L
".icns") == 0 ||
946 StriSubCmp(L
"shell", DirEntry
->FileName
) ||
947 IsIn(DirEntry
->FileName
, GlobalConfig
.DontScanFiles
))
948 continue; // skip this
951 SPrint(FileName
, 255, L
"\\%s\\%s", Path
, DirEntry
->FileName
);
953 SPrint(FileName
, 255, L
"\\%s", DirEntry
->FileName
);
954 CleanUpPathNameSlashes(FileName
);
955 NewLoader
= AllocateZeroPool(sizeof(struct LOADER_LIST
));
956 if (NewLoader
!= NULL
) {
957 NewLoader
->FileName
= StrDuplicate(FileName
);
958 NewLoader
->TimeStamp
= DirEntry
->ModificationTime
;
959 LoaderList
= AddLoaderListEntry(LoaderList
, NewLoader
);
961 MyFreePool(Extension
);
963 NewLoader
= LoaderList
;
964 while (NewLoader
!= NULL
) {
965 AddLoaderEntry(NewLoader
->FileName
, NULL
, Volume
);
966 NewLoader
= NewLoader
->NextEntry
;
968 CleanUpLoaderList(LoaderList
);
969 Status
= DirIterClose(&DirIter
);
970 if (Status
!= EFI_NOT_FOUND
) {
972 SPrint(FileName
, 255, L
"while scanning the %s directory", Path
);
974 StrCpy(FileName
, L
"while scanning the root directory");
975 CheckError(Status
, FileName
);
976 } // if (Status != EFI_NOT_FOUND)
977 } // if not scanning our own directory
978 } /* static VOID ScanLoaderDir() */
980 static VOID
ScanEfiFiles(REFIT_VOLUME
*Volume
) {
982 REFIT_DIR_ITER EfiDirIter
;
983 EFI_FILE_INFO
*EfiDirEntry
;
984 CHAR16 FileName
[256], *Directory
, *MatchPatterns
, *VolName
= NULL
;
985 UINTN i
, Length
, VolNum
;
987 MatchPatterns
= StrDuplicate(LOADER_MATCH_PATTERNS
);
988 if (GlobalConfig
.ScanAllLinux
)
989 MergeStrings(&MatchPatterns
, LINUX_MATCH_PATTERNS
, L
',');
991 if ((Volume
->RootDir
!= NULL
) && (Volume
->VolName
!= NULL
)) {
992 // check for Mac OS X boot loader
993 if (!IsIn(L
"System\\Library\\CoreServices", GlobalConfig
.DontScanDirs
)) {
994 StrCpy(FileName
, MACOSX_LOADER_PATH
);
995 if (FileExists(Volume
->RootDir
, FileName
) && !IsIn(L
"boot.efi", GlobalConfig
.DontScanFiles
)) {
996 AddLoaderEntry(FileName
, L
"Mac OS X", Volume
);
1000 StrCpy(FileName
, L
"System\\Library\\CoreServices\\xom.efi");
1001 if (FileExists(Volume
->RootDir
, FileName
) && !IsIn(L
"boot.efi", GlobalConfig
.DontScanFiles
)) {
1002 AddLoaderEntry(FileName
, L
"Windows XP (XoM)", Volume
);
1004 } // if Mac directory not in GlobalConfig.DontScanDirs list
1006 // check for Microsoft boot loader/menu
1007 StrCpy(FileName
, L
"EFI\\Microsoft\\Boot\\Bootmgfw.efi");
1008 if (FileExists(Volume
->RootDir
, FileName
) && !IsIn(L
"EFI\\Microsoft\\Boot", GlobalConfig
.DontScanDirs
) &&
1009 !IsIn(L
"bootmgfw.efi", GlobalConfig
.DontScanFiles
)) {
1010 AddLoaderEntry(FileName
, L
"Microsoft EFI boot", Volume
);
1013 // scan the root directory for EFI executables
1014 ScanLoaderDir(Volume
, L
"\\", MatchPatterns
);
1016 // scan subdirectories of the EFI directory (as per the standard)
1017 DirIterOpen(Volume
->RootDir
, L
"EFI", &EfiDirIter
);
1018 while (DirIterNext(&EfiDirIter
, 1, NULL
, &EfiDirEntry
)) {
1019 if (StriCmp(EfiDirEntry
->FileName
, L
"tools") == 0 || EfiDirEntry
->FileName
[0] == '.')
1020 continue; // skip this, doesn't contain boot loaders
1021 SPrint(FileName
, 255, L
"EFI\\%s", EfiDirEntry
->FileName
);
1022 ScanLoaderDir(Volume
, FileName
, MatchPatterns
);
1024 Status
= DirIterClose(&EfiDirIter
);
1025 if (Status
!= EFI_NOT_FOUND
)
1026 CheckError(Status
, L
"while scanning the EFI directory");
1028 // Scan user-specified (or additional default) directories....
1030 while ((Directory
= FindCommaDelimited(GlobalConfig
.AlsoScan
, i
++)) != NULL
) {
1031 VolNum
= VOL_DONTSCAN
;
1032 SplitVolumeAndFilename(&Directory
, &VolName
);
1033 CleanUpPathNameSlashes(Directory
);
1034 Length
= StrLen(Directory
);
1035 if ((Length
> 0) && (VolName
[0] == L
'f') && (VolName
[1] == L
's') && (VolName
[2] >= L
'0') && (VolName
[2] <= L
'9'))
1036 VolNum
= Atoi(VolName
+ 2);
1037 if ((Length
> 0) && ((VolName
== NULL
) || (StriCmp(VolName
, Volume
->VolName
) == 0) || (Volume
->VolNumber
== VolNum
)))
1038 ScanLoaderDir(Volume
, Directory
, MatchPatterns
);
1039 MyFreePool(Directory
);
1040 MyFreePool(VolName
);
1043 } // static VOID ScanEfiFiles()
1045 // Scan internal disks for valid EFI boot loaders....
1046 static VOID
ScanInternal(VOID
) {
1049 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1050 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_INTERNAL
) {
1051 ScanEfiFiles(Volumes
[VolumeIndex
]);
1054 } // static VOID ScanInternal()
1056 // Scan external disks for valid EFI boot loaders....
1057 static VOID
ScanExternal(VOID
) {
1060 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1061 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_EXTERNAL
) {
1062 ScanEfiFiles(Volumes
[VolumeIndex
]);
1065 } // static VOID ScanExternal()
1067 // Scan internal disks for valid EFI boot loaders....
1068 static VOID
ScanOptical(VOID
) {
1071 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1072 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_OPTICAL
) {
1073 ScanEfiFiles(Volumes
[VolumeIndex
]);
1076 } // static VOID ScanOptical()
1079 // legacy boot functions
1082 static EFI_STATUS
ActivateMbrPartition(IN EFI_BLOCK_IO
*BlockIO
, IN UINTN PartitionIndex
)
1085 UINT8 SectorBuffer
[512];
1086 MBR_PARTITION_INFO
*MbrTable
, *EMbrTable
;
1087 UINT32 ExtBase
, ExtCurrent
, NextExtCurrent
;
1088 UINTN LogicalPartitionIndex
= 4;
1090 BOOLEAN HaveBootCode
;
1093 Status
= refit_call5_wrapper(BlockIO
->ReadBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, 0, 512, SectorBuffer
);
1094 if (EFI_ERROR(Status
))
1096 if (*((UINT16
*)(SectorBuffer
+ 510)) != 0xaa55)
1097 return EFI_NOT_FOUND
; // safety measure #1
1099 // add boot code if necessary
1100 HaveBootCode
= FALSE
;
1101 for (i
= 0; i
< MBR_BOOTCODE_SIZE
; i
++) {
1102 if (SectorBuffer
[i
] != 0) {
1103 HaveBootCode
= TRUE
;
1107 if (!HaveBootCode
) {
1108 // no boot code found in the MBR, add the syslinux MBR code
1109 SetMem(SectorBuffer
, MBR_BOOTCODE_SIZE
, 0);
1110 CopyMem(SectorBuffer
, syslinux_mbr
, SYSLINUX_MBR_SIZE
);
1113 // set the partition active
1114 MbrTable
= (MBR_PARTITION_INFO
*)(SectorBuffer
+ 446);
1116 for (i
= 0; i
< 4; i
++) {
1117 if (MbrTable
[i
].Flags
!= 0x00 && MbrTable
[i
].Flags
!= 0x80)
1118 return EFI_NOT_FOUND
; // safety measure #2
1119 if (i
== PartitionIndex
)
1120 MbrTable
[i
].Flags
= 0x80;
1121 else if (PartitionIndex
>= 4 && IS_EXTENDED_PART_TYPE(MbrTable
[i
].Type
)) {
1122 MbrTable
[i
].Flags
= 0x80;
1123 ExtBase
= MbrTable
[i
].StartLBA
;
1125 MbrTable
[i
].Flags
= 0x00;
1129 Status
= refit_call5_wrapper(BlockIO
->WriteBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, 0, 512, SectorBuffer
);
1130 if (EFI_ERROR(Status
))
1133 if (PartitionIndex
>= 4) {
1134 // we have to activate a logical partition, so walk the EMBR chain
1136 // NOTE: ExtBase was set above while looking at the MBR table
1137 for (ExtCurrent
= ExtBase
; ExtCurrent
; ExtCurrent
= NextExtCurrent
) {
1138 // read current EMBR
1139 Status
= refit_call5_wrapper(BlockIO
->ReadBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, ExtCurrent
, 512, SectorBuffer
);
1140 if (EFI_ERROR(Status
))
1142 if (*((UINT16
*)(SectorBuffer
+ 510)) != 0xaa55)
1143 return EFI_NOT_FOUND
; // safety measure #3
1145 // scan EMBR, set appropriate partition active
1146 EMbrTable
= (MBR_PARTITION_INFO
*)(SectorBuffer
+ 446);
1148 for (i
= 0; i
< 4; i
++) {
1149 if (EMbrTable
[i
].Flags
!= 0x00 && EMbrTable
[i
].Flags
!= 0x80)
1150 return EFI_NOT_FOUND
; // safety measure #4
1151 if (EMbrTable
[i
].StartLBA
== 0 || EMbrTable
[i
].Size
== 0)
1153 if (IS_EXTENDED_PART_TYPE(EMbrTable
[i
].Type
)) {
1154 // link to next EMBR
1155 NextExtCurrent
= ExtBase
+ EMbrTable
[i
].StartLBA
;
1156 EMbrTable
[i
].Flags
= (PartitionIndex
>= LogicalPartitionIndex
) ? 0x80 : 0x00;
1159 // logical partition
1160 EMbrTable
[i
].Flags
= (PartitionIndex
== LogicalPartitionIndex
) ? 0x80 : 0x00;
1161 LogicalPartitionIndex
++;
1165 // write current EMBR
1166 Status
= refit_call5_wrapper(BlockIO
->WriteBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, ExtCurrent
, 512, SectorBuffer
);
1167 if (EFI_ERROR(Status
))
1170 if (PartitionIndex
< LogicalPartitionIndex
)
1171 break; // stop the loop, no need to touch further EMBRs
1177 } /* static EFI_STATUS ActivateMbrPartition() */
1179 // early 2006 Core Duo / Core Solo models
1180 static UINT8 LegacyLoaderDevicePath1Data
[] = {
1181 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1182 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1183 0xFF, 0xFF, 0xF9, 0xFF, 0x00, 0x00, 0x00, 0x00,
1184 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1185 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1186 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1188 // mid-2006 Mac Pro (and probably other Core 2 models)
1189 static UINT8 LegacyLoaderDevicePath2Data
[] = {
1190 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1191 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1192 0xFF, 0xFF, 0xF7, 0xFF, 0x00, 0x00, 0x00, 0x00,
1193 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1194 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1195 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1197 // mid-2007 MBP ("Santa Rosa" based models)
1198 static UINT8 LegacyLoaderDevicePath3Data
[] = {
1199 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1200 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1201 0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
1202 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1203 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1204 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1207 static UINT8 LegacyLoaderDevicePath4Data
[] = {
1208 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1209 0x00, 0x00, 0xC0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1210 0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
1211 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1212 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1213 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1215 // late-2008 MB/MBP (NVidia chipset)
1216 static UINT8 LegacyLoaderDevicePath5Data
[] = {
1217 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1218 0x00, 0x40, 0xCB, 0xFF, 0x00, 0x00, 0x00, 0x00,
1219 0xFF, 0xBF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
1220 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1221 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1222 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1225 static EFI_DEVICE_PATH
*LegacyLoaderList
[] = {
1226 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath1Data
,
1227 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath2Data
,
1228 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath3Data
,
1229 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath4Data
,
1230 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath5Data
,
1234 #define MAX_DISCOVERED_PATHS (16)
1236 static VOID
StartLegacy(IN LEGACY_ENTRY
*Entry
)
1239 EG_IMAGE
*BootLogoImage
;
1240 UINTN ErrorInStep
= 0;
1241 EFI_DEVICE_PATH
*DiscoveredPathList
[MAX_DISCOVERED_PATHS
];
1243 BeginExternalScreen(TRUE
, L
"Booting Legacy OS (Mac mode)");
1245 BootLogoImage
= LoadOSIcon(Entry
->Volume
->OSIconName
, L
"legacy", TRUE
);
1246 if (BootLogoImage
!= NULL
)
1247 BltImageAlpha(BootLogoImage
,
1248 (UGAWidth
- BootLogoImage
->Width
) >> 1,
1249 (UGAHeight
- BootLogoImage
->Height
) >> 1,
1250 &StdBackgroundPixel
);
1252 if (Entry
->Volume
->IsMbrPartition
) {
1253 ActivateMbrPartition(Entry
->Volume
->WholeDiskBlockIO
, Entry
->Volume
->MbrPartitionIndex
);
1256 ExtractLegacyLoaderPaths(DiscoveredPathList
, MAX_DISCOVERED_PATHS
, LegacyLoaderList
);
1258 Status
= StartEFIImageList(DiscoveredPathList
, Entry
->LoadOptions
, NULL
, L
"legacy loader", 0, &ErrorInStep
, TRUE
);
1259 if (Status
== EFI_NOT_FOUND
) {
1260 if (ErrorInStep
== 1) {
1261 Print(L
"\nPlease make sure that you have the latest firmware update installed.\n");
1262 } else if (ErrorInStep
== 3) {
1263 Print(L
"\nThe firmware refused to boot from the selected volume. Note that external\n"
1264 L
"hard drives are not well-supported by Apple's firmware for legacy OS booting.\n");
1267 FinishExternalScreen();
1268 } /* static VOID StartLegacy() */
1270 // Start a device on a non-Mac using the EFI_LEGACY_BIOS_PROTOCOL
1271 #ifdef __MAKEWITH_TIANO
1272 static VOID
StartLegacyUEFI(IN LEGACY_ENTRY
*Entry
)
1274 BeginExternalScreen(TRUE
, L
"Booting Legacy OS (UEFI mode)");
1276 BdsLibConnectDevicePath (Entry
->BdsOption
->DevicePath
);
1277 BdsLibDoLegacyBoot(Entry
->BdsOption
);
1279 // If we get here, it means that there was a failure....
1280 Print(L
"Failure booting legacy (BIOS) OS.");
1282 FinishExternalScreen();
1283 } // static VOID StartLegacyUEFI()
1284 #endif // __MAKEWITH_TIANO
1286 static LEGACY_ENTRY
* AddLegacyEntry(IN CHAR16
*LoaderTitle
, IN REFIT_VOLUME
*Volume
)
1288 LEGACY_ENTRY
*Entry
, *SubEntry
;
1289 REFIT_MENU_SCREEN
*SubScreen
;
1291 CHAR16 ShortcutLetter
= 0;
1293 if (LoaderTitle
== NULL
) {
1294 if (Volume
->OSName
!= NULL
) {
1295 LoaderTitle
= Volume
->OSName
;
1296 if (LoaderTitle
[0] == 'W' || LoaderTitle
[0] == 'L')
1297 ShortcutLetter
= LoaderTitle
[0];
1299 LoaderTitle
= L
"Legacy OS";
1301 if (Volume
->VolName
!= NULL
)
1302 VolDesc
= Volume
->VolName
;
1304 VolDesc
= (Volume
->DiskKind
== DISK_KIND_OPTICAL
) ? L
"CD" : L
"HD";
1306 // prepare the menu entry
1307 Entry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1308 Entry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1309 SPrint(Entry
->me
.Title
, 255, L
"Boot %s from %s", LoaderTitle
, VolDesc
);
1310 Entry
->me
.Tag
= TAG_LEGACY
;
1312 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1313 Entry
->me
.Image
= LoadOSIcon(Volume
->OSIconName
, L
"legacy", FALSE
);
1314 Entry
->me
.BadgeImage
= Volume
->VolBadgeImage
;
1315 Entry
->Volume
= Volume
;
1316 Entry
->LoadOptions
= (Volume
->DiskKind
== DISK_KIND_OPTICAL
) ? L
"CD" :
1317 ((Volume
->DiskKind
== DISK_KIND_EXTERNAL
) ? L
"USB" : L
"HD");
1318 Entry
->Enabled
= TRUE
;
1320 // create the submenu
1321 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
1322 SubScreen
->Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1323 SPrint(SubScreen
->Title
, 255, L
"Boot Options for %s on %s", LoaderTitle
, VolDesc
);
1324 SubScreen
->TitleImage
= Entry
->me
.Image
;
1325 SubScreen
->Hint1
= StrDuplicate(SUBSCREEN_HINT1
);
1326 if (GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_EDITOR
) {
1327 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2_NO_EDITOR
);
1329 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2
);
1333 SubEntry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1334 SubEntry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1335 SPrint(SubEntry
->me
.Title
, 255, L
"Boot %s", LoaderTitle
);
1336 SubEntry
->me
.Tag
= TAG_LEGACY
;
1337 SubEntry
->Volume
= Entry
->Volume
;
1338 SubEntry
->LoadOptions
= Entry
->LoadOptions
;
1339 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
1341 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
1342 Entry
->me
.SubScreen
= SubScreen
;
1343 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1345 } /* static LEGACY_ENTRY * AddLegacyEntry() */
1348 #ifdef __MAKEWITH_GNUEFI
1349 static VOID
ScanLegacyUEFI(IN UINTN DiskType
){}
1351 // default volume badge icon based on disk kind
1352 static EG_IMAGE
* GetDiskBadge(IN UINTN DiskType
) {
1353 EG_IMAGE
* Badge
= NULL
;
1357 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_INTERNAL
);
1360 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_EXTERNAL
);
1363 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_OPTICAL
);
1367 } // static EG_IMAGE * GetDiskBadge()
1370 Create a rEFInd boot option from a Legacy BIOS protocol option.
1372 static LEGACY_ENTRY
* AddLegacyEntryUEFI(BDS_COMMON_OPTION
*BdsOption
, IN UINT16 DiskType
)
1374 LEGACY_ENTRY
*Entry
, *SubEntry
;
1375 REFIT_MENU_SCREEN
*SubScreen
;
1376 CHAR16 ShortcutLetter
= 0;
1377 CHAR16
*LegacyDescription
= BdsOption
->Description
;
1379 // ScanVolume(Volume);
1381 // prepare the menu entry
1382 Entry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1383 Entry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1384 SPrint(Entry
->me
.Title
, 255, L
"Boot legacy target %s", LegacyDescription
);
1385 Entry
->me
.Tag
= TAG_LEGACY_UEFI
;
1387 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1388 Entry
->me
.Image
= LoadOSIcon(L
"legacy", L
"legacy", TRUE
);
1389 Entry
->LoadOptions
= (DiskType
== BBS_CDROM
) ? L
"CD" :
1390 ((DiskType
== BBS_USB
) ? L
"USB" : L
"HD");
1391 Entry
->me
.BadgeImage
= GetDiskBadge(DiskType
);
1392 // Entry->me.BadgeImage = Volume->VolBadgeImage;
1393 Entry
->BdsOption
= BdsOption
;
1394 Entry
->Enabled
= TRUE
;
1396 // create the submenu
1397 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
1398 SubScreen
->Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1399 SPrint(SubScreen
->Title
, 255, L
"No boot options for legacy target");
1400 SubScreen
->TitleImage
= Entry
->me
.Image
;
1401 SubScreen
->Hint1
= StrDuplicate(SUBSCREEN_HINT1
);
1402 if (GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_EDITOR
) {
1403 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2_NO_EDITOR
);
1405 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2
);
1409 SubEntry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1410 SubEntry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1411 SPrint(SubEntry
->me
.Title
, 255, L
"Boot %s", LegacyDescription
);
1412 SubEntry
->me
.Tag
= TAG_LEGACY_UEFI
;
1413 Entry
->BdsOption
= BdsOption
;
1414 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
1416 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
1417 Entry
->me
.SubScreen
= SubScreen
;
1418 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1420 } /* static LEGACY_ENTRY * AddLegacyEntryUEFI() */
1423 Scan for legacy BIOS targets on machines that implement EFI_LEGACY_BIOS_PROTOCOL.
1424 In testing, protocol has not been implemented on Macs but has been
1425 implemented on several Dell PCs and an ASUS motherboard.
1426 Restricts output to disks of the specified DiskType.
1428 static VOID
ScanLegacyUEFI(IN UINTN DiskType
)
1431 EFI_LEGACY_BIOS_PROTOCOL
*LegacyBios
;
1432 UINT16
*BootOrder
= NULL
;
1434 CHAR16 BootOption
[10];
1435 UINTN BootOrderSize
= 0;
1437 BDS_COMMON_OPTION
*BdsOption
;
1438 LIST_ENTRY TempList
;
1439 BBS_BBS_DEVICE_PATH
* BbsDevicePath
= NULL
;
1441 InitializeListHead (&TempList
);
1442 ZeroMem (Buffer
, sizeof (Buffer
));
1444 // If LegacyBios protocol is not implemented on this platform, then
1445 //we do not support this type of legacy boot on this machine.
1446 Status
= gBS
->LocateProtocol(&gEfiLegacyBootProtocolGuid
, NULL
, (VOID
**) &LegacyBios
);
1447 if (EFI_ERROR (Status
))
1450 // Grab the boot order
1451 BootOrder
= BdsLibGetVariableAndSize(L
"BootOrder", &gEfiGlobalVariableGuid
, &BootOrderSize
);
1452 if (BootOrder
== NULL
) {
1457 while (Index
< BootOrderSize
/ sizeof (UINT16
))
1459 // Grab each boot option variable from the boot order, and convert
1460 // the variable into a BDS boot option
1461 UnicodeSPrint (BootOption
, sizeof (BootOption
), L
"Boot%04x", BootOrder
[Index
]);
1462 BdsOption
= BdsLibVariableToOption (&TempList
, BootOption
);
1464 if (BdsOption
!= NULL
) {
1465 BbsDevicePath
= (BBS_BBS_DEVICE_PATH
*)BdsOption
->DevicePath
;
1467 // Only add the entry if it is of a requested type (e.g. USB, HD)
1469 // Two checks necessary because some systems return EFI boot loaders
1470 // with a DeviceType value that would inappropriately include them
1471 // as legacy loaders....
1472 if ((BbsDevicePath
->DeviceType
== DiskType
) && (BdsOption
->DevicePath
->Type
== DEVICE_TYPE_BIOS
)) {
1473 AddLegacyEntryUEFI(BdsOption
, BbsDevicePath
->DeviceType
);
1478 } /* static VOID ScanLegacyUEFI() */
1479 #endif // __MAKEWITH_GNUEFI
1481 static VOID
ScanLegacyVolume(REFIT_VOLUME
*Volume
, UINTN VolumeIndex
) {
1483 BOOLEAN ShowVolume
, HideIfOthersFound
;
1486 HideIfOthersFound
= FALSE
;
1487 if (Volume
->IsAppleLegacy
) {
1489 HideIfOthersFound
= TRUE
;
1490 } else if (Volume
->HasBootCode
) {
1492 if (Volume
->BlockIO
== Volume
->WholeDiskBlockIO
&&
1493 Volume
->BlockIOOffset
== 0 &&
1494 Volume
->OSName
== NULL
)
1495 // this is a whole disk (MBR) entry; hide if we have entries for partitions
1496 HideIfOthersFound
= TRUE
;
1498 if (HideIfOthersFound
) {
1499 // check for other bootable entries on the same disk
1500 for (VolumeIndex2
= 0; VolumeIndex2
< VolumesCount
; VolumeIndex2
++) {
1501 if (VolumeIndex2
!= VolumeIndex
&& Volumes
[VolumeIndex2
]->HasBootCode
&&
1502 Volumes
[VolumeIndex2
]->WholeDiskBlockIO
== Volume
->WholeDiskBlockIO
)
1508 AddLegacyEntry(NULL
, Volume
);
1509 } // static VOID ScanLegacyVolume()
1511 // Scan attached optical discs for legacy (BIOS) boot code
1512 // and add anything found to the list....
1513 static VOID
ScanLegacyDisc(VOID
)
1516 REFIT_VOLUME
*Volume
;
1518 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
) {
1519 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1520 Volume
= Volumes
[VolumeIndex
];
1521 if (Volume
->DiskKind
== DISK_KIND_OPTICAL
)
1522 ScanLegacyVolume(Volume
, VolumeIndex
);
1524 } else if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
1525 ScanLegacyUEFI(BBS_CDROM
);
1527 } /* static VOID ScanLegacyDisc() */
1529 // Scan internal hard disks for legacy (BIOS) boot code
1530 // and add anything found to the list....
1531 static VOID
ScanLegacyInternal(VOID
)
1534 REFIT_VOLUME
*Volume
;
1536 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
) {
1537 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1538 Volume
= Volumes
[VolumeIndex
];
1539 if (Volume
->DiskKind
== DISK_KIND_INTERNAL
)
1540 ScanLegacyVolume(Volume
, VolumeIndex
);
1542 } else if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
1543 ScanLegacyUEFI(BBS_HARDDISK
);
1545 } /* static VOID ScanLegacyInternal() */
1547 // Scan external disks for legacy (BIOS) boot code
1548 // and add anything found to the list....
1549 static VOID
ScanLegacyExternal(VOID
)
1552 REFIT_VOLUME
*Volume
;
1554 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
) {
1555 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1556 Volume
= Volumes
[VolumeIndex
];
1557 if (Volume
->DiskKind
== DISK_KIND_EXTERNAL
)
1558 ScanLegacyVolume(Volume
, VolumeIndex
);
1560 } else if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
1561 ScanLegacyUEFI(BBS_USB
);
1563 } /* static VOID ScanLegacyExternal() */
1566 // pre-boot tool functions
1569 static VOID
StartTool(IN LOADER_ENTRY
*Entry
)
1571 BeginExternalScreen(Entry
->UseGraphicsMode
, Entry
->me
.Title
+ 6); // assumes "Start <title>" as assigned below
1572 StartEFIImage(Entry
->DevicePath
, Entry
->LoadOptions
, Basename(Entry
->LoaderPath
),
1573 Basename(Entry
->LoaderPath
), Entry
->OSType
, NULL
, TRUE
);
1574 FinishExternalScreen();
1575 } /* static VOID StartTool() */
1577 static LOADER_ENTRY
* AddToolEntry(EFI_HANDLE DeviceHandle
, IN CHAR16
*LoaderPath
, IN CHAR16
*LoaderTitle
, IN EG_IMAGE
*Image
,
1578 IN CHAR16 ShortcutLetter
, IN BOOLEAN UseGraphicsMode
)
1580 LOADER_ENTRY
*Entry
;
1581 CHAR16
*TitleStr
= NULL
;
1583 Entry
= AllocateZeroPool(sizeof(LOADER_ENTRY
));
1585 MergeStrings(&TitleStr
, L
"Start ", 0);
1586 MergeStrings(&TitleStr
, LoaderTitle
, 0);
1587 Entry
->me
.Title
= TitleStr
;
1588 Entry
->me
.Tag
= TAG_TOOL
;
1590 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1591 Entry
->me
.Image
= Image
;
1592 Entry
->LoaderPath
= (LoaderPath
) ? StrDuplicate(LoaderPath
) : NULL
;
1593 Entry
->DevicePath
= FileDevicePath(DeviceHandle
, Entry
->LoaderPath
);
1594 Entry
->UseGraphicsMode
= UseGraphicsMode
;
1596 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1598 } /* static LOADER_ENTRY * AddToolEntry() */
1601 // pre-boot driver functions
1604 static UINTN
ScanDriverDir(IN CHAR16
*Path
)
1607 REFIT_DIR_ITER DirIter
;
1609 EFI_FILE_INFO
*DirEntry
;
1610 CHAR16 FileName
[256];
1612 CleanUpPathNameSlashes(Path
);
1613 // look through contents of the directory
1614 DirIterOpen(SelfRootDir
, Path
, &DirIter
);
1615 while (DirIterNext(&DirIter
, 2, LOADER_MATCH_PATTERNS
, &DirEntry
)) {
1616 if (DirEntry
->FileName
[0] == '.')
1617 continue; // skip this
1619 SPrint(FileName
, 255, L
"%s\\%s", Path
, DirEntry
->FileName
);
1621 Status
= StartEFIImage(FileDevicePath(SelfLoadedImage
->DeviceHandle
, FileName
),
1622 L
"", DirEntry
->FileName
, DirEntry
->FileName
, 0, NULL
, FALSE
);
1624 Status
= DirIterClose(&DirIter
);
1625 if (Status
!= EFI_NOT_FOUND
) {
1626 SPrint(FileName
, 255, L
"while scanning the %s directory", Path
);
1627 CheckError(Status
, FileName
);
1632 #ifdef __MAKEWITH_GNUEFI
1633 static EFI_STATUS
ConnectAllDriversToAllControllers(VOID
)
1636 UINTN AllHandleCount
;
1637 EFI_HANDLE
*AllHandleBuffer
;
1640 EFI_HANDLE
*HandleBuffer
;
1646 Status
= LibLocateHandle(AllHandles
,
1651 if (EFI_ERROR(Status
))
1654 for (Index
= 0; Index
< AllHandleCount
; Index
++) {
1656 // Scan the handle database
1658 Status
= LibScanHandleDatabase(NULL
,
1660 AllHandleBuffer
[Index
],
1665 if (EFI_ERROR (Status
))
1669 if (HandleType
[Index
] & EFI_HANDLE_TYPE_DRIVER_BINDING_HANDLE
)
1671 if (HandleType
[Index
] & EFI_HANDLE_TYPE_IMAGE_HANDLE
)
1676 for (HandleIndex
= 0; HandleIndex
< HandleCount
; HandleIndex
++) {
1677 if (HandleType
[HandleIndex
] & EFI_HANDLE_TYPE_PARENT_HANDLE
)
1682 if (HandleType
[Index
] & EFI_HANDLE_TYPE_DEVICE_HANDLE
) {
1683 Status
= refit_call4_wrapper(BS
->ConnectController
,
1684 AllHandleBuffer
[Index
],
1692 MyFreePool (HandleBuffer
);
1693 MyFreePool (HandleType
);
1697 MyFreePool (AllHandleBuffer
);
1699 } /* EFI_STATUS ConnectAllDriversToAllControllers() */
1701 static EFI_STATUS
ConnectAllDriversToAllControllers(VOID
) {
1702 BdsLibConnectAllDriversToAllControllers();
1707 // Load all EFI drivers from rEFInd's "drivers" subdirectory and from the
1708 // directories specified by the user in the "scan_driver_dirs" configuration
1710 static VOID
LoadDrivers(VOID
)
1712 CHAR16
*Directory
, *SelfDirectory
;
1713 UINTN i
= 0, Length
, NumFound
= 0;
1715 // load drivers from the subdirectories of rEFInd's home directory specified
1716 // in the DRIVER_DIRS constant.
1717 while ((Directory
= FindCommaDelimited(DRIVER_DIRS
, i
++)) != NULL
) {
1718 SelfDirectory
= SelfDirPath
? StrDuplicate(SelfDirPath
) : NULL
;
1719 CleanUpPathNameSlashes(SelfDirectory
);
1720 MergeStrings(&SelfDirectory
, Directory
, L
'\\');
1721 NumFound
+= ScanDriverDir(SelfDirectory
);
1722 MyFreePool(Directory
);
1723 MyFreePool(SelfDirectory
);
1726 // Scan additional user-specified driver directories....
1728 while ((Directory
= FindCommaDelimited(GlobalConfig
.DriverDirs
, i
++)) != NULL
) {
1729 CleanUpPathNameSlashes(Directory
);
1730 Length
= StrLen(Directory
);
1732 NumFound
+= ScanDriverDir(Directory
);
1734 MyFreePool(Directory
);
1737 // connect all devices
1739 ConnectAllDriversToAllControllers();
1740 } /* static VOID LoadDrivers() */
1742 // Determine what (if any) type of legacy (BIOS) boot support is available
1743 static VOID
FindLegacyBootType(VOID
) {
1744 #ifdef __MAKEWITH_TIANO
1746 EFI_LEGACY_BIOS_PROTOCOL
*LegacyBios
;
1749 GlobalConfig
.LegacyType
= LEGACY_TYPE_NONE
;
1751 // UEFI-style legacy BIOS support is available only with the TianoCore EDK2
1752 // build environment, and then only with some EFI implementations....
1753 #ifdef __MAKEWITH_TIANO
1754 Status
= gBS
->LocateProtocol (&gEfiLegacyBootProtocolGuid
, NULL
, (VOID
**) &LegacyBios
);
1755 if (!EFI_ERROR (Status
))
1756 GlobalConfig
.LegacyType
= LEGACY_TYPE_UEFI
;
1759 // Macs have their own system. If the firmware vendor code contains the
1760 // string "Apple", assume it's available. Note that this overrides the
1761 // UEFI type, and might yield false positives if the vendor string
1762 // contains "Apple" as part of something bigger, so this isn't 100%
1764 if (StriSubCmp(L
"Apple", ST
->FirmwareVendor
))
1765 GlobalConfig
.LegacyType
= LEGACY_TYPE_MAC
;
1766 } // static VOID FindLegacyBootType
1768 // Warn the user if legacy OS scans are enabled but the firmware or this
1769 // application can't support them....
1770 static VOID
WarnIfLegacyProblems() {
1771 BOOLEAN found
= FALSE
;
1774 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_NONE
) {
1776 if (GlobalConfig
.ScanFor
[i
] == 'h' || GlobalConfig
.ScanFor
[i
] == 'b' || GlobalConfig
.ScanFor
[i
] == 'c')
1779 } while ((i
< NUM_SCAN_OPTIONS
) && (!found
));
1781 Print(L
"NOTE: refind.conf's 'scanfor' line specifies scanning for one or more legacy\n");
1782 Print(L
"(BIOS) boot options; however, this is not possible because ");
1783 #ifdef __MAKEWITH_TIANO
1784 Print(L
"your computer lacks\n");
1785 Print(L
"the necessary Compatibility Support Module (CSM) support.\n");
1787 Print(L
"this program was\n");
1788 Print(L
"compiled without the necessary support. Please visit\n");
1789 Print(L
"http://www.rodsbooks.com/refind/getting.html and download and install a rEFInd\n");
1790 Print(L
"binary built with the TianoCore EDK2 to enable legacy boot support.\n");
1794 } // if no legacy support
1795 } // static VOID WarnIfLegacyProblems()
1797 // Locates boot loaders. NOTE: This assumes that GlobalConfig.LegacyType is set correctly.
1798 static VOID
ScanForBootloaders(VOID
) {
1803 // scan for loaders and tools, add them to the menu
1804 for (i
= 0; i
< NUM_SCAN_OPTIONS
; i
++) {
1805 switch(GlobalConfig
.ScanFor
[i
]) {
1810 ScanLegacyInternal();
1813 ScanLegacyExternal();
1816 ScanUserConfigured();
1830 // assign shortcut keys
1831 for (i
= 0; i
< MainMenu
.EntryCount
&& MainMenu
.Entries
[i
]->Row
== 0 && i
< 9; i
++)
1832 MainMenu
.Entries
[i
]->ShortcutDigit
= (CHAR16
)('1' + i
);
1834 // wait for user ACK when there were errors
1835 FinishTextScreen(FALSE
);
1836 } // static VOID ScanForBootloaders()
1838 // Add the second-row tags containing built-in and external tools (EFI shell,
1840 static VOID
ScanForTools(VOID
) {
1841 CHAR16
*FileName
= NULL
, Description
[256];
1842 REFIT_MENU_ENTRY
*TempMenuEntry
;
1843 UINTN i
, j
, VolumeIndex
;
1845 for (i
= 0; i
< NUM_TOOLS
; i
++) {
1846 switch(GlobalConfig
.ShowTools
[i
]) {
1848 TempMenuEntry
= CopyMenuEntry(&MenuEntryShutdown
);
1849 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_SHUTDOWN
);
1850 AddMenuEntry(&MainMenu
, TempMenuEntry
);
1853 TempMenuEntry
= CopyMenuEntry(&MenuEntryReset
);
1854 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_RESET
);
1855 AddMenuEntry(&MainMenu
, TempMenuEntry
);
1858 TempMenuEntry
= CopyMenuEntry(&MenuEntryAbout
);
1859 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT
);
1860 AddMenuEntry(&MainMenu
, TempMenuEntry
);
1863 TempMenuEntry
= CopyMenuEntry(&MenuEntryExit
);
1864 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_EXIT
);
1865 AddMenuEntry(&MainMenu
, TempMenuEntry
);
1869 MyFreePool(FileName
);
1870 while ((FileName
= FindCommaDelimited(SHELL_NAMES
, j
++)) != NULL
) {
1871 if (FileExists(SelfRootDir
, FileName
)) {
1872 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, L
"EFI Shell", BuiltinIcon(BUILTIN_ICON_TOOL_SHELL
),
1878 MyFreePool(FileName
);
1879 FileName
= StrDuplicate(L
"\\efi\\tools\\gptsync.efi");
1880 // MergeStrings(&FileName, L"\\efi\\tools\\gptsync.efi", 0);
1881 if (FileExists(SelfRootDir
, FileName
)) {
1882 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, L
"Make Hybrid MBR", BuiltinIcon(BUILTIN_ICON_TOOL_PART
), 'P', FALSE
);
1885 case TAG_APPLE_RECOVERY
:
1886 MyFreePool(FileName
);
1887 FileName
= StrDuplicate(L
"\\com.apple.recovery.boot\\boot.efi");
1888 // MergeStrings(&FileName, L"\\com.apple.recovery.boot\\boot.efi", 0);
1889 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1890 if ((Volumes
[VolumeIndex
]->RootDir
!= NULL
) && (FileExists(Volumes
[VolumeIndex
]->RootDir
, FileName
))) {
1891 SPrint(Description
, 255, L
"Apple Recovery on %s", Volumes
[VolumeIndex
]->VolName
);
1892 AddToolEntry(Volumes
[VolumeIndex
]->DeviceHandle
, FileName
, Description
,
1893 BuiltinIcon(BUILTIN_ICON_TOOL_APPLE_RESCUE
), 'R', TRUE
);
1899 MyFreePool(FileName
);
1900 while ((FileName
= FindCommaDelimited(MOK_NAMES
, j
++)) != NULL
) {
1901 if (FileExists(SelfRootDir
, FileName
)) {
1902 SPrint(Description
, 255, L
"MOK Key Manager at %s", FileName
);
1903 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, Description
,
1904 BuiltinIcon(BUILTIN_ICON_TOOL_MOK_TOOL
), 'S', FALSE
);
1907 if (FileExists(SelfDir
, L
"MokManager.efi")) {
1908 MyFreePool(FileName
);
1909 FileName
= StrDuplicate(SelfDirPath
);
1910 MergeStrings(&FileName
, L
"\\MokManager.efi", 0);
1911 SPrint(Description
, 255, L
"MOK Key Manager at %s", FileName
);
1912 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, Description
,
1913 BuiltinIcon(BUILTIN_ICON_TOOL_MOK_TOOL
), 'S', FALSE
);
1917 MyFreePool(FileName
);
1920 } // static VOID ScanForTools
1922 // Rescan for boot loaders
1923 VOID
RescanAll(VOID
) {
1930 egDisplayMessage(L
"Scanning for new boot loaders; please wait....", &BGColor
);
1931 FreeList((VOID
***) &(MainMenu
.Entries
), &MainMenu
.EntryCount
);
1932 MainMenu
.Entries
= NULL
;
1933 MainMenu
.EntryCount
= 0;
1934 ReadConfig(CONFIG_FILE_NAME
);
1935 ConnectAllDriversToAllControllers();
1937 ScanForBootloaders();
1940 } // VOID RescanAll()
1942 #ifdef __MAKEWITH_TIANO
1944 // Minimal initialization function
1945 static VOID
InitializeLib(IN EFI_HANDLE ImageHandle
, IN EFI_SYSTEM_TABLE
*SystemTable
) {
1947 // gImageHandle = ImageHandle;
1948 gBS
= SystemTable
->BootServices
;
1949 // gRS = SystemTable->RuntimeServices;
1950 gRT
= SystemTable
->RuntimeServices
; // Some BDS functions need gRT to be set
1951 EfiGetSystemConfigurationTable (&gEfiDxeServicesTableGuid
, (VOID
**) &gDS
);
1953 InitializeConsoleSim();
1958 // Set up our own Secure Boot extensions....
1959 // Returns TRUE on success, FALSE otherwise
1960 static BOOLEAN
SecureBootSetup(VOID
) {
1962 BOOLEAN Success
= FALSE
;
1964 if (secure_mode() && ShimLoaded()) {
1965 Status
= security_policy_install();
1966 if (Status
== EFI_SUCCESS
) {
1969 Print(L
"Failed to install MOK Secure Boot extensions");
1973 } // VOID SecureBootSetup()
1975 // Remove our own Secure Boot extensions....
1976 // Returns TRUE on success, FALSE otherwise
1977 static BOOLEAN
SecureBootUninstall(VOID
) {
1979 BOOLEAN Success
= TRUE
;
1981 if (secure_mode()) {
1982 Status
= security_policy_uninstall();
1983 if (Status
!= EFI_SUCCESS
) {
1985 BeginTextScreen(L
"Secure Boot Policy Failure");
1986 Print(L
"Failed to uninstall MOK Secure Boot extensions; forcing a reboot.");
1988 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);
1992 } // VOID SecureBootUninstall
1999 efi_main (EFI_HANDLE ImageHandle
, EFI_SYSTEM_TABLE
*SystemTable
)
2002 BOOLEAN MainLoopRunning
= TRUE
;
2003 BOOLEAN MokProtocol
;
2004 REFIT_MENU_ENTRY
*ChosenEntry
;
2010 InitializeLib(ImageHandle
, SystemTable
);
2011 Status
= InitRefitLib(ImageHandle
);
2012 if (EFI_ERROR(Status
))
2015 // read configuration
2016 CopyMem(GlobalConfig
.ScanFor
, "ieom ", NUM_SCAN_OPTIONS
);
2017 FindLegacyBootType();
2018 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
)
2019 CopyMem(GlobalConfig
.ScanFor
, "ihebocm ", NUM_SCAN_OPTIONS
);
2020 ReadConfig(CONFIG_FILE_NAME
);
2023 WarnIfLegacyProblems();
2024 MainMenu
.TimeoutSeconds
= GlobalConfig
.Timeout
;
2026 // disable EFI watchdog timer
2027 refit_call4_wrapper(BS
->SetWatchdogTimer
, 0x0000, 0x0000, 0x0000, NULL
);
2029 // further bootstrap (now with config available)
2031 MokProtocol
= SecureBootSetup();
2034 ScanForBootloaders();
2037 if (GlobalConfig
.ScanDelay
> 0) {
2042 egDisplayMessage(L
"Pausing before disk scan; please wait....", &BGColor
);
2043 for (i
= 0; i
< GlobalConfig
.ScanDelay
; i
++)
2044 refit_call1_wrapper(BS
->Stall
, 1000000);
2048 Selection
= StrDuplicate(GlobalConfig
.DefaultSelection
);
2049 while (MainLoopRunning
) {
2050 MenuExit
= RunMainMenu(&MainMenu
, Selection
, &ChosenEntry
);
2052 // The Escape key triggers a re-scan operation....
2053 if (MenuExit
== MENU_EXIT_ESCAPE
) {
2058 switch (ChosenEntry
->Tag
) {
2060 case TAG_REBOOT
: // Reboot
2062 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);
2063 MainLoopRunning
= FALSE
; // just in case we get this far
2066 case TAG_SHUTDOWN
: // Shut Down
2068 refit_call4_wrapper(RT
->ResetSystem
, EfiResetShutdown
, EFI_SUCCESS
, 0, NULL
);
2069 MainLoopRunning
= FALSE
; // just in case we get this far
2072 case TAG_ABOUT
: // About rEFInd
2076 case TAG_LOADER
: // Boot OS via .EFI loader
2077 StartLoader((LOADER_ENTRY
*)ChosenEntry
);
2080 case TAG_LEGACY
: // Boot legacy OS
2081 StartLegacy((LEGACY_ENTRY
*)ChosenEntry
);
2084 #ifdef __MAKEWITH_TIANO
2085 case TAG_LEGACY_UEFI
: // Boot a legacy OS on a non-Mac
2086 StartLegacyUEFI((LEGACY_ENTRY
*)ChosenEntry
);
2090 case TAG_TOOL
: // Start a EFI tool
2091 StartTool((LOADER_ENTRY
*)ChosenEntry
);
2094 case TAG_EXIT
: // Terminate rEFInd
2095 if ((MokProtocol
) && !SecureBootUninstall()) {
2096 MainLoopRunning
= FALSE
; // just in case we get this far
2098 BeginTextScreen(L
" ");
2104 MyFreePool(Selection
);
2105 Selection
= (ChosenEntry
->Title
) ? StrDuplicate(ChosenEntry
->Title
) : NULL
;
2108 // If we end up here, things have gone wrong. Try to reboot, and if that
2109 // fails, go into an endless loop.
2110 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);