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-2014 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 #ifndef EFI_SECURITY_VIOLATION
60 #define EFI_SECURITY_VIOLATION EFIERR (26)
63 #include "../EfiLib/BdsHelper.h"
64 #include "../EfiLib/legacy.h"
65 #endif // __MAKEWITH_GNUEFI
67 #ifndef EFI_OS_INDICATIONS_BOOT_TO_FW_UI
68 #define EFI_OS_INDICATIONS_BOOT_TO_FW_UI 0x0000000000000001ULL
74 #define MACOSX_LOADER_PATH L"System\\Library\\CoreServices\\boot.efi"
76 #define SHELL_NAMES L"\\EFI\\tools\\shell.efi,\\EFI\\tools\\shellx64.efi,\\shell.efi,\\shellx64.efi"
77 #define GPTSYNC_NAMES L"\\EFI\\tools\\gptsync.efi,\\EFI\\tools\\gptsync_x64.efi"
78 #define GDISK_NAMES L"\\EFI\\tools\\gdisk.efi,\\EFI\\tools\\gdisk_x64.efi"
79 #define MEMTEST_NAMES L"memtest86.efi,memtest86_x64.efi,memtest86x64.efi,bootx64.efi"
80 #define DRIVER_DIRS L"drivers,drivers_x64"
81 #define FALLBACK_FULLNAME L"EFI\\BOOT\\bootx64.efi"
82 #define FALLBACK_BASENAME L"bootx64.efi"
83 #define EFI_STUB_ARCH 0x8664
85 #define SHELL_NAMES L"\\EFI\\tools\\shell.efi,\\EFI\\tools\\shellia32.efi,\\shell.efi,\\shellia32.efi"
86 #define GPTSYNC_NAMES L"\\EFI\\tools\\gptsync.efi,\\EFI\\tools\\gptsync_ia32.efi"
87 #define GDISK_NAMES L"\\EFI\\tools\\gdisk.efi,\\EFI\\tools\\gdisk_ia32.efi"
88 #define MEMTEST_NAMES L"memtest86.efi,memtest86_ia32.efi,memtest86ia32.efi,bootia32.efi"
89 #define DRIVER_DIRS L"drivers,drivers_ia32"
90 #define FALLBACK_FULLNAME L"EFI\\BOOT\\bootia32.efi"
91 #define FALLBACK_BASENAME L"bootia32.efi"
92 #define EFI_STUB_ARCH 0x014c
94 #define SHELL_NAMES L"\\EFI\\tools\\shell.efi,\\shell.efi"
95 #define GPTSYNC_NAMES L"\\EFI\\tools\\gptsync.efi"
96 #define GDISK_NAMES L"\\EFI\\tools\\gdisk.efi"
97 #define MEMTEST_NAMES L"memtest86.efi"
98 #define DRIVER_DIRS L"drivers"
99 #define FALLBACK_FULLNAME L"EFI\\BOOT\\boot.efi" /* Not really correct */
100 #define FALLBACK_BASENAME L"boot.efi" /* Not really correct */
102 #define FAT_ARCH 0x0ef1fab9 /* ID for Apple "fat" binary */
104 // Filename patterns that identify EFI boot loaders. Note that a single case (either L"*.efi" or
105 // L"*.EFI") is fine for most systems; but Gigabyte's buggy Hybrid EFI does a case-sensitive
106 // comparison when it should do a case-insensitive comparison, so I'm doubling this up. It does
107 // no harm on other computers, AFAIK. In theory, every case variation should be done for
108 // completeness, but that's ridiculous....
109 #define LOADER_MATCH_PATTERNS L"*.efi,*.EFI"
111 // Patterns that identify Linux kernels. Added to the loader match pattern when the
112 // scan_all_linux_kernels option is set in the configuration file. Causes kernels WITHOUT
113 // a ".efi" extension to be found when scanning for boot loaders.
114 #define LINUX_MATCH_PATTERNS L"vmlinuz*,bzImage*"
116 // Default hint text for program-launch submenus
117 #define SUBSCREEN_HINT1 L"Use arrow keys to move cursor; Enter to boot;"
118 #define SUBSCREEN_HINT2 L"Insert or F2 to edit options; Esc to return to main menu"
119 #define SUBSCREEN_HINT2_NO_EDITOR L"Esc to return to main menu"
123 #define TYPE_LEGACY 2
125 static REFIT_MENU_ENTRY MenuEntryAbout
= { L
"About rEFInd", TAG_ABOUT
, 1, 0, 'A', NULL
, NULL
, NULL
};
126 static REFIT_MENU_ENTRY MenuEntryReset
= { L
"Reboot Computer", TAG_REBOOT
, 1, 0, 'R', NULL
, NULL
, NULL
};
127 static REFIT_MENU_ENTRY MenuEntryShutdown
= { L
"Shut Down Computer", TAG_SHUTDOWN
, 1, 0, 'U', NULL
, NULL
, NULL
};
128 static REFIT_MENU_ENTRY MenuEntryReturn
= { L
"Return to Main Menu", TAG_RETURN
, 1, 0, 0, NULL
, NULL
, NULL
};
129 static REFIT_MENU_ENTRY MenuEntryExit
= { L
"Exit rEFInd", TAG_EXIT
, 1, 0, 0, NULL
, NULL
, NULL
};
130 static REFIT_MENU_ENTRY MenuEntryFirmware
= { L
"Reboot to Computer Setup Utility", TAG_FIRMWARE
, 1, 0, 0, NULL
, NULL
, NULL
};
132 static REFIT_MENU_SCREEN MainMenu
= { L
"Main Menu", NULL
, 0, NULL
, 0, NULL
, 0, L
"Automatic boot",
133 L
"Use arrow keys to move cursor; Enter to boot;",
134 L
"Insert or F2 for more options; Esc to refresh" };
135 static REFIT_MENU_SCREEN AboutMenu
= { L
"About", NULL
, 0, NULL
, 0, NULL
, 0, NULL
, L
"Press Enter to return to main menu", L
"" };
137 REFIT_CONFIG GlobalConfig
= { FALSE
, FALSE
, 0, 0, 0, DONT_CHANGE_TEXT_MODE
, 20, 0, 0, GRAPHICS_FOR_OSX
, LEGACY_TYPE_MAC
, 0, 0,
138 { DEFAULT_BIG_ICON_SIZE
/ 4, DEFAULT_SMALL_ICON_SIZE
, DEFAULT_BIG_ICON_SIZE
}, BANNER_NOSCALE
,
139 NULL
, NULL
, CONFIG_FILE_NAME
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
140 { TAG_SHELL
, TAG_MEMTEST
, TAG_GDISK
, TAG_APPLE_RECOVERY
, TAG_WINDOWS_RECOVERY
, TAG_MOK_TOOL
,
141 TAG_ABOUT
, TAG_SHUTDOWN
, TAG_REBOOT
, TAG_FIRMWARE
, 0, 0, 0, 0, 0, 0 }
144 EFI_GUID GlobalGuid
= EFI_GLOBAL_VARIABLE
;
146 // Structure used to hold boot loader filenames and time stamps in
147 // a linked list; used to sort entries within a directory.
151 struct LOADER_LIST
*NextEntry
;
158 static VOID
AboutrEFInd(VOID
)
160 if (AboutMenu
.EntryCount
== 0) {
161 AboutMenu
.TitleImage
= BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT
);
162 AddMenuInfoLine(&AboutMenu
, L
"rEFInd Version 0.7.8.6");
163 AddMenuInfoLine(&AboutMenu
, L
"");
164 AddMenuInfoLine(&AboutMenu
, L
"Copyright (c) 2006-2010 Christoph Pfisterer");
165 AddMenuInfoLine(&AboutMenu
, L
"Copyright (c) 2012-2014 Roderick W. Smith");
166 AddMenuInfoLine(&AboutMenu
, L
"Portions Copyright (c) Intel Corporation and others");
167 AddMenuInfoLine(&AboutMenu
, L
"Distributed under the terms of the GNU GPLv3 license");
168 AddMenuInfoLine(&AboutMenu
, L
"");
169 AddMenuInfoLine(&AboutMenu
, L
"Running on:");
170 AddMenuInfoLine(&AboutMenu
, PoolPrint(L
" EFI Revision %d.%02d", ST
->Hdr
.Revision
>> 16, ST
->Hdr
.Revision
& ((1 << 16) - 1)));
172 AddMenuInfoLine(&AboutMenu
, L
" Platform: x86 (32 bit)");
173 #elif defined(EFIX64)
174 AddMenuInfoLine(&AboutMenu
, PoolPrint(L
" Platform: x86_64 (64 bit); Secure Boot %s",
175 secure_mode() ? L
"active" : L
"inactive"));
177 AddMenuInfoLine(&AboutMenu
, L
" Platform: unknown");
179 AddMenuInfoLine(&AboutMenu
, PoolPrint(L
" Firmware: %s %d.%02d", ST
->FirmwareVendor
, ST
->FirmwareRevision
>> 16,
180 ST
->FirmwareRevision
& ((1 << 16) - 1)));
181 AddMenuInfoLine(&AboutMenu
, PoolPrint(L
" Screen Output: %s", egScreenDescription()));
182 AddMenuInfoLine(&AboutMenu
, L
"");
183 #if defined(__MAKEWITH_GNUEFI)
184 AddMenuInfoLine(&AboutMenu
, L
"Built with GNU-EFI");
186 AddMenuInfoLine(&AboutMenu
, L
"Built with TianoCore EDK2");
188 AddMenuInfoLine(&AboutMenu
, L
"");
189 AddMenuInfoLine(&AboutMenu
, L
"For more information, see the rEFInd Web site:");
190 AddMenuInfoLine(&AboutMenu
, L
"http://www.rodsbooks.com/refind/");
191 AddMenuEntry(&AboutMenu
, &MenuEntryReturn
);
194 RunMenu(&AboutMenu
, NULL
);
195 } /* VOID AboutrEFInd() */
197 static VOID
WarnSecureBootError(CHAR16
*Name
, BOOLEAN Verbose
) {
199 Name
= L
"the loader";
201 refit_call2_wrapper(ST
->ConOut
->SetAttribute
, ST
->ConOut
, ATTR_ERROR
);
202 Print(L
"Secure Boot validation failure loading %s!\n", Name
);
203 refit_call2_wrapper(ST
->ConOut
->SetAttribute
, ST
->ConOut
, ATTR_BASIC
);
204 if (Verbose
&& secure_mode()) {
205 Print(L
"\nThis computer is configured with Secure Boot active, but\n%s has failed validation.\n", Name
);
206 Print(L
"\nYou can:\n * Launch another boot loader\n");
207 Print(L
" * Disable Secure Boot in your firmware\n");
208 Print(L
" * Sign %s with a machine owner key (MOK)\n", Name
);
209 Print(L
" * Use a MOK utility (often present on the second row) to add a MOK with which\n");
210 Print(L
" %s has already been signed.\n", Name
);
211 Print(L
" * Use a MOK utility to register %s (\"enroll its hash\") without\n", Name
);
212 Print(L
" signing it.\n");
213 Print(L
"\nSee http://www.rodsbooks.com/refind/secureboot.html for more information\n");
216 } // VOID WarnSecureBootError()
218 // Returns TRUE if this file is a valid EFI loader file, and is proper ARCH
219 static BOOLEAN
IsValidLoader(EFI_FILE
*RootDir
, CHAR16
*FileName
) {
220 BOOLEAN IsValid
= TRUE
;
221 #if defined (EFIX64) | defined (EFI32)
223 EFI_FILE_HANDLE FileHandle
;
225 UINTN Size
= sizeof(Header
);
227 if ((RootDir
== NULL
) || (FileName
== NULL
)) {
228 // Assume valid here, because Macs produce NULL RootDir (& maybe FileName)
229 // when launching from a Firewire drive. This should be handled better, but
230 // fix would have to be in StartEFIImageList() and/or in FindVolumeAndFilename().
234 Status
= refit_call5_wrapper(RootDir
->Open
, RootDir
, &FileHandle
, FileName
, EFI_FILE_MODE_READ
, 0);
235 if (EFI_ERROR(Status
))
238 Status
= refit_call3_wrapper(FileHandle
->Read
, FileHandle
, &Size
, Header
);
239 refit_call1_wrapper(FileHandle
->Close
, FileHandle
);
241 IsValid
= !EFI_ERROR(Status
) &&
242 Size
== sizeof(Header
) &&
243 ((Header
[0] == 'M' && Header
[1] == 'Z' &&
244 (Size
= *(UINT32
*)&Header
[0x3c]) < 0x180 &&
245 Header
[Size
] == 'P' && Header
[Size
+1] == 'E' &&
246 Header
[Size
+2] == 0 && Header
[Size
+3] == 0 &&
247 *(UINT16
*)&Header
[Size
+4] == EFI_STUB_ARCH
) ||
248 (*(UINT32
*)&Header
== FAT_ARCH
));
251 } // BOOLEAN IsValidLoader()
253 // Launch an EFI binary.
254 static EFI_STATUS
StartEFIImageList(IN EFI_DEVICE_PATH
**DevicePaths
,
255 IN CHAR16
*LoadOptions
, IN UINTN LoaderType
,
256 IN CHAR16
*ImageTitle
, IN CHAR8 OSType
,
257 OUT UINTN
*ErrorInStep
,
260 EFI_STATUS Status
, ReturnStatus
;
261 EFI_HANDLE ChildImageHandle
;
262 EFI_LOADED_IMAGE
*ChildLoadedImage
= NULL
;
263 REFIT_VOLUME
*Volume
= NULL
;
264 UINTN DevicePathIndex
;
265 CHAR16 ErrorInfo
[256];
266 CHAR16
*FullLoadOptions
= NULL
;
267 CHAR16
*Filename
= NULL
;
270 if (ErrorInStep
!= NULL
)
274 if (LoadOptions
!= NULL
) {
275 FullLoadOptions
= StrDuplicate(LoadOptions
);
276 if ((LoaderType
== TYPE_EFI
) && (OSType
== 'M')) {
277 MergeStrings(&FullLoadOptions
, L
" ", 0);
278 // NOTE: That last space is also added by the EFI shell and seems to be significant
279 // when passing options to Apple's boot.efi...
281 } // if (LoadOptions != NULL)
283 Print(L
"Starting %s\nUsing load options '%s'\n", ImageTitle
, FullLoadOptions
? FullLoadOptions
: L
"");
285 // load the image into memory (and execute it, in the case of a shim/MOK image).
286 ReturnStatus
= Status
= EFI_NOT_FOUND
; // in case the list is empty
287 for (DevicePathIndex
= 0; DevicePaths
[DevicePathIndex
] != NULL
; DevicePathIndex
++) {
288 FindVolumeAndFilename(DevicePaths
[DevicePathIndex
], &Volume
, &Filename
);
289 // Some EFIs crash if attempting to load driver for invalid architecture, so
290 // protect for this condition; but sometimes Volume comes back NULL, so provide
291 // an exception. (TODO: Handle this special condition better.)
292 if ((LoaderType
== TYPE_LEGACY
) || (Volume
== NULL
) || IsValidLoader(Volume
->RootDir
, Filename
)) {
293 if (Filename
&& (LoaderType
!= TYPE_LEGACY
)) {
294 Temp
= PoolPrint(L
"\\%s %s", Filename
, FullLoadOptions
? FullLoadOptions
: L
"");
296 MyFreePool(FullLoadOptions
);
297 FullLoadOptions
= Temp
;
301 // NOTE: Below commented-out line could be more efficient if file were read ahead of
302 // time and passed as a pre-loaded image to LoadImage(), but it doesn't work on my
303 // 32-bit Mac Mini or my 64-bit Intel box when launching a Linux kernel; the
304 // kernel returns a "Failed to handle fs_proto" error message.
305 // TODO: Track down the cause of this error and fix it, if possible.
306 // ReturnStatus = Status = refit_call6_wrapper(BS->LoadImage, FALSE, SelfImageHandle, DevicePaths[DevicePathIndex],
307 // ImageData, ImageSize, &ChildImageHandle);
308 ReturnStatus
= Status
= refit_call6_wrapper(BS
->LoadImage
, FALSE
, SelfImageHandle
, DevicePaths
[DevicePathIndex
],
309 NULL
, 0, &ChildImageHandle
);
311 Print(L
"Invalid loader file!\n");
312 ReturnStatus
= EFI_LOAD_ERROR
;
314 if (ReturnStatus
!= EFI_NOT_FOUND
) {
318 if ((Status
== EFI_ACCESS_DENIED
) || (Status
== EFI_SECURITY_VIOLATION
)) {
319 WarnSecureBootError(ImageTitle
, Verbose
);
322 SPrint(ErrorInfo
, 255, L
"while loading %s", ImageTitle
);
323 if (CheckError(Status
, ErrorInfo
)) {
324 if (ErrorInStep
!= NULL
)
329 ReturnStatus
= Status
= refit_call3_wrapper(BS
->HandleProtocol
, ChildImageHandle
, &LoadedImageProtocol
,
330 (VOID
**) &ChildLoadedImage
);
331 if (CheckError(Status
, L
"while getting a LoadedImageProtocol handle")) {
332 if (ErrorInStep
!= NULL
)
336 ChildLoadedImage
->LoadOptions
= (VOID
*)FullLoadOptions
;
337 ChildLoadedImage
->LoadOptionsSize
= FullLoadOptions
? ((UINT32
)StrLen(FullLoadOptions
) + 1) * sizeof(CHAR16
) : 0;
338 // turn control over to the image
339 // TODO: (optionally) re-enable the EFI watchdog timer!
341 // close open file handles
343 ReturnStatus
= Status
= refit_call3_wrapper(BS
->StartImage
, ChildImageHandle
, NULL
, NULL
);
345 // control returns here when the child image calls Exit()
346 SPrint(ErrorInfo
, 255, L
"returned from %s", ImageTitle
);
347 if (CheckError(Status
, ErrorInfo
)) {
348 if (ErrorInStep
!= NULL
)
352 // re-open file handles
356 // unload the image, we don't care if it works or not...
357 Status
= refit_call1_wrapper(BS
->UnloadImage
, ChildImageHandle
);
360 MyFreePool(FullLoadOptions
);
362 } /* static EFI_STATUS StartEFIImageList() */
364 static EFI_STATUS
StartEFIImage(IN EFI_DEVICE_PATH
*DevicePath
,
365 IN CHAR16
*LoadOptions
, IN UINTN LoaderType
,
366 IN CHAR16
*ImageTitle
, IN CHAR8 OSType
,
367 OUT UINTN
*ErrorInStep
,
370 EFI_DEVICE_PATH
*DevicePaths
[2];
372 DevicePaths
[0] = DevicePath
;
373 DevicePaths
[1] = NULL
;
374 return StartEFIImageList(DevicePaths
, LoadOptions
, LoaderType
, ImageTitle
, OSType
, ErrorInStep
, Verbose
);
375 } /* static EFI_STATUS StartEFIImage() */
377 // From gummiboot: Retrieve a raw EFI variable.
378 // Returns EFI status
379 static EFI_STATUS
EfivarGetRaw(EFI_GUID
*vendor
, CHAR16
*name
, CHAR8
**buffer
, UINTN
*size
) {
384 l
= sizeof(CHAR16
*) * EFI_MAXIMUM_VARIABLE_SIZE
;
385 buf
= AllocatePool(l
);
387 return EFI_OUT_OF_RESOURCES
;
389 err
= refit_call5_wrapper(RT
->GetVariable
, name
, vendor
, NULL
, &l
, buf
);
390 if (EFI_ERROR(err
) == EFI_SUCCESS
) {
397 } // EFI_STATUS EfivarGetRaw()
399 // From gummiboot: Set an EFI variable
400 static EFI_STATUS
EfivarSetRaw(EFI_GUID
*vendor
, CHAR16
*name
, CHAR8
*buf
, UINTN size
, BOOLEAN persistent
) {
403 flags
= EFI_VARIABLE_BOOTSERVICE_ACCESS
|EFI_VARIABLE_RUNTIME_ACCESS
;
405 flags
|= EFI_VARIABLE_NON_VOLATILE
;
407 return refit_call5_wrapper(RT
->SetVariable
, name
, vendor
, flags
, size
, buf
);
408 } // EFI_STATUS EfivarSetRaw()
410 // From gummiboot: Reboot the computer into its built-in user interface
411 static EFI_STATUS
RebootIntoFirmware(VOID
) {
417 osind
= EFI_OS_INDICATIONS_BOOT_TO_FW_UI
;
419 err
= EfivarGetRaw(&GlobalGuid
, L
"OsIndications", &b
, &size
);
420 if (err
== EFI_SUCCESS
)
424 err
= EfivarSetRaw(&GlobalGuid
, L
"OsIndications", (CHAR8
*)&osind
, sizeof(UINT64
), TRUE
);
425 if (err
!= EFI_SUCCESS
)
428 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);
429 Print(L
"Error calling ResetSystem: %r", err
);
436 // EFI OS loader functions
439 static VOID
StartLoader(LOADER_ENTRY
*Entry
)
441 UINTN ErrorInStep
= 0;
443 BeginExternalScreen(Entry
->UseGraphicsMode
, L
"Booting OS");
444 StartEFIImage(Entry
->DevicePath
, Entry
->LoadOptions
, TYPE_EFI
,
445 Basename(Entry
->LoaderPath
), Entry
->OSType
, &ErrorInStep
, !Entry
->UseGraphicsMode
);
446 FinishExternalScreen();
449 // Locate an initrd or initramfs file that matches the kernel specified by LoaderPath.
450 // The matching file has a name that begins with "init" and includes the same version
451 // number string as is found in LoaderPath -- but not a longer version number string.
452 // For instance, if LoaderPath is \EFI\kernels\bzImage-3.3.0.efi, and if \EFI\kernels
453 // has a file called initramfs-3.3.0.img, this function will return the string
454 // '\EFI\kernels\initramfs-3.3.0.img'. If the directory ALSO contains the file
455 // initramfs-3.3.0-rc7.img or initramfs-13.3.0.img, those files will NOT match;
456 // however, initmine-3.3.0.img might match. (FindInitrd() returns the first match it
457 // finds). Thus, care should be taken to avoid placing duplicate matching files in
458 // the kernel's directory.
459 // If no matching init file can be found, returns NULL.
460 static CHAR16
* FindInitrd(IN CHAR16
*LoaderPath
, IN REFIT_VOLUME
*Volume
) {
461 CHAR16
*InitrdName
= NULL
, *FileName
, *KernelVersion
, *InitrdVersion
, *Path
;
462 REFIT_DIR_ITER DirIter
;
463 EFI_FILE_INFO
*DirEntry
;
465 FileName
= Basename(LoaderPath
);
466 KernelVersion
= FindNumbers(FileName
);
467 Path
= FindPath(LoaderPath
);
469 // Add trailing backslash for root directory; necessary on some systems, but must
470 // NOT be added to all directories, since on other systems, a trailing backslash on
471 // anything but the root directory causes them to flake out!
472 if (StrLen(Path
) == 0) {
473 MergeStrings(&Path
, L
"\\", 0);
475 DirIterOpen(Volume
->RootDir
, Path
, &DirIter
);
476 // Now add a trailing backslash if it was NOT added earlier, for consistency in
477 // building the InitrdName later....
478 if ((StrLen(Path
) > 0) && (Path
[StrLen(Path
) - 1] != L
'\\'))
479 MergeStrings(&Path
, L
"\\", 0);
480 while ((DirIterNext(&DirIter
, 2, L
"init*", &DirEntry
)) && (InitrdName
== NULL
)) {
481 InitrdVersion
= FindNumbers(DirEntry
->FileName
);
482 if (KernelVersion
!= NULL
) {
483 if (StriCmp(InitrdVersion
, KernelVersion
) == 0) {
484 InitrdName
= PoolPrint(L
"%s%s", Path
, DirEntry
->FileName
);
487 if (InitrdVersion
== NULL
) {
488 InitrdName
= PoolPrint(L
"%s%s", Path
, DirEntry
->FileName
);
491 MyFreePool(InitrdVersion
);
493 DirIterClose(&DirIter
);
495 // Note: Don't FreePool(FileName), since Basename returns a pointer WITHIN the string it's passed.
496 MyFreePool(KernelVersion
);
499 } // static CHAR16 * FindInitrd()
501 LOADER_ENTRY
* AddPreparedLoaderEntry(LOADER_ENTRY
*Entry
) {
502 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
505 } // LOADER_ENTRY * AddPreparedLoaderEntry()
507 // Creates a copy of a menu screen.
508 // Returns a pointer to the copy of the menu screen.
509 static REFIT_MENU_SCREEN
* CopyMenuScreen(REFIT_MENU_SCREEN
*Entry
) {
510 REFIT_MENU_SCREEN
*NewEntry
;
513 NewEntry
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
514 if ((Entry
!= NULL
) && (NewEntry
!= NULL
)) {
515 CopyMem(NewEntry
, Entry
, sizeof(REFIT_MENU_SCREEN
));
516 NewEntry
->Title
= (Entry
->Title
) ? StrDuplicate(Entry
->Title
) : NULL
;
517 NewEntry
->TimeoutText
= (Entry
->TimeoutText
) ? StrDuplicate(Entry
->TimeoutText
) : NULL
;
518 if (Entry
->TitleImage
!= NULL
) {
519 NewEntry
->TitleImage
= AllocatePool(sizeof(EG_IMAGE
));
520 if (NewEntry
->TitleImage
!= NULL
)
521 CopyMem(NewEntry
->TitleImage
, Entry
->TitleImage
, sizeof(EG_IMAGE
));
523 NewEntry
->InfoLines
= (CHAR16
**) AllocateZeroPool(Entry
->InfoLineCount
* (sizeof(CHAR16
*)));
524 for (i
= 0; i
< Entry
->InfoLineCount
&& NewEntry
->InfoLines
; i
++) {
525 NewEntry
->InfoLines
[i
] = (Entry
->InfoLines
[i
]) ? StrDuplicate(Entry
->InfoLines
[i
]) : NULL
;
527 NewEntry
->Entries
= (REFIT_MENU_ENTRY
**) AllocateZeroPool(Entry
->EntryCount
* (sizeof (REFIT_MENU_ENTRY
*)));
528 for (i
= 0; i
< Entry
->EntryCount
&& NewEntry
->Entries
; i
++) {
529 AddMenuEntry(NewEntry
, Entry
->Entries
[i
]);
531 NewEntry
->Hint1
= (Entry
->Hint1
) ? StrDuplicate(Entry
->Hint1
) : NULL
;
532 NewEntry
->Hint2
= (Entry
->Hint2
) ? StrDuplicate(Entry
->Hint2
) : NULL
;
535 } // static REFIT_MENU_SCREEN* CopyMenuScreen()
537 // Creates a copy of a menu entry. Intended to enable moving a stack-based
538 // menu entry (such as the ones for the "reboot" and "exit" functions) to
539 // to the heap. This enables easier deletion of the whole set of menu
540 // entries when re-scanning.
541 // Returns a pointer to the copy of the menu entry.
542 static REFIT_MENU_ENTRY
* CopyMenuEntry(REFIT_MENU_ENTRY
*Entry
) {
543 REFIT_MENU_ENTRY
*NewEntry
;
545 NewEntry
= AllocateZeroPool(sizeof(REFIT_MENU_ENTRY
));
546 if ((Entry
!= NULL
) && (NewEntry
!= NULL
)) {
547 CopyMem(NewEntry
, Entry
, sizeof(REFIT_MENU_ENTRY
));
548 NewEntry
->Title
= (Entry
->Title
) ? StrDuplicate(Entry
->Title
) : NULL
;
549 if (Entry
->BadgeImage
!= NULL
) {
550 NewEntry
->BadgeImage
= AllocatePool(sizeof(EG_IMAGE
));
551 if (NewEntry
->BadgeImage
!= NULL
)
552 CopyMem(NewEntry
->BadgeImage
, Entry
->BadgeImage
, sizeof(EG_IMAGE
));
554 if (Entry
->Image
!= NULL
) {
555 NewEntry
->Image
= AllocatePool(sizeof(EG_IMAGE
));
556 if (NewEntry
->Image
!= NULL
)
557 CopyMem(NewEntry
->Image
, Entry
->Image
, sizeof(EG_IMAGE
));
559 if (Entry
->SubScreen
!= NULL
) {
560 NewEntry
->SubScreen
= CopyMenuScreen(Entry
->SubScreen
);
564 } // REFIT_MENU_ENTRY* CopyMenuEntry()
566 // Creates a new LOADER_ENTRY data structure and populates it with
567 // default values from the specified Entry, or NULL values if Entry
568 // is unspecified (NULL).
569 // Returns a pointer to the new data structure, or NULL if it
570 // couldn't be allocated
571 LOADER_ENTRY
*InitializeLoaderEntry(IN LOADER_ENTRY
*Entry
) {
572 LOADER_ENTRY
*NewEntry
= NULL
;
574 NewEntry
= AllocateZeroPool(sizeof(LOADER_ENTRY
));
575 if (NewEntry
!= NULL
) {
576 NewEntry
->me
.Title
= NULL
;
577 NewEntry
->me
.Tag
= TAG_LOADER
;
578 NewEntry
->Enabled
= TRUE
;
579 NewEntry
->UseGraphicsMode
= FALSE
;
580 NewEntry
->OSType
= 0;
582 NewEntry
->LoaderPath
= (Entry
->LoaderPath
) ? StrDuplicate(Entry
->LoaderPath
) : NULL
;
583 NewEntry
->VolName
= (Entry
->VolName
) ? StrDuplicate(Entry
->VolName
) : NULL
;
584 NewEntry
->DevicePath
= Entry
->DevicePath
;
585 NewEntry
->UseGraphicsMode
= Entry
->UseGraphicsMode
;
586 NewEntry
->LoadOptions
= (Entry
->LoadOptions
) ? StrDuplicate(Entry
->LoadOptions
) : NULL
;
587 NewEntry
->InitrdPath
= (Entry
->InitrdPath
) ? StrDuplicate(Entry
->InitrdPath
) : NULL
;
591 } // LOADER_ENTRY *InitializeLoaderEntry()
593 // Adds InitrdPath to Options, but only if Options doesn't already include an
594 // initrd= line. Done to enable overriding the default initrd selection in a
595 // refind_linux.conf file's options list.
596 // Returns a pointer to a new string. The calling function is responsible for
597 // freeing its memory.
598 static CHAR16
*AddInitrdToOptions(CHAR16
*Options
, CHAR16
*InitrdPath
) {
599 CHAR16
*NewOptions
= NULL
;
602 NewOptions
= StrDuplicate(Options
);
603 if ((InitrdPath
!= NULL
) && !StriSubCmp(L
"initrd=", Options
)) {
604 MergeStrings(&NewOptions
, L
"initrd=", L
' ');
605 MergeStrings(&NewOptions
, InitrdPath
, 0);
608 } // CHAR16 *AddInitrdToOptions()
610 // Prepare a REFIT_MENU_SCREEN data structure for a subscreen entry. This sets up
611 // the default entry that launches the boot loader using the same options as the
612 // main Entry does. Subsequent options can be added by the calling function.
613 // If a subscreen already exists in the Entry that's passed to this function,
614 // it's left unchanged and a pointer to it is returned.
615 // Returns a pointer to the new subscreen data structure, or NULL if there
616 // were problems allocating memory.
617 REFIT_MENU_SCREEN
*InitializeSubScreen(IN LOADER_ENTRY
*Entry
) {
618 CHAR16
*FileName
, *MainOptions
= NULL
;
619 REFIT_MENU_SCREEN
*SubScreen
= NULL
;
620 LOADER_ENTRY
*SubEntry
;
622 FileName
= Basename(Entry
->LoaderPath
);
623 if (Entry
->me
.SubScreen
== NULL
) { // No subscreen yet; initialize default entry....
624 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
625 if (SubScreen
!= NULL
) {
626 SubScreen
->Title
= AllocateZeroPool(sizeof(CHAR16
) * 256);
627 SPrint(SubScreen
->Title
, 255, L
"Boot Options for %s on %s",
628 (Entry
->Title
!= NULL
) ? Entry
->Title
: FileName
, Entry
->VolName
);
629 SubScreen
->TitleImage
= Entry
->me
.Image
;
631 SubEntry
= InitializeLoaderEntry(Entry
);
632 if (SubEntry
!= NULL
) {
633 SubEntry
->me
.Title
= StrDuplicate(L
"Boot using default options");
634 MainOptions
= SubEntry
->LoadOptions
;
635 SubEntry
->LoadOptions
= AddInitrdToOptions(MainOptions
, SubEntry
->InitrdPath
);
636 MyFreePool(MainOptions
);
637 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
638 } // if (SubEntry != NULL)
639 SubScreen
->Hint1
= StrDuplicate(SUBSCREEN_HINT1
);
640 if (GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_EDITOR
) {
641 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2_NO_EDITOR
);
643 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2
);
645 } // if (SubScreen != NULL)
646 } else { // existing subscreen; less initialization, and just add new entry later....
647 SubScreen
= Entry
->me
.SubScreen
;
650 } // REFIT_MENU_SCREEN *InitializeSubScreen()
652 VOID
GenerateSubScreen(LOADER_ENTRY
*Entry
, IN REFIT_VOLUME
*Volume
) {
653 REFIT_MENU_SCREEN
*SubScreen
;
654 LOADER_ENTRY
*SubEntry
;
656 CHAR16 DiagsFileName
[256];
661 // create the submenu
662 if (StrLen(Entry
->Title
) == 0) {
663 MyFreePool(Entry
->Title
);
666 SubScreen
= InitializeSubScreen(Entry
);
668 // loader-specific submenu entries
669 if (Entry
->OSType
== 'M') { // entries for Mac OS X
671 SubEntry
= InitializeLoaderEntry(Entry
);
672 if (SubEntry
!= NULL
) {
673 SubEntry
->me
.Title
= L
"Boot Mac OS X with a 64-bit kernel";
674 SubEntry
->LoadOptions
= L
"arch=x86_64";
675 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
676 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
679 SubEntry
= InitializeLoaderEntry(Entry
);
680 if (SubEntry
!= NULL
) {
681 SubEntry
->me
.Title
= L
"Boot Mac OS X with a 32-bit kernel";
682 SubEntry
->LoadOptions
= L
"arch=i386";
683 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
684 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
688 if (!(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_SINGLEUSER
)) {
689 SubEntry
= InitializeLoaderEntry(Entry
);
690 if (SubEntry
!= NULL
) {
691 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode";
692 SubEntry
->UseGraphicsMode
= FALSE
;
693 SubEntry
->LoadOptions
= L
"-v";
694 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
698 SubEntry
= InitializeLoaderEntry(Entry
);
699 if (SubEntry
!= NULL
) {
700 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode (64-bit)";
701 SubEntry
->UseGraphicsMode
= FALSE
;
702 SubEntry
->LoadOptions
= L
"-v arch=x86_64";
703 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
706 SubEntry
= InitializeLoaderEntry(Entry
);
707 if (SubEntry
!= NULL
) {
708 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode (32-bit)";
709 SubEntry
->UseGraphicsMode
= FALSE
;
710 SubEntry
->LoadOptions
= L
"-v arch=i386";
711 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
715 SubEntry
= InitializeLoaderEntry(Entry
);
716 if (SubEntry
!= NULL
) {
717 SubEntry
->me
.Title
= L
"Boot Mac OS X in single user mode";
718 SubEntry
->UseGraphicsMode
= FALSE
;
719 SubEntry
->LoadOptions
= L
"-v -s";
720 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
722 } // single-user mode allowed
724 if (!(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_SAFEMODE
)) {
725 SubEntry
= InitializeLoaderEntry(Entry
);
726 if (SubEntry
!= NULL
) {
727 SubEntry
->me
.Title
= L
"Boot Mac OS X in safe mode";
728 SubEntry
->UseGraphicsMode
= FALSE
;
729 SubEntry
->LoadOptions
= L
"-v -x";
730 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
732 } // safe mode allowed
734 // check for Apple hardware diagnostics
735 StrCpy(DiagsFileName
, L
"System\\Library\\CoreServices\\.diagnostics\\diags.efi");
736 if (FileExists(Volume
->RootDir
, DiagsFileName
) && !(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_HWTEST
)) {
737 SubEntry
= InitializeLoaderEntry(Entry
);
738 if (SubEntry
!= NULL
) {
739 SubEntry
->me
.Title
= L
"Run Apple Hardware Test";
740 MyFreePool(SubEntry
->LoaderPath
);
741 SubEntry
->LoaderPath
= StrDuplicate(DiagsFileName
);
742 SubEntry
->DevicePath
= FileDevicePath(Volume
->DeviceHandle
, SubEntry
->LoaderPath
);
743 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
744 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
746 } // if diagnostics entry found
748 } else if (Entry
->OSType
== 'L') { // entries for Linux kernels with EFI stub loaders
749 File
= ReadLinuxOptionsFile(Entry
->LoaderPath
, Volume
);
751 InitrdName
= FindInitrd(Entry
->LoaderPath
, Volume
);
752 TokenCount
= ReadTokenLine(File
, &TokenList
);
753 // first entry requires special processing, since it was initially set
754 // up with a default title but correct options by InitializeSubScreen(),
756 if ((SubScreen
->Entries
!= NULL
) && (SubScreen
->Entries
[0] != NULL
)) {
757 MyFreePool(SubScreen
->Entries
[0]->Title
);
758 SubScreen
->Entries
[0]->Title
= TokenList
[0] ? StrDuplicate(TokenList
[0]) : StrDuplicate(L
"Boot Linux");
760 FreeTokenLine(&TokenList
, &TokenCount
);
761 while ((TokenCount
= ReadTokenLine(File
, &TokenList
)) > 1) {
762 SubEntry
= InitializeLoaderEntry(Entry
);
763 SubEntry
->me
.Title
= TokenList
[0] ? StrDuplicate(TokenList
[0]) : StrDuplicate(L
"Boot Linux");
764 MyFreePool(SubEntry
->LoadOptions
);
765 SubEntry
->LoadOptions
= AddInitrdToOptions(TokenList
[1], InitrdName
);
766 FreeTokenLine(&TokenList
, &TokenCount
);
767 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_LINUX
;
768 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
770 MyFreePool(InitrdName
);
774 } else if (Entry
->OSType
== 'E') { // entries for ELILO
775 SubEntry
= InitializeLoaderEntry(Entry
);
776 if (SubEntry
!= NULL
) {
777 SubEntry
->me
.Title
= L
"Run ELILO in interactive mode";
778 SubEntry
->LoadOptions
= L
"-p";
779 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
780 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
783 SubEntry
= InitializeLoaderEntry(Entry
);
784 if (SubEntry
!= NULL
) {
785 SubEntry
->me
.Title
= L
"Boot Linux for a 17\" iMac or a 15\" MacBook Pro (*)";
786 SubEntry
->UseGraphicsMode
= TRUE
;
787 SubEntry
->LoadOptions
= L
"-d 0 i17";
788 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
789 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
792 SubEntry
= InitializeLoaderEntry(Entry
);
793 if (SubEntry
!= NULL
) {
794 SubEntry
->me
.Title
= L
"Boot Linux for a 20\" iMac (*)";
795 SubEntry
->UseGraphicsMode
= TRUE
;
796 SubEntry
->LoadOptions
= L
"-d 0 i20";
797 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
798 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
801 SubEntry
= InitializeLoaderEntry(Entry
);
802 if (SubEntry
!= NULL
) {
803 SubEntry
->me
.Title
= L
"Boot Linux for a Mac Mini (*)";
804 SubEntry
->UseGraphicsMode
= TRUE
;
805 SubEntry
->LoadOptions
= L
"-d 0 mini";
806 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
807 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
810 AddMenuInfoLine(SubScreen
, L
"NOTE: This is an example. Entries");
811 AddMenuInfoLine(SubScreen
, L
"marked with (*) may not work.");
813 } else if (Entry
->OSType
== 'X') { // entries for xom.efi
814 // by default, skip the built-in selection and boot from hard disk only
815 Entry
->LoadOptions
= L
"-s -h";
817 SubEntry
= InitializeLoaderEntry(Entry
);
818 if (SubEntry
!= NULL
) {
819 SubEntry
->me
.Title
= L
"Boot Windows from Hard Disk";
820 SubEntry
->LoadOptions
= L
"-s -h";
821 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
822 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
825 SubEntry
= InitializeLoaderEntry(Entry
);
826 if (SubEntry
!= NULL
) {
827 SubEntry
->me
.Title
= L
"Boot Windows from CD-ROM";
828 SubEntry
->LoadOptions
= L
"-s -c";
829 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
830 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
833 SubEntry
= InitializeLoaderEntry(Entry
);
834 if (SubEntry
!= NULL
) {
835 SubEntry
->me
.Title
= L
"Run XOM in text mode";
836 SubEntry
->UseGraphicsMode
= FALSE
;
837 SubEntry
->LoadOptions
= L
"-v";
838 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
839 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
841 } // entries for xom.efi
842 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
843 Entry
->me
.SubScreen
= SubScreen
;
844 } // VOID GenerateSubScreen()
846 // Returns options for a Linux kernel. Reads them from an options file in the
847 // kernel's directory; and if present, adds an initrd= option for an initial
848 // RAM disk file with the same version number as the kernel file.
849 static CHAR16
* GetMainLinuxOptions(IN CHAR16
* LoaderPath
, IN REFIT_VOLUME
*Volume
) {
850 CHAR16
*Options
= NULL
, *InitrdName
, *FullOptions
= NULL
;
852 Options
= GetFirstOptionsFromFile(LoaderPath
, Volume
);
853 InitrdName
= FindInitrd(LoaderPath
, Volume
);
854 FullOptions
= AddInitrdToOptions(Options
, InitrdName
);
857 MyFreePool(InitrdName
);
858 return (FullOptions
);
859 } // static CHAR16 * GetMainLinuxOptions()
861 // Try to guess the name of the Linux distribution & add that name to
863 static VOID
GuessLinuxDistribution(CHAR16
**OSIconName
, REFIT_VOLUME
*Volume
, CHAR16
*LoaderPath
) {
867 UINTN TokenCount
= 0;
869 // If on Linux root fs, /etc/os-release file probably has clues....
870 if (FileExists(Volume
->RootDir
, L
"etc\\os-release") &&
871 (ReadFile(Volume
->RootDir
, L
"etc\\os-release", &File
, &FileSize
) == EFI_SUCCESS
)) {
873 TokenCount
= ReadTokenLine(&File
, &TokenList
);
874 if ((TokenCount
> 1) && ((StriCmp(TokenList
[0], L
"ID") == 0) || (StriCmp(TokenList
[0], L
"NAME") == 0))) {
875 MergeStrings(OSIconName
, TokenList
[1], L
',');
877 FreeTokenLine(&TokenList
, &TokenCount
);
878 } while (TokenCount
> 0);
879 MyFreePool(File
.Buffer
);
882 // Search for clues in the kernel's filename....
883 if (StriSubCmp(L
".fc", LoaderPath
))
884 MergeStrings(OSIconName
, L
"fedora", L
',');
885 if (StriSubCmp(L
".el", LoaderPath
))
886 MergeStrings(OSIconName
, L
"redhat", L
',');
887 } // VOID GuessLinuxDistribution()
889 // Sets a few defaults for a loader entry -- mainly the icon, but also the OS type
890 // code and shortcut letter. For Linux EFI stub loaders, also sets kernel options
891 // that will (with luck) work fairly automatically.
892 VOID
SetLoaderDefaults(LOADER_ENTRY
*Entry
, CHAR16
*LoaderPath
, REFIT_VOLUME
*Volume
) {
893 CHAR16
*FileName
, *PathOnly
, *NoExtension
, *OSIconName
= NULL
, *Temp
, *SubString
;
894 CHAR16 ShortcutLetter
= 0;
897 FileName
= Basename(LoaderPath
);
898 PathOnly
= FindPath(LoaderPath
);
899 NoExtension
= StripEfiExtension(FileName
);
901 // locate a custom icon for the loader
902 // Anything found here takes precedence over the "hints" in the OSIconName variable
903 if (!Entry
->me
.Image
)
904 Entry
->me
.Image
= egLoadIconAnyType(Volume
->RootDir
, PathOnly
, NoExtension
, GlobalConfig
.IconSizes
[ICON_SIZE_BIG
]);
905 if (!Entry
->me
.Image
)
906 Entry
->me
.Image
= egCopyImage(Volume
->VolIconImage
);
908 // Begin creating icon "hints" by using last part of directory path leading
910 Temp
= FindLastDirName(LoaderPath
);
911 MergeStrings(&OSIconName
, Temp
, L
',');
914 if (OSIconName
!= NULL
) {
915 ShortcutLetter
= OSIconName
[0];
918 // Add every "word" in the volume label, delimited by spaces, dashes (-), or
919 // underscores (_), to the list of hints to be used in searching for OS
921 if ((Volume
->VolName
) && (StrLen(Volume
->VolName
) > 0)) {
922 Temp
= SubString
= StrDuplicate(Volume
->VolName
);
924 Length
= StrLen(Temp
);
925 for (i
= 0; i
< Length
; i
++) {
926 if ((Temp
[i
] == L
' ') || (Temp
[i
] == L
'_') || (Temp
[i
] == L
'-')) {
928 if (StrLen(SubString
) > 0)
929 MergeStrings(&OSIconName
, SubString
, L
',');
930 SubString
= Temp
+ i
+ 1;
933 MergeStrings(&OSIconName
, SubString
, L
',');
938 // detect specific loaders
939 if (StriSubCmp(L
"bzImage", FileName
) || StriSubCmp(L
"vmlinuz", FileName
)) {
940 GuessLinuxDistribution(&OSIconName
, Volume
, LoaderPath
);
941 MergeStrings(&OSIconName
, L
"linux", L
',');
943 if (ShortcutLetter
== 0)
944 ShortcutLetter
= 'L';
945 Entry
->LoadOptions
= GetMainLinuxOptions(LoaderPath
, Volume
);
946 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_LINUX
;
947 } else if (StriSubCmp(L
"refit", LoaderPath
)) {
948 MergeStrings(&OSIconName
, L
"refit", L
',');
950 ShortcutLetter
= 'R';
951 } else if (StriSubCmp(L
"refind", LoaderPath
)) {
952 MergeStrings(&OSIconName
, L
"refind", L
',');
954 ShortcutLetter
= 'R';
955 } else if (StriCmp(LoaderPath
, MACOSX_LOADER_PATH
) == 0) {
956 if (Volume
->VolIconImage
!= NULL
) { // custom icon file found
957 Entry
->me
.Image
= Volume
->VolIconImage
;
959 MergeStrings(&OSIconName
, L
"mac", L
',');
961 ShortcutLetter
= 'M';
962 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
963 } else if (StriCmp(FileName
, L
"diags.efi") == 0) {
964 MergeStrings(&OSIconName
, L
"hwtest", L
',');
965 } else if (StriCmp(FileName
, L
"e.efi") == 0 || StriCmp(FileName
, L
"elilo.efi") == 0 || StriSubCmp(L
"elilo", FileName
)) {
966 MergeStrings(&OSIconName
, L
"elilo,linux", L
',');
968 if (ShortcutLetter
== 0)
969 ShortcutLetter
= 'L';
970 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
971 } else if (StriSubCmp(L
"grub", FileName
)) {
973 ShortcutLetter
= 'G';
974 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_GRUB
;
975 } else if (StriCmp(FileName
, L
"cdboot.efi") == 0 ||
976 StriCmp(FileName
, L
"bootmgr.efi") == 0 ||
977 StriCmp(FileName
, L
"bootmgfw.efi") == 0 ||
978 StriCmp(FileName
, L
"bkpbootmgfw.efi") == 0) {
979 MergeStrings(&OSIconName
, L
"win", L
',');
981 ShortcutLetter
= 'W';
982 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
983 } else if (StriCmp(FileName
, L
"xom.efi") == 0) {
984 MergeStrings(&OSIconName
, L
"xom,win", L
',');
985 Entry
->UseGraphicsMode
= TRUE
;
987 ShortcutLetter
= 'W';
988 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
991 if ((ShortcutLetter
>= 'a') && (ShortcutLetter
<= 'z'))
992 ShortcutLetter
= ShortcutLetter
- 'a' + 'A'; // convert lowercase to uppercase
993 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
994 if (Entry
->me
.Image
== NULL
)
995 Entry
->me
.Image
= LoadOSIcon(OSIconName
, L
"unknown", FALSE
);
996 MyFreePool(PathOnly
);
997 } // VOID SetLoaderDefaults()
999 // Add a specified EFI boot loader to the list, using automatic settings
1000 // for icons, options, etc.
1001 LOADER_ENTRY
* AddLoaderEntry(IN CHAR16
*LoaderPath
, IN CHAR16
*LoaderTitle
, IN REFIT_VOLUME
*Volume
) {
1002 LOADER_ENTRY
*Entry
;
1004 CleanUpPathNameSlashes(LoaderPath
);
1005 Entry
= InitializeLoaderEntry(NULL
);
1006 if (Entry
!= NULL
) {
1007 Entry
->Title
= StrDuplicate((LoaderTitle
!= NULL
) ? LoaderTitle
: LoaderPath
);
1008 Entry
->me
.Title
= AllocateZeroPool(sizeof(CHAR16
) * 256);
1009 SPrint(Entry
->me
.Title
, 255, L
"Boot %s from %s ", (LoaderTitle
!= NULL
) ? LoaderTitle
: LoaderPath
, Volume
->VolName
);
1011 Entry
->me
.BadgeImage
= Volume
->VolBadgeImage
;
1012 if ((LoaderPath
!= NULL
) && (LoaderPath
[0] != L
'\\')) {
1013 Entry
->LoaderPath
= StrDuplicate(L
"\\");
1015 Entry
->LoaderPath
= NULL
;
1017 MergeStrings(&(Entry
->LoaderPath
), LoaderPath
, 0);
1018 Entry
->VolName
= Volume
->VolName
;
1019 Entry
->DevicePath
= FileDevicePath(Volume
->DeviceHandle
, Entry
->LoaderPath
);
1020 SetLoaderDefaults(Entry
, LoaderPath
, Volume
);
1021 GenerateSubScreen(Entry
, Volume
);
1022 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1026 } // LOADER_ENTRY * AddLoaderEntry()
1028 // Returns -1 if (Time1 < Time2), +1 if (Time1 > Time2), or 0 if
1029 // (Time1 == Time2). Precision is only to the nearest second; since
1030 // this is used for sorting boot loader entries, differences smaller
1031 // than this are likely to be meaningless (and unlikely!).
1032 INTN
TimeComp(IN EFI_TIME
*Time1
, IN EFI_TIME
*Time2
) {
1033 INT64 Time1InSeconds
, Time2InSeconds
;
1035 // Following values are overestimates; I'm assuming 31 days in every month.
1036 // This is fine for the purpose of this function, which is limited
1037 Time1InSeconds
= Time1
->Second
+ (Time1
->Minute
* 60) + (Time1
->Hour
* 3600) + (Time1
->Day
* 86400) +
1038 (Time1
->Month
* 2678400) + ((Time1
->Year
- 1998) * 32140800);
1039 Time2InSeconds
= Time2
->Second
+ (Time2
->Minute
* 60) + (Time2
->Hour
* 3600) + (Time2
->Day
* 86400) +
1040 (Time2
->Month
* 2678400) + ((Time2
->Year
- 1998) * 32140800);
1041 if (Time1InSeconds
< Time2InSeconds
)
1043 else if (Time1InSeconds
> Time2InSeconds
)
1047 } // INTN TimeComp()
1049 // Adds a loader list element, keeping it sorted by date. Returns the new
1050 // first element (the one with the most recent date).
1051 static struct LOADER_LIST
* AddLoaderListEntry(struct LOADER_LIST
*LoaderList
, struct LOADER_LIST
*NewEntry
) {
1052 struct LOADER_LIST
*LatestEntry
, *CurrentEntry
, *PrevEntry
= NULL
;
1054 LatestEntry
= CurrentEntry
= LoaderList
;
1055 if (LoaderList
== NULL
) {
1056 LatestEntry
= NewEntry
;
1058 while ((CurrentEntry
!= NULL
) && (TimeComp(&(NewEntry
->TimeStamp
), &(CurrentEntry
->TimeStamp
)) < 0)) {
1059 PrevEntry
= CurrentEntry
;
1060 CurrentEntry
= CurrentEntry
->NextEntry
;
1062 NewEntry
->NextEntry
= CurrentEntry
;
1063 if (PrevEntry
== NULL
) {
1064 LatestEntry
= NewEntry
;
1066 PrevEntry
->NextEntry
= NewEntry
;
1069 return (LatestEntry
);
1070 } // static VOID AddLoaderListEntry()
1072 // Delete the LOADER_LIST linked list
1073 static VOID
CleanUpLoaderList(struct LOADER_LIST
*LoaderList
) {
1074 struct LOADER_LIST
*Temp
;
1076 while (LoaderList
!= NULL
) {
1078 LoaderList
= LoaderList
->NextEntry
;
1079 MyFreePool(Temp
->FileName
);
1082 } // static VOID CleanUpLoaderList()
1084 // Returns FALSE if the specified file/volume matches the GlobalConfig.DontScanDirs
1085 // or GlobalConfig.DontScanVolumes specification, or if Path points to a volume
1086 // other than the one specified by Volume, or if the specified path is SelfDir.
1087 // Returns TRUE if none of these conditions is met -- that is, if the path is
1088 // eligible for scanning.
1089 static BOOLEAN
ShouldScan(REFIT_VOLUME
*Volume
, CHAR16
*Path
) {
1090 CHAR16
*VolName
= NULL
, *DontScanDir
, *PathCopy
= NULL
;
1092 BOOLEAN ScanIt
= TRUE
;
1094 if (IsIn(Volume
->VolName
, GlobalConfig
.DontScanVolumes
))
1097 if ((StriCmp(Path
, SelfDirPath
) == 0) && (Volume
->DeviceHandle
== SelfVolume
->DeviceHandle
))
1100 // See if Path includes an explicit volume declaration that's NOT Volume....
1101 PathCopy
= StrDuplicate(Path
);
1102 if (SplitVolumeAndFilename(&PathCopy
, &VolName
)) {
1103 VolumeNumberToName(Volume
, &VolName
);
1104 if (VolName
&& StriCmp(VolName
, Volume
->VolName
) != 0) {
1107 } // if Path includes volume specification
1108 MyFreePool(PathCopy
);
1109 MyFreePool(VolName
);
1112 // See if Volume is in GlobalConfig.DontScanDirs....
1113 while ((DontScanDir
= FindCommaDelimited(GlobalConfig
.DontScanDirs
, i
++)) && ScanIt
) {
1114 SplitVolumeAndFilename(&DontScanDir
, &VolName
);
1115 CleanUpPathNameSlashes(DontScanDir
);
1116 VolumeNumberToName(Volume
, &VolName
);
1117 if (VolName
!= NULL
) {
1118 if ((StriCmp(VolName
, Volume
->VolName
) == 0) && (StriCmp(DontScanDir
, Path
) == 0))
1121 if (StriCmp(DontScanDir
, Path
) == 0)
1124 MyFreePool(DontScanDir
);
1125 MyFreePool(VolName
);
1130 } // BOOLEAN ShouldScan()
1132 // Returns TRUE if the file is byte-for-byte identical with the fallback file
1133 // on the volume AND if the file is not itself the fallback file; returns
1134 // FALSE if the file is not identical to the fallback file OR if the file
1135 // IS the fallback file. Intended for use in excluding the fallback boot
1136 // loader when it's a duplicate of another boot loader.
1137 static BOOLEAN
DuplicatesFallback(IN REFIT_VOLUME
*Volume
, IN CHAR16
*FileName
) {
1138 CHAR8
*FileContents
, *FallbackContents
;
1139 EFI_FILE_HANDLE FileHandle
, FallbackHandle
;
1140 EFI_FILE_INFO
*FileInfo
, *FallbackInfo
;
1141 UINTN FileSize
= 0, FallbackSize
= 0;
1143 BOOLEAN AreIdentical
= FALSE
;
1145 if (!FileExists(Volume
->RootDir
, FileName
) || !FileExists(Volume
->RootDir
, FALLBACK_FULLNAME
))
1148 CleanUpPathNameSlashes(FileName
);
1150 if (StriCmp(FileName
, FALLBACK_FULLNAME
) == 0)
1151 return FALSE
; // identical filenames, so not a duplicate....
1153 Status
= refit_call5_wrapper(Volume
->RootDir
->Open
, Volume
->RootDir
, &FileHandle
, FileName
, EFI_FILE_MODE_READ
, 0);
1154 if (Status
== EFI_SUCCESS
) {
1155 FileInfo
= LibFileInfo(FileHandle
);
1156 FileSize
= FileInfo
->FileSize
;
1161 Status
= refit_call5_wrapper(Volume
->RootDir
->Open
, Volume
->RootDir
, &FallbackHandle
, FALLBACK_FULLNAME
, EFI_FILE_MODE_READ
, 0);
1162 if (Status
== EFI_SUCCESS
) {
1163 FallbackInfo
= LibFileInfo(FallbackHandle
);
1164 FallbackSize
= FallbackInfo
->FileSize
;
1166 refit_call1_wrapper(FileHandle
->Close
, FileHandle
);
1170 if (FallbackSize
!= FileSize
) { // not same size, so can't be identical
1171 AreIdentical
= FALSE
;
1172 } else { // could be identical; do full check....
1173 FileContents
= AllocatePool(FileSize
);
1174 FallbackContents
= AllocatePool(FallbackSize
);
1175 if (FileContents
&& FallbackContents
) {
1176 Status
= refit_call3_wrapper(FileHandle
->Read
, FileHandle
, &FileSize
, FileContents
);
1177 if (Status
== EFI_SUCCESS
) {
1178 Status
= refit_call3_wrapper(FallbackHandle
->Read
, FallbackHandle
, &FallbackSize
, FallbackContents
);
1180 if (Status
== EFI_SUCCESS
) {
1181 AreIdentical
= (CompareMem(FileContents
, FallbackContents
, FileSize
) == 0);
1184 MyFreePool(FileContents
);
1185 MyFreePool(FallbackContents
);
1188 // BUG ALERT: Some systems (e.g., DUET, some Macs with large displays) crash if the
1189 // following two calls are reversed. Go figure....
1190 refit_call1_wrapper(FileHandle
->Close
, FallbackHandle
);
1191 refit_call1_wrapper(FileHandle
->Close
, FileHandle
);
1192 return AreIdentical
;
1193 } // BOOLEAN DuplicatesFallback()
1195 // Returns FALSE if two measures of file size are identical for a single file,
1196 // TRUE if not or if the file can't be opened and the other measure is non-0.
1197 // Despite the function's name, this isn't really a direct test of symbolic
1198 // link status, since EFI doesn't officially support symlinks. It does seem
1199 // to be a reliable indicator, though. (OTOH, some disk errors might cause a
1200 // file to fail to open, which would return a false positive -- but as I use
1201 // this function to exclude symbolic links from the list of boot loaders,
1202 // that would be fine, since such boot loaders wouldn't work.)
1203 static BOOLEAN
IsSymbolicLink(REFIT_VOLUME
*Volume
, CHAR16
*Path
, EFI_FILE_INFO
*DirEntry
) {
1204 EFI_FILE_HANDLE FileHandle
;
1205 EFI_FILE_INFO
*FileInfo
= NULL
;
1207 UINTN FileSize2
= 0;
1210 FileName
= StrDuplicate(Path
);
1211 MergeStrings(&FileName
, DirEntry
->FileName
, L
'\\');
1212 CleanUpPathNameSlashes(FileName
);
1214 Status
= refit_call5_wrapper(Volume
->RootDir
->Open
, Volume
->RootDir
, &FileHandle
, FileName
, EFI_FILE_MODE_READ
, 0);
1215 if (Status
== EFI_SUCCESS
) {
1216 FileInfo
= LibFileInfo(FileHandle
);
1217 if (FileInfo
!= NULL
)
1218 FileSize2
= FileInfo
->FileSize
;
1221 MyFreePool(FileName
);
1222 MyFreePool(FileInfo
);
1224 return (DirEntry
->FileSize
!= FileSize2
);
1225 } // BOOLEAN IsSymbolicLink()
1227 // Returns TRUE if a file with the same name as the original but with
1228 // ".efi.signed" is also present in the same directory. Ubuntu is using
1229 // this filename as a signed version of the original unsigned kernel, and
1230 // there's no point in cluttering the display with two kernels that will
1231 // behave identically on non-SB systems, or when one will fail when SB
1233 static BOOLEAN
HasSignedCounterpart(IN REFIT_VOLUME
*Volume
, IN CHAR16
*Path
, IN CHAR16
*Filename
) {
1234 CHAR16
*NewFile
= NULL
;
1235 BOOLEAN retval
= FALSE
;
1237 MergeStrings(&NewFile
, Path
, 0);
1238 MergeStrings(&NewFile
, Filename
, L
'\\');
1239 MergeStrings(&NewFile
, L
".efi.signed", 0);
1240 if (FileExists(Volume
->RootDir
, NewFile
))
1242 MyFreePool(NewFile
);
1245 } // BOOLEAN HasSignedCounterpart()
1247 // Scan an individual directory for EFI boot loader files and, if found,
1248 // add them to the list. Exception: Ignores FALLBACK_FULLNAME, which is picked
1249 // up in ScanEfiFiles(). Sorts the entries within the loader directory so that
1250 // the most recent one appears first in the list.
1251 // Returns TRUE if a duplicate for FALLBACK_FILENAME was found, FALSE if not.
1252 static BOOLEAN
ScanLoaderDir(IN REFIT_VOLUME
*Volume
, IN CHAR16
*Path
, IN CHAR16
*Pattern
)
1255 REFIT_DIR_ITER DirIter
;
1256 EFI_FILE_INFO
*DirEntry
;
1257 CHAR16 FileName
[256], *Extension
;
1258 struct LOADER_LIST
*LoaderList
= NULL
, *NewLoader
;
1259 BOOLEAN FoundFallbackDuplicate
= FALSE
;
1261 if ((!SelfDirPath
|| !Path
|| ((StriCmp(Path
, SelfDirPath
) == 0) && (Volume
->DeviceHandle
!= SelfVolume
->DeviceHandle
)) ||
1262 (StriCmp(Path
, SelfDirPath
) != 0)) &&
1263 (ShouldScan(Volume
, Path
))) {
1264 // look through contents of the directory
1265 DirIterOpen(Volume
->RootDir
, Path
, &DirIter
);
1266 while (DirIterNext(&DirIter
, 2, Pattern
, &DirEntry
)) {
1267 Extension
= FindExtension(DirEntry
->FileName
);
1268 if (DirEntry
->FileName
[0] == '.' ||
1269 StriCmp(Extension
, L
".icns") == 0 ||
1270 StriCmp(Extension
, L
".png") == 0 ||
1271 (StriCmp(DirEntry
->FileName
, FALLBACK_BASENAME
) == 0 && (StriCmp(Path
, L
"EFI\\BOOT") == 0)) ||
1272 StriSubCmp(L
"shell", DirEntry
->FileName
) ||
1273 IsSymbolicLink(Volume
, Path
, DirEntry
) || /* is symbolic link */
1274 HasSignedCounterpart(Volume
, Path
, DirEntry
->FileName
) || /* a file with same name plus ".efi.signed" is present */
1275 FilenameIn(Volume
, Path
, DirEntry
->FileName
, GlobalConfig
.DontScanFiles
))
1276 continue; // skip this
1279 SPrint(FileName
, 255, L
"\\%s\\%s", Path
, DirEntry
->FileName
);
1281 SPrint(FileName
, 255, L
"\\%s", DirEntry
->FileName
);
1282 CleanUpPathNameSlashes(FileName
);
1284 if(!IsValidLoader(Volume
->RootDir
, FileName
))
1287 NewLoader
= AllocateZeroPool(sizeof(struct LOADER_LIST
));
1288 if (NewLoader
!= NULL
) {
1289 NewLoader
->FileName
= StrDuplicate(FileName
);
1290 NewLoader
->TimeStamp
= DirEntry
->ModificationTime
;
1291 LoaderList
= AddLoaderListEntry(LoaderList
, NewLoader
);
1292 if (DuplicatesFallback(Volume
, FileName
))
1293 FoundFallbackDuplicate
= TRUE
;
1295 MyFreePool(Extension
);
1298 NewLoader
= LoaderList
;
1299 while (NewLoader
!= NULL
) {
1300 AddLoaderEntry(NewLoader
->FileName
, NULL
, Volume
);
1301 NewLoader
= NewLoader
->NextEntry
;
1304 CleanUpLoaderList(LoaderList
);
1305 Status
= DirIterClose(&DirIter
);
1306 // NOTE: EFI_INVALID_PARAMETER really is an error that should be reported;
1307 // but I've gotten reports from users who are getting this error occasionally
1308 // and I can't find anything wrong or reproduce the problem, so I'm putting
1309 // it down to buggy EFI implementations and ignoring that particular error....
1310 if ((Status
!= EFI_NOT_FOUND
) && (Status
!= EFI_INVALID_PARAMETER
)) {
1312 SPrint(FileName
, 255, L
"while scanning the %s directory", Path
);
1314 StrCpy(FileName
, L
"while scanning the root directory");
1315 CheckError(Status
, FileName
);
1316 } // if (Status != EFI_NOT_FOUND)
1317 } // if not scanning a blacklisted directory
1319 return FoundFallbackDuplicate
;
1320 } /* static VOID ScanLoaderDir() */
1322 static VOID
ScanEfiFiles(REFIT_VOLUME
*Volume
) {
1324 REFIT_DIR_ITER EfiDirIter
;
1325 EFI_FILE_INFO
*EfiDirEntry
;
1326 CHAR16 FileName
[256], *Directory
= NULL
, *MatchPatterns
, *VolName
= NULL
, *SelfPath
;
1328 BOOLEAN ScanFallbackLoader
= TRUE
;
1329 BOOLEAN FoundBRBackup
= FALSE
;
1331 MatchPatterns
= StrDuplicate(LOADER_MATCH_PATTERNS
);
1332 if (GlobalConfig
.ScanAllLinux
)
1333 MergeStrings(&MatchPatterns
, LINUX_MATCH_PATTERNS
, L
',');
1335 if ((Volume
->RootDir
!= NULL
) && (Volume
->VolName
!= NULL
) && (Volume
->IsReadable
)) {
1336 // check for Mac OS X boot loader
1337 if (ShouldScan(Volume
, L
"System\\Library\\CoreServices")) {
1338 StrCpy(FileName
, MACOSX_LOADER_PATH
);
1339 if (FileExists(Volume
->RootDir
, FileName
) && !FilenameIn(Volume
, Directory
, L
"boot.efi", GlobalConfig
.DontScanFiles
)) {
1340 AddLoaderEntry(FileName
, L
"Mac OS X", Volume
);
1341 if (DuplicatesFallback(Volume
, FileName
))
1342 ScanFallbackLoader
= FALSE
;
1346 StrCpy(FileName
, L
"System\\Library\\CoreServices\\xom.efi");
1347 if (FileExists(Volume
->RootDir
, FileName
) && !FilenameIn(Volume
, Directory
, L
"boot.efi", GlobalConfig
.DontScanFiles
)) {
1348 AddLoaderEntry(FileName
, L
"Windows XP (XoM)", Volume
);
1349 if (DuplicatesFallback(Volume
, FileName
))
1350 ScanFallbackLoader
= FALSE
;
1352 } // if should scan Mac directory
1354 // check for Microsoft boot loader/menu
1355 if (ShouldScan(Volume
, L
"EFI\\Microsoft\\Boot")) {
1356 StrCpy(FileName
, L
"EFI\\Microsoft\\Boot\\bkpbootmgfw.efi");
1357 if (FileExists(Volume
->RootDir
, FileName
) && !FilenameIn(Volume
, Directory
, L
"bkpbootmgfw.efi", GlobalConfig
.DontScanFiles
)) {
1358 AddLoaderEntry(FileName
, L
"Microsoft EFI boot (Boot Repair backup)", Volume
);
1359 FoundBRBackup
= TRUE
;
1360 if (DuplicatesFallback(Volume
, FileName
))
1361 ScanFallbackLoader
= FALSE
;
1363 StrCpy(FileName
, L
"EFI\\Microsoft\\Boot\\bootmgfw.efi");
1364 if (FileExists(Volume
->RootDir
, FileName
) && !FilenameIn(Volume
, Directory
, L
"bootmgfw.efi", GlobalConfig
.DontScanFiles
)) {
1366 AddLoaderEntry(FileName
, L
"Supposed Microsoft EFI boot (probably GRUB)", Volume
);
1368 AddLoaderEntry(FileName
, L
"Microsoft EFI boot", Volume
);
1369 if (DuplicatesFallback(Volume
, FileName
))
1370 ScanFallbackLoader
= FALSE
;
1374 // scan the root directory for EFI executables
1375 if (ScanLoaderDir(Volume
, L
"\\", MatchPatterns
))
1376 ScanFallbackLoader
= FALSE
;
1378 // scan subdirectories of the EFI directory (as per the standard)
1379 DirIterOpen(Volume
->RootDir
, L
"EFI", &EfiDirIter
);
1380 while (DirIterNext(&EfiDirIter
, 1, NULL
, &EfiDirEntry
)) {
1381 if (StriCmp(EfiDirEntry
->FileName
, L
"tools") == 0 || EfiDirEntry
->FileName
[0] == '.')
1382 continue; // skip this, doesn't contain boot loaders or is scanned later
1383 SPrint(FileName
, 255, L
"EFI\\%s", EfiDirEntry
->FileName
);
1384 if (ScanLoaderDir(Volume
, FileName
, MatchPatterns
))
1385 ScanFallbackLoader
= FALSE
;
1387 Status
= DirIterClose(&EfiDirIter
);
1388 if (Status
!= EFI_NOT_FOUND
)
1389 CheckError(Status
, L
"while scanning the EFI directory");
1391 // Scan user-specified (or additional default) directories....
1393 while ((Directory
= FindCommaDelimited(GlobalConfig
.AlsoScan
, i
++)) != NULL
) {
1394 if (ShouldScan(Volume
, Directory
)) {
1395 SplitVolumeAndFilename(&Directory
, &VolName
);
1396 CleanUpPathNameSlashes(Directory
);
1397 Length
= StrLen(Directory
);
1398 if ((Length
> 0) && ScanLoaderDir(Volume
, Directory
, MatchPatterns
))
1399 ScanFallbackLoader
= FALSE
;
1400 MyFreePool(VolName
);
1401 } // if should scan dir
1402 MyFreePool(Directory
);
1405 // Don't scan the fallback loader if it's on the same volume and a duplicate of rEFInd itself....
1406 SelfPath
= DevicePathToStr(SelfLoadedImage
->FilePath
);
1407 CleanUpPathNameSlashes(SelfPath
);
1408 if ((Volume
->DeviceHandle
== SelfLoadedImage
->DeviceHandle
) && DuplicatesFallback(Volume
, SelfPath
))
1409 ScanFallbackLoader
= FALSE
;
1411 // If not a duplicate & if it exists & if it's not us, create an entry
1412 // for the fallback boot loader
1413 if (ScanFallbackLoader
&& FileExists(Volume
->RootDir
, FALLBACK_FULLNAME
) && ShouldScan(Volume
, L
"EFI\\BOOT"))
1414 AddLoaderEntry(FALLBACK_FULLNAME
, L
"Fallback boot loader", Volume
);
1416 } // static VOID ScanEfiFiles()
1418 // Scan internal disks for valid EFI boot loaders....
1419 static VOID
ScanInternal(VOID
) {
1422 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1423 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_INTERNAL
) {
1424 ScanEfiFiles(Volumes
[VolumeIndex
]);
1427 } // static VOID ScanInternal()
1429 // Scan external disks for valid EFI boot loaders....
1430 static VOID
ScanExternal(VOID
) {
1433 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1434 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_EXTERNAL
) {
1435 ScanEfiFiles(Volumes
[VolumeIndex
]);
1438 } // static VOID ScanExternal()
1440 // Scan internal disks for valid EFI boot loaders....
1441 static VOID
ScanOptical(VOID
) {
1444 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1445 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_OPTICAL
) {
1446 ScanEfiFiles(Volumes
[VolumeIndex
]);
1449 } // static VOID ScanOptical()
1452 // legacy boot functions
1455 static EFI_STATUS
ActivateMbrPartition(IN EFI_BLOCK_IO
*BlockIO
, IN UINTN PartitionIndex
)
1458 UINT8 SectorBuffer
[512];
1459 MBR_PARTITION_INFO
*MbrTable
, *EMbrTable
;
1460 UINT32 ExtBase
, ExtCurrent
, NextExtCurrent
;
1461 UINTN LogicalPartitionIndex
= 4;
1463 BOOLEAN HaveBootCode
;
1466 Status
= refit_call5_wrapper(BlockIO
->ReadBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, 0, 512, SectorBuffer
);
1467 if (EFI_ERROR(Status
))
1469 if (*((UINT16
*)(SectorBuffer
+ 510)) != 0xaa55)
1470 return EFI_NOT_FOUND
; // safety measure #1
1472 // add boot code if necessary
1473 HaveBootCode
= FALSE
;
1474 for (i
= 0; i
< MBR_BOOTCODE_SIZE
; i
++) {
1475 if (SectorBuffer
[i
] != 0) {
1476 HaveBootCode
= TRUE
;
1480 if (!HaveBootCode
) {
1481 // no boot code found in the MBR, add the syslinux MBR code
1482 SetMem(SectorBuffer
, MBR_BOOTCODE_SIZE
, 0);
1483 CopyMem(SectorBuffer
, syslinux_mbr
, SYSLINUX_MBR_SIZE
);
1486 // set the partition active
1487 MbrTable
= (MBR_PARTITION_INFO
*)(SectorBuffer
+ 446);
1489 for (i
= 0; i
< 4; i
++) {
1490 if (MbrTable
[i
].Flags
!= 0x00 && MbrTable
[i
].Flags
!= 0x80)
1491 return EFI_NOT_FOUND
; // safety measure #2
1492 if (i
== PartitionIndex
)
1493 MbrTable
[i
].Flags
= 0x80;
1494 else if (PartitionIndex
>= 4 && IS_EXTENDED_PART_TYPE(MbrTable
[i
].Type
)) {
1495 MbrTable
[i
].Flags
= 0x80;
1496 ExtBase
= MbrTable
[i
].StartLBA
;
1498 MbrTable
[i
].Flags
= 0x00;
1502 Status
= refit_call5_wrapper(BlockIO
->WriteBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, 0, 512, SectorBuffer
);
1503 if (EFI_ERROR(Status
))
1506 if (PartitionIndex
>= 4) {
1507 // we have to activate a logical partition, so walk the EMBR chain
1509 // NOTE: ExtBase was set above while looking at the MBR table
1510 for (ExtCurrent
= ExtBase
; ExtCurrent
; ExtCurrent
= NextExtCurrent
) {
1511 // read current EMBR
1512 Status
= refit_call5_wrapper(BlockIO
->ReadBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, ExtCurrent
, 512, SectorBuffer
);
1513 if (EFI_ERROR(Status
))
1515 if (*((UINT16
*)(SectorBuffer
+ 510)) != 0xaa55)
1516 return EFI_NOT_FOUND
; // safety measure #3
1518 // scan EMBR, set appropriate partition active
1519 EMbrTable
= (MBR_PARTITION_INFO
*)(SectorBuffer
+ 446);
1521 for (i
= 0; i
< 4; i
++) {
1522 if (EMbrTable
[i
].Flags
!= 0x00 && EMbrTable
[i
].Flags
!= 0x80)
1523 return EFI_NOT_FOUND
; // safety measure #4
1524 if (EMbrTable
[i
].StartLBA
== 0 || EMbrTable
[i
].Size
== 0)
1526 if (IS_EXTENDED_PART_TYPE(EMbrTable
[i
].Type
)) {
1527 // link to next EMBR
1528 NextExtCurrent
= ExtBase
+ EMbrTable
[i
].StartLBA
;
1529 EMbrTable
[i
].Flags
= (PartitionIndex
>= LogicalPartitionIndex
) ? 0x80 : 0x00;
1532 // logical partition
1533 EMbrTable
[i
].Flags
= (PartitionIndex
== LogicalPartitionIndex
) ? 0x80 : 0x00;
1534 LogicalPartitionIndex
++;
1538 // write current EMBR
1539 Status
= refit_call5_wrapper(BlockIO
->WriteBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, ExtCurrent
, 512, SectorBuffer
);
1540 if (EFI_ERROR(Status
))
1543 if (PartitionIndex
< LogicalPartitionIndex
)
1544 break; // stop the loop, no need to touch further EMBRs
1550 } /* static EFI_STATUS ActivateMbrPartition() */
1552 // early 2006 Core Duo / Core Solo models
1553 static UINT8 LegacyLoaderDevicePath1Data
[] = {
1554 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1555 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1556 0xFF, 0xFF, 0xF9, 0xFF, 0x00, 0x00, 0x00, 0x00,
1557 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1558 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1559 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1561 // mid-2006 Mac Pro (and probably other Core 2 models)
1562 static UINT8 LegacyLoaderDevicePath2Data
[] = {
1563 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1564 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1565 0xFF, 0xFF, 0xF7, 0xFF, 0x00, 0x00, 0x00, 0x00,
1566 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1567 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1568 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1570 // mid-2007 MBP ("Santa Rosa" based models)
1571 static UINT8 LegacyLoaderDevicePath3Data
[] = {
1572 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1573 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1574 0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
1575 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1576 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1577 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1580 static UINT8 LegacyLoaderDevicePath4Data
[] = {
1581 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1582 0x00, 0x00, 0xC0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1583 0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
1584 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1585 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1586 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1588 // late-2008 MB/MBP (NVidia chipset)
1589 static UINT8 LegacyLoaderDevicePath5Data
[] = {
1590 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1591 0x00, 0x40, 0xCB, 0xFF, 0x00, 0x00, 0x00, 0x00,
1592 0xFF, 0xBF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
1593 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1594 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1595 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1598 static EFI_DEVICE_PATH
*LegacyLoaderList
[] = {
1599 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath1Data
,
1600 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath2Data
,
1601 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath3Data
,
1602 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath4Data
,
1603 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath5Data
,
1607 #define MAX_DISCOVERED_PATHS (16)
1609 static VOID
StartLegacy(IN LEGACY_ENTRY
*Entry
)
1612 EG_IMAGE
*BootLogoImage
;
1613 UINTN ErrorInStep
= 0;
1614 EFI_DEVICE_PATH
*DiscoveredPathList
[MAX_DISCOVERED_PATHS
];
1616 BeginExternalScreen(TRUE
, L
"Booting Legacy OS (Mac mode)");
1618 BootLogoImage
= LoadOSIcon(Entry
->Volume
->OSIconName
, L
"legacy", TRUE
);
1619 if (BootLogoImage
!= NULL
)
1620 BltImageAlpha(BootLogoImage
,
1621 (UGAWidth
- BootLogoImage
->Width
) >> 1,
1622 (UGAHeight
- BootLogoImage
->Height
) >> 1,
1623 &StdBackgroundPixel
);
1625 if (Entry
->Volume
->IsMbrPartition
) {
1626 ActivateMbrPartition(Entry
->Volume
->WholeDiskBlockIO
, Entry
->Volume
->MbrPartitionIndex
);
1629 ExtractLegacyLoaderPaths(DiscoveredPathList
, MAX_DISCOVERED_PATHS
, LegacyLoaderList
);
1631 Status
= StartEFIImageList(DiscoveredPathList
, Entry
->LoadOptions
, TYPE_LEGACY
, L
"legacy loader", 0, &ErrorInStep
, TRUE
);
1632 if (Status
== EFI_NOT_FOUND
) {
1633 if (ErrorInStep
== 1) {
1634 Print(L
"\nPlease make sure that you have the latest firmware update installed.\n");
1635 } else if (ErrorInStep
== 3) {
1636 Print(L
"\nThe firmware refused to boot from the selected volume. Note that external\n"
1637 L
"hard drives are not well-supported by Apple's firmware for legacy OS booting.\n");
1640 FinishExternalScreen();
1641 } /* static VOID StartLegacy() */
1643 // Start a device on a non-Mac using the EFI_LEGACY_BIOS_PROTOCOL
1644 #ifdef __MAKEWITH_TIANO
1645 static VOID
StartLegacyUEFI(LEGACY_ENTRY
*Entry
)
1647 // UINTN ExitDataSize = 0;
1648 // CHAR16 *ExitData = NULL;
1649 // EFI_STATUS Status;
1651 BeginExternalScreen(TRUE
, L
"Booting Legacy OS (UEFI mode)");
1652 // Print(L"Launching from '%s'\n", DevicePathToStr(Entry->BdsOption->DevicePath));
1655 // Status = BdsLibBootViaBootOption(Entry->BdsOption, Entry->BdsOption->DevicePath, &ExitDataSize, &ExitData);
1656 // Print(L"BdsLibBootViaBootOption() returned %d\n", Status);
1657 BdsLibConnectDevicePath (Entry
->BdsOption
->DevicePath
);
1658 BdsLibDoLegacyBoot(Entry
->BdsOption
);
1660 // If we get here, it means that there was a failure....
1661 Print(L
"Failure booting legacy (BIOS) OS.");
1663 FinishExternalScreen();
1664 } // static VOID StartLegacyUEFI()
1665 #endif // __MAKEWITH_TIANO
1667 static LEGACY_ENTRY
* AddLegacyEntry(IN CHAR16
*LoaderTitle
, IN REFIT_VOLUME
*Volume
)
1669 LEGACY_ENTRY
*Entry
, *SubEntry
;
1670 REFIT_MENU_SCREEN
*SubScreen
;
1672 CHAR16 ShortcutLetter
= 0;
1674 if (LoaderTitle
== NULL
) {
1675 if (Volume
->OSName
!= NULL
) {
1676 LoaderTitle
= Volume
->OSName
;
1677 if (LoaderTitle
[0] == 'W' || LoaderTitle
[0] == 'L')
1678 ShortcutLetter
= LoaderTitle
[0];
1680 LoaderTitle
= L
"Legacy OS";
1682 if (Volume
->VolName
!= NULL
)
1683 VolDesc
= Volume
->VolName
;
1685 VolDesc
= (Volume
->DiskKind
== DISK_KIND_OPTICAL
) ? L
"CD" : L
"HD";
1687 // prepare the menu entry
1688 Entry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1689 Entry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1690 SPrint(Entry
->me
.Title
, 255, L
"Boot %s from %s", LoaderTitle
, VolDesc
);
1691 Entry
->me
.Tag
= TAG_LEGACY
;
1693 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1694 Entry
->me
.Image
= LoadOSIcon(Volume
->OSIconName
, L
"legacy", FALSE
);
1695 Entry
->me
.BadgeImage
= Volume
->VolBadgeImage
;
1696 Entry
->Volume
= Volume
;
1697 Entry
->LoadOptions
= (Volume
->DiskKind
== DISK_KIND_OPTICAL
) ? L
"CD" :
1698 ((Volume
->DiskKind
== DISK_KIND_EXTERNAL
) ? L
"USB" : L
"HD");
1699 Entry
->Enabled
= TRUE
;
1701 // create the submenu
1702 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
1703 SubScreen
->Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1704 SPrint(SubScreen
->Title
, 255, L
"Boot Options for %s on %s", LoaderTitle
, VolDesc
);
1705 SubScreen
->TitleImage
= Entry
->me
.Image
;
1706 SubScreen
->Hint1
= StrDuplicate(SUBSCREEN_HINT1
);
1707 if (GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_EDITOR
) {
1708 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2_NO_EDITOR
);
1710 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2
);
1714 SubEntry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1715 SubEntry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1716 SPrint(SubEntry
->me
.Title
, 255, L
"Boot %s", LoaderTitle
);
1717 SubEntry
->me
.Tag
= TAG_LEGACY
;
1718 SubEntry
->Volume
= Entry
->Volume
;
1719 SubEntry
->LoadOptions
= Entry
->LoadOptions
;
1720 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
1722 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
1723 Entry
->me
.SubScreen
= SubScreen
;
1724 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1726 } /* static LEGACY_ENTRY * AddLegacyEntry() */
1729 #ifdef __MAKEWITH_GNUEFI
1730 static VOID
ScanLegacyUEFI(IN UINTN DiskType
){}
1732 // default volume badge icon based on disk kind
1733 static EG_IMAGE
* GetDiskBadge(IN UINTN DiskType
) {
1734 EG_IMAGE
* Badge
= NULL
;
1738 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_INTERNAL
);
1741 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_EXTERNAL
);
1744 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_OPTICAL
);
1748 } // static EG_IMAGE * GetDiskBadge()
1751 Create a rEFInd boot option from a Legacy BIOS protocol option.
1753 static LEGACY_ENTRY
* AddLegacyEntryUEFI(BDS_COMMON_OPTION
*BdsOption
, IN UINT16 DiskType
)
1755 LEGACY_ENTRY
*Entry
, *SubEntry
;
1756 REFIT_MENU_SCREEN
*SubScreen
;
1757 CHAR16 ShortcutLetter
= 0;
1758 CHAR16
*LegacyDescription
= BdsOption
->Description
;
1760 // prepare the menu entry
1761 Entry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1762 Entry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1763 SPrint(Entry
->me
.Title
, 255, L
"Boot legacy target %s", LegacyDescription
);
1764 Entry
->me
.Tag
= TAG_LEGACY_UEFI
;
1766 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1767 Entry
->me
.Image
= LoadOSIcon(L
"legacy", L
"legacy", TRUE
);
1768 Entry
->LoadOptions
= (DiskType
== BBS_CDROM
) ? L
"CD" :
1769 ((DiskType
== BBS_USB
) ? L
"USB" : L
"HD");
1770 Entry
->me
.BadgeImage
= GetDiskBadge(DiskType
);
1771 Entry
->BdsOption
= BdsOption
;
1772 Entry
->Enabled
= TRUE
;
1774 // create the submenu
1775 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
1776 SubScreen
->Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1777 SPrint(SubScreen
->Title
, 255, L
"No boot options for legacy target");
1778 SubScreen
->TitleImage
= Entry
->me
.Image
;
1779 SubScreen
->Hint1
= StrDuplicate(SUBSCREEN_HINT1
);
1780 if (GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_EDITOR
) {
1781 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2_NO_EDITOR
);
1783 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2
);
1787 SubEntry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1788 SubEntry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1789 SPrint(SubEntry
->me
.Title
, 255, L
"Boot %s", LegacyDescription
);
1790 SubEntry
->me
.Tag
= TAG_LEGACY_UEFI
;
1791 Entry
->BdsOption
= BdsOption
;
1792 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
1794 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
1795 Entry
->me
.SubScreen
= SubScreen
;
1796 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1798 } /* static LEGACY_ENTRY * AddLegacyEntryUEFI() */
1801 Scan for legacy BIOS targets on machines that implement EFI_LEGACY_BIOS_PROTOCOL.
1802 In testing, protocol has not been implemented on Macs but has been
1803 implemented on several Dell PCs and an ASUS motherboard.
1804 Restricts output to disks of the specified DiskType.
1806 static VOID
ScanLegacyUEFI(IN UINTN DiskType
)
1809 EFI_LEGACY_BIOS_PROTOCOL
*LegacyBios
;
1810 UINT16
*BootOrder
= NULL
;
1812 CHAR16 BootOption
[10];
1813 UINTN BootOrderSize
= 0;
1815 BDS_COMMON_OPTION
*BdsOption
;
1816 LIST_ENTRY TempList
;
1817 BBS_BBS_DEVICE_PATH
* BbsDevicePath
= NULL
;
1819 InitializeListHead (&TempList
);
1820 ZeroMem (Buffer
, sizeof (Buffer
));
1822 // If LegacyBios protocol is not implemented on this platform, then
1823 //we do not support this type of legacy boot on this machine.
1824 Status
= gBS
->LocateProtocol(&gEfiLegacyBootProtocolGuid
, NULL
, (VOID
**) &LegacyBios
);
1825 if (EFI_ERROR (Status
))
1828 // Grab the boot order
1829 BootOrder
= BdsLibGetVariableAndSize(L
"BootOrder", &gEfiGlobalVariableGuid
, &BootOrderSize
);
1830 if (BootOrder
== NULL
) {
1835 while (Index
< BootOrderSize
/ sizeof (UINT16
))
1837 // Grab each boot option variable from the boot order, and convert
1838 // the variable into a BDS boot option
1839 UnicodeSPrint (BootOption
, sizeof (BootOption
), L
"Boot%04x", BootOrder
[Index
]);
1840 BdsOption
= BdsLibVariableToOption (&TempList
, BootOption
);
1842 if (BdsOption
!= NULL
) {
1843 BbsDevicePath
= (BBS_BBS_DEVICE_PATH
*)BdsOption
->DevicePath
;
1845 // Only add the entry if it is of a requested type (e.g. USB, HD)
1847 // Two checks necessary because some systems return EFI boot loaders
1848 // with a DeviceType value that would inappropriately include them
1849 // as legacy loaders....
1850 if ((BbsDevicePath
->DeviceType
== DiskType
) && (BdsOption
->DevicePath
->Type
== DEVICE_TYPE_BIOS
)) {
1851 AddLegacyEntryUEFI(BdsOption
, BbsDevicePath
->DeviceType
);
1856 } /* static VOID ScanLegacyUEFI() */
1857 #endif // __MAKEWITH_GNUEFI
1859 static VOID
ScanLegacyVolume(REFIT_VOLUME
*Volume
, UINTN VolumeIndex
) {
1861 BOOLEAN ShowVolume
, HideIfOthersFound
;
1864 HideIfOthersFound
= FALSE
;
1865 if (Volume
->IsAppleLegacy
) {
1867 HideIfOthersFound
= TRUE
;
1868 } else if (Volume
->HasBootCode
) {
1870 if (Volume
->BlockIO
== Volume
->WholeDiskBlockIO
&&
1871 Volume
->BlockIOOffset
== 0 &&
1872 Volume
->OSName
== NULL
)
1873 // this is a whole disk (MBR) entry; hide if we have entries for partitions
1874 HideIfOthersFound
= TRUE
;
1876 if (HideIfOthersFound
) {
1877 // check for other bootable entries on the same disk
1878 for (VolumeIndex2
= 0; VolumeIndex2
< VolumesCount
; VolumeIndex2
++) {
1879 if (VolumeIndex2
!= VolumeIndex
&& Volumes
[VolumeIndex2
]->HasBootCode
&&
1880 Volumes
[VolumeIndex2
]->WholeDiskBlockIO
== Volume
->WholeDiskBlockIO
)
1886 AddLegacyEntry(NULL
, Volume
);
1887 } // static VOID ScanLegacyVolume()
1889 // Scan attached optical discs for legacy (BIOS) boot code
1890 // and add anything found to the list....
1891 static VOID
ScanLegacyDisc(VOID
)
1894 REFIT_VOLUME
*Volume
;
1896 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
) {
1897 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1898 Volume
= Volumes
[VolumeIndex
];
1899 if (Volume
->DiskKind
== DISK_KIND_OPTICAL
)
1900 ScanLegacyVolume(Volume
, VolumeIndex
);
1902 } else if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
1903 ScanLegacyUEFI(BBS_CDROM
);
1905 } /* static VOID ScanLegacyDisc() */
1907 // Scan internal hard disks for legacy (BIOS) boot code
1908 // and add anything found to the list....
1909 static VOID
ScanLegacyInternal(VOID
)
1912 REFIT_VOLUME
*Volume
;
1914 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
) {
1915 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1916 Volume
= Volumes
[VolumeIndex
];
1917 if (Volume
->DiskKind
== DISK_KIND_INTERNAL
)
1918 ScanLegacyVolume(Volume
, VolumeIndex
);
1920 } else if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
1921 ScanLegacyUEFI(BBS_HARDDISK
);
1923 } /* static VOID ScanLegacyInternal() */
1925 // Scan external disks for legacy (BIOS) boot code
1926 // and add anything found to the list....
1927 static VOID
ScanLegacyExternal(VOID
)
1930 REFIT_VOLUME
*Volume
;
1932 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
) {
1933 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1934 Volume
= Volumes
[VolumeIndex
];
1935 if (Volume
->DiskKind
== DISK_KIND_EXTERNAL
)
1936 ScanLegacyVolume(Volume
, VolumeIndex
);
1938 } else if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
1939 ScanLegacyUEFI(BBS_USB
);
1941 } /* static VOID ScanLegacyExternal() */
1944 // pre-boot tool functions
1947 static VOID
StartTool(IN LOADER_ENTRY
*Entry
)
1949 BeginExternalScreen(Entry
->UseGraphicsMode
, Entry
->me
.Title
+ 6); // assumes "Start <title>" as assigned below
1950 StartEFIImage(Entry
->DevicePath
, Entry
->LoadOptions
, TYPE_EFI
,
1951 Basename(Entry
->LoaderPath
), Entry
->OSType
, NULL
, TRUE
);
1952 FinishExternalScreen();
1953 } /* static VOID StartTool() */
1955 static LOADER_ENTRY
* AddToolEntry(EFI_HANDLE DeviceHandle
, IN CHAR16
*LoaderPath
, IN CHAR16
*LoaderTitle
, IN EG_IMAGE
*Image
,
1956 IN CHAR16 ShortcutLetter
, IN BOOLEAN UseGraphicsMode
)
1958 LOADER_ENTRY
*Entry
;
1959 CHAR16
*TitleStr
= NULL
;
1961 Entry
= AllocateZeroPool(sizeof(LOADER_ENTRY
));
1963 TitleStr
= PoolPrint(L
"Start %s", LoaderTitle
);
1964 Entry
->me
.Title
= TitleStr
;
1965 Entry
->me
.Tag
= TAG_TOOL
;
1967 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1968 Entry
->me
.Image
= Image
;
1969 Entry
->LoaderPath
= (LoaderPath
) ? StrDuplicate(LoaderPath
) : NULL
;
1970 Entry
->DevicePath
= FileDevicePath(DeviceHandle
, Entry
->LoaderPath
);
1971 Entry
->UseGraphicsMode
= UseGraphicsMode
;
1973 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1975 } /* static LOADER_ENTRY * AddToolEntry() */
1978 // pre-boot driver functions
1981 static UINTN
ScanDriverDir(IN CHAR16
*Path
)
1984 REFIT_DIR_ITER DirIter
;
1986 EFI_FILE_INFO
*DirEntry
;
1987 CHAR16 FileName
[256];
1989 CleanUpPathNameSlashes(Path
);
1990 // look through contents of the directory
1991 DirIterOpen(SelfRootDir
, Path
, &DirIter
);
1992 while (DirIterNext(&DirIter
, 2, LOADER_MATCH_PATTERNS
, &DirEntry
)) {
1993 if (DirEntry
->FileName
[0] == '.')
1994 continue; // skip this
1996 SPrint(FileName
, 255, L
"%s\\%s", Path
, DirEntry
->FileName
);
1998 Status
= StartEFIImage(FileDevicePath(SelfLoadedImage
->DeviceHandle
, FileName
),
1999 L
"", TYPE_EFI
, DirEntry
->FileName
, 0, NULL
, FALSE
);
2001 Status
= DirIterClose(&DirIter
);
2002 if (Status
!= EFI_NOT_FOUND
) {
2003 SPrint(FileName
, 255, L
"while scanning the %s directory", Path
);
2004 CheckError(Status
, FileName
);
2009 #ifdef __MAKEWITH_GNUEFI
2010 static EFI_STATUS
ConnectAllDriversToAllControllers(VOID
)
2013 UINTN AllHandleCount
;
2014 EFI_HANDLE
*AllHandleBuffer
;
2017 EFI_HANDLE
*HandleBuffer
;
2023 Status
= LibLocateHandle(AllHandles
,
2028 if (EFI_ERROR(Status
))
2031 for (Index
= 0; Index
< AllHandleCount
; Index
++) {
2033 // Scan the handle database
2035 Status
= LibScanHandleDatabase(NULL
,
2037 AllHandleBuffer
[Index
],
2042 if (EFI_ERROR (Status
))
2046 if (HandleType
[Index
] & EFI_HANDLE_TYPE_DRIVER_BINDING_HANDLE
)
2048 if (HandleType
[Index
] & EFI_HANDLE_TYPE_IMAGE_HANDLE
)
2053 for (HandleIndex
= 0; HandleIndex
< HandleCount
; HandleIndex
++) {
2054 if (HandleType
[HandleIndex
] & EFI_HANDLE_TYPE_PARENT_HANDLE
)
2059 if (HandleType
[Index
] & EFI_HANDLE_TYPE_DEVICE_HANDLE
) {
2060 Status
= refit_call4_wrapper(BS
->ConnectController
,
2061 AllHandleBuffer
[Index
],
2069 MyFreePool (HandleBuffer
);
2070 MyFreePool (HandleType
);
2074 MyFreePool (AllHandleBuffer
);
2076 } /* EFI_STATUS ConnectAllDriversToAllControllers() */
2078 static EFI_STATUS
ConnectAllDriversToAllControllers(VOID
) {
2079 BdsLibConnectAllDriversToAllControllers();
2084 // Load all EFI drivers from rEFInd's "drivers" subdirectory and from the
2085 // directories specified by the user in the "scan_driver_dirs" configuration
2087 static VOID
LoadDrivers(VOID
)
2089 CHAR16
*Directory
, *SelfDirectory
;
2090 UINTN i
= 0, Length
, NumFound
= 0;
2092 // load drivers from the subdirectories of rEFInd's home directory specified
2093 // in the DRIVER_DIRS constant.
2094 while ((Directory
= FindCommaDelimited(DRIVER_DIRS
, i
++)) != NULL
) {
2095 SelfDirectory
= SelfDirPath
? StrDuplicate(SelfDirPath
) : NULL
;
2096 CleanUpPathNameSlashes(SelfDirectory
);
2097 MergeStrings(&SelfDirectory
, Directory
, L
'\\');
2098 NumFound
+= ScanDriverDir(SelfDirectory
);
2099 MyFreePool(Directory
);
2100 MyFreePool(SelfDirectory
);
2103 // Scan additional user-specified driver directories....
2105 while ((Directory
= FindCommaDelimited(GlobalConfig
.DriverDirs
, i
++)) != NULL
) {
2106 CleanUpPathNameSlashes(Directory
);
2107 Length
= StrLen(Directory
);
2109 NumFound
+= ScanDriverDir(Directory
);
2111 MyFreePool(Directory
);
2114 // connect all devices
2116 ConnectAllDriversToAllControllers();
2117 } /* static VOID LoadDrivers() */
2119 // Determine what (if any) type of legacy (BIOS) boot support is available
2120 static VOID
FindLegacyBootType(VOID
) {
2121 #ifdef __MAKEWITH_TIANO
2123 EFI_LEGACY_BIOS_PROTOCOL
*LegacyBios
;
2126 GlobalConfig
.LegacyType
= LEGACY_TYPE_NONE
;
2128 // UEFI-style legacy BIOS support is available only with the TianoCore EDK2
2129 // build environment, and then only with some EFI implementations....
2130 #ifdef __MAKEWITH_TIANO
2131 Status
= gBS
->LocateProtocol (&gEfiLegacyBootProtocolGuid
, NULL
, (VOID
**) &LegacyBios
);
2132 if (!EFI_ERROR (Status
))
2133 GlobalConfig
.LegacyType
= LEGACY_TYPE_UEFI
;
2136 // Macs have their own system. If the firmware vendor code contains the
2137 // string "Apple", assume it's available. Note that this overrides the
2138 // UEFI type, and might yield false positives if the vendor string
2139 // contains "Apple" as part of something bigger, so this isn't 100%
2141 if (StriSubCmp(L
"Apple", ST
->FirmwareVendor
))
2142 GlobalConfig
.LegacyType
= LEGACY_TYPE_MAC
;
2143 } // static VOID FindLegacyBootType
2145 // Warn the user if legacy OS scans are enabled but the firmware or this
2146 // application can't support them....
2147 static VOID
WarnIfLegacyProblems() {
2148 BOOLEAN found
= FALSE
;
2151 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_NONE
) {
2153 if (GlobalConfig
.ScanFor
[i
] == 'h' || GlobalConfig
.ScanFor
[i
] == 'b' || GlobalConfig
.ScanFor
[i
] == 'c')
2156 } while ((i
< NUM_SCAN_OPTIONS
) && (!found
));
2158 Print(L
"NOTE: refind.conf's 'scanfor' line specifies scanning for one or more legacy\n");
2159 Print(L
"(BIOS) boot options; however, this is not possible because ");
2160 #ifdef __MAKEWITH_TIANO
2161 Print(L
"your computer lacks\n");
2162 Print(L
"the necessary Compatibility Support Module (CSM) support.\n");
2164 Print(L
"this program was\n");
2165 Print(L
"compiled without the necessary support. Please visit\n");
2166 Print(L
"http://www.rodsbooks.com/refind/getting.html and download and install a rEFInd\n");
2167 Print(L
"binary built with the TianoCore EDK2 to enable legacy boot support.\n");
2171 } // if no legacy support
2172 } // static VOID WarnIfLegacyProblems()
2174 // Locates boot loaders. NOTE: This assumes that GlobalConfig.LegacyType is set correctly.
2175 static VOID
ScanForBootloaders(VOID
) {
2178 // if (GlobalConfig.LegacyType == LEGACY_TYPE_UEFI) {
2179 // Print(L"About to call BdsDeleteAllInvalidLegacyBootOptions()\n");
2180 // BdsDeleteAllInvalidLegacyBootOptions();
2181 // Print(L"About to call BdsAddNonExistingLegacyBootOptions()\n");
2182 // BdsAddNonExistingLegacyBootOptions();
2183 // Print(L"About to call BdsUpdateLegacyDevOrder()\n");
2184 // // BdsUpdateLegacyDevOrder(); // EXTREME CAUTION: HOSED ONE FIRMWARE!
2185 // Print(L"Done with legacy boot updates!\n");
2191 // scan for loaders and tools, add them to the menu
2192 for (i
= 0; i
< NUM_SCAN_OPTIONS
; i
++) {
2193 switch(GlobalConfig
.ScanFor
[i
]) {
2198 ScanLegacyInternal();
2201 ScanLegacyExternal();
2204 ScanUserConfigured(GlobalConfig
.ConfigFilename
);
2218 // assign shortcut keys
2219 for (i
= 0; i
< MainMenu
.EntryCount
&& MainMenu
.Entries
[i
]->Row
== 0 && i
< 9; i
++)
2220 MainMenu
.Entries
[i
]->ShortcutDigit
= (CHAR16
)('1' + i
);
2222 // wait for user ACK when there were errors
2223 FinishTextScreen(FALSE
);
2224 } // static VOID ScanForBootloaders()
2226 // Locate a single tool from the specified Locations using one of the
2227 // specified Names and add it to the menu.
2228 static VOID
FindTool(CHAR16
*Locations
, CHAR16
*Names
, CHAR16
*Description
, UINTN Icon
) {
2229 UINTN j
= 0, k
, VolumeIndex
;
2230 CHAR16
*DirName
, *FileName
, *PathName
, FullDescription
[256];
2232 while ((DirName
= FindCommaDelimited(Locations
, j
++)) != NULL
) {
2234 while ((FileName
= FindCommaDelimited(Names
, k
++)) != NULL
) {
2235 PathName
= StrDuplicate(DirName
);
2236 MergeStrings(&PathName
, FileName
, (StriCmp(PathName
, L
"\\") == 0) ? 0 : L
'\\');
2237 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
2238 if ((Volumes
[VolumeIndex
]->RootDir
!= NULL
) && (FileExists(Volumes
[VolumeIndex
]->RootDir
, PathName
))) {
2239 SPrint(FullDescription
, 255, L
"%s at %s on %s", Description
, PathName
, Volumes
[VolumeIndex
]->VolName
);
2240 AddToolEntry(Volumes
[VolumeIndex
]->DeviceHandle
, PathName
, FullDescription
, BuiltinIcon(Icon
), 'S', FALSE
);
2243 MyFreePool(PathName
);
2244 MyFreePool(FileName
);
2246 MyFreePool(DirName
);
2247 } // while Locations
2248 } // VOID FindTool()
2250 // Add the second-row tags containing built-in and external tools (EFI shell,
2252 static VOID
ScanForTools(VOID
) {
2253 CHAR16
*FileName
= NULL
, *VolName
= NULL
, *MokLocations
, Description
[256];
2254 REFIT_MENU_ENTRY
*TempMenuEntry
;
2255 UINTN i
, j
, VolumeIndex
;
2259 MokLocations
= StrDuplicate(MOK_LOCATIONS
);
2260 if (MokLocations
!= NULL
)
2261 MergeStrings(&MokLocations
, SelfDirPath
, L
',');
2263 for (i
= 0; i
< NUM_TOOLS
; i
++) {
2264 switch(GlobalConfig
.ShowTools
[i
]) {
2265 // NOTE: Be sure that FileName is NULL at the end of each case.
2267 TempMenuEntry
= CopyMenuEntry(&MenuEntryShutdown
);
2268 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_SHUTDOWN
);
2269 AddMenuEntry(&MainMenu
, TempMenuEntry
);
2273 TempMenuEntry
= CopyMenuEntry(&MenuEntryReset
);
2274 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_RESET
);
2275 AddMenuEntry(&MainMenu
, TempMenuEntry
);
2279 TempMenuEntry
= CopyMenuEntry(&MenuEntryAbout
);
2280 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT
);
2281 AddMenuEntry(&MainMenu
, TempMenuEntry
);
2285 TempMenuEntry
= CopyMenuEntry(&MenuEntryExit
);
2286 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_EXIT
);
2287 AddMenuEntry(&MainMenu
, TempMenuEntry
);
2291 if (EfivarGetRaw(&GlobalGuid
, L
"OsIndicationsSupported", &b
, &j
) == EFI_SUCCESS
) {
2293 if (osind
& EFI_OS_INDICATIONS_BOOT_TO_FW_UI
) {
2294 TempMenuEntry
= CopyMenuEntry(&MenuEntryFirmware
);
2295 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_FIRMWARE
);
2296 AddMenuEntry(&MainMenu
, TempMenuEntry
);
2303 while ((FileName
= FindCommaDelimited(SHELL_NAMES
, j
++)) != NULL
) {
2304 if (FileExists(SelfRootDir
, FileName
)) {
2305 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, L
"EFI Shell", BuiltinIcon(BUILTIN_ICON_TOOL_SHELL
),
2308 MyFreePool(FileName
);
2314 while ((FileName
= FindCommaDelimited(GPTSYNC_NAMES
, j
++)) != NULL
) {
2315 if (FileExists(SelfRootDir
, FileName
)) {
2316 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, L
"Hybrid MBR tool", BuiltinIcon(BUILTIN_ICON_TOOL_PART
),
2319 MyFreePool(FileName
);
2326 while ((FileName
= FindCommaDelimited(GDISK_NAMES
, j
++)) != NULL
) {
2327 if (FileExists(SelfRootDir
, FileName
)) {
2328 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, L
"disk partitioning tool",
2329 BuiltinIcon(BUILTIN_ICON_TOOL_PART
), 'G', FALSE
);
2331 MyFreePool(FileName
);
2336 case TAG_APPLE_RECOVERY
:
2337 FileName
= StrDuplicate(L
"\\com.apple.recovery.boot\\boot.efi");
2338 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
2339 if ((Volumes
[VolumeIndex
]->RootDir
!= NULL
) && (FileExists(Volumes
[VolumeIndex
]->RootDir
, FileName
))) {
2340 SPrint(Description
, 255, L
"Apple Recovery on %s", Volumes
[VolumeIndex
]->VolName
);
2341 AddToolEntry(Volumes
[VolumeIndex
]->DeviceHandle
, FileName
, Description
,
2342 BuiltinIcon(BUILTIN_ICON_TOOL_APPLE_RESCUE
), 'R', TRUE
);
2345 MyFreePool(FileName
);
2349 case TAG_WINDOWS_RECOVERY
:
2351 while ((FileName
= FindCommaDelimited(GlobalConfig
.WindowsRecoveryFiles
, j
++)) != NULL
) {
2352 SplitVolumeAndFilename(&FileName
, &VolName
);
2353 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
2354 if ((Volumes
[VolumeIndex
]->RootDir
!= NULL
) && (FileExists(Volumes
[VolumeIndex
]->RootDir
, FileName
)) &&
2355 ((VolName
== NULL
) || (StriCmp(VolName
, Volumes
[VolumeIndex
]->VolName
) == 0))) {
2356 SPrint(Description
, 255, L
"Microsoft Recovery on %s", Volumes
[VolumeIndex
]->VolName
);
2357 AddToolEntry(Volumes
[VolumeIndex
]->DeviceHandle
, FileName
, Description
,
2358 BuiltinIcon(BUILTIN_ICON_TOOL_WINDOWS_RESCUE
), 'R', TRUE
);
2362 MyFreePool(FileName
);
2364 MyFreePool(VolName
);
2369 FindTool(MokLocations
, MOK_NAMES
, L
"MOK utility", BUILTIN_ICON_TOOL_MOK_TOOL
);
2373 FindTool(MEMTEST_LOCATIONS
, MEMTEST_NAMES
, L
"Memory test utility", BUILTIN_ICON_TOOL_MEMTEST
);
2378 } // static VOID ScanForTools
2380 // Rescan for boot loaders
2381 VOID
RescanAll(VOID
) {
2388 egDisplayMessage(L
"Scanning for new boot loaders; please wait....", &BGColor
);
2389 FreeList((VOID
***) &(MainMenu
.Entries
), &MainMenu
.EntryCount
);
2390 MainMenu
.Entries
= NULL
;
2391 MainMenu
.EntryCount
= 0;
2392 ReadConfig(GlobalConfig
.ConfigFilename
);
2393 ConnectAllDriversToAllControllers();
2395 ScanForBootloaders();
2398 } // VOID RescanAll()
2400 #ifdef __MAKEWITH_TIANO
2402 // Minimal initialization function
2403 static VOID
InitializeLib(IN EFI_HANDLE ImageHandle
, IN EFI_SYSTEM_TABLE
*SystemTable
) {
2405 // gImageHandle = ImageHandle;
2406 gBS
= SystemTable
->BootServices
;
2407 // gRS = SystemTable->RuntimeServices;
2408 gRT
= SystemTable
->RuntimeServices
; // Some BDS functions need gRT to be set
2409 EfiGetSystemConfigurationTable (&gEfiDxeServicesTableGuid
, (VOID
**) &gDS
);
2411 InitializeConsoleSim();
2416 // Set up our own Secure Boot extensions....
2417 // Returns TRUE on success, FALSE otherwise
2418 static BOOLEAN
SecureBootSetup(VOID
) {
2420 BOOLEAN Success
= FALSE
;
2422 if (secure_mode() && ShimLoaded()) {
2423 Status
= security_policy_install();
2424 if (Status
== EFI_SUCCESS
) {
2427 Print(L
"Failed to install MOK Secure Boot extensions");
2431 } // VOID SecureBootSetup()
2433 // Remove our own Secure Boot extensions....
2434 // Returns TRUE on success, FALSE otherwise
2435 static BOOLEAN
SecureBootUninstall(VOID
) {
2437 BOOLEAN Success
= TRUE
;
2439 if (secure_mode()) {
2440 Status
= security_policy_uninstall();
2441 if (Status
!= EFI_SUCCESS
) {
2443 BeginTextScreen(L
"Secure Boot Policy Failure");
2444 Print(L
"Failed to uninstall MOK Secure Boot extensions; forcing a reboot.");
2446 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);
2450 } // VOID SecureBootUninstall
2452 // Sets the global configuration filename; will be CONFIG_FILE_NAME unless the
2453 // "-c" command-line option is set, in which case that takes precedence.
2454 // If an error is encountered, leaves the value alone (it should be set to
2455 // CONFIG_FILE_NAME when GlobalConfig is initialized).
2456 static VOID
SetConfigFilename(EFI_HANDLE ImageHandle
) {
2457 EFI_LOADED_IMAGE
*Info
;
2458 CHAR16
*Options
, *FileName
;
2462 Status
= refit_call3_wrapper(BS
->HandleProtocol
, ImageHandle
, &LoadedImageProtocol
, (VOID
**) &Info
);
2463 if ((Status
== EFI_SUCCESS
) && (Info
->LoadOptionsSize
> 0)) {
2464 Options
= (CHAR16
*) Info
->LoadOptions
;
2465 Where
= FindSubString(L
" -c ", Options
);
2467 FileName
= StrDuplicate(&Options
[Where
+ 4]);
2468 Where
= FindSubString(L
" ", FileName
);
2470 FileName
[Where
] = L
'\0';
2472 if (FileExists(SelfDir
, FileName
)) {
2473 GlobalConfig
.ConfigFilename
= FileName
;
2475 Print(L
"Specified configuration file (%s) doesn't exist; using\n'refind.conf' default\n", FileName
);
2476 MyFreePool(FileName
);
2480 } // VOID SetConfigFilename()
2487 efi_main (EFI_HANDLE ImageHandle
, EFI_SYSTEM_TABLE
*SystemTable
)
2490 BOOLEAN MainLoopRunning
= TRUE
;
2491 BOOLEAN MokProtocol
;
2492 REFIT_MENU_ENTRY
*ChosenEntry
;
2494 CHAR16
*Selection
= NULL
;
2498 InitializeLib(ImageHandle
, SystemTable
);
2499 Status
= InitRefitLib(ImageHandle
);
2500 if (EFI_ERROR(Status
))
2503 // read configuration
2504 CopyMem(GlobalConfig
.ScanFor
, "ieom ", NUM_SCAN_OPTIONS
);
2505 FindLegacyBootType();
2506 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
)
2507 CopyMem(GlobalConfig
.ScanFor
, "ihebocm ", NUM_SCAN_OPTIONS
);
2508 SetConfigFilename(ImageHandle
);
2509 ReadConfig(GlobalConfig
.ConfigFilename
);
2513 WarnIfLegacyProblems();
2514 MainMenu
.TimeoutSeconds
= GlobalConfig
.Timeout
;
2516 // disable EFI watchdog timer
2517 refit_call4_wrapper(BS
->SetWatchdogTimer
, 0x0000, 0x0000, 0x0000, NULL
);
2519 // further bootstrap (now with config available)
2520 MokProtocol
= SecureBootSetup();
2522 ScanForBootloaders();
2526 if (GlobalConfig
.ScanDelay
> 0) {
2531 egDisplayMessage(L
"Pausing before disk scan; please wait....", &BGColor
);
2532 for (i
= 0; i
< GlobalConfig
.ScanDelay
; i
++)
2533 refit_call1_wrapper(BS
->Stall
, 1000000);
2537 if (GlobalConfig
.DefaultSelection
)
2538 Selection
= StrDuplicate(GlobalConfig
.DefaultSelection
);
2540 while (MainLoopRunning
) {
2541 MenuExit
= RunMainMenu(&MainMenu
, Selection
, &ChosenEntry
);
2543 // The Escape key triggers a re-scan operation....
2544 if (MenuExit
== MENU_EXIT_ESCAPE
) {
2550 switch (ChosenEntry
->Tag
) {
2552 case TAG_REBOOT
: // Reboot
2554 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);
2555 MainLoopRunning
= FALSE
; // just in case we get this far
2558 case TAG_SHUTDOWN
: // Shut Down
2560 refit_call4_wrapper(RT
->ResetSystem
, EfiResetShutdown
, EFI_SUCCESS
, 0, NULL
);
2561 MainLoopRunning
= FALSE
; // just in case we get this far
2564 case TAG_ABOUT
: // About rEFInd
2568 case TAG_LOADER
: // Boot OS via .EFI loader
2569 StartLoader((LOADER_ENTRY
*)ChosenEntry
);
2572 case TAG_LEGACY
: // Boot legacy OS
2573 StartLegacy((LEGACY_ENTRY
*)ChosenEntry
);
2576 #ifdef __MAKEWITH_TIANO
2577 case TAG_LEGACY_UEFI
: // Boot a legacy OS on a non-Mac
2578 StartLegacyUEFI((LEGACY_ENTRY
*)ChosenEntry
);
2582 case TAG_TOOL
: // Start a EFI tool
2583 StartTool((LOADER_ENTRY
*)ChosenEntry
);
2586 case TAG_EXIT
: // Terminate rEFInd
2587 if ((MokProtocol
) && !SecureBootUninstall()) {
2588 MainLoopRunning
= FALSE
; // just in case we get this far
2590 BeginTextScreen(L
" ");
2595 case TAG_FIRMWARE
: // Reboot into firmware's user interface
2596 RebootIntoFirmware();
2600 MyFreePool(Selection
);
2601 Selection
= (ChosenEntry
->Title
) ? StrDuplicate(ChosenEntry
->Title
) : NULL
;
2604 // If we end up here, things have gone wrong. Try to reboot, and if that
2605 // fails, go into an endless loop.
2606 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);