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.
53 #include "security_policy.h"
54 #include "../include/Handle.h"
55 #include "../include/refit_call_wrapper.h"
56 #include "driver_support.h"
57 #include "../include/syslinux_mbr.h"
59 #ifdef __MAKEWITH_GNUEFI
60 #ifndef EFI_SECURITY_VIOLATION
61 #define EFI_SECURITY_VIOLATION EFIERR (26)
65 #include "../EfiLib/BdsHelper.h"
66 #include "../EfiLib/legacy.h"
68 #ifndef EFI_OS_INDICATIONS_BOOT_TO_FW_UI
69 #define EFI_OS_INDICATIONS_BOOT_TO_FW_UI 0x0000000000000001ULL
71 #define LibLocateHandle gBS->LocateHandleBuffer
77 #define MACOSX_LOADER_PATH L"System\\Library\\CoreServices\\boot.efi"
79 #define SHELL_NAMES L"\\EFI\\tools\\shell.efi,\\EFI\\tools\\shellx64.efi,\\shell.efi,\\shellx64.efi"
80 #define GPTSYNC_NAMES L"\\EFI\\tools\\gptsync.efi,\\EFI\\tools\\gptsync_x64.efi"
81 #define GDISK_NAMES L"\\EFI\\tools\\gdisk.efi,\\EFI\\tools\\gdisk_x64.efi"
82 #define MEMTEST_NAMES L"memtest86.efi,memtest86_x64.efi,memtest86x64.efi,bootx64.efi"
83 #define DRIVER_DIRS L"drivers,drivers_x64"
84 #define FALLBACK_FULLNAME L"EFI\\BOOT\\bootx64.efi"
85 #define FALLBACK_BASENAME L"bootx64.efi"
86 #define EFI_STUB_ARCH 0x8664
88 #define SHELL_NAMES L"\\EFI\\tools\\shell.efi,\\EFI\\tools\\shellia32.efi,\\shell.efi,\\shellia32.efi"
89 #define GPTSYNC_NAMES L"\\EFI\\tools\\gptsync.efi,\\EFI\\tools\\gptsync_ia32.efi"
90 #define GDISK_NAMES L"\\EFI\\tools\\gdisk.efi,\\EFI\\tools\\gdisk_ia32.efi"
91 #define MEMTEST_NAMES L"memtest86.efi,memtest86_ia32.efi,memtest86ia32.efi,bootia32.efi"
92 #define DRIVER_DIRS L"drivers,drivers_ia32"
93 #define FALLBACK_FULLNAME L"EFI\\BOOT\\bootia32.efi"
94 #define FALLBACK_BASENAME L"bootia32.efi"
95 #define EFI_STUB_ARCH 0x014c
97 #define SHELL_NAMES L"\\EFI\\tools\\shell.efi,\\shell.efi"
98 #define GPTSYNC_NAMES L"\\EFI\\tools\\gptsync.efi"
99 #define GDISK_NAMES L"\\EFI\\tools\\gdisk.efi"
100 #define MEMTEST_NAMES L"memtest86.efi"
101 #define DRIVER_DIRS L"drivers"
102 #define FALLBACK_FULLNAME L"EFI\\BOOT\\boot.efi" /* Not really correct */
103 #define FALLBACK_BASENAME L"boot.efi" /* Not really correct */
105 #define FAT_ARCH 0x0ef1fab9 /* ID for Apple "fat" binary */
107 // Filename patterns that identify EFI boot loaders. Note that a single case (either L"*.efi" or
108 // L"*.EFI") is fine for most systems; but Gigabyte's buggy Hybrid EFI does a case-sensitive
109 // comparison when it should do a case-insensitive comparison, so I'm doubling this up. It does
110 // no harm on other computers, AFAIK. In theory, every case variation should be done for
111 // completeness, but that's ridiculous....
112 #define LOADER_MATCH_PATTERNS L"*.efi,*.EFI"
114 // Patterns that identify Linux kernels. Added to the loader match pattern when the
115 // scan_all_linux_kernels option is set in the configuration file. Causes kernels WITHOUT
116 // a ".efi" extension to be found when scanning for boot loaders.
117 #define LINUX_MATCH_PATTERNS L"vmlinuz*,bzImage*"
119 // Default hint text for program-launch submenus
120 #define SUBSCREEN_HINT1 L"Use arrow keys to move cursor; Enter to boot;"
121 #define SUBSCREEN_HINT2 L"Insert or F2 to edit options; Esc to return to main menu"
122 #define SUBSCREEN_HINT2_NO_EDITOR L"Esc to return to main menu"
126 #define TYPE_LEGACY 2
128 static REFIT_MENU_ENTRY MenuEntryAbout
= { L
"About rEFInd", TAG_ABOUT
, 1, 0, 'A', NULL
, NULL
, NULL
};
129 static REFIT_MENU_ENTRY MenuEntryReset
= { L
"Reboot Computer", TAG_REBOOT
, 1, 0, 'R', NULL
, NULL
, NULL
};
130 static REFIT_MENU_ENTRY MenuEntryShutdown
= { L
"Shut Down Computer", TAG_SHUTDOWN
, 1, 0, 'U', NULL
, NULL
, NULL
};
131 static REFIT_MENU_ENTRY MenuEntryReturn
= { L
"Return to Main Menu", TAG_RETURN
, 1, 0, 0, NULL
, NULL
, NULL
};
132 static REFIT_MENU_ENTRY MenuEntryExit
= { L
"Exit rEFInd", TAG_EXIT
, 1, 0, 0, NULL
, NULL
, NULL
};
133 static REFIT_MENU_ENTRY MenuEntryFirmware
= { L
"Reboot to Computer Setup Utility", TAG_FIRMWARE
, 1, 0, 0, NULL
, NULL
, NULL
};
135 static REFIT_MENU_SCREEN MainMenu
= { L
"Main Menu", NULL
, 0, NULL
, 0, NULL
, 0, L
"Automatic boot",
136 L
"Use arrow keys to move cursor; Enter to boot;",
137 L
"Insert or F2 for more options; Esc to refresh" };
138 static REFIT_MENU_SCREEN AboutMenu
= { L
"About", NULL
, 0, NULL
, 0, NULL
, 0, NULL
, L
"Press Enter to return to main menu", L
"" };
140 REFIT_CONFIG GlobalConfig
= { FALSE
, FALSE
, 0, 0, 0, DONT_CHANGE_TEXT_MODE
, 20, 0, 0, GRAPHICS_FOR_OSX
, LEGACY_TYPE_MAC
, 0, 0,
141 { DEFAULT_BIG_ICON_SIZE
/ 4, DEFAULT_SMALL_ICON_SIZE
, DEFAULT_BIG_ICON_SIZE
}, BANNER_NOSCALE
,
142 NULL
, NULL
, CONFIG_FILE_NAME
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
143 { TAG_SHELL
, TAG_MEMTEST
, TAG_GDISK
, TAG_APPLE_RECOVERY
, TAG_WINDOWS_RECOVERY
, TAG_MOK_TOOL
,
144 TAG_ABOUT
, TAG_SHUTDOWN
, TAG_REBOOT
, TAG_FIRMWARE
, 0, 0, 0, 0, 0, 0 }
147 EFI_GUID GlobalGuid
= EFI_GLOBAL_VARIABLE
;
149 GPT_DATA
*gPartitions
= NULL
;
151 // Structure used to hold boot loader filenames and time stamps in
152 // a linked list; used to sort entries within a directory.
156 struct LOADER_LIST
*NextEntry
;
163 static VOID
AboutrEFInd(VOID
)
165 CHAR16
*FirmwareVendor
;
167 if (AboutMenu
.EntryCount
== 0) {
168 AboutMenu
.TitleImage
= BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT
);
169 AddMenuInfoLine(&AboutMenu
, L
"rEFInd Version 0.8.0.3");
170 AddMenuInfoLine(&AboutMenu
, L
"");
171 AddMenuInfoLine(&AboutMenu
, L
"Copyright (c) 2006-2010 Christoph Pfisterer");
172 AddMenuInfoLine(&AboutMenu
, L
"Copyright (c) 2012-2014 Roderick W. Smith");
173 AddMenuInfoLine(&AboutMenu
, L
"Portions Copyright (c) Intel Corporation and others");
174 AddMenuInfoLine(&AboutMenu
, L
"Distributed under the terms of the GNU GPLv3 license");
175 AddMenuInfoLine(&AboutMenu
, L
"");
176 AddMenuInfoLine(&AboutMenu
, L
"Running on:");
177 AddMenuInfoLine(&AboutMenu
, PoolPrint(L
" EFI Revision %d.%02d", ST
->Hdr
.Revision
>> 16, ST
->Hdr
.Revision
& ((1 << 16) - 1)));
179 AddMenuInfoLine(&AboutMenu
, L
" Platform: x86 (32 bit)");
180 #elif defined(EFIX64)
181 AddMenuInfoLine(&AboutMenu
, PoolPrint(L
" Platform: x86_64 (64 bit); Secure Boot %s",
182 secure_mode() ? L
"active" : L
"inactive"));
184 AddMenuInfoLine(&AboutMenu
, L
" Platform: unknown");
186 FirmwareVendor
= StrDuplicate(ST
->FirmwareVendor
);
187 LimitStringLength(FirmwareVendor
, 65); // More than ~65 causes empty info page on 800x600 display
188 AddMenuInfoLine(&AboutMenu
, PoolPrint(L
" Firmware: %s %d.%02d", FirmwareVendor
, ST
->FirmwareRevision
>> 16,
189 ST
->FirmwareRevision
& ((1 << 16) - 1)));
190 AddMenuInfoLine(&AboutMenu
, PoolPrint(L
" Screen Output: %s", egScreenDescription()));
191 AddMenuInfoLine(&AboutMenu
, L
"");
192 #if defined(__MAKEWITH_GNUEFI)
193 AddMenuInfoLine(&AboutMenu
, L
"Built with GNU-EFI");
195 AddMenuInfoLine(&AboutMenu
, L
"Built with TianoCore EDK2");
197 AddMenuInfoLine(&AboutMenu
, L
"");
198 AddMenuInfoLine(&AboutMenu
, L
"For more information, see the rEFInd Web site:");
199 AddMenuInfoLine(&AboutMenu
, L
"http://www.rodsbooks.com/refind/");
200 AddMenuEntry(&AboutMenu
, &MenuEntryReturn
);
203 RunMenu(&AboutMenu
, NULL
);
204 } /* VOID AboutrEFInd() */
206 static VOID
WarnSecureBootError(CHAR16
*Name
, BOOLEAN Verbose
) {
208 Name
= L
"the loader";
210 refit_call2_wrapper(ST
->ConOut
->SetAttribute
, ST
->ConOut
, ATTR_ERROR
);
211 Print(L
"Secure Boot validation failure loading %s!\n", Name
);
212 refit_call2_wrapper(ST
->ConOut
->SetAttribute
, ST
->ConOut
, ATTR_BASIC
);
213 if (Verbose
&& secure_mode()) {
214 Print(L
"\nThis computer is configured with Secure Boot active, but\n%s has failed validation.\n", Name
);
215 Print(L
"\nYou can:\n * Launch another boot loader\n");
216 Print(L
" * Disable Secure Boot in your firmware\n");
217 Print(L
" * Sign %s with a machine owner key (MOK)\n", Name
);
218 Print(L
" * Use a MOK utility (often present on the second row) to add a MOK with which\n");
219 Print(L
" %s has already been signed.\n", Name
);
220 Print(L
" * Use a MOK utility to register %s (\"enroll its hash\") without\n", Name
);
221 Print(L
" signing it.\n");
222 Print(L
"\nSee http://www.rodsbooks.com/refind/secureboot.html for more information\n");
225 } // VOID WarnSecureBootError()
227 // Returns TRUE if this file is a valid EFI loader file, and is proper ARCH
228 static BOOLEAN
IsValidLoader(EFI_FILE
*RootDir
, CHAR16
*FileName
) {
229 BOOLEAN IsValid
= TRUE
;
230 #if defined (EFIX64) | defined (EFI32)
232 EFI_FILE_HANDLE FileHandle
;
234 UINTN Size
= sizeof(Header
);
236 if ((RootDir
== NULL
) || (FileName
== NULL
)) {
237 // Assume valid here, because Macs produce NULL RootDir (& maybe FileName)
238 // when launching from a Firewire drive. This should be handled better, but
239 // fix would have to be in StartEFIImageList() and/or in FindVolumeAndFilename().
243 Status
= refit_call5_wrapper(RootDir
->Open
, RootDir
, &FileHandle
, FileName
, EFI_FILE_MODE_READ
, 0);
244 if (EFI_ERROR(Status
))
247 Status
= refit_call3_wrapper(FileHandle
->Read
, FileHandle
, &Size
, Header
);
248 refit_call1_wrapper(FileHandle
->Close
, FileHandle
);
250 IsValid
= !EFI_ERROR(Status
) &&
251 Size
== sizeof(Header
) &&
252 ((Header
[0] == 'M' && Header
[1] == 'Z' &&
253 (Size
= *(UINT32
*)&Header
[0x3c]) < 0x180 &&
254 Header
[Size
] == 'P' && Header
[Size
+1] == 'E' &&
255 Header
[Size
+2] == 0 && Header
[Size
+3] == 0 &&
256 *(UINT16
*)&Header
[Size
+4] == EFI_STUB_ARCH
) ||
257 (*(UINT32
*)&Header
== FAT_ARCH
));
260 } // BOOLEAN IsValidLoader()
262 // Launch an EFI binary.
263 static EFI_STATUS
StartEFIImageList(IN EFI_DEVICE_PATH
**DevicePaths
,
264 IN CHAR16
*LoadOptions
, IN UINTN LoaderType
,
265 IN CHAR16
*ImageTitle
, IN CHAR8 OSType
,
266 OUT UINTN
*ErrorInStep
,
269 EFI_STATUS Status
, ReturnStatus
;
270 EFI_HANDLE ChildImageHandle
;
271 EFI_LOADED_IMAGE
*ChildLoadedImage
= NULL
;
272 REFIT_VOLUME
*Volume
= NULL
;
273 UINTN DevicePathIndex
;
274 CHAR16 ErrorInfo
[256];
275 CHAR16
*FullLoadOptions
= NULL
;
276 CHAR16
*Filename
= NULL
;
279 if (ErrorInStep
!= NULL
)
283 if (LoadOptions
!= NULL
) {
284 FullLoadOptions
= StrDuplicate(LoadOptions
);
285 if ((LoaderType
== TYPE_EFI
) && (OSType
== 'M')) {
286 MergeStrings(&FullLoadOptions
, L
" ", 0);
287 // NOTE: That last space is also added by the EFI shell and seems to be significant
288 // when passing options to Apple's boot.efi...
290 } // if (LoadOptions != NULL)
292 Print(L
"Starting %s\nUsing load options '%s'\n", ImageTitle
, FullLoadOptions
? FullLoadOptions
: L
"");
294 // load the image into memory (and execute it, in the case of a shim/MOK image).
295 ReturnStatus
= Status
= EFI_NOT_FOUND
; // in case the list is empty
296 for (DevicePathIndex
= 0; DevicePaths
[DevicePathIndex
] != NULL
; DevicePathIndex
++) {
297 FindVolumeAndFilename(DevicePaths
[DevicePathIndex
], &Volume
, &Filename
);
298 // Some EFIs crash if attempting to load driver for invalid architecture, so
299 // protect for this condition; but sometimes Volume comes back NULL, so provide
300 // an exception. (TODO: Handle this special condition better.)
301 if ((LoaderType
== TYPE_LEGACY
) || (Volume
== NULL
) || IsValidLoader(Volume
->RootDir
, Filename
)) {
302 if (Filename
&& (LoaderType
!= TYPE_LEGACY
)) {
303 Temp
= PoolPrint(L
"\\%s %s", Filename
, FullLoadOptions
? FullLoadOptions
: L
"");
305 MyFreePool(FullLoadOptions
);
306 FullLoadOptions
= Temp
;
310 // NOTE: Below commented-out line could be more efficient if file were read ahead of
311 // time and passed as a pre-loaded image to LoadImage(), but it doesn't work on my
312 // 32-bit Mac Mini or my 64-bit Intel box when launching a Linux kernel; the
313 // kernel returns a "Failed to handle fs_proto" error message.
314 // TODO: Track down the cause of this error and fix it, if possible.
315 // ReturnStatus = Status = refit_call6_wrapper(BS->LoadImage, FALSE, SelfImageHandle, DevicePaths[DevicePathIndex],
316 // ImageData, ImageSize, &ChildImageHandle);
317 ReturnStatus
= Status
= refit_call6_wrapper(BS
->LoadImage
, FALSE
, SelfImageHandle
, DevicePaths
[DevicePathIndex
],
318 NULL
, 0, &ChildImageHandle
);
320 Print(L
"Invalid loader file!\n");
321 ReturnStatus
= EFI_LOAD_ERROR
;
323 if (ReturnStatus
!= EFI_NOT_FOUND
) {
327 if ((Status
== EFI_ACCESS_DENIED
) || (Status
== EFI_SECURITY_VIOLATION
)) {
328 WarnSecureBootError(ImageTitle
, Verbose
);
331 SPrint(ErrorInfo
, 255, L
"while loading %s", ImageTitle
);
332 if (CheckError(Status
, ErrorInfo
)) {
333 if (ErrorInStep
!= NULL
)
338 ReturnStatus
= Status
= refit_call3_wrapper(BS
->HandleProtocol
, ChildImageHandle
, &LoadedImageProtocol
,
339 (VOID
**) &ChildLoadedImage
);
340 if (CheckError(Status
, L
"while getting a LoadedImageProtocol handle")) {
341 if (ErrorInStep
!= NULL
)
345 ChildLoadedImage
->LoadOptions
= (VOID
*)FullLoadOptions
;
346 ChildLoadedImage
->LoadOptionsSize
= FullLoadOptions
? ((UINT32
)StrLen(FullLoadOptions
) + 1) * sizeof(CHAR16
) : 0;
347 // turn control over to the image
348 // TODO: (optionally) re-enable the EFI watchdog timer!
350 // close open file handles
352 ReturnStatus
= Status
= refit_call3_wrapper(BS
->StartImage
, ChildImageHandle
, NULL
, NULL
);
354 // control returns here when the child image calls Exit()
355 SPrint(ErrorInfo
, 255, L
"returned from %s", ImageTitle
);
356 if (CheckError(Status
, ErrorInfo
)) {
357 if (ErrorInStep
!= NULL
)
361 // re-open file handles
365 // unload the image, we don't care if it works or not...
366 Status
= refit_call1_wrapper(BS
->UnloadImage
, ChildImageHandle
);
369 MyFreePool(FullLoadOptions
);
371 } /* static EFI_STATUS StartEFIImageList() */
373 static EFI_STATUS
StartEFIImage(IN EFI_DEVICE_PATH
*DevicePath
,
374 IN CHAR16
*LoadOptions
, IN UINTN LoaderType
,
375 IN CHAR16
*ImageTitle
, IN CHAR8 OSType
,
376 OUT UINTN
*ErrorInStep
,
379 EFI_DEVICE_PATH
*DevicePaths
[2];
381 DevicePaths
[0] = DevicePath
;
382 DevicePaths
[1] = NULL
;
383 return StartEFIImageList(DevicePaths
, LoadOptions
, LoaderType
, ImageTitle
, OSType
, ErrorInStep
, Verbose
);
384 } /* static EFI_STATUS StartEFIImage() */
386 // From gummiboot: Retrieve a raw EFI variable.
387 // Returns EFI status
388 static EFI_STATUS
EfivarGetRaw(EFI_GUID
*vendor
, CHAR16
*name
, CHAR8
**buffer
, UINTN
*size
) {
393 l
= sizeof(CHAR16
*) * EFI_MAXIMUM_VARIABLE_SIZE
;
394 buf
= AllocatePool(l
);
396 return EFI_OUT_OF_RESOURCES
;
398 err
= refit_call5_wrapper(RT
->GetVariable
, name
, vendor
, NULL
, &l
, buf
);
399 if (EFI_ERROR(err
) == EFI_SUCCESS
) {
406 } // EFI_STATUS EfivarGetRaw()
408 // From gummiboot: Set an EFI variable
409 static EFI_STATUS
EfivarSetRaw(EFI_GUID
*vendor
, CHAR16
*name
, CHAR8
*buf
, UINTN size
, BOOLEAN persistent
) {
412 flags
= EFI_VARIABLE_BOOTSERVICE_ACCESS
|EFI_VARIABLE_RUNTIME_ACCESS
;
414 flags
|= EFI_VARIABLE_NON_VOLATILE
;
416 return refit_call5_wrapper(RT
->SetVariable
, name
, vendor
, flags
, size
, buf
);
417 } // EFI_STATUS EfivarSetRaw()
419 // From gummiboot: Reboot the computer into its built-in user interface
420 static EFI_STATUS
RebootIntoFirmware(VOID
) {
426 osind
= EFI_OS_INDICATIONS_BOOT_TO_FW_UI
;
428 err
= EfivarGetRaw(&GlobalGuid
, L
"OsIndications", &b
, &size
);
429 if (err
== EFI_SUCCESS
)
433 err
= EfivarSetRaw(&GlobalGuid
, L
"OsIndications", (CHAR8
*)&osind
, sizeof(UINT64
), TRUE
);
434 if (err
!= EFI_SUCCESS
)
437 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);
438 Print(L
"Error calling ResetSystem: %r", err
);
445 // EFI OS loader functions
448 static VOID
StartLoader(LOADER_ENTRY
*Entry
)
450 UINTN ErrorInStep
= 0;
452 BeginExternalScreen(Entry
->UseGraphicsMode
, L
"Booting OS");
453 StartEFIImage(Entry
->DevicePath
, Entry
->LoadOptions
, TYPE_EFI
,
454 Basename(Entry
->LoaderPath
), Entry
->OSType
, &ErrorInStep
, !Entry
->UseGraphicsMode
);
455 FinishExternalScreen();
458 // Locate an initrd or initramfs file that matches the kernel specified by LoaderPath.
459 // The matching file has a name that begins with "init" and includes the same version
460 // number string as is found in LoaderPath -- but not a longer version number string.
461 // For instance, if LoaderPath is \EFI\kernels\bzImage-3.3.0.efi, and if \EFI\kernels
462 // has a file called initramfs-3.3.0.img, this function will return the string
463 // '\EFI\kernels\initramfs-3.3.0.img'. If the directory ALSO contains the file
464 // initramfs-3.3.0-rc7.img or initramfs-13.3.0.img, those files will NOT match;
465 // however, initmine-3.3.0.img might match. (FindInitrd() returns the first match it
466 // finds). Thus, care should be taken to avoid placing duplicate matching files in
467 // the kernel's directory.
468 // If no matching init file can be found, returns NULL.
469 static CHAR16
* FindInitrd(IN CHAR16
*LoaderPath
, IN REFIT_VOLUME
*Volume
) {
470 CHAR16
*InitrdName
= NULL
, *FileName
, *KernelVersion
, *InitrdVersion
, *Path
;
471 REFIT_DIR_ITER DirIter
;
472 EFI_FILE_INFO
*DirEntry
;
474 FileName
= Basename(LoaderPath
);
475 KernelVersion
= FindNumbers(FileName
);
476 Path
= FindPath(LoaderPath
);
478 // Add trailing backslash for root directory; necessary on some systems, but must
479 // NOT be added to all directories, since on other systems, a trailing backslash on
480 // anything but the root directory causes them to flake out!
481 if (StrLen(Path
) == 0) {
482 MergeStrings(&Path
, L
"\\", 0);
484 DirIterOpen(Volume
->RootDir
, Path
, &DirIter
);
485 // Now add a trailing backslash if it was NOT added earlier, for consistency in
486 // building the InitrdName later....
487 if ((StrLen(Path
) > 0) && (Path
[StrLen(Path
) - 1] != L
'\\'))
488 MergeStrings(&Path
, L
"\\", 0);
489 while ((DirIterNext(&DirIter
, 2, L
"init*", &DirEntry
)) && (InitrdName
== NULL
)) {
490 InitrdVersion
= FindNumbers(DirEntry
->FileName
);
491 if (KernelVersion
!= NULL
) {
492 if (StriCmp(InitrdVersion
, KernelVersion
) == 0) {
493 InitrdName
= PoolPrint(L
"%s%s", Path
, DirEntry
->FileName
);
496 if (InitrdVersion
== NULL
) {
497 InitrdName
= PoolPrint(L
"%s%s", Path
, DirEntry
->FileName
);
500 MyFreePool(InitrdVersion
);
502 DirIterClose(&DirIter
);
504 // Note: Don't FreePool(FileName), since Basename returns a pointer WITHIN the string it's passed.
505 MyFreePool(KernelVersion
);
508 } // static CHAR16 * FindInitrd()
510 LOADER_ENTRY
* AddPreparedLoaderEntry(LOADER_ENTRY
*Entry
) {
511 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
514 } // LOADER_ENTRY * AddPreparedLoaderEntry()
516 // Creates a copy of a menu screen.
517 // Returns a pointer to the copy of the menu screen.
518 static REFIT_MENU_SCREEN
* CopyMenuScreen(REFIT_MENU_SCREEN
*Entry
) {
519 REFIT_MENU_SCREEN
*NewEntry
;
522 NewEntry
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
523 if ((Entry
!= NULL
) && (NewEntry
!= NULL
)) {
524 CopyMem(NewEntry
, Entry
, sizeof(REFIT_MENU_SCREEN
));
525 NewEntry
->Title
= (Entry
->Title
) ? StrDuplicate(Entry
->Title
) : NULL
;
526 NewEntry
->TimeoutText
= (Entry
->TimeoutText
) ? StrDuplicate(Entry
->TimeoutText
) : NULL
;
527 if (Entry
->TitleImage
!= NULL
) {
528 NewEntry
->TitleImage
= AllocatePool(sizeof(EG_IMAGE
));
529 if (NewEntry
->TitleImage
!= NULL
)
530 CopyMem(NewEntry
->TitleImage
, Entry
->TitleImage
, sizeof(EG_IMAGE
));
532 NewEntry
->InfoLines
= (CHAR16
**) AllocateZeroPool(Entry
->InfoLineCount
* (sizeof(CHAR16
*)));
533 for (i
= 0; i
< Entry
->InfoLineCount
&& NewEntry
->InfoLines
; i
++) {
534 NewEntry
->InfoLines
[i
] = (Entry
->InfoLines
[i
]) ? StrDuplicate(Entry
->InfoLines
[i
]) : NULL
;
536 NewEntry
->Entries
= (REFIT_MENU_ENTRY
**) AllocateZeroPool(Entry
->EntryCount
* (sizeof (REFIT_MENU_ENTRY
*)));
537 for (i
= 0; i
< Entry
->EntryCount
&& NewEntry
->Entries
; i
++) {
538 AddMenuEntry(NewEntry
, Entry
->Entries
[i
]);
540 NewEntry
->Hint1
= (Entry
->Hint1
) ? StrDuplicate(Entry
->Hint1
) : NULL
;
541 NewEntry
->Hint2
= (Entry
->Hint2
) ? StrDuplicate(Entry
->Hint2
) : NULL
;
544 } // static REFIT_MENU_SCREEN* CopyMenuScreen()
546 // Creates a copy of a menu entry. Intended to enable moving a stack-based
547 // menu entry (such as the ones for the "reboot" and "exit" functions) to
548 // to the heap. This enables easier deletion of the whole set of menu
549 // entries when re-scanning.
550 // Returns a pointer to the copy of the menu entry.
551 static REFIT_MENU_ENTRY
* CopyMenuEntry(REFIT_MENU_ENTRY
*Entry
) {
552 REFIT_MENU_ENTRY
*NewEntry
;
554 NewEntry
= AllocateZeroPool(sizeof(REFIT_MENU_ENTRY
));
555 if ((Entry
!= NULL
) && (NewEntry
!= NULL
)) {
556 CopyMem(NewEntry
, Entry
, sizeof(REFIT_MENU_ENTRY
));
557 NewEntry
->Title
= (Entry
->Title
) ? StrDuplicate(Entry
->Title
) : NULL
;
558 if (Entry
->BadgeImage
!= NULL
) {
559 NewEntry
->BadgeImage
= AllocatePool(sizeof(EG_IMAGE
));
560 if (NewEntry
->BadgeImage
!= NULL
)
561 CopyMem(NewEntry
->BadgeImage
, Entry
->BadgeImage
, sizeof(EG_IMAGE
));
563 if (Entry
->Image
!= NULL
) {
564 NewEntry
->Image
= AllocatePool(sizeof(EG_IMAGE
));
565 if (NewEntry
->Image
!= NULL
)
566 CopyMem(NewEntry
->Image
, Entry
->Image
, sizeof(EG_IMAGE
));
568 if (Entry
->SubScreen
!= NULL
) {
569 NewEntry
->SubScreen
= CopyMenuScreen(Entry
->SubScreen
);
573 } // REFIT_MENU_ENTRY* CopyMenuEntry()
575 // Creates a new LOADER_ENTRY data structure and populates it with
576 // default values from the specified Entry, or NULL values if Entry
577 // is unspecified (NULL).
578 // Returns a pointer to the new data structure, or NULL if it
579 // couldn't be allocated
580 LOADER_ENTRY
*InitializeLoaderEntry(IN LOADER_ENTRY
*Entry
) {
581 LOADER_ENTRY
*NewEntry
= NULL
;
583 NewEntry
= AllocateZeroPool(sizeof(LOADER_ENTRY
));
584 if (NewEntry
!= NULL
) {
585 NewEntry
->me
.Title
= NULL
;
586 NewEntry
->me
.Tag
= TAG_LOADER
;
587 NewEntry
->Enabled
= TRUE
;
588 NewEntry
->UseGraphicsMode
= FALSE
;
589 NewEntry
->OSType
= 0;
591 NewEntry
->LoaderPath
= (Entry
->LoaderPath
) ? StrDuplicate(Entry
->LoaderPath
) : NULL
;
592 NewEntry
->VolName
= (Entry
->VolName
) ? StrDuplicate(Entry
->VolName
) : NULL
;
593 NewEntry
->DevicePath
= Entry
->DevicePath
;
594 NewEntry
->UseGraphicsMode
= Entry
->UseGraphicsMode
;
595 NewEntry
->LoadOptions
= (Entry
->LoadOptions
) ? StrDuplicate(Entry
->LoadOptions
) : NULL
;
596 NewEntry
->InitrdPath
= (Entry
->InitrdPath
) ? StrDuplicate(Entry
->InitrdPath
) : NULL
;
600 } // LOADER_ENTRY *InitializeLoaderEntry()
602 // Adds InitrdPath to Options, but only if Options doesn't already include an
603 // initrd= line. Done to enable overriding the default initrd selection in a
604 // refind_linux.conf file's options list.
605 // Returns a pointer to a new string. The calling function is responsible for
606 // freeing its memory.
607 static CHAR16
*AddInitrdToOptions(CHAR16
*Options
, CHAR16
*InitrdPath
) {
608 CHAR16
*NewOptions
= NULL
;
611 NewOptions
= StrDuplicate(Options
);
612 if ((InitrdPath
!= NULL
) && !StriSubCmp(L
"initrd=", Options
)) {
613 MergeStrings(&NewOptions
, L
"initrd=", L
' ');
614 MergeStrings(&NewOptions
, InitrdPath
, 0);
617 } // CHAR16 *AddInitrdToOptions()
619 // Prepare a REFIT_MENU_SCREEN data structure for a subscreen entry. This sets up
620 // the default entry that launches the boot loader using the same options as the
621 // main Entry does. Subsequent options can be added by the calling function.
622 // If a subscreen already exists in the Entry that's passed to this function,
623 // it's left unchanged and a pointer to it is returned.
624 // Returns a pointer to the new subscreen data structure, or NULL if there
625 // were problems allocating memory.
626 REFIT_MENU_SCREEN
*InitializeSubScreen(IN LOADER_ENTRY
*Entry
) {
627 CHAR16
*FileName
, *MainOptions
= NULL
;
628 REFIT_MENU_SCREEN
*SubScreen
= NULL
;
629 LOADER_ENTRY
*SubEntry
;
631 FileName
= Basename(Entry
->LoaderPath
);
632 if (Entry
->me
.SubScreen
== NULL
) { // No subscreen yet; initialize default entry....
633 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
634 if (SubScreen
!= NULL
) {
635 SubScreen
->Title
= AllocateZeroPool(sizeof(CHAR16
) * 256);
636 SPrint(SubScreen
->Title
, 255, L
"Boot Options for %s on %s",
637 (Entry
->Title
!= NULL
) ? Entry
->Title
: FileName
, Entry
->VolName
);
638 SubScreen
->TitleImage
= Entry
->me
.Image
;
640 SubEntry
= InitializeLoaderEntry(Entry
);
641 if (SubEntry
!= NULL
) {
642 SubEntry
->me
.Title
= StrDuplicate(L
"Boot using default options");
643 MainOptions
= SubEntry
->LoadOptions
;
644 SubEntry
->LoadOptions
= AddInitrdToOptions(MainOptions
, SubEntry
->InitrdPath
);
645 MyFreePool(MainOptions
);
646 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
647 } // if (SubEntry != NULL)
648 SubScreen
->Hint1
= StrDuplicate(SUBSCREEN_HINT1
);
649 if (GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_EDITOR
) {
650 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2_NO_EDITOR
);
652 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2
);
654 } // if (SubScreen != NULL)
655 } else { // existing subscreen; less initialization, and just add new entry later....
656 SubScreen
= Entry
->me
.SubScreen
;
659 } // REFIT_MENU_SCREEN *InitializeSubScreen()
661 VOID
GenerateSubScreen(LOADER_ENTRY
*Entry
, IN REFIT_VOLUME
*Volume
) {
662 REFIT_MENU_SCREEN
*SubScreen
;
663 LOADER_ENTRY
*SubEntry
;
665 CHAR16 DiagsFileName
[256];
670 // create the submenu
671 if (StrLen(Entry
->Title
) == 0) {
672 MyFreePool(Entry
->Title
);
675 SubScreen
= InitializeSubScreen(Entry
);
677 // loader-specific submenu entries
678 if (Entry
->OSType
== 'M') { // entries for Mac OS X
680 SubEntry
= InitializeLoaderEntry(Entry
);
681 if (SubEntry
!= NULL
) {
682 SubEntry
->me
.Title
= L
"Boot Mac OS X with a 64-bit kernel";
683 SubEntry
->LoadOptions
= L
"arch=x86_64";
684 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
685 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
688 SubEntry
= InitializeLoaderEntry(Entry
);
689 if (SubEntry
!= NULL
) {
690 SubEntry
->me
.Title
= L
"Boot Mac OS X with a 32-bit kernel";
691 SubEntry
->LoadOptions
= L
"arch=i386";
692 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
693 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
697 if (!(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_SINGLEUSER
)) {
698 SubEntry
= InitializeLoaderEntry(Entry
);
699 if (SubEntry
!= NULL
) {
700 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode";
701 SubEntry
->UseGraphicsMode
= FALSE
;
702 SubEntry
->LoadOptions
= L
"-v";
703 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
707 SubEntry
= InitializeLoaderEntry(Entry
);
708 if (SubEntry
!= NULL
) {
709 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode (64-bit)";
710 SubEntry
->UseGraphicsMode
= FALSE
;
711 SubEntry
->LoadOptions
= L
"-v arch=x86_64";
712 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
715 SubEntry
= InitializeLoaderEntry(Entry
);
716 if (SubEntry
!= NULL
) {
717 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode (32-bit)";
718 SubEntry
->UseGraphicsMode
= FALSE
;
719 SubEntry
->LoadOptions
= L
"-v arch=i386";
720 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
724 SubEntry
= InitializeLoaderEntry(Entry
);
725 if (SubEntry
!= NULL
) {
726 SubEntry
->me
.Title
= L
"Boot Mac OS X in single user mode";
727 SubEntry
->UseGraphicsMode
= FALSE
;
728 SubEntry
->LoadOptions
= L
"-v -s";
729 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
731 } // single-user mode allowed
733 if (!(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_SAFEMODE
)) {
734 SubEntry
= InitializeLoaderEntry(Entry
);
735 if (SubEntry
!= NULL
) {
736 SubEntry
->me
.Title
= L
"Boot Mac OS X in safe mode";
737 SubEntry
->UseGraphicsMode
= FALSE
;
738 SubEntry
->LoadOptions
= L
"-v -x";
739 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
741 } // safe mode allowed
743 // check for Apple hardware diagnostics
744 StrCpy(DiagsFileName
, L
"System\\Library\\CoreServices\\.diagnostics\\diags.efi");
745 if (FileExists(Volume
->RootDir
, DiagsFileName
) && !(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_HWTEST
)) {
746 SubEntry
= InitializeLoaderEntry(Entry
);
747 if (SubEntry
!= NULL
) {
748 SubEntry
->me
.Title
= L
"Run Apple Hardware Test";
749 MyFreePool(SubEntry
->LoaderPath
);
750 SubEntry
->LoaderPath
= StrDuplicate(DiagsFileName
);
751 SubEntry
->DevicePath
= FileDevicePath(Volume
->DeviceHandle
, SubEntry
->LoaderPath
);
752 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
753 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
755 } // if diagnostics entry found
757 } else if (Entry
->OSType
== 'L') { // entries for Linux kernels with EFI stub loaders
758 File
= ReadLinuxOptionsFile(Entry
->LoaderPath
, Volume
);
760 InitrdName
= FindInitrd(Entry
->LoaderPath
, Volume
);
761 TokenCount
= ReadTokenLine(File
, &TokenList
);
762 // first entry requires special processing, since it was initially set
763 // up with a default title but correct options by InitializeSubScreen(),
765 if ((SubScreen
->Entries
!= NULL
) && (SubScreen
->Entries
[0] != NULL
)) {
766 MyFreePool(SubScreen
->Entries
[0]->Title
);
767 SubScreen
->Entries
[0]->Title
= TokenList
[0] ? StrDuplicate(TokenList
[0]) : StrDuplicate(L
"Boot Linux");
769 FreeTokenLine(&TokenList
, &TokenCount
);
770 while ((TokenCount
= ReadTokenLine(File
, &TokenList
)) > 1) {
771 SubEntry
= InitializeLoaderEntry(Entry
);
772 SubEntry
->me
.Title
= TokenList
[0] ? StrDuplicate(TokenList
[0]) : StrDuplicate(L
"Boot Linux");
773 MyFreePool(SubEntry
->LoadOptions
);
774 SubEntry
->LoadOptions
= AddInitrdToOptions(TokenList
[1], InitrdName
);
775 FreeTokenLine(&TokenList
, &TokenCount
);
776 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_LINUX
;
777 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
779 MyFreePool(InitrdName
);
783 } else if (Entry
->OSType
== 'E') { // entries for ELILO
784 SubEntry
= InitializeLoaderEntry(Entry
);
785 if (SubEntry
!= NULL
) {
786 SubEntry
->me
.Title
= L
"Run ELILO in interactive mode";
787 SubEntry
->LoadOptions
= L
"-p";
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 17\" iMac or a 15\" MacBook Pro (*)";
795 SubEntry
->UseGraphicsMode
= TRUE
;
796 SubEntry
->LoadOptions
= L
"-d 0 i17";
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 20\" iMac (*)";
804 SubEntry
->UseGraphicsMode
= TRUE
;
805 SubEntry
->LoadOptions
= L
"-d 0 i20";
806 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
807 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
810 SubEntry
= InitializeLoaderEntry(Entry
);
811 if (SubEntry
!= NULL
) {
812 SubEntry
->me
.Title
= L
"Boot Linux for a Mac Mini (*)";
813 SubEntry
->UseGraphicsMode
= TRUE
;
814 SubEntry
->LoadOptions
= L
"-d 0 mini";
815 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
816 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
819 AddMenuInfoLine(SubScreen
, L
"NOTE: This is an example. Entries");
820 AddMenuInfoLine(SubScreen
, L
"marked with (*) may not work.");
822 } else if (Entry
->OSType
== 'X') { // entries for xom.efi
823 // by default, skip the built-in selection and boot from hard disk only
824 Entry
->LoadOptions
= L
"-s -h";
826 SubEntry
= InitializeLoaderEntry(Entry
);
827 if (SubEntry
!= NULL
) {
828 SubEntry
->me
.Title
= L
"Boot Windows from Hard Disk";
829 SubEntry
->LoadOptions
= L
"-s -h";
830 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
831 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
834 SubEntry
= InitializeLoaderEntry(Entry
);
835 if (SubEntry
!= NULL
) {
836 SubEntry
->me
.Title
= L
"Boot Windows from CD-ROM";
837 SubEntry
->LoadOptions
= L
"-s -c";
838 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
839 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
842 SubEntry
= InitializeLoaderEntry(Entry
);
843 if (SubEntry
!= NULL
) {
844 SubEntry
->me
.Title
= L
"Run XOM in text mode";
845 SubEntry
->UseGraphicsMode
= FALSE
;
846 SubEntry
->LoadOptions
= L
"-v";
847 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
848 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
850 } // entries for xom.efi
851 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
852 Entry
->me
.SubScreen
= SubScreen
;
853 } // VOID GenerateSubScreen()
855 // Returns options for a Linux kernel. Reads them from an options file in the
856 // kernel's directory; and if present, adds an initrd= option for an initial
857 // RAM disk file with the same version number as the kernel file.
858 static CHAR16
* GetMainLinuxOptions(IN CHAR16
* LoaderPath
, IN REFIT_VOLUME
*Volume
) {
859 CHAR16
*Options
= NULL
, *InitrdName
, *FullOptions
= NULL
;
861 Options
= GetFirstOptionsFromFile(LoaderPath
, Volume
);
862 InitrdName
= FindInitrd(LoaderPath
, Volume
);
863 FullOptions
= AddInitrdToOptions(Options
, InitrdName
);
866 MyFreePool(InitrdName
);
867 return (FullOptions
);
868 } // static CHAR16 * GetMainLinuxOptions()
870 // Try to guess the name of the Linux distribution & add that name to
872 static VOID
GuessLinuxDistribution(CHAR16
**OSIconName
, REFIT_VOLUME
*Volume
, CHAR16
*LoaderPath
) {
876 UINTN TokenCount
= 0;
878 // If on Linux root fs, /etc/os-release file probably has clues....
879 if (FileExists(Volume
->RootDir
, L
"etc\\os-release") &&
880 (ReadFile(Volume
->RootDir
, L
"etc\\os-release", &File
, &FileSize
) == EFI_SUCCESS
)) {
882 TokenCount
= ReadTokenLine(&File
, &TokenList
);
883 if ((TokenCount
> 1) && ((StriCmp(TokenList
[0], L
"ID") == 0) || (StriCmp(TokenList
[0], L
"NAME") == 0))) {
884 MergeStrings(OSIconName
, TokenList
[1], L
',');
886 FreeTokenLine(&TokenList
, &TokenCount
);
887 } while (TokenCount
> 0);
888 MyFreePool(File
.Buffer
);
891 // Search for clues in the kernel's filename....
892 if (StriSubCmp(L
".fc", LoaderPath
))
893 MergeStrings(OSIconName
, L
"fedora", L
',');
894 if (StriSubCmp(L
".el", LoaderPath
))
895 MergeStrings(OSIconName
, L
"redhat", L
',');
896 } // VOID GuessLinuxDistribution()
898 // Sets a few defaults for a loader entry -- mainly the icon, but also the OS type
899 // code and shortcut letter. For Linux EFI stub loaders, also sets kernel options
900 // that will (with luck) work fairly automatically.
901 VOID
SetLoaderDefaults(LOADER_ENTRY
*Entry
, CHAR16
*LoaderPath
, REFIT_VOLUME
*Volume
) {
902 CHAR16
*FileName
, *PathOnly
, *NoExtension
, *OSIconName
= NULL
, *Temp
, *SubString
;
903 CHAR16 ShortcutLetter
= 0;
906 FileName
= Basename(LoaderPath
);
907 PathOnly
= FindPath(LoaderPath
);
908 NoExtension
= StripEfiExtension(FileName
);
910 // locate a custom icon for the loader
911 // Anything found here takes precedence over the "hints" in the OSIconName variable
912 if (!Entry
->me
.Image
) {
913 Entry
->me
.Image
= egLoadIconAnyType(Volume
->RootDir
, PathOnly
, NoExtension
, GlobalConfig
.IconSizes
[ICON_SIZE_BIG
]);
915 if (!Entry
->me
.Image
) {
916 Entry
->me
.Image
= egCopyImage(Volume
->VolIconImage
);
919 // Begin creating icon "hints" by using last part of directory path leading
921 Temp
= FindLastDirName(LoaderPath
);
922 MergeStrings(&OSIconName
, Temp
, L
',');
925 if (OSIconName
!= NULL
) {
926 ShortcutLetter
= OSIconName
[0];
929 // Add every "word" in the volume label, delimited by spaces, dashes (-), or
930 // underscores (_), to the list of hints to be used in searching for OS
932 if ((Volume
->VolName
) && (StrLen(Volume
->VolName
) > 0)) {
933 Temp
= SubString
= StrDuplicate(Volume
->VolName
);
935 Length
= StrLen(Temp
);
936 for (i
= 0; i
< Length
; i
++) {
937 if ((Temp
[i
] == L
' ') || (Temp
[i
] == L
'_') || (Temp
[i
] == L
'-')) {
939 if (StrLen(SubString
) > 0)
940 MergeStrings(&OSIconName
, SubString
, L
',');
941 SubString
= Temp
+ i
+ 1;
944 MergeStrings(&OSIconName
, SubString
, L
',');
949 // detect specific loaders
950 if (StriSubCmp(L
"bzImage", FileName
) || StriSubCmp(L
"vmlinuz", FileName
)) {
951 GuessLinuxDistribution(&OSIconName
, Volume
, LoaderPath
);
952 MergeStrings(&OSIconName
, L
"linux", L
',');
954 if (ShortcutLetter
== 0)
955 ShortcutLetter
= 'L';
956 Entry
->LoadOptions
= GetMainLinuxOptions(LoaderPath
, Volume
);
957 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_LINUX
;
958 } else if (StriSubCmp(L
"refit", LoaderPath
)) {
959 MergeStrings(&OSIconName
, L
"refit", L
',');
961 ShortcutLetter
= 'R';
962 } else if (StriSubCmp(L
"refind", LoaderPath
)) {
963 MergeStrings(&OSIconName
, L
"refind", L
',');
965 ShortcutLetter
= 'R';
966 } else if (StriCmp(LoaderPath
, MACOSX_LOADER_PATH
) == 0) {
967 MergeStrings(&OSIconName
, L
"mac", L
',');
969 ShortcutLetter
= 'M';
970 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
971 } else if (StriCmp(FileName
, L
"diags.efi") == 0) {
972 MergeStrings(&OSIconName
, L
"hwtest", L
',');
973 } else if (StriCmp(FileName
, L
"e.efi") == 0 || StriCmp(FileName
, L
"elilo.efi") == 0 || StriSubCmp(L
"elilo", FileName
)) {
974 MergeStrings(&OSIconName
, L
"elilo,linux", L
',');
976 if (ShortcutLetter
== 0)
977 ShortcutLetter
= 'L';
978 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
979 } else if (StriSubCmp(L
"grub", FileName
)) {
981 ShortcutLetter
= 'G';
982 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_GRUB
;
983 } else if (StriCmp(FileName
, L
"cdboot.efi") == 0 ||
984 StriCmp(FileName
, L
"bootmgr.efi") == 0 ||
985 StriCmp(FileName
, L
"bootmgfw.efi") == 0 ||
986 StriCmp(FileName
, L
"bkpbootmgfw.efi") == 0) {
987 MergeStrings(&OSIconName
, L
"win", L
',');
989 ShortcutLetter
= 'W';
990 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
991 } else if (StriCmp(FileName
, L
"xom.efi") == 0) {
992 MergeStrings(&OSIconName
, L
"xom,win", L
',');
993 Entry
->UseGraphicsMode
= TRUE
;
995 ShortcutLetter
= 'W';
996 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
999 if ((ShortcutLetter
>= 'a') && (ShortcutLetter
<= 'z'))
1000 ShortcutLetter
= ShortcutLetter
- 'a' + 'A'; // convert lowercase to uppercase
1001 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1002 if (Entry
->me
.Image
== NULL
)
1003 Entry
->me
.Image
= LoadOSIcon(OSIconName
, L
"unknown", FALSE
);
1004 MyFreePool(PathOnly
);
1005 } // VOID SetLoaderDefaults()
1007 // Add a specified EFI boot loader to the list, using automatic settings
1008 // for icons, options, etc.
1009 LOADER_ENTRY
* AddLoaderEntry(IN CHAR16
*LoaderPath
, IN CHAR16
*LoaderTitle
, IN REFIT_VOLUME
*Volume
) {
1010 LOADER_ENTRY
*Entry
;
1012 CleanUpPathNameSlashes(LoaderPath
);
1013 Entry
= InitializeLoaderEntry(NULL
);
1014 if (Entry
!= NULL
) {
1015 Entry
->Title
= StrDuplicate((LoaderTitle
!= NULL
) ? LoaderTitle
: LoaderPath
);
1016 Entry
->me
.Title
= AllocateZeroPool(sizeof(CHAR16
) * 256);
1017 SPrint(Entry
->me
.Title
, 255, L
"Boot %s from %s ", (LoaderTitle
!= NULL
) ? LoaderTitle
: LoaderPath
, Volume
->VolName
);
1019 Entry
->me
.BadgeImage
= Volume
->VolBadgeImage
;
1020 if ((LoaderPath
!= NULL
) && (LoaderPath
[0] != L
'\\')) {
1021 Entry
->LoaderPath
= StrDuplicate(L
"\\");
1023 Entry
->LoaderPath
= NULL
;
1025 MergeStrings(&(Entry
->LoaderPath
), LoaderPath
, 0);
1026 Entry
->VolName
= Volume
->VolName
;
1027 Entry
->DevicePath
= FileDevicePath(Volume
->DeviceHandle
, Entry
->LoaderPath
);
1028 SetLoaderDefaults(Entry
, LoaderPath
, Volume
);
1029 GenerateSubScreen(Entry
, Volume
);
1030 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1034 } // LOADER_ENTRY * AddLoaderEntry()
1036 // Returns -1 if (Time1 < Time2), +1 if (Time1 > Time2), or 0 if
1037 // (Time1 == Time2). Precision is only to the nearest second; since
1038 // this is used for sorting boot loader entries, differences smaller
1039 // than this are likely to be meaningless (and unlikely!).
1040 INTN
TimeComp(IN EFI_TIME
*Time1
, IN EFI_TIME
*Time2
) {
1041 INT64 Time1InSeconds
, Time2InSeconds
;
1043 // Following values are overestimates; I'm assuming 31 days in every month.
1044 // This is fine for the purpose of this function, which is limited
1045 Time1InSeconds
= Time1
->Second
+ (Time1
->Minute
* 60) + (Time1
->Hour
* 3600) + (Time1
->Day
* 86400) +
1046 (Time1
->Month
* 2678400) + ((Time1
->Year
- 1998) * 32140800);
1047 Time2InSeconds
= Time2
->Second
+ (Time2
->Minute
* 60) + (Time2
->Hour
* 3600) + (Time2
->Day
* 86400) +
1048 (Time2
->Month
* 2678400) + ((Time2
->Year
- 1998) * 32140800);
1049 if (Time1InSeconds
< Time2InSeconds
)
1051 else if (Time1InSeconds
> Time2InSeconds
)
1055 } // INTN TimeComp()
1057 // Adds a loader list element, keeping it sorted by date. Returns the new
1058 // first element (the one with the most recent date).
1059 static struct LOADER_LIST
* AddLoaderListEntry(struct LOADER_LIST
*LoaderList
, struct LOADER_LIST
*NewEntry
) {
1060 struct LOADER_LIST
*LatestEntry
, *CurrentEntry
, *PrevEntry
= NULL
;
1062 LatestEntry
= CurrentEntry
= LoaderList
;
1063 if (LoaderList
== NULL
) {
1064 LatestEntry
= NewEntry
;
1066 while ((CurrentEntry
!= NULL
) && (TimeComp(&(NewEntry
->TimeStamp
), &(CurrentEntry
->TimeStamp
)) < 0)) {
1067 PrevEntry
= CurrentEntry
;
1068 CurrentEntry
= CurrentEntry
->NextEntry
;
1070 NewEntry
->NextEntry
= CurrentEntry
;
1071 if (PrevEntry
== NULL
) {
1072 LatestEntry
= NewEntry
;
1074 PrevEntry
->NextEntry
= NewEntry
;
1077 return (LatestEntry
);
1078 } // static VOID AddLoaderListEntry()
1080 // Delete the LOADER_LIST linked list
1081 static VOID
CleanUpLoaderList(struct LOADER_LIST
*LoaderList
) {
1082 struct LOADER_LIST
*Temp
;
1084 while (LoaderList
!= NULL
) {
1086 LoaderList
= LoaderList
->NextEntry
;
1087 MyFreePool(Temp
->FileName
);
1090 } // static VOID CleanUpLoaderList()
1092 // Returns FALSE if the specified file/volume matches the GlobalConfig.DontScanDirs
1093 // or GlobalConfig.DontScanVolumes specification, or if Path points to a volume
1094 // other than the one specified by Volume, or if the specified path is SelfDir.
1095 // Returns TRUE if none of these conditions is met -- that is, if the path is
1096 // eligible for scanning.
1097 static BOOLEAN
ShouldScan(REFIT_VOLUME
*Volume
, CHAR16
*Path
) {
1098 CHAR16
*VolName
= NULL
, *DontScanDir
, *PathCopy
= NULL
;
1100 BOOLEAN ScanIt
= TRUE
;
1102 if ((IsIn(Volume
->VolName
, GlobalConfig
.DontScanVolumes
)) || (IsIn(Volume
->PartName
, GlobalConfig
.DontScanVolumes
)))
1105 if ((StriCmp(Path
, SelfDirPath
) == 0) && (Volume
->DeviceHandle
== SelfVolume
->DeviceHandle
))
1108 // See if Path includes an explicit volume declaration that's NOT Volume....
1109 PathCopy
= StrDuplicate(Path
);
1110 if (SplitVolumeAndFilename(&PathCopy
, &VolName
)) {
1111 VolumeNumberToName(Volume
, &VolName
);
1112 if (VolName
&& StriCmp(VolName
, Volume
->VolName
) != 0) {
1115 } // if Path includes volume specification
1116 MyFreePool(PathCopy
);
1117 MyFreePool(VolName
);
1120 // See if Volume is in GlobalConfig.DontScanDirs....
1121 while (ScanIt
&& (DontScanDir
= FindCommaDelimited(GlobalConfig
.DontScanDirs
, i
++))) {
1122 SplitVolumeAndFilename(&DontScanDir
, &VolName
);
1123 CleanUpPathNameSlashes(DontScanDir
);
1124 VolumeNumberToName(Volume
, &VolName
);
1125 if (VolName
!= NULL
) {
1126 if ((StriCmp(VolName
, Volume
->VolName
) == 0) && (StriCmp(DontScanDir
, Path
) == 0))
1129 if (StriCmp(DontScanDir
, Path
) == 0)
1132 MyFreePool(DontScanDir
);
1133 MyFreePool(VolName
);
1139 } // BOOLEAN ShouldScan()
1141 // Returns TRUE if the file is byte-for-byte identical with the fallback file
1142 // on the volume AND if the file is not itself the fallback file; returns
1143 // FALSE if the file is not identical to the fallback file OR if the file
1144 // IS the fallback file. Intended for use in excluding the fallback boot
1145 // loader when it's a duplicate of another boot loader.
1146 static BOOLEAN
DuplicatesFallback(IN REFIT_VOLUME
*Volume
, IN CHAR16
*FileName
) {
1147 CHAR8
*FileContents
, *FallbackContents
;
1148 EFI_FILE_HANDLE FileHandle
, FallbackHandle
;
1149 EFI_FILE_INFO
*FileInfo
, *FallbackInfo
;
1150 UINTN FileSize
= 0, FallbackSize
= 0;
1152 BOOLEAN AreIdentical
= FALSE
;
1154 if (!FileExists(Volume
->RootDir
, FileName
) || !FileExists(Volume
->RootDir
, FALLBACK_FULLNAME
))
1157 CleanUpPathNameSlashes(FileName
);
1159 if (StriCmp(FileName
, FALLBACK_FULLNAME
) == 0)
1160 return FALSE
; // identical filenames, so not a duplicate....
1162 Status
= refit_call5_wrapper(Volume
->RootDir
->Open
, Volume
->RootDir
, &FileHandle
, FileName
, EFI_FILE_MODE_READ
, 0);
1163 if (Status
== EFI_SUCCESS
) {
1164 FileInfo
= LibFileInfo(FileHandle
);
1165 FileSize
= FileInfo
->FileSize
;
1170 Status
= refit_call5_wrapper(Volume
->RootDir
->Open
, Volume
->RootDir
, &FallbackHandle
, FALLBACK_FULLNAME
, EFI_FILE_MODE_READ
, 0);
1171 if (Status
== EFI_SUCCESS
) {
1172 FallbackInfo
= LibFileInfo(FallbackHandle
);
1173 FallbackSize
= FallbackInfo
->FileSize
;
1175 refit_call1_wrapper(FileHandle
->Close
, FileHandle
);
1179 if (FallbackSize
!= FileSize
) { // not same size, so can't be identical
1180 AreIdentical
= FALSE
;
1181 } else { // could be identical; do full check....
1182 FileContents
= AllocatePool(FileSize
);
1183 FallbackContents
= AllocatePool(FallbackSize
);
1184 if (FileContents
&& FallbackContents
) {
1185 Status
= refit_call3_wrapper(FileHandle
->Read
, FileHandle
, &FileSize
, FileContents
);
1186 if (Status
== EFI_SUCCESS
) {
1187 Status
= refit_call3_wrapper(FallbackHandle
->Read
, FallbackHandle
, &FallbackSize
, FallbackContents
);
1189 if (Status
== EFI_SUCCESS
) {
1190 AreIdentical
= (CompareMem(FileContents
, FallbackContents
, FileSize
) == 0);
1193 MyFreePool(FileContents
);
1194 MyFreePool(FallbackContents
);
1197 // BUG ALERT: Some systems (e.g., DUET, some Macs with large displays) crash if the
1198 // following two calls are reversed. Go figure....
1199 refit_call1_wrapper(FileHandle
->Close
, FallbackHandle
);
1200 refit_call1_wrapper(FileHandle
->Close
, FileHandle
);
1201 return AreIdentical
;
1202 } // BOOLEAN DuplicatesFallback()
1204 // Returns FALSE if two measures of file size are identical for a single file,
1205 // TRUE if not or if the file can't be opened and the other measure is non-0.
1206 // Despite the function's name, this isn't really a direct test of symbolic
1207 // link status, since EFI doesn't officially support symlinks. It does seem
1208 // to be a reliable indicator, though. (OTOH, some disk errors might cause a
1209 // file to fail to open, which would return a false positive -- but as I use
1210 // this function to exclude symbolic links from the list of boot loaders,
1211 // that would be fine, since such boot loaders wouldn't work.)
1212 static BOOLEAN
IsSymbolicLink(REFIT_VOLUME
*Volume
, CHAR16
*Path
, EFI_FILE_INFO
*DirEntry
) {
1213 EFI_FILE_HANDLE FileHandle
;
1214 EFI_FILE_INFO
*FileInfo
= NULL
;
1216 UINTN FileSize2
= 0;
1219 FileName
= StrDuplicate(Path
);
1220 MergeStrings(&FileName
, DirEntry
->FileName
, L
'\\');
1221 CleanUpPathNameSlashes(FileName
);
1223 Status
= refit_call5_wrapper(Volume
->RootDir
->Open
, Volume
->RootDir
, &FileHandle
, FileName
, EFI_FILE_MODE_READ
, 0);
1224 if (Status
== EFI_SUCCESS
) {
1225 FileInfo
= LibFileInfo(FileHandle
);
1226 if (FileInfo
!= NULL
)
1227 FileSize2
= FileInfo
->FileSize
;
1230 MyFreePool(FileName
);
1231 MyFreePool(FileInfo
);
1233 return (DirEntry
->FileSize
!= FileSize2
);
1234 } // BOOLEAN IsSymbolicLink()
1236 // Returns TRUE if a file with the same name as the original but with
1237 // ".efi.signed" is also present in the same directory. Ubuntu is using
1238 // this filename as a signed version of the original unsigned kernel, and
1239 // there's no point in cluttering the display with two kernels that will
1240 // behave identically on non-SB systems, or when one will fail when SB
1242 static BOOLEAN
HasSignedCounterpart(IN REFIT_VOLUME
*Volume
, IN CHAR16
*Path
, IN CHAR16
*Filename
) {
1243 CHAR16
*NewFile
= NULL
;
1244 BOOLEAN retval
= FALSE
;
1246 MergeStrings(&NewFile
, Path
, 0);
1247 MergeStrings(&NewFile
, Filename
, L
'\\');
1248 MergeStrings(&NewFile
, L
".efi.signed", 0);
1249 if (FileExists(Volume
->RootDir
, NewFile
))
1251 MyFreePool(NewFile
);
1254 } // BOOLEAN HasSignedCounterpart()
1256 // Scan an individual directory for EFI boot loader files and, if found,
1257 // add them to the list. Exception: Ignores FALLBACK_FULLNAME, which is picked
1258 // up in ScanEfiFiles(). Sorts the entries within the loader directory so that
1259 // the most recent one appears first in the list.
1260 // Returns TRUE if a duplicate for FALLBACK_FILENAME was found, FALSE if not.
1261 static BOOLEAN
ScanLoaderDir(IN REFIT_VOLUME
*Volume
, IN CHAR16
*Path
, IN CHAR16
*Pattern
)
1264 REFIT_DIR_ITER DirIter
;
1265 EFI_FILE_INFO
*DirEntry
;
1266 CHAR16 FileName
[256], *Extension
;
1267 struct LOADER_LIST
*LoaderList
= NULL
, *NewLoader
;
1268 BOOLEAN FoundFallbackDuplicate
= FALSE
;
1270 if ((!SelfDirPath
|| !Path
|| ((StriCmp(Path
, SelfDirPath
) == 0) && (Volume
->DeviceHandle
!= SelfVolume
->DeviceHandle
)) ||
1271 (StriCmp(Path
, SelfDirPath
) != 0)) &&
1272 (ShouldScan(Volume
, Path
))) {
1273 // look through contents of the directory
1274 DirIterOpen(Volume
->RootDir
, Path
, &DirIter
);
1275 while (DirIterNext(&DirIter
, 2, Pattern
, &DirEntry
)) {
1276 Extension
= FindExtension(DirEntry
->FileName
);
1277 if (DirEntry
->FileName
[0] == '.' ||
1278 StriCmp(Extension
, L
".icns") == 0 ||
1279 StriCmp(Extension
, L
".png") == 0 ||
1280 (StriCmp(DirEntry
->FileName
, FALLBACK_BASENAME
) == 0 && (StriCmp(Path
, L
"EFI\\BOOT") == 0)) ||
1281 StriSubCmp(L
"shell", DirEntry
->FileName
) ||
1282 IsSymbolicLink(Volume
, Path
, DirEntry
) || /* is symbolic link */
1283 HasSignedCounterpart(Volume
, Path
, DirEntry
->FileName
) || /* a file with same name plus ".efi.signed" is present */
1284 FilenameIn(Volume
, Path
, DirEntry
->FileName
, GlobalConfig
.DontScanFiles
))
1285 continue; // skip this
1288 SPrint(FileName
, 255, L
"\\%s\\%s", Path
, DirEntry
->FileName
);
1290 SPrint(FileName
, 255, L
"\\%s", DirEntry
->FileName
);
1291 CleanUpPathNameSlashes(FileName
);
1293 if(!IsValidLoader(Volume
->RootDir
, FileName
))
1296 NewLoader
= AllocateZeroPool(sizeof(struct LOADER_LIST
));
1297 if (NewLoader
!= NULL
) {
1298 NewLoader
->FileName
= StrDuplicate(FileName
);
1299 NewLoader
->TimeStamp
= DirEntry
->ModificationTime
;
1300 LoaderList
= AddLoaderListEntry(LoaderList
, NewLoader
);
1301 if (DuplicatesFallback(Volume
, FileName
))
1302 FoundFallbackDuplicate
= TRUE
;
1304 MyFreePool(Extension
);
1307 NewLoader
= LoaderList
;
1308 while (NewLoader
!= NULL
) {
1309 AddLoaderEntry(NewLoader
->FileName
, NULL
, Volume
);
1310 NewLoader
= NewLoader
->NextEntry
;
1313 CleanUpLoaderList(LoaderList
);
1314 Status
= DirIterClose(&DirIter
);
1315 // NOTE: EFI_INVALID_PARAMETER really is an error that should be reported;
1316 // but I've gotten reports from users who are getting this error occasionally
1317 // and I can't find anything wrong or reproduce the problem, so I'm putting
1318 // it down to buggy EFI implementations and ignoring that particular error....
1319 if ((Status
!= EFI_NOT_FOUND
) && (Status
!= EFI_INVALID_PARAMETER
)) {
1321 SPrint(FileName
, 255, L
"while scanning the %s directory", Path
);
1323 StrCpy(FileName
, L
"while scanning the root directory");
1324 CheckError(Status
, FileName
);
1325 } // if (Status != EFI_NOT_FOUND)
1326 } // if not scanning a blacklisted directory
1328 return FoundFallbackDuplicate
;
1329 } /* static VOID ScanLoaderDir() */
1331 static VOID
ScanEfiFiles(REFIT_VOLUME
*Volume
) {
1333 REFIT_DIR_ITER EfiDirIter
;
1334 EFI_FILE_INFO
*EfiDirEntry
;
1335 CHAR16 FileName
[256], *Directory
= NULL
, *MatchPatterns
, *VolName
= NULL
, *SelfPath
;
1337 BOOLEAN ScanFallbackLoader
= TRUE
;
1338 BOOLEAN FoundBRBackup
= FALSE
;
1340 if ((Volume
->RootDir
!= NULL
) && (Volume
->VolName
!= NULL
) && (Volume
->IsReadable
)) {
1341 MatchPatterns
= StrDuplicate(LOADER_MATCH_PATTERNS
);
1342 if (GlobalConfig
.ScanAllLinux
)
1343 MergeStrings(&MatchPatterns
, LINUX_MATCH_PATTERNS
, L
',');
1345 // check for Mac OS X boot loader
1346 if (ShouldScan(Volume
, L
"System\\Library\\CoreServices")) {
1347 StrCpy(FileName
, MACOSX_LOADER_PATH
);
1348 if (FileExists(Volume
->RootDir
, FileName
) && !FilenameIn(Volume
, Directory
, L
"boot.efi", GlobalConfig
.DontScanFiles
)) {
1349 AddLoaderEntry(FileName
, L
"Mac OS X", Volume
);
1350 if (DuplicatesFallback(Volume
, FileName
))
1351 ScanFallbackLoader
= FALSE
;
1355 StrCpy(FileName
, L
"System\\Library\\CoreServices\\xom.efi");
1356 if (FileExists(Volume
->RootDir
, FileName
) && !FilenameIn(Volume
, Directory
, L
"boot.efi", GlobalConfig
.DontScanFiles
)) {
1357 AddLoaderEntry(FileName
, L
"Windows XP (XoM)", Volume
);
1358 if (DuplicatesFallback(Volume
, FileName
))
1359 ScanFallbackLoader
= FALSE
;
1361 } // if should scan Mac directory
1363 // check for Microsoft boot loader/menu
1364 if (ShouldScan(Volume
, L
"EFI\\Microsoft\\Boot")) {
1365 StrCpy(FileName
, L
"EFI\\Microsoft\\Boot\\bkpbootmgfw.efi");
1366 if (FileExists(Volume
->RootDir
, FileName
) && !FilenameIn(Volume
, Directory
, L
"bkpbootmgfw.efi",
1367 GlobalConfig
.DontScanFiles
)) {
1368 AddLoaderEntry(FileName
, L
"Microsoft EFI boot (Boot Repair backup)", Volume
);
1369 FoundBRBackup
= TRUE
;
1370 if (DuplicatesFallback(Volume
, FileName
))
1371 ScanFallbackLoader
= FALSE
;
1373 StrCpy(FileName
, L
"EFI\\Microsoft\\Boot\\bootmgfw.efi");
1374 if (FileExists(Volume
->RootDir
, FileName
) && !FilenameIn(Volume
, Directory
, L
"bootmgfw.efi", GlobalConfig
.DontScanFiles
)) {
1376 AddLoaderEntry(FileName
, L
"Supposed Microsoft EFI boot (probably GRUB)", Volume
);
1378 AddLoaderEntry(FileName
, L
"Microsoft EFI boot", Volume
);
1379 if (DuplicatesFallback(Volume
, FileName
))
1380 ScanFallbackLoader
= FALSE
;
1384 // scan the root directory for EFI executables
1385 if (ScanLoaderDir(Volume
, L
"\\", MatchPatterns
))
1386 ScanFallbackLoader
= FALSE
;
1388 // scan subdirectories of the EFI directory (as per the standard)
1389 DirIterOpen(Volume
->RootDir
, L
"EFI", &EfiDirIter
);
1390 while (DirIterNext(&EfiDirIter
, 1, NULL
, &EfiDirEntry
)) {
1391 if (StriCmp(EfiDirEntry
->FileName
, L
"tools") == 0 || EfiDirEntry
->FileName
[0] == '.')
1392 continue; // skip this, doesn't contain boot loaders or is scanned later
1393 SPrint(FileName
, 255, L
"EFI\\%s", EfiDirEntry
->FileName
);
1394 if (ScanLoaderDir(Volume
, FileName
, MatchPatterns
))
1395 ScanFallbackLoader
= FALSE
;
1397 Status
= DirIterClose(&EfiDirIter
);
1398 if (Status
!= EFI_NOT_FOUND
)
1399 CheckError(Status
, L
"while scanning the EFI directory");
1401 // Scan user-specified (or additional default) directories....
1403 while ((Directory
= FindCommaDelimited(GlobalConfig
.AlsoScan
, i
++)) != NULL
) {
1404 if (ShouldScan(Volume
, Directory
)) {
1405 SplitVolumeAndFilename(&Directory
, &VolName
);
1406 CleanUpPathNameSlashes(Directory
);
1407 Length
= StrLen(Directory
);
1408 if ((Length
> 0) && ScanLoaderDir(Volume
, Directory
, MatchPatterns
))
1409 ScanFallbackLoader
= FALSE
;
1410 MyFreePool(VolName
);
1411 } // if should scan dir
1412 MyFreePool(Directory
);
1415 // Don't scan the fallback loader if it's on the same volume and a duplicate of rEFInd itself....
1416 SelfPath
= DevicePathToStr(SelfLoadedImage
->FilePath
);
1417 CleanUpPathNameSlashes(SelfPath
);
1418 if ((Volume
->DeviceHandle
== SelfLoadedImage
->DeviceHandle
) && DuplicatesFallback(Volume
, SelfPath
))
1419 ScanFallbackLoader
= FALSE
;
1421 // If not a duplicate & if it exists & if it's not us, create an entry
1422 // for the fallback boot loader
1423 if (ScanFallbackLoader
&& FileExists(Volume
->RootDir
, FALLBACK_FULLNAME
) && ShouldScan(Volume
, L
"EFI\\BOOT"))
1424 AddLoaderEntry(FALLBACK_FULLNAME
, L
"Fallback boot loader", Volume
);
1426 } // static VOID ScanEfiFiles()
1428 // Scan internal disks for valid EFI boot loaders....
1429 static VOID
ScanInternal(VOID
) {
1432 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1433 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_INTERNAL
) {
1434 ScanEfiFiles(Volumes
[VolumeIndex
]);
1437 } // static VOID ScanInternal()
1439 // Scan external disks for valid EFI boot loaders....
1440 static VOID
ScanExternal(VOID
) {
1443 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1444 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_EXTERNAL
) {
1445 ScanEfiFiles(Volumes
[VolumeIndex
]);
1448 } // static VOID ScanExternal()
1450 // Scan internal disks for valid EFI boot loaders....
1451 static VOID
ScanOptical(VOID
) {
1454 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1455 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_OPTICAL
) {
1456 ScanEfiFiles(Volumes
[VolumeIndex
]);
1459 } // static VOID ScanOptical()
1462 // legacy boot functions
1465 static EFI_STATUS
ActivateMbrPartition(IN EFI_BLOCK_IO
*BlockIO
, IN UINTN PartitionIndex
)
1468 UINT8 SectorBuffer
[512];
1469 MBR_PARTITION_INFO
*MbrTable
, *EMbrTable
;
1470 UINT32 ExtBase
, ExtCurrent
, NextExtCurrent
;
1471 UINTN LogicalPartitionIndex
= 4;
1473 BOOLEAN HaveBootCode
;
1476 Status
= refit_call5_wrapper(BlockIO
->ReadBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, 0, 512, SectorBuffer
);
1477 if (EFI_ERROR(Status
))
1479 if (*((UINT16
*)(SectorBuffer
+ 510)) != 0xaa55)
1480 return EFI_NOT_FOUND
; // safety measure #1
1482 // add boot code if necessary
1483 HaveBootCode
= FALSE
;
1484 for (i
= 0; i
< MBR_BOOTCODE_SIZE
; i
++) {
1485 if (SectorBuffer
[i
] != 0) {
1486 HaveBootCode
= TRUE
;
1490 if (!HaveBootCode
) {
1491 // no boot code found in the MBR, add the syslinux MBR code
1492 SetMem(SectorBuffer
, MBR_BOOTCODE_SIZE
, 0);
1493 CopyMem(SectorBuffer
, syslinux_mbr
, SYSLINUX_MBR_SIZE
);
1496 // set the partition active
1497 MbrTable
= (MBR_PARTITION_INFO
*)(SectorBuffer
+ 446);
1499 for (i
= 0; i
< 4; i
++) {
1500 if (MbrTable
[i
].Flags
!= 0x00 && MbrTable
[i
].Flags
!= 0x80)
1501 return EFI_NOT_FOUND
; // safety measure #2
1502 if (i
== PartitionIndex
)
1503 MbrTable
[i
].Flags
= 0x80;
1504 else if (PartitionIndex
>= 4 && IS_EXTENDED_PART_TYPE(MbrTable
[i
].Type
)) {
1505 MbrTable
[i
].Flags
= 0x80;
1506 ExtBase
= MbrTable
[i
].StartLBA
;
1508 MbrTable
[i
].Flags
= 0x00;
1512 Status
= refit_call5_wrapper(BlockIO
->WriteBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, 0, 512, SectorBuffer
);
1513 if (EFI_ERROR(Status
))
1516 if (PartitionIndex
>= 4) {
1517 // we have to activate a logical partition, so walk the EMBR chain
1519 // NOTE: ExtBase was set above while looking at the MBR table
1520 for (ExtCurrent
= ExtBase
; ExtCurrent
; ExtCurrent
= NextExtCurrent
) {
1521 // read current EMBR
1522 Status
= refit_call5_wrapper(BlockIO
->ReadBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, ExtCurrent
, 512, SectorBuffer
);
1523 if (EFI_ERROR(Status
))
1525 if (*((UINT16
*)(SectorBuffer
+ 510)) != 0xaa55)
1526 return EFI_NOT_FOUND
; // safety measure #3
1528 // scan EMBR, set appropriate partition active
1529 EMbrTable
= (MBR_PARTITION_INFO
*)(SectorBuffer
+ 446);
1531 for (i
= 0; i
< 4; i
++) {
1532 if (EMbrTable
[i
].Flags
!= 0x00 && EMbrTable
[i
].Flags
!= 0x80)
1533 return EFI_NOT_FOUND
; // safety measure #4
1534 if (EMbrTable
[i
].StartLBA
== 0 || EMbrTable
[i
].Size
== 0)
1536 if (IS_EXTENDED_PART_TYPE(EMbrTable
[i
].Type
)) {
1537 // link to next EMBR
1538 NextExtCurrent
= ExtBase
+ EMbrTable
[i
].StartLBA
;
1539 EMbrTable
[i
].Flags
= (PartitionIndex
>= LogicalPartitionIndex
) ? 0x80 : 0x00;
1542 // logical partition
1543 EMbrTable
[i
].Flags
= (PartitionIndex
== LogicalPartitionIndex
) ? 0x80 : 0x00;
1544 LogicalPartitionIndex
++;
1548 // write current EMBR
1549 Status
= refit_call5_wrapper(BlockIO
->WriteBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, ExtCurrent
, 512, SectorBuffer
);
1550 if (EFI_ERROR(Status
))
1553 if (PartitionIndex
< LogicalPartitionIndex
)
1554 break; // stop the loop, no need to touch further EMBRs
1560 } /* static EFI_STATUS ActivateMbrPartition() */
1562 // early 2006 Core Duo / Core Solo models
1563 static UINT8 LegacyLoaderDevicePath1Data
[] = {
1564 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1565 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1566 0xFF, 0xFF, 0xF9, 0xFF, 0x00, 0x00, 0x00, 0x00,
1567 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1568 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1569 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1571 // mid-2006 Mac Pro (and probably other Core 2 models)
1572 static UINT8 LegacyLoaderDevicePath2Data
[] = {
1573 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1574 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1575 0xFF, 0xFF, 0xF7, 0xFF, 0x00, 0x00, 0x00, 0x00,
1576 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1577 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1578 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1580 // mid-2007 MBP ("Santa Rosa" based models)
1581 static UINT8 LegacyLoaderDevicePath3Data
[] = {
1582 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1583 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1584 0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
1585 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1586 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1587 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1590 static UINT8 LegacyLoaderDevicePath4Data
[] = {
1591 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1592 0x00, 0x00, 0xC0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1593 0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
1594 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1595 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1596 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1598 // late-2008 MB/MBP (NVidia chipset)
1599 static UINT8 LegacyLoaderDevicePath5Data
[] = {
1600 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1601 0x00, 0x40, 0xCB, 0xFF, 0x00, 0x00, 0x00, 0x00,
1602 0xFF, 0xBF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
1603 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1604 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1605 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1608 static EFI_DEVICE_PATH
*LegacyLoaderList
[] = {
1609 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath1Data
,
1610 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath2Data
,
1611 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath3Data
,
1612 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath4Data
,
1613 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath5Data
,
1617 #define MAX_DISCOVERED_PATHS (16)
1619 static VOID
StartLegacy(IN LEGACY_ENTRY
*Entry
)
1622 EG_IMAGE
*BootLogoImage
;
1623 UINTN ErrorInStep
= 0;
1624 EFI_DEVICE_PATH
*DiscoveredPathList
[MAX_DISCOVERED_PATHS
];
1626 BeginExternalScreen(TRUE
, L
"Booting Legacy OS (Mac mode)");
1628 BootLogoImage
= LoadOSIcon(Entry
->Volume
->OSIconName
, L
"legacy", TRUE
);
1629 if (BootLogoImage
!= NULL
)
1630 BltImageAlpha(BootLogoImage
,
1631 (UGAWidth
- BootLogoImage
->Width
) >> 1,
1632 (UGAHeight
- BootLogoImage
->Height
) >> 1,
1633 &StdBackgroundPixel
);
1635 if (Entry
->Volume
->IsMbrPartition
) {
1636 ActivateMbrPartition(Entry
->Volume
->WholeDiskBlockIO
, Entry
->Volume
->MbrPartitionIndex
);
1639 ExtractLegacyLoaderPaths(DiscoveredPathList
, MAX_DISCOVERED_PATHS
, LegacyLoaderList
);
1641 Status
= StartEFIImageList(DiscoveredPathList
, Entry
->LoadOptions
, TYPE_LEGACY
, L
"legacy loader", 0, &ErrorInStep
, TRUE
);
1642 if (Status
== EFI_NOT_FOUND
) {
1643 if (ErrorInStep
== 1) {
1644 Print(L
"\nPlease make sure that you have the latest firmware update installed.\n");
1645 } else if (ErrorInStep
== 3) {
1646 Print(L
"\nThe firmware refused to boot from the selected volume. Note that external\n"
1647 L
"hard drives are not well-supported by Apple's firmware for legacy OS booting.\n");
1650 FinishExternalScreen();
1651 } /* static VOID StartLegacy() */
1653 // Start a device on a non-Mac using the EFI_LEGACY_BIOS_PROTOCOL
1654 static VOID
StartLegacyUEFI(LEGACY_ENTRY
*Entry
)
1656 BeginExternalScreen(TRUE
, L
"Booting Legacy OS (UEFI mode)");
1658 BdsLibConnectDevicePath (Entry
->BdsOption
->DevicePath
);
1659 BdsLibDoLegacyBoot(Entry
->BdsOption
);
1661 // If we get here, it means that there was a failure....
1662 Print(L
"Failure booting legacy (BIOS) OS.");
1664 FinishExternalScreen();
1665 } // static VOID StartLegacyUEFI()
1667 static LEGACY_ENTRY
* AddLegacyEntry(IN CHAR16
*LoaderTitle
, IN REFIT_VOLUME
*Volume
)
1669 LEGACY_ENTRY
*Entry
, *SubEntry
;
1670 REFIT_MENU_SCREEN
*SubScreen
;
1671 CHAR16
*VolDesc
, *LegacyTitle
;
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 LegacyTitle
= AllocateZeroPool(256 * sizeof(CHAR16
));
1688 if (LegacyTitle
!= NULL
)
1689 SPrint(LegacyTitle
, 255, L
"Boot %s from %s", LoaderTitle
, VolDesc
);
1690 if (IsInSubstring(LegacyTitle
, GlobalConfig
.DontScanVolumes
)) {
1691 MyFreePool(LegacyTitle
);
1695 // prepare the menu entry
1696 Entry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1697 Entry
->me
.Title
= LegacyTitle
;
1698 Entry
->me
.Tag
= TAG_LEGACY
;
1700 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1701 Entry
->me
.Image
= LoadOSIcon(Volume
->OSIconName
, L
"legacy", FALSE
);
1702 Entry
->me
.BadgeImage
= Volume
->VolBadgeImage
;
1703 Entry
->Volume
= Volume
;
1704 Entry
->LoadOptions
= (Volume
->DiskKind
== DISK_KIND_OPTICAL
) ? L
"CD" :
1705 ((Volume
->DiskKind
== DISK_KIND_EXTERNAL
) ? L
"USB" : L
"HD");
1706 Entry
->Enabled
= TRUE
;
1708 // create the submenu
1709 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
1710 SubScreen
->Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1711 SPrint(SubScreen
->Title
, 255, L
"Boot Options for %s on %s", LoaderTitle
, VolDesc
);
1712 SubScreen
->TitleImage
= Entry
->me
.Image
;
1713 SubScreen
->Hint1
= StrDuplicate(SUBSCREEN_HINT1
);
1714 if (GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_EDITOR
) {
1715 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2_NO_EDITOR
);
1717 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2
);
1721 SubEntry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1722 SubEntry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1723 SPrint(SubEntry
->me
.Title
, 255, L
"Boot %s", LoaderTitle
);
1724 SubEntry
->me
.Tag
= TAG_LEGACY
;
1725 SubEntry
->Volume
= Entry
->Volume
;
1726 SubEntry
->LoadOptions
= Entry
->LoadOptions
;
1727 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
1729 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
1730 Entry
->me
.SubScreen
= SubScreen
;
1731 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1733 } /* static LEGACY_ENTRY * AddLegacyEntry() */
1736 // #ifdef __MAKEWITH_GNUEFI
1737 // static VOID ScanLegacyUEFI(IN UINTN DiskType){}
1739 // default volume badge icon based on disk kind
1740 static EG_IMAGE
* GetDiskBadge(IN UINTN DiskType
) {
1741 EG_IMAGE
* Badge
= NULL
;
1745 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_INTERNAL
);
1748 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_EXTERNAL
);
1751 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_OPTICAL
);
1755 } // static EG_IMAGE * GetDiskBadge()
1758 Create a rEFInd boot option from a Legacy BIOS protocol option.
1760 static LEGACY_ENTRY
* AddLegacyEntryUEFI(BDS_COMMON_OPTION
*BdsOption
, IN UINT16 DiskType
)
1762 LEGACY_ENTRY
*Entry
, *SubEntry
;
1763 REFIT_MENU_SCREEN
*SubScreen
;
1764 CHAR16 ShortcutLetter
= 0;
1765 CHAR16
*LegacyDescription
= BdsOption
->Description
;
1767 if (IsInSubstring(LegacyDescription
, GlobalConfig
.DontScanVolumes
))
1770 // prepare the menu entry
1771 Entry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1772 Entry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1773 SPrint(Entry
->me
.Title
, 255, L
"Boot legacy target %s", LegacyDescription
);
1774 Entry
->me
.Tag
= TAG_LEGACY_UEFI
;
1776 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1777 Entry
->me
.Image
= LoadOSIcon(L
"legacy", L
"legacy", TRUE
);
1778 Entry
->LoadOptions
= (DiskType
== BBS_CDROM
) ? L
"CD" :
1779 ((DiskType
== BBS_USB
) ? L
"USB" : L
"HD");
1780 Entry
->me
.BadgeImage
= GetDiskBadge(DiskType
);
1781 Entry
->BdsOption
= BdsOption
;
1782 Entry
->Enabled
= TRUE
;
1784 // create the submenu
1785 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
1786 SubScreen
->Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1787 SPrint(SubScreen
->Title
, 255, L
"No boot options for legacy target");
1788 SubScreen
->TitleImage
= Entry
->me
.Image
;
1789 SubScreen
->Hint1
= StrDuplicate(SUBSCREEN_HINT1
);
1790 if (GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_EDITOR
) {
1791 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2_NO_EDITOR
);
1793 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2
);
1797 SubEntry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1798 SubEntry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1799 SPrint(SubEntry
->me
.Title
, 255, L
"Boot %s", LegacyDescription
);
1800 SubEntry
->me
.Tag
= TAG_LEGACY_UEFI
;
1801 Entry
->BdsOption
= BdsOption
;
1802 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
1804 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
1805 Entry
->me
.SubScreen
= SubScreen
;
1806 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1808 } /* static LEGACY_ENTRY * AddLegacyEntryUEFI() */
1811 Scan for legacy BIOS targets on machines that implement EFI_LEGACY_BIOS_PROTOCOL.
1812 In testing, protocol has not been implemented on Macs but has been
1813 implemented on several Dell PCs and an ASUS motherboard.
1814 Restricts output to disks of the specified DiskType.
1816 static VOID
ScanLegacyUEFI(IN UINTN DiskType
)
1819 EFI_LEGACY_BIOS_PROTOCOL
*LegacyBios
;
1820 UINT16
*BootOrder
= NULL
;
1822 CHAR16 BootOption
[10];
1823 UINTN BootOrderSize
= 0;
1825 BDS_COMMON_OPTION
*BdsOption
;
1826 LIST_ENTRY TempList
;
1827 BBS_BBS_DEVICE_PATH
*BbsDevicePath
= NULL
;
1828 BOOLEAN SearchingForUsb
= FALSE
;
1830 InitializeListHead (&TempList
);
1831 ZeroMem (Buffer
, sizeof (Buffer
));
1833 // If LegacyBios protocol is not implemented on this platform, then
1834 //we do not support this type of legacy boot on this machine.
1835 Status
= refit_call3_wrapper(gBS
->LocateProtocol
, &gEfiLegacyBootProtocolGuid
, NULL
, (VOID
**) &LegacyBios
);
1836 if (EFI_ERROR (Status
))
1839 // EFI calls USB drives BBS_HARDDRIVE, but we want to distinguish them,
1840 // so we set DiskType inappropriately elsewhere in the program and
1841 // "translate" it here.
1842 if (DiskType
== BBS_USB
) {
1843 DiskType
= BBS_HARDDISK
;
1844 SearchingForUsb
= TRUE
;
1847 // Grab the boot order
1848 BootOrder
= BdsLibGetVariableAndSize(L
"BootOrder", &gEfiGlobalVariableGuid
, &BootOrderSize
);
1849 if (BootOrder
== NULL
) {
1854 while (Index
< BootOrderSize
/ sizeof (UINT16
))
1856 // Grab each boot option variable from the boot order, and convert
1857 // the variable into a BDS boot option
1858 UnicodeSPrint (BootOption
, sizeof (BootOption
), L
"Boot%04x", BootOrder
[Index
]);
1859 BdsOption
= BdsLibVariableToOption (&TempList
, BootOption
);
1861 if (BdsOption
!= NULL
) {
1862 BbsDevicePath
= (BBS_BBS_DEVICE_PATH
*)BdsOption
->DevicePath
;
1863 // Only add the entry if it is of a requested type (e.g. USB, HD)
1864 // Two checks necessary because some systems return EFI boot loaders
1865 // with a DeviceType value that would inappropriately include them
1866 // as legacy loaders....
1867 if ((BbsDevicePath
->DeviceType
== DiskType
) && (BdsOption
->DevicePath
->Type
== DEVICE_TYPE_BIOS
)) {
1868 // USB flash drives appear as hard disks with certain media flags set.
1869 // Look for this, and if present, pass it on with the (technically
1870 // incorrect, but internally useful) BBS_TYPE_USB flag set.
1871 if (DiskType
== BBS_HARDDISK
) {
1872 if (SearchingForUsb
&& (BbsDevicePath
->StatusFlag
& (BBS_MEDIA_PRESENT
| BBS_MEDIA_MAYBE_PRESENT
))) {
1873 AddLegacyEntryUEFI(BdsOption
, BBS_USB
);
1874 } else if (!SearchingForUsb
&& !(BbsDevicePath
->StatusFlag
& (BBS_MEDIA_PRESENT
| BBS_MEDIA_MAYBE_PRESENT
))) {
1875 AddLegacyEntryUEFI(BdsOption
, DiskType
);
1878 AddLegacyEntryUEFI(BdsOption
, DiskType
);
1881 } // if (BdsOption != NULL)
1884 } /* static VOID ScanLegacyUEFI() */
1885 //#endif // __MAKEWITH_GNUEFI
1887 static VOID
ScanLegacyVolume(REFIT_VOLUME
*Volume
, UINTN VolumeIndex
) {
1889 BOOLEAN ShowVolume
, HideIfOthersFound
;
1892 HideIfOthersFound
= FALSE
;
1893 if (Volume
->IsAppleLegacy
) {
1895 HideIfOthersFound
= TRUE
;
1896 } else if (Volume
->HasBootCode
) {
1898 if (Volume
->BlockIO
== Volume
->WholeDiskBlockIO
&&
1899 Volume
->BlockIOOffset
== 0 &&
1900 Volume
->OSName
== NULL
)
1901 // this is a whole disk (MBR) entry; hide if we have entries for partitions
1902 HideIfOthersFound
= TRUE
;
1904 if (HideIfOthersFound
) {
1905 // check for other bootable entries on the same disk
1906 for (VolumeIndex2
= 0; VolumeIndex2
< VolumesCount
; VolumeIndex2
++) {
1907 if (VolumeIndex2
!= VolumeIndex
&& Volumes
[VolumeIndex2
]->HasBootCode
&&
1908 Volumes
[VolumeIndex2
]->WholeDiskBlockIO
== Volume
->WholeDiskBlockIO
)
1914 AddLegacyEntry(NULL
, Volume
);
1915 } // static VOID ScanLegacyVolume()
1917 // Scan attached optical discs for legacy (BIOS) boot code
1918 // and add anything found to the list....
1919 static VOID
ScanLegacyDisc(VOID
)
1922 REFIT_VOLUME
*Volume
;
1924 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
) {
1925 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1926 Volume
= Volumes
[VolumeIndex
];
1927 if (Volume
->DiskKind
== DISK_KIND_OPTICAL
)
1928 ScanLegacyVolume(Volume
, VolumeIndex
);
1930 } else if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
1931 ScanLegacyUEFI(BBS_CDROM
);
1933 } /* static VOID ScanLegacyDisc() */
1935 // Scan internal hard disks for legacy (BIOS) boot code
1936 // and add anything found to the list....
1937 static VOID
ScanLegacyInternal(VOID
)
1940 REFIT_VOLUME
*Volume
;
1942 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
) {
1943 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1944 Volume
= Volumes
[VolumeIndex
];
1945 if (Volume
->DiskKind
== DISK_KIND_INTERNAL
)
1946 ScanLegacyVolume(Volume
, VolumeIndex
);
1948 } else if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
1949 // TODO: This actually picks up USB flash drives, too; try to find
1950 // a way to differentiate the two....
1951 ScanLegacyUEFI(BBS_HARDDISK
);
1953 } /* static VOID ScanLegacyInternal() */
1955 // Scan external disks for legacy (BIOS) boot code
1956 // and add anything found to the list....
1957 static VOID
ScanLegacyExternal(VOID
)
1960 REFIT_VOLUME
*Volume
;
1962 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
) {
1963 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1964 Volume
= Volumes
[VolumeIndex
];
1965 if (Volume
->DiskKind
== DISK_KIND_EXTERNAL
)
1966 ScanLegacyVolume(Volume
, VolumeIndex
);
1968 } else if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
1969 // TODO: This actually doesn't do anything useful; leaving in hopes of
1970 // fixing it later....
1971 ScanLegacyUEFI(BBS_USB
);
1973 } /* static VOID ScanLegacyExternal() */
1976 // pre-boot tool functions
1979 static VOID
StartTool(IN LOADER_ENTRY
*Entry
)
1981 BeginExternalScreen(Entry
->UseGraphicsMode
, Entry
->me
.Title
+ 6); // assumes "Start <title>" as assigned below
1982 StartEFIImage(Entry
->DevicePath
, Entry
->LoadOptions
, TYPE_EFI
,
1983 Basename(Entry
->LoaderPath
), Entry
->OSType
, NULL
, TRUE
);
1984 FinishExternalScreen();
1985 } /* static VOID StartTool() */
1987 static LOADER_ENTRY
* AddToolEntry(EFI_HANDLE DeviceHandle
, IN CHAR16
*LoaderPath
, IN CHAR16
*LoaderTitle
, IN EG_IMAGE
*Image
,
1988 IN CHAR16 ShortcutLetter
, IN BOOLEAN UseGraphicsMode
)
1990 LOADER_ENTRY
*Entry
;
1991 CHAR16
*TitleStr
= NULL
;
1993 Entry
= AllocateZeroPool(sizeof(LOADER_ENTRY
));
1995 TitleStr
= PoolPrint(L
"Start %s", LoaderTitle
);
1996 Entry
->me
.Title
= TitleStr
;
1997 Entry
->me
.Tag
= TAG_TOOL
;
1999 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
2000 Entry
->me
.Image
= Image
;
2001 Entry
->LoaderPath
= (LoaderPath
) ? StrDuplicate(LoaderPath
) : NULL
;
2002 Entry
->DevicePath
= FileDevicePath(DeviceHandle
, Entry
->LoaderPath
);
2003 Entry
->UseGraphicsMode
= UseGraphicsMode
;
2005 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
2007 } /* static LOADER_ENTRY * AddToolEntry() */
2010 // pre-boot driver functions
2013 static UINTN
ScanDriverDir(IN CHAR16
*Path
)
2016 REFIT_DIR_ITER DirIter
;
2018 EFI_FILE_INFO
*DirEntry
;
2019 CHAR16 FileName
[256];
2021 CleanUpPathNameSlashes(Path
);
2022 // look through contents of the directory
2023 DirIterOpen(SelfRootDir
, Path
, &DirIter
);
2024 while (DirIterNext(&DirIter
, 2, LOADER_MATCH_PATTERNS
, &DirEntry
)) {
2025 if (DirEntry
->FileName
[0] == '.')
2026 continue; // skip this
2028 SPrint(FileName
, 255, L
"%s\\%s", Path
, DirEntry
->FileName
);
2030 Status
= StartEFIImage(FileDevicePath(SelfLoadedImage
->DeviceHandle
, FileName
),
2031 L
"", TYPE_EFI
, DirEntry
->FileName
, 0, NULL
, FALSE
);
2033 Status
= DirIterClose(&DirIter
);
2034 if (Status
!= EFI_NOT_FOUND
) {
2035 SPrint(FileName
, 255, L
"while scanning the %s directory", Path
);
2036 CheckError(Status
, FileName
);
2041 static EFI_STATUS
ConnectAllDriversToAllControllers(VOID
)
2044 UINTN AllHandleCount
;
2045 EFI_HANDLE
*AllHandleBuffer
;
2048 EFI_HANDLE
*HandleBuffer
;
2054 Status
= LibLocateHandle(AllHandles
,
2059 if (EFI_ERROR(Status
))
2062 for (Index
= 0; Index
< AllHandleCount
; Index
++) {
2064 // Scan the handle database
2066 Status
= LibScanHandleDatabase(NULL
,
2068 AllHandleBuffer
[Index
],
2073 if (EFI_ERROR (Status
))
2077 if (HandleType
[Index
] & EFI_HANDLE_TYPE_DRIVER_BINDING_HANDLE
)
2079 if (HandleType
[Index
] & EFI_HANDLE_TYPE_IMAGE_HANDLE
)
2084 for (HandleIndex
= 0; HandleIndex
< HandleCount
; HandleIndex
++) {
2085 if (HandleType
[HandleIndex
] & EFI_HANDLE_TYPE_PARENT_HANDLE
)
2090 if (HandleType
[Index
] & EFI_HANDLE_TYPE_DEVICE_HANDLE
) {
2091 Status
= refit_call4_wrapper(BS
->ConnectController
,
2092 AllHandleBuffer
[Index
],
2100 MyFreePool (HandleBuffer
);
2101 MyFreePool (HandleType
);
2105 MyFreePool (AllHandleBuffer
);
2107 } /* EFI_STATUS ConnectAllDriversToAllControllers() */
2109 // Load all EFI drivers from rEFInd's "drivers" subdirectory and from the
2110 // directories specified by the user in the "scan_driver_dirs" configuration
2112 static VOID
LoadDrivers(VOID
)
2114 CHAR16
*Directory
, *SelfDirectory
;
2115 UINTN i
= 0, Length
, NumFound
= 0;
2117 // load drivers from the subdirectories of rEFInd's home directory specified
2118 // in the DRIVER_DIRS constant.
2119 while ((Directory
= FindCommaDelimited(DRIVER_DIRS
, i
++)) != NULL
) {
2120 SelfDirectory
= SelfDirPath
? StrDuplicate(SelfDirPath
) : NULL
;
2121 CleanUpPathNameSlashes(SelfDirectory
);
2122 MergeStrings(&SelfDirectory
, Directory
, L
'\\');
2123 NumFound
+= ScanDriverDir(SelfDirectory
);
2124 MyFreePool(Directory
);
2125 MyFreePool(SelfDirectory
);
2128 // Scan additional user-specified driver directories....
2130 while ((Directory
= FindCommaDelimited(GlobalConfig
.DriverDirs
, i
++)) != NULL
) {
2131 CleanUpPathNameSlashes(Directory
);
2132 Length
= StrLen(Directory
);
2134 NumFound
+= ScanDriverDir(Directory
);
2136 MyFreePool(Directory
);
2139 // connect all devices
2141 ConnectAllDriversToAllControllers();
2142 } /* static VOID LoadDrivers() */
2144 // Determine what (if any) type of legacy (BIOS) boot support is available
2145 static VOID
FindLegacyBootType(VOID
) {
2147 EFI_LEGACY_BIOS_PROTOCOL
*LegacyBios
;
2149 GlobalConfig
.LegacyType
= LEGACY_TYPE_NONE
;
2151 // UEFI-style legacy BIOS support is available only with the TianoCore EDK2
2152 // build environment, and then only with some EFI implementations....
2153 Status
= refit_call3_wrapper(gBS
->LocateProtocol
, &gEfiLegacyBootProtocolGuid
, NULL
, (VOID
**) &LegacyBios
);
2154 if (!EFI_ERROR (Status
))
2155 GlobalConfig
.LegacyType
= LEGACY_TYPE_UEFI
;
2157 // Macs have their own system. If the firmware vendor code contains the
2158 // string "Apple", assume it's available. Note that this overrides the
2159 // UEFI type, and might yield false positives if the vendor string
2160 // contains "Apple" as part of something bigger, so this isn't 100%
2162 if (StriSubCmp(L
"Apple", ST
->FirmwareVendor
))
2163 GlobalConfig
.LegacyType
= LEGACY_TYPE_MAC
;
2164 } // static VOID FindLegacyBootType
2166 // Warn the user if legacy OS scans are enabled but the firmware or this
2167 // application can't support them....
2168 static VOID
WarnIfLegacyProblems() {
2169 BOOLEAN found
= FALSE
;
2172 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_NONE
) {
2174 if (GlobalConfig
.ScanFor
[i
] == 'h' || GlobalConfig
.ScanFor
[i
] == 'b' || GlobalConfig
.ScanFor
[i
] == 'c')
2177 } while ((i
< NUM_SCAN_OPTIONS
) && (!found
));
2179 Print(L
"NOTE: refind.conf's 'scanfor' line specifies scanning for one or more legacy\n");
2180 Print(L
"(BIOS) boot options; however, this is not possible because your computer lacks\n");
2181 Print(L
"the necessary Compatibility Support Module (CSM) support.\n");
2184 } // if no legacy support
2185 } // static VOID WarnIfLegacyProblems()
2187 // Locates boot loaders. NOTE: This assumes that GlobalConfig.LegacyType is set correctly.
2188 static VOID
ScanForBootloaders(VOID
) {
2191 BOOLEAN ScanForLegacy
= FALSE
;
2193 // Determine up-front if we'll be scanning for legacy loaders....
2194 for (i
= 0; i
< NUM_SCAN_OPTIONS
; i
++) {
2195 s
= GlobalConfig
.ScanFor
[i
];
2196 if ((s
== 'c') || (s
== 'C') || (s
== 'h') || (s
== 'H') || (s
== 'b') || (s
== 'B'))
2197 ScanForLegacy
= TRUE
;
2200 // If UEFI & scanning for legacy loaders, update NVRAM boot manager list
2201 if ((GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) && ScanForLegacy
) {
2202 BdsDeleteAllInvalidLegacyBootOptions();
2203 BdsAddNonExistingLegacyBootOptions();
2206 // scan for loaders and tools, add them to the menu
2207 for (i
= 0; i
< NUM_SCAN_OPTIONS
; i
++) {
2208 switch(GlobalConfig
.ScanFor
[i
]) {
2213 ScanLegacyInternal();
2216 ScanLegacyExternal();
2219 ScanUserConfigured(GlobalConfig
.ConfigFilename
);
2233 // assign shortcut keys
2234 for (i
= 0; i
< MainMenu
.EntryCount
&& MainMenu
.Entries
[i
]->Row
== 0 && i
< 9; i
++)
2235 MainMenu
.Entries
[i
]->ShortcutDigit
= (CHAR16
)('1' + i
);
2237 // wait for user ACK when there were errors
2238 FinishTextScreen(FALSE
);
2239 } // static VOID ScanForBootloaders()
2241 // Locate a single tool from the specified Locations using one of the
2242 // specified Names and add it to the menu.
2243 static VOID
FindTool(CHAR16
*Locations
, CHAR16
*Names
, CHAR16
*Description
, UINTN Icon
) {
2244 UINTN j
= 0, k
, VolumeIndex
;
2245 CHAR16
*DirName
, *FileName
, *PathName
, FullDescription
[256];
2247 while ((DirName
= FindCommaDelimited(Locations
, j
++)) != NULL
) {
2249 while ((FileName
= FindCommaDelimited(Names
, k
++)) != NULL
) {
2250 PathName
= StrDuplicate(DirName
);
2251 MergeStrings(&PathName
, FileName
, (StriCmp(PathName
, L
"\\") == 0) ? 0 : L
'\\');
2252 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
2253 if ((Volumes
[VolumeIndex
]->RootDir
!= NULL
) && (FileExists(Volumes
[VolumeIndex
]->RootDir
, PathName
))) {
2254 SPrint(FullDescription
, 255, L
"%s at %s on %s", Description
, PathName
, Volumes
[VolumeIndex
]->VolName
);
2255 AddToolEntry(Volumes
[VolumeIndex
]->DeviceHandle
, PathName
, FullDescription
, BuiltinIcon(Icon
), 'S', FALSE
);
2258 MyFreePool(PathName
);
2259 MyFreePool(FileName
);
2261 MyFreePool(DirName
);
2262 } // while Locations
2263 } // VOID FindTool()
2265 // Add the second-row tags containing built-in and external tools (EFI shell,
2267 static VOID
ScanForTools(VOID
) {
2268 CHAR16
*FileName
= NULL
, *VolName
= NULL
, *MokLocations
, Description
[256];
2269 REFIT_MENU_ENTRY
*TempMenuEntry
;
2270 UINTN i
, j
, VolumeIndex
;
2274 MokLocations
= StrDuplicate(MOK_LOCATIONS
);
2275 if (MokLocations
!= NULL
)
2276 MergeStrings(&MokLocations
, SelfDirPath
, L
',');
2278 for (i
= 0; i
< NUM_TOOLS
; i
++) {
2279 switch(GlobalConfig
.ShowTools
[i
]) {
2280 // NOTE: Be sure that FileName is NULL at the end of each case.
2282 TempMenuEntry
= CopyMenuEntry(&MenuEntryShutdown
);
2283 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_SHUTDOWN
);
2284 AddMenuEntry(&MainMenu
, TempMenuEntry
);
2288 TempMenuEntry
= CopyMenuEntry(&MenuEntryReset
);
2289 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_RESET
);
2290 AddMenuEntry(&MainMenu
, TempMenuEntry
);
2294 TempMenuEntry
= CopyMenuEntry(&MenuEntryAbout
);
2295 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT
);
2296 AddMenuEntry(&MainMenu
, TempMenuEntry
);
2300 TempMenuEntry
= CopyMenuEntry(&MenuEntryExit
);
2301 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_EXIT
);
2302 AddMenuEntry(&MainMenu
, TempMenuEntry
);
2306 if (EfivarGetRaw(&GlobalGuid
, L
"OsIndicationsSupported", &b
, &j
) == EFI_SUCCESS
) {
2308 if (osind
& EFI_OS_INDICATIONS_BOOT_TO_FW_UI
) {
2309 TempMenuEntry
= CopyMenuEntry(&MenuEntryFirmware
);
2310 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_FIRMWARE
);
2311 AddMenuEntry(&MainMenu
, TempMenuEntry
);
2318 while ((FileName
= FindCommaDelimited(SHELL_NAMES
, j
++)) != NULL
) {
2319 if (FileExists(SelfRootDir
, FileName
)) {
2320 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, L
"EFI Shell", BuiltinIcon(BUILTIN_ICON_TOOL_SHELL
),
2323 MyFreePool(FileName
);
2329 while ((FileName
= FindCommaDelimited(GPTSYNC_NAMES
, j
++)) != NULL
) {
2330 if (FileExists(SelfRootDir
, FileName
)) {
2331 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, L
"Hybrid MBR tool", BuiltinIcon(BUILTIN_ICON_TOOL_PART
),
2334 MyFreePool(FileName
);
2341 while ((FileName
= FindCommaDelimited(GDISK_NAMES
, j
++)) != NULL
) {
2342 if (FileExists(SelfRootDir
, FileName
)) {
2343 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, L
"disk partitioning tool",
2344 BuiltinIcon(BUILTIN_ICON_TOOL_PART
), 'G', FALSE
);
2346 MyFreePool(FileName
);
2351 case TAG_APPLE_RECOVERY
:
2352 FileName
= StrDuplicate(L
"\\com.apple.recovery.boot\\boot.efi");
2353 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
2354 if ((Volumes
[VolumeIndex
]->RootDir
!= NULL
) && (FileExists(Volumes
[VolumeIndex
]->RootDir
, FileName
))) {
2355 SPrint(Description
, 255, L
"Apple Recovery on %s", Volumes
[VolumeIndex
]->VolName
);
2356 AddToolEntry(Volumes
[VolumeIndex
]->DeviceHandle
, FileName
, Description
,
2357 BuiltinIcon(BUILTIN_ICON_TOOL_APPLE_RESCUE
), 'R', TRUE
);
2360 MyFreePool(FileName
);
2364 case TAG_WINDOWS_RECOVERY
:
2366 while ((FileName
= FindCommaDelimited(GlobalConfig
.WindowsRecoveryFiles
, j
++)) != NULL
) {
2367 SplitVolumeAndFilename(&FileName
, &VolName
);
2368 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
2369 if ((Volumes
[VolumeIndex
]->RootDir
!= NULL
) && (FileExists(Volumes
[VolumeIndex
]->RootDir
, FileName
)) &&
2370 ((VolName
== NULL
) || (StriCmp(VolName
, Volumes
[VolumeIndex
]->VolName
) == 0))) {
2371 SPrint(Description
, 255, L
"Microsoft Recovery on %s", Volumes
[VolumeIndex
]->VolName
);
2372 AddToolEntry(Volumes
[VolumeIndex
]->DeviceHandle
, FileName
, Description
,
2373 BuiltinIcon(BUILTIN_ICON_TOOL_WINDOWS_RESCUE
), 'R', TRUE
);
2377 MyFreePool(FileName
);
2379 MyFreePool(VolName
);
2384 FindTool(MokLocations
, MOK_NAMES
, L
"MOK utility", BUILTIN_ICON_TOOL_MOK_TOOL
);
2388 FindTool(MEMTEST_LOCATIONS
, MEMTEST_NAMES
, L
"Memory test utility", BUILTIN_ICON_TOOL_MEMTEST
);
2393 } // static VOID ScanForTools
2395 // Rescan for boot loaders
2396 VOID
RescanAll(VOID
) {
2403 egDisplayMessage(L
"Scanning for new boot loaders; please wait....", &BGColor
);
2404 FreeList((VOID
***) &(MainMenu
.Entries
), &MainMenu
.EntryCount
);
2405 MainMenu
.Entries
= NULL
;
2406 MainMenu
.EntryCount
= 0;
2407 ReadConfig(GlobalConfig
.ConfigFilename
);
2408 ConnectAllDriversToAllControllers();
2410 ScanForBootloaders();
2413 } // VOID RescanAll()
2415 #ifdef __MAKEWITH_TIANO
2417 // Minimal initialization function
2418 static VOID
InitializeLib(IN EFI_HANDLE ImageHandle
, IN EFI_SYSTEM_TABLE
*SystemTable
) {
2420 // gImageHandle = ImageHandle;
2421 gBS
= SystemTable
->BootServices
;
2422 // gRS = SystemTable->RuntimeServices;
2423 gRT
= SystemTable
->RuntimeServices
; // Some BDS functions need gRT to be set
2424 EfiGetSystemConfigurationTable (&gEfiDxeServicesTableGuid
, (VOID
**) &gDS
);
2426 // InitializeConsoleSim();
2431 // Set up our own Secure Boot extensions....
2432 // Returns TRUE on success, FALSE otherwise
2433 static BOOLEAN
SecureBootSetup(VOID
) {
2435 BOOLEAN Success
= FALSE
;
2437 if (secure_mode() && ShimLoaded()) {
2438 Status
= security_policy_install();
2439 if (Status
== EFI_SUCCESS
) {
2442 Print(L
"Failed to install MOK Secure Boot extensions");
2446 } // VOID SecureBootSetup()
2448 // Remove our own Secure Boot extensions....
2449 // Returns TRUE on success, FALSE otherwise
2450 static BOOLEAN
SecureBootUninstall(VOID
) {
2452 BOOLEAN Success
= TRUE
;
2454 if (secure_mode()) {
2455 Status
= security_policy_uninstall();
2456 if (Status
!= EFI_SUCCESS
) {
2458 BeginTextScreen(L
"Secure Boot Policy Failure");
2459 Print(L
"Failed to uninstall MOK Secure Boot extensions; forcing a reboot.");
2461 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);
2465 } // VOID SecureBootUninstall
2467 // Sets the global configuration filename; will be CONFIG_FILE_NAME unless the
2468 // "-c" command-line option is set, in which case that takes precedence.
2469 // If an error is encountered, leaves the value alone (it should be set to
2470 // CONFIG_FILE_NAME when GlobalConfig is initialized).
2471 static VOID
SetConfigFilename(EFI_HANDLE ImageHandle
) {
2472 EFI_LOADED_IMAGE
*Info
;
2473 CHAR16
*Options
, *FileName
;
2477 Status
= refit_call3_wrapper(BS
->HandleProtocol
, ImageHandle
, &LoadedImageProtocol
, (VOID
**) &Info
);
2478 if ((Status
== EFI_SUCCESS
) && (Info
->LoadOptionsSize
> 0)) {
2479 Options
= (CHAR16
*) Info
->LoadOptions
;
2480 Where
= FindSubString(L
" -c ", Options
);
2482 FileName
= StrDuplicate(&Options
[Where
+ 4]);
2483 Where
= FindSubString(L
" ", FileName
);
2485 FileName
[Where
] = L
'\0';
2487 if (FileExists(SelfDir
, FileName
)) {
2488 GlobalConfig
.ConfigFilename
= FileName
;
2490 Print(L
"Specified configuration file (%s) doesn't exist; using\n'refind.conf' default\n", FileName
);
2491 MyFreePool(FileName
);
2495 } // VOID SetConfigFilename()
2502 efi_main (EFI_HANDLE ImageHandle
, EFI_SYSTEM_TABLE
*SystemTable
)
2505 BOOLEAN MainLoopRunning
= TRUE
;
2506 BOOLEAN MokProtocol
;
2507 REFIT_MENU_ENTRY
*ChosenEntry
;
2509 CHAR16
*Selection
= NULL
;
2513 InitializeLib(ImageHandle
, SystemTable
);
2514 Status
= InitRefitLib(ImageHandle
);
2515 if (EFI_ERROR(Status
))
2518 // read configuration
2519 CopyMem(GlobalConfig
.ScanFor
, "ieom ", NUM_SCAN_OPTIONS
);
2520 FindLegacyBootType();
2521 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
)
2522 CopyMem(GlobalConfig
.ScanFor
, "ihebocm ", NUM_SCAN_OPTIONS
);
2523 SetConfigFilename(ImageHandle
);
2524 ReadConfig(GlobalConfig
.ConfigFilename
);
2528 WarnIfLegacyProblems();
2529 MainMenu
.TimeoutSeconds
= GlobalConfig
.Timeout
;
2531 // disable EFI watchdog timer
2532 refit_call4_wrapper(BS
->SetWatchdogTimer
, 0x0000, 0x0000, 0x0000, NULL
);
2534 // further bootstrap (now with config available)
2535 MokProtocol
= SecureBootSetup();
2538 ScanForBootloaders();
2542 if (GlobalConfig
.ScanDelay
> 0) {
2547 egDisplayMessage(L
"Pausing before disk scan; please wait....", &BGColor
);
2548 for (i
= 0; i
< GlobalConfig
.ScanDelay
; i
++)
2549 refit_call1_wrapper(BS
->Stall
, 1000000);
2553 if (GlobalConfig
.DefaultSelection
)
2554 Selection
= StrDuplicate(GlobalConfig
.DefaultSelection
);
2556 while (MainLoopRunning
) {
2557 MenuExit
= RunMainMenu(&MainMenu
, Selection
, &ChosenEntry
);
2559 // The Escape key triggers a re-scan operation....
2560 if (MenuExit
== MENU_EXIT_ESCAPE
) {
2566 switch (ChosenEntry
->Tag
) {
2568 case TAG_REBOOT
: // Reboot
2570 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);
2571 MainLoopRunning
= FALSE
; // just in case we get this far
2574 case TAG_SHUTDOWN
: // Shut Down
2576 refit_call4_wrapper(RT
->ResetSystem
, EfiResetShutdown
, EFI_SUCCESS
, 0, NULL
);
2577 MainLoopRunning
= FALSE
; // just in case we get this far
2580 case TAG_ABOUT
: // About rEFInd
2584 case TAG_LOADER
: // Boot OS via .EFI loader
2585 StartLoader((LOADER_ENTRY
*)ChosenEntry
);
2588 case TAG_LEGACY
: // Boot legacy OS
2589 StartLegacy((LEGACY_ENTRY
*)ChosenEntry
);
2592 case TAG_LEGACY_UEFI
: // Boot a legacy OS on a non-Mac
2593 StartLegacyUEFI((LEGACY_ENTRY
*)ChosenEntry
);
2596 case TAG_TOOL
: // Start a EFI tool
2597 StartTool((LOADER_ENTRY
*)ChosenEntry
);
2600 case TAG_EXIT
: // Terminate rEFInd
2601 if ((MokProtocol
) && !SecureBootUninstall()) {
2602 MainLoopRunning
= FALSE
; // just in case we get this far
2604 BeginTextScreen(L
" ");
2609 case TAG_FIRMWARE
: // Reboot into firmware's user interface
2610 RebootIntoFirmware();
2614 MyFreePool(Selection
);
2615 Selection
= (ChosenEntry
->Title
) ? StrDuplicate(ChosenEntry
->Title
) : NULL
;
2618 // If we end up here, things have gone wrong. Try to reboot, and if that
2619 // fails, go into an endless loop.
2620 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);