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
72 #ifdef __MAKEWITH_TIANO
73 #define LibLocateHandle gBS->LocateHandleBuffer
79 #define MACOSX_LOADER_PATH L"System\\Library\\CoreServices\\boot.efi"
81 #define SHELL_NAMES L"\\EFI\\tools\\shell.efi,\\EFI\\tools\\shellx64.efi,\\shell.efi,\\shellx64.efi"
82 #define GPTSYNC_NAMES L"\\EFI\\tools\\gptsync.efi,\\EFI\\tools\\gptsync_x64.efi"
83 #define GDISK_NAMES L"\\EFI\\tools\\gdisk.efi,\\EFI\\tools\\gdisk_x64.efi"
84 #define MEMTEST_NAMES L"memtest86.efi,memtest86_x64.efi,memtest86x64.efi,bootx64.efi"
85 #define DRIVER_DIRS L"drivers,drivers_x64"
86 #define FALLBACK_FULLNAME L"EFI\\BOOT\\bootx64.efi"
87 #define FALLBACK_BASENAME L"bootx64.efi"
88 #define EFI_STUB_ARCH 0x8664
90 #define SHELL_NAMES L"\\EFI\\tools\\shell.efi,\\EFI\\tools\\shellia32.efi,\\shell.efi,\\shellia32.efi"
91 #define GPTSYNC_NAMES L"\\EFI\\tools\\gptsync.efi,\\EFI\\tools\\gptsync_ia32.efi"
92 #define GDISK_NAMES L"\\EFI\\tools\\gdisk.efi,\\EFI\\tools\\gdisk_ia32.efi"
93 #define MEMTEST_NAMES L"memtest86.efi,memtest86_ia32.efi,memtest86ia32.efi,bootia32.efi"
94 #define DRIVER_DIRS L"drivers,drivers_ia32"
95 #define FALLBACK_FULLNAME L"EFI\\BOOT\\bootia32.efi"
96 #define FALLBACK_BASENAME L"bootia32.efi"
97 #define EFI_STUB_ARCH 0x014c
99 #define SHELL_NAMES L"\\EFI\\tools\\shell.efi,\\shell.efi"
100 #define GPTSYNC_NAMES L"\\EFI\\tools\\gptsync.efi"
101 #define GDISK_NAMES L"\\EFI\\tools\\gdisk.efi"
102 #define MEMTEST_NAMES L"memtest86.efi"
103 #define DRIVER_DIRS L"drivers"
104 #define FALLBACK_FULLNAME L"EFI\\BOOT\\boot.efi" /* Not really correct */
105 #define FALLBACK_BASENAME L"boot.efi" /* Not really correct */
107 #define FAT_ARCH 0x0ef1fab9 /* ID for Apple "fat" binary */
109 // Filename patterns that identify EFI boot loaders. Note that a single case (either L"*.efi" or
110 // L"*.EFI") is fine for most systems; but Gigabyte's buggy Hybrid EFI does a case-sensitive
111 // comparison when it should do a case-insensitive comparison, so I'm doubling this up. It does
112 // no harm on other computers, AFAIK. In theory, every case variation should be done for
113 // completeness, but that's ridiculous....
114 #define LOADER_MATCH_PATTERNS L"*.efi,*.EFI"
116 // Patterns that identify Linux kernels. Added to the loader match pattern when the
117 // scan_all_linux_kernels option is set in the configuration file. Causes kernels WITHOUT
118 // a ".efi" extension to be found when scanning for boot loaders.
119 #define LINUX_MATCH_PATTERNS L"vmlinuz*,bzImage*"
121 // Default hint text for program-launch submenus
122 #define SUBSCREEN_HINT1 L"Use arrow keys to move cursor; Enter to boot;"
123 #define SUBSCREEN_HINT2 L"Insert or F2 to edit options; Esc to return to main menu"
124 #define SUBSCREEN_HINT2_NO_EDITOR L"Esc to return to main menu"
128 #define TYPE_LEGACY 2
130 static REFIT_MENU_ENTRY MenuEntryAbout
= { L
"About rEFInd", TAG_ABOUT
, 1, 0, 'A', NULL
, NULL
, NULL
};
131 static REFIT_MENU_ENTRY MenuEntryReset
= { L
"Reboot Computer", TAG_REBOOT
, 1, 0, 'R', NULL
, NULL
, NULL
};
132 static REFIT_MENU_ENTRY MenuEntryShutdown
= { L
"Shut Down Computer", TAG_SHUTDOWN
, 1, 0, 'U', NULL
, NULL
, NULL
};
133 static REFIT_MENU_ENTRY MenuEntryReturn
= { L
"Return to Main Menu", TAG_RETURN
, 1, 0, 0, NULL
, NULL
, NULL
};
134 static REFIT_MENU_ENTRY MenuEntryExit
= { L
"Exit rEFInd", TAG_EXIT
, 1, 0, 0, NULL
, NULL
, NULL
};
135 static REFIT_MENU_ENTRY MenuEntryFirmware
= { L
"Reboot to Computer Setup Utility", TAG_FIRMWARE
, 1, 0, 0, NULL
, NULL
, NULL
};
137 static REFIT_MENU_SCREEN MainMenu
= { L
"Main Menu", NULL
, 0, NULL
, 0, NULL
, 0, L
"Automatic boot",
138 L
"Use arrow keys to move cursor; Enter to boot;",
139 L
"Insert or F2 for more options; Esc to refresh" };
140 static REFIT_MENU_SCREEN AboutMenu
= { L
"About", NULL
, 0, NULL
, 0, NULL
, 0, NULL
, L
"Press Enter to return to main menu", L
"" };
142 REFIT_CONFIG GlobalConfig
= { FALSE
, FALSE
, 0, 0, 0, DONT_CHANGE_TEXT_MODE
, 20, 0, 0, GRAPHICS_FOR_OSX
, LEGACY_TYPE_MAC
, 0, 0,
143 { DEFAULT_BIG_ICON_SIZE
/ 4, DEFAULT_SMALL_ICON_SIZE
, DEFAULT_BIG_ICON_SIZE
}, BANNER_NOSCALE
,
144 NULL
, NULL
, CONFIG_FILE_NAME
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
145 { TAG_SHELL
, TAG_MEMTEST
, TAG_GDISK
, TAG_APPLE_RECOVERY
, TAG_WINDOWS_RECOVERY
, TAG_MOK_TOOL
,
146 TAG_ABOUT
, TAG_SHUTDOWN
, TAG_REBOOT
, TAG_FIRMWARE
, 0, 0, 0, 0, 0, 0 }
149 EFI_GUID GlobalGuid
= EFI_GLOBAL_VARIABLE
;
151 GPT_DATA
*gPartitions
= NULL
;
153 // Structure used to hold boot loader filenames and time stamps in
154 // a linked list; used to sort entries within a directory.
158 struct LOADER_LIST
*NextEntry
;
165 static VOID
AboutrEFInd(VOID
)
167 CHAR16
*FirmwareVendor
;
169 if (AboutMenu
.EntryCount
== 0) {
170 AboutMenu
.TitleImage
= BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT
);
171 AddMenuInfoLine(&AboutMenu
, L
"rEFInd Version 0.8.0.6");
172 AddMenuInfoLine(&AboutMenu
, L
"");
173 AddMenuInfoLine(&AboutMenu
, L
"Copyright (c) 2006-2010 Christoph Pfisterer");
174 AddMenuInfoLine(&AboutMenu
, L
"Copyright (c) 2012-2014 Roderick W. Smith");
175 AddMenuInfoLine(&AboutMenu
, L
"Portions Copyright (c) Intel Corporation and others");
176 AddMenuInfoLine(&AboutMenu
, L
"Distributed under the terms of the GNU GPLv3 license");
177 AddMenuInfoLine(&AboutMenu
, L
"");
178 AddMenuInfoLine(&AboutMenu
, L
"Running on:");
179 AddMenuInfoLine(&AboutMenu
, PoolPrint(L
" EFI Revision %d.%02d", ST
->Hdr
.Revision
>> 16, ST
->Hdr
.Revision
& ((1 << 16) - 1)));
181 AddMenuInfoLine(&AboutMenu
, L
" Platform: x86 (32 bit)");
182 #elif defined(EFIX64)
183 AddMenuInfoLine(&AboutMenu
, PoolPrint(L
" Platform: x86_64 (64 bit); Secure Boot %s",
184 secure_mode() ? L
"active" : L
"inactive"));
186 AddMenuInfoLine(&AboutMenu
, L
" Platform: unknown");
188 FirmwareVendor
= StrDuplicate(ST
->FirmwareVendor
);
189 LimitStringLength(FirmwareVendor
, 65); // More than ~65 causes empty info page on 800x600 display
190 AddMenuInfoLine(&AboutMenu
, PoolPrint(L
" Firmware: %s %d.%02d", FirmwareVendor
, ST
->FirmwareRevision
>> 16,
191 ST
->FirmwareRevision
& ((1 << 16) - 1)));
192 AddMenuInfoLine(&AboutMenu
, PoolPrint(L
" Screen Output: %s", egScreenDescription()));
193 AddMenuInfoLine(&AboutMenu
, L
"");
194 #if defined(__MAKEWITH_GNUEFI)
195 AddMenuInfoLine(&AboutMenu
, L
"Built with GNU-EFI");
197 AddMenuInfoLine(&AboutMenu
, L
"Built with TianoCore EDK2");
199 AddMenuInfoLine(&AboutMenu
, L
"");
200 AddMenuInfoLine(&AboutMenu
, L
"For more information, see the rEFInd Web site:");
201 AddMenuInfoLine(&AboutMenu
, L
"http://www.rodsbooks.com/refind/");
202 AddMenuEntry(&AboutMenu
, &MenuEntryReturn
);
205 RunMenu(&AboutMenu
, NULL
);
206 } /* VOID AboutrEFInd() */
208 static VOID
WarnSecureBootError(CHAR16
*Name
, BOOLEAN Verbose
) {
210 Name
= L
"the loader";
212 refit_call2_wrapper(ST
->ConOut
->SetAttribute
, ST
->ConOut
, ATTR_ERROR
);
213 Print(L
"Secure Boot validation failure loading %s!\n", Name
);
214 refit_call2_wrapper(ST
->ConOut
->SetAttribute
, ST
->ConOut
, ATTR_BASIC
);
215 if (Verbose
&& secure_mode()) {
216 Print(L
"\nThis computer is configured with Secure Boot active, but\n%s has failed validation.\n", Name
);
217 Print(L
"\nYou can:\n * Launch another boot loader\n");
218 Print(L
" * Disable Secure Boot in your firmware\n");
219 Print(L
" * Sign %s with a machine owner key (MOK)\n", Name
);
220 Print(L
" * Use a MOK utility (often present on the second row) to add a MOK with which\n");
221 Print(L
" %s has already been signed.\n", Name
);
222 Print(L
" * Use a MOK utility to register %s (\"enroll its hash\") without\n", Name
);
223 Print(L
" signing it.\n");
224 Print(L
"\nSee http://www.rodsbooks.com/refind/secureboot.html for more information\n");
227 } // VOID WarnSecureBootError()
229 // Returns TRUE if this file is a valid EFI loader file, and is proper ARCH
230 static BOOLEAN
IsValidLoader(EFI_FILE
*RootDir
, CHAR16
*FileName
) {
231 BOOLEAN IsValid
= TRUE
;
232 #if defined (EFIX64) | defined (EFI32)
234 EFI_FILE_HANDLE FileHandle
;
236 UINTN Size
= sizeof(Header
);
238 if ((RootDir
== NULL
) || (FileName
== NULL
)) {
239 // Assume valid here, because Macs produce NULL RootDir (& maybe FileName)
240 // when launching from a Firewire drive. This should be handled better, but
241 // fix would have to be in StartEFIImageList() and/or in FindVolumeAndFilename().
245 Status
= refit_call5_wrapper(RootDir
->Open
, RootDir
, &FileHandle
, FileName
, EFI_FILE_MODE_READ
, 0);
246 if (EFI_ERROR(Status
))
249 Status
= refit_call3_wrapper(FileHandle
->Read
, FileHandle
, &Size
, Header
);
250 refit_call1_wrapper(FileHandle
->Close
, FileHandle
);
252 IsValid
= !EFI_ERROR(Status
) &&
253 Size
== sizeof(Header
) &&
254 ((Header
[0] == 'M' && Header
[1] == 'Z' &&
255 (Size
= *(UINT32
*)&Header
[0x3c]) < 0x180 &&
256 Header
[Size
] == 'P' && Header
[Size
+1] == 'E' &&
257 Header
[Size
+2] == 0 && Header
[Size
+3] == 0 &&
258 *(UINT16
*)&Header
[Size
+4] == EFI_STUB_ARCH
) ||
259 (*(UINT32
*)&Header
== FAT_ARCH
));
262 } // BOOLEAN IsValidLoader()
264 // Launch an EFI binary.
265 static EFI_STATUS
StartEFIImageList(IN EFI_DEVICE_PATH
**DevicePaths
,
266 IN CHAR16
*LoadOptions
, IN UINTN LoaderType
,
267 IN CHAR16
*ImageTitle
, IN CHAR8 OSType
,
268 OUT UINTN
*ErrorInStep
,
271 EFI_STATUS Status
, ReturnStatus
;
272 EFI_HANDLE ChildImageHandle
;
273 EFI_LOADED_IMAGE
*ChildLoadedImage
= NULL
;
274 REFIT_VOLUME
*Volume
= NULL
;
275 UINTN DevicePathIndex
;
276 CHAR16 ErrorInfo
[256];
277 CHAR16
*FullLoadOptions
= NULL
;
278 CHAR16
*Filename
= NULL
;
281 if (ErrorInStep
!= NULL
)
285 if (LoadOptions
!= NULL
) {
286 FullLoadOptions
= StrDuplicate(LoadOptions
);
287 if ((LoaderType
== TYPE_EFI
) && (OSType
== 'M')) {
288 MergeStrings(&FullLoadOptions
, L
" ", 0);
289 // NOTE: That last space is also added by the EFI shell and seems to be significant
290 // when passing options to Apple's boot.efi...
292 } // if (LoadOptions != NULL)
294 Print(L
"Starting %s\nUsing load options '%s'\n", ImageTitle
, FullLoadOptions
? FullLoadOptions
: L
"");
296 // load the image into memory (and execute it, in the case of a shim/MOK image).
297 ReturnStatus
= Status
= EFI_NOT_FOUND
; // in case the list is empty
298 for (DevicePathIndex
= 0; DevicePaths
[DevicePathIndex
] != NULL
; DevicePathIndex
++) {
299 FindVolumeAndFilename(DevicePaths
[DevicePathIndex
], &Volume
, &Filename
);
300 // Some EFIs crash if attempting to load driver for invalid architecture, so
301 // protect for this condition; but sometimes Volume comes back NULL, so provide
302 // an exception. (TODO: Handle this special condition better.)
303 if ((LoaderType
== TYPE_LEGACY
) || (Volume
== NULL
) || IsValidLoader(Volume
->RootDir
, Filename
)) {
304 if (Filename
&& (LoaderType
!= TYPE_LEGACY
)) {
305 Temp
= PoolPrint(L
"\\%s %s", Filename
, FullLoadOptions
? FullLoadOptions
: L
"");
307 MyFreePool(FullLoadOptions
);
308 FullLoadOptions
= Temp
;
312 // NOTE: Below commented-out line could be more efficient if file were read ahead of
313 // time and passed as a pre-loaded image to LoadImage(), but it doesn't work on my
314 // 32-bit Mac Mini or my 64-bit Intel box when launching a Linux kernel; the
315 // kernel returns a "Failed to handle fs_proto" error message.
316 // TODO: Track down the cause of this error and fix it, if possible.
317 // ReturnStatus = Status = refit_call6_wrapper(BS->LoadImage, FALSE, SelfImageHandle, DevicePaths[DevicePathIndex],
318 // ImageData, ImageSize, &ChildImageHandle);
319 ReturnStatus
= Status
= refit_call6_wrapper(BS
->LoadImage
, FALSE
, SelfImageHandle
, DevicePaths
[DevicePathIndex
],
320 NULL
, 0, &ChildImageHandle
);
322 Print(L
"Invalid loader file!\n");
323 ReturnStatus
= EFI_LOAD_ERROR
;
325 if (ReturnStatus
!= EFI_NOT_FOUND
) {
329 if ((Status
== EFI_ACCESS_DENIED
) || (Status
== EFI_SECURITY_VIOLATION
)) {
330 WarnSecureBootError(ImageTitle
, Verbose
);
333 SPrint(ErrorInfo
, 255, L
"while loading %s", ImageTitle
);
334 if (CheckError(Status
, ErrorInfo
)) {
335 if (ErrorInStep
!= NULL
)
340 ReturnStatus
= Status
= refit_call3_wrapper(BS
->HandleProtocol
, ChildImageHandle
, &LoadedImageProtocol
,
341 (VOID
**) &ChildLoadedImage
);
342 if (CheckError(Status
, L
"while getting a LoadedImageProtocol handle")) {
343 if (ErrorInStep
!= NULL
)
347 ChildLoadedImage
->LoadOptions
= (VOID
*)FullLoadOptions
;
348 ChildLoadedImage
->LoadOptionsSize
= FullLoadOptions
? ((UINT32
)StrLen(FullLoadOptions
) + 1) * sizeof(CHAR16
) : 0;
349 // turn control over to the image
350 // TODO: (optionally) re-enable the EFI watchdog timer!
352 // close open file handles
354 ReturnStatus
= Status
= refit_call3_wrapper(BS
->StartImage
, ChildImageHandle
, NULL
, NULL
);
356 // control returns here when the child image calls Exit()
357 SPrint(ErrorInfo
, 255, L
"returned from %s", ImageTitle
);
358 if (CheckError(Status
, ErrorInfo
)) {
359 if (ErrorInStep
!= NULL
)
363 // re-open file handles
367 // unload the image, we don't care if it works or not...
368 Status
= refit_call1_wrapper(BS
->UnloadImage
, ChildImageHandle
);
371 MyFreePool(FullLoadOptions
);
373 } /* static EFI_STATUS StartEFIImageList() */
375 static EFI_STATUS
StartEFIImage(IN EFI_DEVICE_PATH
*DevicePath
,
376 IN CHAR16
*LoadOptions
, IN UINTN LoaderType
,
377 IN CHAR16
*ImageTitle
, IN CHAR8 OSType
,
378 OUT UINTN
*ErrorInStep
,
381 EFI_DEVICE_PATH
*DevicePaths
[2];
383 DevicePaths
[0] = DevicePath
;
384 DevicePaths
[1] = NULL
;
385 return StartEFIImageList(DevicePaths
, LoadOptions
, LoaderType
, ImageTitle
, OSType
, ErrorInStep
, Verbose
);
386 } /* static EFI_STATUS StartEFIImage() */
388 // From gummiboot: Retrieve a raw EFI variable.
389 // Returns EFI status
390 static EFI_STATUS
EfivarGetRaw(EFI_GUID
*vendor
, CHAR16
*name
, CHAR8
**buffer
, UINTN
*size
) {
395 l
= sizeof(CHAR16
*) * EFI_MAXIMUM_VARIABLE_SIZE
;
396 buf
= AllocatePool(l
);
398 return EFI_OUT_OF_RESOURCES
;
400 err
= refit_call5_wrapper(RT
->GetVariable
, name
, vendor
, NULL
, &l
, buf
);
401 if (EFI_ERROR(err
) == EFI_SUCCESS
) {
408 } // EFI_STATUS EfivarGetRaw()
410 // From gummiboot: Set an EFI variable
411 static EFI_STATUS
EfivarSetRaw(EFI_GUID
*vendor
, CHAR16
*name
, CHAR8
*buf
, UINTN size
, BOOLEAN persistent
) {
414 flags
= EFI_VARIABLE_BOOTSERVICE_ACCESS
|EFI_VARIABLE_RUNTIME_ACCESS
;
416 flags
|= EFI_VARIABLE_NON_VOLATILE
;
418 return refit_call5_wrapper(RT
->SetVariable
, name
, vendor
, flags
, size
, buf
);
419 } // EFI_STATUS EfivarSetRaw()
421 // From gummiboot: Reboot the computer into its built-in user interface
422 static EFI_STATUS
RebootIntoFirmware(VOID
) {
428 osind
= EFI_OS_INDICATIONS_BOOT_TO_FW_UI
;
430 err
= EfivarGetRaw(&GlobalGuid
, L
"OsIndications", &b
, &size
);
431 if (err
== EFI_SUCCESS
)
435 err
= EfivarSetRaw(&GlobalGuid
, L
"OsIndications", (CHAR8
*)&osind
, sizeof(UINT64
), TRUE
);
436 if (err
!= EFI_SUCCESS
)
439 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);
440 Print(L
"Error calling ResetSystem: %r", err
);
447 // EFI OS loader functions
450 static VOID
StartLoader(LOADER_ENTRY
*Entry
)
452 UINTN ErrorInStep
= 0;
454 BeginExternalScreen(Entry
->UseGraphicsMode
, L
"Booting OS");
455 StartEFIImage(Entry
->DevicePath
, Entry
->LoadOptions
, TYPE_EFI
,
456 Basename(Entry
->LoaderPath
), Entry
->OSType
, &ErrorInStep
, !Entry
->UseGraphicsMode
);
457 FinishExternalScreen();
460 // Locate an initrd or initramfs file that matches the kernel specified by LoaderPath.
461 // The matching file has a name that begins with "init" and includes the same version
462 // number string as is found in LoaderPath -- but not a longer version number string.
463 // For instance, if LoaderPath is \EFI\kernels\bzImage-3.3.0.efi, and if \EFI\kernels
464 // has a file called initramfs-3.3.0.img, this function will return the string
465 // '\EFI\kernels\initramfs-3.3.0.img'. If the directory ALSO contains the file
466 // initramfs-3.3.0-rc7.img or initramfs-13.3.0.img, those files will NOT match;
467 // however, initmine-3.3.0.img might match. (FindInitrd() returns the first match it
468 // finds). Thus, care should be taken to avoid placing duplicate matching files in
469 // the kernel's directory.
470 // If no matching init file can be found, returns NULL.
471 static CHAR16
* FindInitrd(IN CHAR16
*LoaderPath
, IN REFIT_VOLUME
*Volume
) {
472 CHAR16
*InitrdName
= NULL
, *FileName
, *KernelVersion
, *InitrdVersion
, *Path
;
473 REFIT_DIR_ITER DirIter
;
474 EFI_FILE_INFO
*DirEntry
;
476 FileName
= Basename(LoaderPath
);
477 KernelVersion
= FindNumbers(FileName
);
478 Path
= FindPath(LoaderPath
);
480 // Add trailing backslash for root directory; necessary on some systems, but must
481 // NOT be added to all directories, since on other systems, a trailing backslash on
482 // anything but the root directory causes them to flake out!
483 if (StrLen(Path
) == 0) {
484 MergeStrings(&Path
, L
"\\", 0);
486 DirIterOpen(Volume
->RootDir
, Path
, &DirIter
);
487 // Now add a trailing backslash if it was NOT added earlier, for consistency in
488 // building the InitrdName later....
489 if ((StrLen(Path
) > 0) && (Path
[StrLen(Path
) - 1] != L
'\\'))
490 MergeStrings(&Path
, L
"\\", 0);
491 while ((DirIterNext(&DirIter
, 2, L
"init*", &DirEntry
)) && (InitrdName
== NULL
)) {
492 InitrdVersion
= FindNumbers(DirEntry
->FileName
);
493 if (KernelVersion
!= NULL
) {
494 if (StriCmp(InitrdVersion
, KernelVersion
) == 0) {
495 InitrdName
= PoolPrint(L
"%s%s", Path
, DirEntry
->FileName
);
498 if (InitrdVersion
== NULL
) {
499 InitrdName
= PoolPrint(L
"%s%s", Path
, DirEntry
->FileName
);
502 MyFreePool(InitrdVersion
);
504 DirIterClose(&DirIter
);
506 // Note: Don't FreePool(FileName), since Basename returns a pointer WITHIN the string it's passed.
507 MyFreePool(KernelVersion
);
510 } // static CHAR16 * FindInitrd()
512 LOADER_ENTRY
* AddPreparedLoaderEntry(LOADER_ENTRY
*Entry
) {
513 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
516 } // LOADER_ENTRY * AddPreparedLoaderEntry()
518 // Creates a copy of a menu screen.
519 // Returns a pointer to the copy of the menu screen.
520 static REFIT_MENU_SCREEN
* CopyMenuScreen(REFIT_MENU_SCREEN
*Entry
) {
521 REFIT_MENU_SCREEN
*NewEntry
;
524 NewEntry
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
525 if ((Entry
!= NULL
) && (NewEntry
!= NULL
)) {
526 CopyMem(NewEntry
, Entry
, sizeof(REFIT_MENU_SCREEN
));
527 NewEntry
->Title
= (Entry
->Title
) ? StrDuplicate(Entry
->Title
) : NULL
;
528 NewEntry
->TimeoutText
= (Entry
->TimeoutText
) ? StrDuplicate(Entry
->TimeoutText
) : NULL
;
529 if (Entry
->TitleImage
!= NULL
) {
530 NewEntry
->TitleImage
= AllocatePool(sizeof(EG_IMAGE
));
531 if (NewEntry
->TitleImage
!= NULL
)
532 CopyMem(NewEntry
->TitleImage
, Entry
->TitleImage
, sizeof(EG_IMAGE
));
534 NewEntry
->InfoLines
= (CHAR16
**) AllocateZeroPool(Entry
->InfoLineCount
* (sizeof(CHAR16
*)));
535 for (i
= 0; i
< Entry
->InfoLineCount
&& NewEntry
->InfoLines
; i
++) {
536 NewEntry
->InfoLines
[i
] = (Entry
->InfoLines
[i
]) ? StrDuplicate(Entry
->InfoLines
[i
]) : NULL
;
538 NewEntry
->Entries
= (REFIT_MENU_ENTRY
**) AllocateZeroPool(Entry
->EntryCount
* (sizeof (REFIT_MENU_ENTRY
*)));
539 for (i
= 0; i
< Entry
->EntryCount
&& NewEntry
->Entries
; i
++) {
540 AddMenuEntry(NewEntry
, Entry
->Entries
[i
]);
542 NewEntry
->Hint1
= (Entry
->Hint1
) ? StrDuplicate(Entry
->Hint1
) : NULL
;
543 NewEntry
->Hint2
= (Entry
->Hint2
) ? StrDuplicate(Entry
->Hint2
) : NULL
;
546 } // static REFIT_MENU_SCREEN* CopyMenuScreen()
548 // Creates a copy of a menu entry. Intended to enable moving a stack-based
549 // menu entry (such as the ones for the "reboot" and "exit" functions) to
550 // to the heap. This enables easier deletion of the whole set of menu
551 // entries when re-scanning.
552 // Returns a pointer to the copy of the menu entry.
553 static REFIT_MENU_ENTRY
* CopyMenuEntry(REFIT_MENU_ENTRY
*Entry
) {
554 REFIT_MENU_ENTRY
*NewEntry
;
556 NewEntry
= AllocateZeroPool(sizeof(REFIT_MENU_ENTRY
));
557 if ((Entry
!= NULL
) && (NewEntry
!= NULL
)) {
558 CopyMem(NewEntry
, Entry
, sizeof(REFIT_MENU_ENTRY
));
559 NewEntry
->Title
= (Entry
->Title
) ? StrDuplicate(Entry
->Title
) : NULL
;
560 if (Entry
->BadgeImage
!= NULL
) {
561 NewEntry
->BadgeImage
= AllocatePool(sizeof(EG_IMAGE
));
562 if (NewEntry
->BadgeImage
!= NULL
)
563 CopyMem(NewEntry
->BadgeImage
, Entry
->BadgeImage
, sizeof(EG_IMAGE
));
565 if (Entry
->Image
!= NULL
) {
566 NewEntry
->Image
= AllocatePool(sizeof(EG_IMAGE
));
567 if (NewEntry
->Image
!= NULL
)
568 CopyMem(NewEntry
->Image
, Entry
->Image
, sizeof(EG_IMAGE
));
570 if (Entry
->SubScreen
!= NULL
) {
571 NewEntry
->SubScreen
= CopyMenuScreen(Entry
->SubScreen
);
575 } // REFIT_MENU_ENTRY* CopyMenuEntry()
577 // Creates a new LOADER_ENTRY data structure and populates it with
578 // default values from the specified Entry, or NULL values if Entry
579 // is unspecified (NULL).
580 // Returns a pointer to the new data structure, or NULL if it
581 // couldn't be allocated
582 LOADER_ENTRY
*InitializeLoaderEntry(IN LOADER_ENTRY
*Entry
) {
583 LOADER_ENTRY
*NewEntry
= NULL
;
585 NewEntry
= AllocateZeroPool(sizeof(LOADER_ENTRY
));
586 if (NewEntry
!= NULL
) {
587 NewEntry
->me
.Title
= NULL
;
588 NewEntry
->me
.Tag
= TAG_LOADER
;
589 NewEntry
->Enabled
= TRUE
;
590 NewEntry
->UseGraphicsMode
= FALSE
;
591 NewEntry
->OSType
= 0;
593 NewEntry
->LoaderPath
= (Entry
->LoaderPath
) ? StrDuplicate(Entry
->LoaderPath
) : NULL
;
594 NewEntry
->VolName
= (Entry
->VolName
) ? StrDuplicate(Entry
->VolName
) : NULL
;
595 NewEntry
->DevicePath
= Entry
->DevicePath
;
596 NewEntry
->UseGraphicsMode
= Entry
->UseGraphicsMode
;
597 NewEntry
->LoadOptions
= (Entry
->LoadOptions
) ? StrDuplicate(Entry
->LoadOptions
) : NULL
;
598 NewEntry
->InitrdPath
= (Entry
->InitrdPath
) ? StrDuplicate(Entry
->InitrdPath
) : NULL
;
602 } // LOADER_ENTRY *InitializeLoaderEntry()
604 // Adds InitrdPath to Options, but only if Options doesn't already include an
605 // initrd= line. Done to enable overriding the default initrd selection in a
606 // refind_linux.conf file's options list.
607 // Returns a pointer to a new string. The calling function is responsible for
608 // freeing its memory.
609 static CHAR16
*AddInitrdToOptions(CHAR16
*Options
, CHAR16
*InitrdPath
) {
610 CHAR16
*NewOptions
= NULL
;
613 NewOptions
= StrDuplicate(Options
);
614 if ((InitrdPath
!= NULL
) && !StriSubCmp(L
"initrd=", Options
)) {
615 MergeStrings(&NewOptions
, L
"initrd=", L
' ');
616 MergeStrings(&NewOptions
, InitrdPath
, 0);
619 } // CHAR16 *AddInitrdToOptions()
621 // Prepare a REFIT_MENU_SCREEN data structure for a subscreen entry. This sets up
622 // the default entry that launches the boot loader using the same options as the
623 // main Entry does. Subsequent options can be added by the calling function.
624 // If a subscreen already exists in the Entry that's passed to this function,
625 // it's left unchanged and a pointer to it is returned.
626 // Returns a pointer to the new subscreen data structure, or NULL if there
627 // were problems allocating memory.
628 REFIT_MENU_SCREEN
*InitializeSubScreen(IN LOADER_ENTRY
*Entry
) {
629 CHAR16
*FileName
, *MainOptions
= NULL
;
630 REFIT_MENU_SCREEN
*SubScreen
= NULL
;
631 LOADER_ENTRY
*SubEntry
;
633 FileName
= Basename(Entry
->LoaderPath
);
634 if (Entry
->me
.SubScreen
== NULL
) { // No subscreen yet; initialize default entry....
635 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
636 if (SubScreen
!= NULL
) {
637 SubScreen
->Title
= AllocateZeroPool(sizeof(CHAR16
) * 256);
638 SPrint(SubScreen
->Title
, 255, L
"Boot Options for %s on %s",
639 (Entry
->Title
!= NULL
) ? Entry
->Title
: FileName
, Entry
->VolName
);
640 SubScreen
->TitleImage
= Entry
->me
.Image
;
642 SubEntry
= InitializeLoaderEntry(Entry
);
643 if (SubEntry
!= NULL
) {
644 SubEntry
->me
.Title
= StrDuplicate(L
"Boot using default options");
645 MainOptions
= SubEntry
->LoadOptions
;
646 SubEntry
->LoadOptions
= AddInitrdToOptions(MainOptions
, SubEntry
->InitrdPath
);
647 MyFreePool(MainOptions
);
648 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
649 } // if (SubEntry != NULL)
650 SubScreen
->Hint1
= StrDuplicate(SUBSCREEN_HINT1
);
651 if (GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_EDITOR
) {
652 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2_NO_EDITOR
);
654 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2
);
656 } // if (SubScreen != NULL)
657 } else { // existing subscreen; less initialization, and just add new entry later....
658 SubScreen
= Entry
->me
.SubScreen
;
661 } // REFIT_MENU_SCREEN *InitializeSubScreen()
663 VOID
GenerateSubScreen(LOADER_ENTRY
*Entry
, IN REFIT_VOLUME
*Volume
) {
664 REFIT_MENU_SCREEN
*SubScreen
;
665 LOADER_ENTRY
*SubEntry
;
667 CHAR16 DiagsFileName
[256];
672 // create the submenu
673 if (StrLen(Entry
->Title
) == 0) {
674 MyFreePool(Entry
->Title
);
677 SubScreen
= InitializeSubScreen(Entry
);
679 // loader-specific submenu entries
680 if (Entry
->OSType
== 'M') { // entries for Mac OS X
682 SubEntry
= InitializeLoaderEntry(Entry
);
683 if (SubEntry
!= NULL
) {
684 SubEntry
->me
.Title
= L
"Boot Mac OS X with a 64-bit kernel";
685 SubEntry
->LoadOptions
= L
"arch=x86_64";
686 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
687 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
690 SubEntry
= InitializeLoaderEntry(Entry
);
691 if (SubEntry
!= NULL
) {
692 SubEntry
->me
.Title
= L
"Boot Mac OS X with a 32-bit kernel";
693 SubEntry
->LoadOptions
= L
"arch=i386";
694 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
695 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
699 if (!(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_SINGLEUSER
)) {
700 SubEntry
= InitializeLoaderEntry(Entry
);
701 if (SubEntry
!= NULL
) {
702 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode";
703 SubEntry
->UseGraphicsMode
= FALSE
;
704 SubEntry
->LoadOptions
= L
"-v";
705 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
709 SubEntry
= InitializeLoaderEntry(Entry
);
710 if (SubEntry
!= NULL
) {
711 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode (64-bit)";
712 SubEntry
->UseGraphicsMode
= FALSE
;
713 SubEntry
->LoadOptions
= L
"-v arch=x86_64";
714 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
717 SubEntry
= InitializeLoaderEntry(Entry
);
718 if (SubEntry
!= NULL
) {
719 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode (32-bit)";
720 SubEntry
->UseGraphicsMode
= FALSE
;
721 SubEntry
->LoadOptions
= L
"-v arch=i386";
722 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
726 SubEntry
= InitializeLoaderEntry(Entry
);
727 if (SubEntry
!= NULL
) {
728 SubEntry
->me
.Title
= L
"Boot Mac OS X in single user mode";
729 SubEntry
->UseGraphicsMode
= FALSE
;
730 SubEntry
->LoadOptions
= L
"-v -s";
731 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
733 } // single-user mode allowed
735 if (!(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_SAFEMODE
)) {
736 SubEntry
= InitializeLoaderEntry(Entry
);
737 if (SubEntry
!= NULL
) {
738 SubEntry
->me
.Title
= L
"Boot Mac OS X in safe mode";
739 SubEntry
->UseGraphicsMode
= FALSE
;
740 SubEntry
->LoadOptions
= L
"-v -x";
741 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
743 } // safe mode allowed
745 // check for Apple hardware diagnostics
746 StrCpy(DiagsFileName
, L
"System\\Library\\CoreServices\\.diagnostics\\diags.efi");
747 if (FileExists(Volume
->RootDir
, DiagsFileName
) && !(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_HWTEST
)) {
748 SubEntry
= InitializeLoaderEntry(Entry
);
749 if (SubEntry
!= NULL
) {
750 SubEntry
->me
.Title
= L
"Run Apple Hardware Test";
751 MyFreePool(SubEntry
->LoaderPath
);
752 SubEntry
->LoaderPath
= StrDuplicate(DiagsFileName
);
753 SubEntry
->DevicePath
= FileDevicePath(Volume
->DeviceHandle
, SubEntry
->LoaderPath
);
754 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
755 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
757 } // if diagnostics entry found
759 } else if (Entry
->OSType
== 'L') { // entries for Linux kernels with EFI stub loaders
760 File
= ReadLinuxOptionsFile(Entry
->LoaderPath
, Volume
);
762 InitrdName
= FindInitrd(Entry
->LoaderPath
, Volume
);
763 TokenCount
= ReadTokenLine(File
, &TokenList
);
764 // first entry requires special processing, since it was initially set
765 // up with a default title but correct options by InitializeSubScreen(),
767 if ((SubScreen
->Entries
!= NULL
) && (SubScreen
->Entries
[0] != NULL
)) {
768 MyFreePool(SubScreen
->Entries
[0]->Title
);
769 SubScreen
->Entries
[0]->Title
= TokenList
[0] ? StrDuplicate(TokenList
[0]) : StrDuplicate(L
"Boot Linux");
771 FreeTokenLine(&TokenList
, &TokenCount
);
772 while ((TokenCount
= ReadTokenLine(File
, &TokenList
)) > 1) {
773 SubEntry
= InitializeLoaderEntry(Entry
);
774 SubEntry
->me
.Title
= TokenList
[0] ? StrDuplicate(TokenList
[0]) : StrDuplicate(L
"Boot Linux");
775 MyFreePool(SubEntry
->LoadOptions
);
776 SubEntry
->LoadOptions
= AddInitrdToOptions(TokenList
[1], InitrdName
);
777 FreeTokenLine(&TokenList
, &TokenCount
);
778 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_LINUX
;
779 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
781 MyFreePool(InitrdName
);
785 } else if (Entry
->OSType
== 'E') { // entries for ELILO
786 SubEntry
= InitializeLoaderEntry(Entry
);
787 if (SubEntry
!= NULL
) {
788 SubEntry
->me
.Title
= L
"Run ELILO in interactive mode";
789 SubEntry
->LoadOptions
= L
"-p";
790 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
791 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
794 SubEntry
= InitializeLoaderEntry(Entry
);
795 if (SubEntry
!= NULL
) {
796 SubEntry
->me
.Title
= L
"Boot Linux for a 17\" iMac or a 15\" MacBook Pro (*)";
797 SubEntry
->UseGraphicsMode
= TRUE
;
798 SubEntry
->LoadOptions
= L
"-d 0 i17";
799 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
800 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
803 SubEntry
= InitializeLoaderEntry(Entry
);
804 if (SubEntry
!= NULL
) {
805 SubEntry
->me
.Title
= L
"Boot Linux for a 20\" iMac (*)";
806 SubEntry
->UseGraphicsMode
= TRUE
;
807 SubEntry
->LoadOptions
= L
"-d 0 i20";
808 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
809 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
812 SubEntry
= InitializeLoaderEntry(Entry
);
813 if (SubEntry
!= NULL
) {
814 SubEntry
->me
.Title
= L
"Boot Linux for a Mac Mini (*)";
815 SubEntry
->UseGraphicsMode
= TRUE
;
816 SubEntry
->LoadOptions
= L
"-d 0 mini";
817 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
818 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
821 AddMenuInfoLine(SubScreen
, L
"NOTE: This is an example. Entries");
822 AddMenuInfoLine(SubScreen
, L
"marked with (*) may not work.");
824 } else if (Entry
->OSType
== 'X') { // entries for xom.efi
825 // by default, skip the built-in selection and boot from hard disk only
826 Entry
->LoadOptions
= L
"-s -h";
828 SubEntry
= InitializeLoaderEntry(Entry
);
829 if (SubEntry
!= NULL
) {
830 SubEntry
->me
.Title
= L
"Boot Windows from Hard Disk";
831 SubEntry
->LoadOptions
= L
"-s -h";
832 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
833 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
836 SubEntry
= InitializeLoaderEntry(Entry
);
837 if (SubEntry
!= NULL
) {
838 SubEntry
->me
.Title
= L
"Boot Windows from CD-ROM";
839 SubEntry
->LoadOptions
= L
"-s -c";
840 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
841 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
844 SubEntry
= InitializeLoaderEntry(Entry
);
845 if (SubEntry
!= NULL
) {
846 SubEntry
->me
.Title
= L
"Run XOM in text mode";
847 SubEntry
->UseGraphicsMode
= FALSE
;
848 SubEntry
->LoadOptions
= L
"-v";
849 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
850 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
852 } // entries for xom.efi
853 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
854 Entry
->me
.SubScreen
= SubScreen
;
855 } // VOID GenerateSubScreen()
857 // Returns options for a Linux kernel. Reads them from an options file in the
858 // kernel's directory; and if present, adds an initrd= option for an initial
859 // RAM disk file with the same version number as the kernel file.
860 static CHAR16
* GetMainLinuxOptions(IN CHAR16
* LoaderPath
, IN REFIT_VOLUME
*Volume
) {
861 CHAR16
*Options
= NULL
, *InitrdName
, *FullOptions
= NULL
;
863 Options
= GetFirstOptionsFromFile(LoaderPath
, Volume
);
864 InitrdName
= FindInitrd(LoaderPath
, Volume
);
865 FullOptions
= AddInitrdToOptions(Options
, InitrdName
);
868 MyFreePool(InitrdName
);
869 return (FullOptions
);
870 } // static CHAR16 * GetMainLinuxOptions()
872 // Try to guess the name of the Linux distribution & add that name to
874 static VOID
GuessLinuxDistribution(CHAR16
**OSIconName
, REFIT_VOLUME
*Volume
, CHAR16
*LoaderPath
) {
878 UINTN TokenCount
= 0;
880 // If on Linux root fs, /etc/os-release file probably has clues....
881 if (FileExists(Volume
->RootDir
, L
"etc\\os-release") &&
882 (ReadFile(Volume
->RootDir
, L
"etc\\os-release", &File
, &FileSize
) == EFI_SUCCESS
)) {
884 TokenCount
= ReadTokenLine(&File
, &TokenList
);
885 if ((TokenCount
> 1) && ((StriCmp(TokenList
[0], L
"ID") == 0) || (StriCmp(TokenList
[0], L
"NAME") == 0))) {
886 MergeStrings(OSIconName
, TokenList
[1], L
',');
888 FreeTokenLine(&TokenList
, &TokenCount
);
889 } while (TokenCount
> 0);
890 MyFreePool(File
.Buffer
);
893 // Search for clues in the kernel's filename....
894 if (StriSubCmp(L
".fc", LoaderPath
))
895 MergeStrings(OSIconName
, L
"fedora", L
',');
896 if (StriSubCmp(L
".el", LoaderPath
))
897 MergeStrings(OSIconName
, L
"redhat", L
',');
898 } // VOID GuessLinuxDistribution()
900 // Sets a few defaults for a loader entry -- mainly the icon, but also the OS type
901 // code and shortcut letter. For Linux EFI stub loaders, also sets kernel options
902 // that will (with luck) work fairly automatically.
903 VOID
SetLoaderDefaults(LOADER_ENTRY
*Entry
, CHAR16
*LoaderPath
, REFIT_VOLUME
*Volume
) {
904 CHAR16
*FileName
, *PathOnly
, *NoExtension
, *OSIconName
= NULL
, *Temp
, *SubString
;
905 CHAR16 ShortcutLetter
= 0;
908 FileName
= Basename(LoaderPath
);
909 PathOnly
= FindPath(LoaderPath
);
910 NoExtension
= StripEfiExtension(FileName
);
912 // locate a custom icon for the loader
913 // Anything found here takes precedence over the "hints" in the OSIconName variable
914 if (!Entry
->me
.Image
) {
915 Entry
->me
.Image
= egLoadIconAnyType(Volume
->RootDir
, PathOnly
, NoExtension
, GlobalConfig
.IconSizes
[ICON_SIZE_BIG
]);
917 if (!Entry
->me
.Image
) {
918 Entry
->me
.Image
= egCopyImage(Volume
->VolIconImage
);
921 // Begin creating icon "hints" by using last part of directory path leading
923 Temp
= FindLastDirName(LoaderPath
);
924 MergeStrings(&OSIconName
, Temp
, L
',');
927 if (OSIconName
!= NULL
) {
928 ShortcutLetter
= OSIconName
[0];
931 // Add every "word" in the volume label, delimited by spaces, dashes (-), or
932 // underscores (_), to the list of hints to be used in searching for OS
934 if ((Volume
->VolName
) && (StrLen(Volume
->VolName
) > 0)) {
935 Temp
= SubString
= StrDuplicate(Volume
->VolName
);
937 Length
= StrLen(Temp
);
938 for (i
= 0; i
< Length
; i
++) {
939 if ((Temp
[i
] == L
' ') || (Temp
[i
] == L
'_') || (Temp
[i
] == L
'-')) {
941 if (StrLen(SubString
) > 0)
942 MergeStrings(&OSIconName
, SubString
, L
',');
943 SubString
= Temp
+ i
+ 1;
946 MergeStrings(&OSIconName
, SubString
, L
',');
951 // detect specific loaders
952 if (StriSubCmp(L
"bzImage", FileName
) || StriSubCmp(L
"vmlinuz", FileName
)) {
953 GuessLinuxDistribution(&OSIconName
, Volume
, LoaderPath
);
954 MergeStrings(&OSIconName
, L
"linux", L
',');
956 if (ShortcutLetter
== 0)
957 ShortcutLetter
= 'L';
958 Entry
->LoadOptions
= GetMainLinuxOptions(LoaderPath
, Volume
);
959 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_LINUX
;
960 } else if (StriSubCmp(L
"refit", LoaderPath
)) {
961 MergeStrings(&OSIconName
, L
"refit", L
',');
963 ShortcutLetter
= 'R';
964 } else if (StriSubCmp(L
"refind", LoaderPath
)) {
965 MergeStrings(&OSIconName
, L
"refind", L
',');
967 ShortcutLetter
= 'R';
968 } else if (StriCmp(LoaderPath
, MACOSX_LOADER_PATH
) == 0) {
969 MergeStrings(&OSIconName
, L
"mac", L
',');
971 ShortcutLetter
= 'M';
972 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
973 } else if (StriCmp(FileName
, L
"diags.efi") == 0) {
974 MergeStrings(&OSIconName
, L
"hwtest", L
',');
975 } else if (StriCmp(FileName
, L
"e.efi") == 0 || StriCmp(FileName
, L
"elilo.efi") == 0 || StriSubCmp(L
"elilo", FileName
)) {
976 MergeStrings(&OSIconName
, L
"elilo,linux", L
',');
978 if (ShortcutLetter
== 0)
979 ShortcutLetter
= 'L';
980 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
981 } else if (StriSubCmp(L
"grub", FileName
)) {
983 ShortcutLetter
= 'G';
984 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_GRUB
;
985 } else if (StriCmp(FileName
, L
"cdboot.efi") == 0 ||
986 StriCmp(FileName
, L
"bootmgr.efi") == 0 ||
987 StriCmp(FileName
, L
"bootmgfw.efi") == 0 ||
988 StriCmp(FileName
, L
"bkpbootmgfw.efi") == 0) {
989 MergeStrings(&OSIconName
, L
"win", L
',');
991 ShortcutLetter
= 'W';
992 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
993 } else if (StriCmp(FileName
, L
"xom.efi") == 0) {
994 MergeStrings(&OSIconName
, L
"xom,win", L
',');
995 Entry
->UseGraphicsMode
= TRUE
;
997 ShortcutLetter
= 'W';
998 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
1001 if ((ShortcutLetter
>= 'a') && (ShortcutLetter
<= 'z'))
1002 ShortcutLetter
= ShortcutLetter
- 'a' + 'A'; // convert lowercase to uppercase
1003 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1004 if (Entry
->me
.Image
== NULL
)
1005 Entry
->me
.Image
= LoadOSIcon(OSIconName
, L
"unknown", FALSE
);
1006 MyFreePool(PathOnly
);
1007 } // VOID SetLoaderDefaults()
1009 // Add a specified EFI boot loader to the list, using automatic settings
1010 // for icons, options, etc.
1011 LOADER_ENTRY
* AddLoaderEntry(IN CHAR16
*LoaderPath
, IN CHAR16
*LoaderTitle
, IN REFIT_VOLUME
*Volume
) {
1012 LOADER_ENTRY
*Entry
;
1014 CleanUpPathNameSlashes(LoaderPath
);
1015 Entry
= InitializeLoaderEntry(NULL
);
1016 if (Entry
!= NULL
) {
1017 Entry
->Title
= StrDuplicate((LoaderTitle
!= NULL
) ? LoaderTitle
: LoaderPath
);
1018 Entry
->me
.Title
= AllocateZeroPool(sizeof(CHAR16
) * 256);
1019 SPrint(Entry
->me
.Title
, 255, L
"Boot %s from %s ", (LoaderTitle
!= NULL
) ? LoaderTitle
: LoaderPath
, Volume
->VolName
);
1021 Entry
->me
.BadgeImage
= Volume
->VolBadgeImage
;
1022 if ((LoaderPath
!= NULL
) && (LoaderPath
[0] != L
'\\')) {
1023 Entry
->LoaderPath
= StrDuplicate(L
"\\");
1025 Entry
->LoaderPath
= NULL
;
1027 MergeStrings(&(Entry
->LoaderPath
), LoaderPath
, 0);
1028 Entry
->VolName
= Volume
->VolName
;
1029 Entry
->DevicePath
= FileDevicePath(Volume
->DeviceHandle
, Entry
->LoaderPath
);
1030 SetLoaderDefaults(Entry
, LoaderPath
, Volume
);
1031 GenerateSubScreen(Entry
, Volume
);
1032 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1036 } // LOADER_ENTRY * AddLoaderEntry()
1038 // Returns -1 if (Time1 < Time2), +1 if (Time1 > Time2), or 0 if
1039 // (Time1 == Time2). Precision is only to the nearest second; since
1040 // this is used for sorting boot loader entries, differences smaller
1041 // than this are likely to be meaningless (and unlikely!).
1042 INTN
TimeComp(IN EFI_TIME
*Time1
, IN EFI_TIME
*Time2
) {
1043 INT64 Time1InSeconds
, Time2InSeconds
;
1045 // Following values are overestimates; I'm assuming 31 days in every month.
1046 // This is fine for the purpose of this function, which is limited
1047 Time1InSeconds
= Time1
->Second
+ (Time1
->Minute
* 60) + (Time1
->Hour
* 3600) + (Time1
->Day
* 86400) +
1048 (Time1
->Month
* 2678400) + ((Time1
->Year
- 1998) * 32140800);
1049 Time2InSeconds
= Time2
->Second
+ (Time2
->Minute
* 60) + (Time2
->Hour
* 3600) + (Time2
->Day
* 86400) +
1050 (Time2
->Month
* 2678400) + ((Time2
->Year
- 1998) * 32140800);
1051 if (Time1InSeconds
< Time2InSeconds
)
1053 else if (Time1InSeconds
> Time2InSeconds
)
1057 } // INTN TimeComp()
1059 // Adds a loader list element, keeping it sorted by date. Returns the new
1060 // first element (the one with the most recent date).
1061 static struct LOADER_LIST
* AddLoaderListEntry(struct LOADER_LIST
*LoaderList
, struct LOADER_LIST
*NewEntry
) {
1062 struct LOADER_LIST
*LatestEntry
, *CurrentEntry
, *PrevEntry
= NULL
;
1064 LatestEntry
= CurrentEntry
= LoaderList
;
1065 if (LoaderList
== NULL
) {
1066 LatestEntry
= NewEntry
;
1068 while ((CurrentEntry
!= NULL
) && (TimeComp(&(NewEntry
->TimeStamp
), &(CurrentEntry
->TimeStamp
)) < 0)) {
1069 PrevEntry
= CurrentEntry
;
1070 CurrentEntry
= CurrentEntry
->NextEntry
;
1072 NewEntry
->NextEntry
= CurrentEntry
;
1073 if (PrevEntry
== NULL
) {
1074 LatestEntry
= NewEntry
;
1076 PrevEntry
->NextEntry
= NewEntry
;
1079 return (LatestEntry
);
1080 } // static VOID AddLoaderListEntry()
1082 // Delete the LOADER_LIST linked list
1083 static VOID
CleanUpLoaderList(struct LOADER_LIST
*LoaderList
) {
1084 struct LOADER_LIST
*Temp
;
1086 while (LoaderList
!= NULL
) {
1088 LoaderList
= LoaderList
->NextEntry
;
1089 MyFreePool(Temp
->FileName
);
1092 } // static VOID CleanUpLoaderList()
1094 // Returns FALSE if the specified file/volume matches the GlobalConfig.DontScanDirs
1095 // or GlobalConfig.DontScanVolumes specification, or if Path points to a volume
1096 // other than the one specified by Volume, or if the specified path is SelfDir.
1097 // Returns TRUE if none of these conditions is met -- that is, if the path is
1098 // eligible for scanning.
1099 static BOOLEAN
ShouldScan(REFIT_VOLUME
*Volume
, CHAR16
*Path
) {
1100 CHAR16
*VolName
= NULL
, *DontScanDir
, *PathCopy
= NULL
;
1102 BOOLEAN ScanIt
= TRUE
;
1104 if ((IsIn(Volume
->VolName
, GlobalConfig
.DontScanVolumes
)) || (IsIn(Volume
->PartName
, GlobalConfig
.DontScanVolumes
)))
1107 if ((StriCmp(Path
, SelfDirPath
) == 0) && (Volume
->DeviceHandle
== SelfVolume
->DeviceHandle
))
1110 // See if Path includes an explicit volume declaration that's NOT Volume....
1111 PathCopy
= StrDuplicate(Path
);
1112 if (SplitVolumeAndFilename(&PathCopy
, &VolName
)) {
1113 VolumeNumberToName(Volume
, &VolName
);
1114 if (VolName
&& StriCmp(VolName
, Volume
->VolName
) != 0) {
1117 } // if Path includes volume specification
1118 MyFreePool(PathCopy
);
1119 MyFreePool(VolName
);
1122 // See if Volume is in GlobalConfig.DontScanDirs....
1123 while (ScanIt
&& (DontScanDir
= FindCommaDelimited(GlobalConfig
.DontScanDirs
, i
++))) {
1124 SplitVolumeAndFilename(&DontScanDir
, &VolName
);
1125 CleanUpPathNameSlashes(DontScanDir
);
1126 VolumeNumberToName(Volume
, &VolName
);
1127 if (VolName
!= NULL
) {
1128 if ((StriCmp(VolName
, Volume
->VolName
) == 0) && (StriCmp(DontScanDir
, Path
) == 0))
1131 if (StriCmp(DontScanDir
, Path
) == 0)
1134 MyFreePool(DontScanDir
);
1135 MyFreePool(VolName
);
1141 } // BOOLEAN ShouldScan()
1143 // Returns TRUE if the file is byte-for-byte identical with the fallback file
1144 // on the volume AND if the file is not itself the fallback file; returns
1145 // FALSE if the file is not identical to the fallback file OR if the file
1146 // IS the fallback file. Intended for use in excluding the fallback boot
1147 // loader when it's a duplicate of another boot loader.
1148 static BOOLEAN
DuplicatesFallback(IN REFIT_VOLUME
*Volume
, IN CHAR16
*FileName
) {
1149 CHAR8
*FileContents
, *FallbackContents
;
1150 EFI_FILE_HANDLE FileHandle
, FallbackHandle
;
1151 EFI_FILE_INFO
*FileInfo
, *FallbackInfo
;
1152 UINTN FileSize
= 0, FallbackSize
= 0;
1154 BOOLEAN AreIdentical
= FALSE
;
1156 if (!FileExists(Volume
->RootDir
, FileName
) || !FileExists(Volume
->RootDir
, FALLBACK_FULLNAME
))
1159 CleanUpPathNameSlashes(FileName
);
1161 if (StriCmp(FileName
, FALLBACK_FULLNAME
) == 0)
1162 return FALSE
; // identical filenames, so not a duplicate....
1164 Status
= refit_call5_wrapper(Volume
->RootDir
->Open
, Volume
->RootDir
, &FileHandle
, FileName
, EFI_FILE_MODE_READ
, 0);
1165 if (Status
== EFI_SUCCESS
) {
1166 FileInfo
= LibFileInfo(FileHandle
);
1167 FileSize
= FileInfo
->FileSize
;
1172 Status
= refit_call5_wrapper(Volume
->RootDir
->Open
, Volume
->RootDir
, &FallbackHandle
, FALLBACK_FULLNAME
, EFI_FILE_MODE_READ
, 0);
1173 if (Status
== EFI_SUCCESS
) {
1174 FallbackInfo
= LibFileInfo(FallbackHandle
);
1175 FallbackSize
= FallbackInfo
->FileSize
;
1177 refit_call1_wrapper(FileHandle
->Close
, FileHandle
);
1181 if (FallbackSize
!= FileSize
) { // not same size, so can't be identical
1182 AreIdentical
= FALSE
;
1183 } else { // could be identical; do full check....
1184 FileContents
= AllocatePool(FileSize
);
1185 FallbackContents
= AllocatePool(FallbackSize
);
1186 if (FileContents
&& FallbackContents
) {
1187 Status
= refit_call3_wrapper(FileHandle
->Read
, FileHandle
, &FileSize
, FileContents
);
1188 if (Status
== EFI_SUCCESS
) {
1189 Status
= refit_call3_wrapper(FallbackHandle
->Read
, FallbackHandle
, &FallbackSize
, FallbackContents
);
1191 if (Status
== EFI_SUCCESS
) {
1192 AreIdentical
= (CompareMem(FileContents
, FallbackContents
, FileSize
) == 0);
1195 MyFreePool(FileContents
);
1196 MyFreePool(FallbackContents
);
1199 // BUG ALERT: Some systems (e.g., DUET, some Macs with large displays) crash if the
1200 // following two calls are reversed. Go figure....
1201 refit_call1_wrapper(FileHandle
->Close
, FallbackHandle
);
1202 refit_call1_wrapper(FileHandle
->Close
, FileHandle
);
1203 return AreIdentical
;
1204 } // BOOLEAN DuplicatesFallback()
1206 // Returns FALSE if two measures of file size are identical for a single file,
1207 // TRUE if not or if the file can't be opened and the other measure is non-0.
1208 // Despite the function's name, this isn't really a direct test of symbolic
1209 // link status, since EFI doesn't officially support symlinks. It does seem
1210 // to be a reliable indicator, though. (OTOH, some disk errors might cause a
1211 // file to fail to open, which would return a false positive -- but as I use
1212 // this function to exclude symbolic links from the list of boot loaders,
1213 // that would be fine, since such boot loaders wouldn't work.)
1214 static BOOLEAN
IsSymbolicLink(REFIT_VOLUME
*Volume
, CHAR16
*Path
, EFI_FILE_INFO
*DirEntry
) {
1215 EFI_FILE_HANDLE FileHandle
;
1216 EFI_FILE_INFO
*FileInfo
= NULL
;
1218 UINTN FileSize2
= 0;
1221 FileName
= StrDuplicate(Path
);
1222 MergeStrings(&FileName
, DirEntry
->FileName
, L
'\\');
1223 CleanUpPathNameSlashes(FileName
);
1225 Status
= refit_call5_wrapper(Volume
->RootDir
->Open
, Volume
->RootDir
, &FileHandle
, FileName
, EFI_FILE_MODE_READ
, 0);
1226 if (Status
== EFI_SUCCESS
) {
1227 FileInfo
= LibFileInfo(FileHandle
);
1228 if (FileInfo
!= NULL
)
1229 FileSize2
= FileInfo
->FileSize
;
1232 MyFreePool(FileName
);
1233 MyFreePool(FileInfo
);
1235 return (DirEntry
->FileSize
!= FileSize2
);
1236 } // BOOLEAN IsSymbolicLink()
1238 // Returns TRUE if a file with the same name as the original but with
1239 // ".efi.signed" is also present in the same directory. Ubuntu is using
1240 // this filename as a signed version of the original unsigned kernel, and
1241 // there's no point in cluttering the display with two kernels that will
1242 // behave identically on non-SB systems, or when one will fail when SB
1244 static BOOLEAN
HasSignedCounterpart(IN REFIT_VOLUME
*Volume
, IN CHAR16
*Path
, IN CHAR16
*Filename
) {
1245 CHAR16
*NewFile
= NULL
;
1246 BOOLEAN retval
= FALSE
;
1248 MergeStrings(&NewFile
, Path
, 0);
1249 MergeStrings(&NewFile
, Filename
, L
'\\');
1250 MergeStrings(&NewFile
, L
".efi.signed", 0);
1251 if (FileExists(Volume
->RootDir
, NewFile
))
1253 MyFreePool(NewFile
);
1256 } // BOOLEAN HasSignedCounterpart()
1258 // Scan an individual directory for EFI boot loader files and, if found,
1259 // add them to the list. Exception: Ignores FALLBACK_FULLNAME, which is picked
1260 // up in ScanEfiFiles(). Sorts the entries within the loader directory so that
1261 // the most recent one appears first in the list.
1262 // Returns TRUE if a duplicate for FALLBACK_FILENAME was found, FALSE if not.
1263 static BOOLEAN
ScanLoaderDir(IN REFIT_VOLUME
*Volume
, IN CHAR16
*Path
, IN CHAR16
*Pattern
)
1266 REFIT_DIR_ITER DirIter
;
1267 EFI_FILE_INFO
*DirEntry
;
1268 CHAR16 FileName
[256], *Extension
;
1269 struct LOADER_LIST
*LoaderList
= NULL
, *NewLoader
;
1270 BOOLEAN FoundFallbackDuplicate
= FALSE
;
1272 if ((!SelfDirPath
|| !Path
|| ((StriCmp(Path
, SelfDirPath
) == 0) && (Volume
->DeviceHandle
!= SelfVolume
->DeviceHandle
)) ||
1273 (StriCmp(Path
, SelfDirPath
) != 0)) &&
1274 (ShouldScan(Volume
, Path
))) {
1275 // look through contents of the directory
1276 DirIterOpen(Volume
->RootDir
, Path
, &DirIter
);
1277 while (DirIterNext(&DirIter
, 2, Pattern
, &DirEntry
)) {
1278 Extension
= FindExtension(DirEntry
->FileName
);
1279 if (DirEntry
->FileName
[0] == '.' ||
1280 StriCmp(Extension
, L
".icns") == 0 ||
1281 StriCmp(Extension
, L
".png") == 0 ||
1282 (StriCmp(DirEntry
->FileName
, FALLBACK_BASENAME
) == 0 && (StriCmp(Path
, L
"EFI\\BOOT") == 0)) ||
1283 StriSubCmp(L
"shell", DirEntry
->FileName
) ||
1284 IsSymbolicLink(Volume
, Path
, DirEntry
) || /* is symbolic link */
1285 HasSignedCounterpart(Volume
, Path
, DirEntry
->FileName
) || /* a file with same name plus ".efi.signed" is present */
1286 FilenameIn(Volume
, Path
, DirEntry
->FileName
, GlobalConfig
.DontScanFiles
))
1287 continue; // skip this
1290 SPrint(FileName
, 255, L
"\\%s\\%s", Path
, DirEntry
->FileName
);
1292 SPrint(FileName
, 255, L
"\\%s", DirEntry
->FileName
);
1293 CleanUpPathNameSlashes(FileName
);
1295 if(!IsValidLoader(Volume
->RootDir
, FileName
))
1298 NewLoader
= AllocateZeroPool(sizeof(struct LOADER_LIST
));
1299 if (NewLoader
!= NULL
) {
1300 NewLoader
->FileName
= StrDuplicate(FileName
);
1301 NewLoader
->TimeStamp
= DirEntry
->ModificationTime
;
1302 LoaderList
= AddLoaderListEntry(LoaderList
, NewLoader
);
1303 if (DuplicatesFallback(Volume
, FileName
))
1304 FoundFallbackDuplicate
= TRUE
;
1306 MyFreePool(Extension
);
1309 NewLoader
= LoaderList
;
1310 while (NewLoader
!= NULL
) {
1311 AddLoaderEntry(NewLoader
->FileName
, NULL
, Volume
);
1312 NewLoader
= NewLoader
->NextEntry
;
1315 CleanUpLoaderList(LoaderList
);
1316 Status
= DirIterClose(&DirIter
);
1317 // NOTE: EFI_INVALID_PARAMETER really is an error that should be reported;
1318 // but I've gotten reports from users who are getting this error occasionally
1319 // and I can't find anything wrong or reproduce the problem, so I'm putting
1320 // it down to buggy EFI implementations and ignoring that particular error....
1321 if ((Status
!= EFI_NOT_FOUND
) && (Status
!= EFI_INVALID_PARAMETER
)) {
1323 SPrint(FileName
, 255, L
"while scanning the %s directory", Path
);
1325 StrCpy(FileName
, L
"while scanning the root directory");
1326 CheckError(Status
, FileName
);
1327 } // if (Status != EFI_NOT_FOUND)
1328 } // if not scanning a blacklisted directory
1330 return FoundFallbackDuplicate
;
1331 } /* static VOID ScanLoaderDir() */
1333 static VOID
ScanEfiFiles(REFIT_VOLUME
*Volume
) {
1335 REFIT_DIR_ITER EfiDirIter
;
1336 EFI_FILE_INFO
*EfiDirEntry
;
1337 CHAR16 FileName
[256], *Directory
= NULL
, *MatchPatterns
, *VolName
= NULL
, *SelfPath
;
1339 BOOLEAN ScanFallbackLoader
= TRUE
;
1340 BOOLEAN FoundBRBackup
= FALSE
;
1342 if ((Volume
->RootDir
!= NULL
) && (Volume
->VolName
!= NULL
) && (Volume
->IsReadable
)) {
1343 MatchPatterns
= StrDuplicate(LOADER_MATCH_PATTERNS
);
1344 if (GlobalConfig
.ScanAllLinux
)
1345 MergeStrings(&MatchPatterns
, LINUX_MATCH_PATTERNS
, L
',');
1347 // check for Mac OS X boot loader
1348 if (ShouldScan(Volume
, L
"System\\Library\\CoreServices")) {
1349 StrCpy(FileName
, MACOSX_LOADER_PATH
);
1350 if (FileExists(Volume
->RootDir
, FileName
) && !FilenameIn(Volume
, Directory
, L
"boot.efi", GlobalConfig
.DontScanFiles
)) {
1351 AddLoaderEntry(FileName
, L
"Mac OS X", Volume
);
1352 if (DuplicatesFallback(Volume
, FileName
))
1353 ScanFallbackLoader
= FALSE
;
1357 StrCpy(FileName
, L
"System\\Library\\CoreServices\\xom.efi");
1358 if (FileExists(Volume
->RootDir
, FileName
) && !FilenameIn(Volume
, Directory
, L
"boot.efi", GlobalConfig
.DontScanFiles
)) {
1359 AddLoaderEntry(FileName
, L
"Windows XP (XoM)", Volume
);
1360 if (DuplicatesFallback(Volume
, FileName
))
1361 ScanFallbackLoader
= FALSE
;
1363 } // if should scan Mac directory
1365 // check for Microsoft boot loader/menu
1366 if (ShouldScan(Volume
, L
"EFI\\Microsoft\\Boot")) {
1367 StrCpy(FileName
, L
"EFI\\Microsoft\\Boot\\bkpbootmgfw.efi");
1368 if (FileExists(Volume
->RootDir
, FileName
) && !FilenameIn(Volume
, Directory
, L
"bkpbootmgfw.efi",
1369 GlobalConfig
.DontScanFiles
)) {
1370 AddLoaderEntry(FileName
, L
"Microsoft EFI boot (Boot Repair backup)", Volume
);
1371 FoundBRBackup
= TRUE
;
1372 if (DuplicatesFallback(Volume
, FileName
))
1373 ScanFallbackLoader
= FALSE
;
1375 StrCpy(FileName
, L
"EFI\\Microsoft\\Boot\\bootmgfw.efi");
1376 if (FileExists(Volume
->RootDir
, FileName
) && !FilenameIn(Volume
, Directory
, L
"bootmgfw.efi", GlobalConfig
.DontScanFiles
)) {
1378 AddLoaderEntry(FileName
, L
"Supposed Microsoft EFI boot (probably GRUB)", Volume
);
1380 AddLoaderEntry(FileName
, L
"Microsoft EFI boot", Volume
);
1381 if (DuplicatesFallback(Volume
, FileName
))
1382 ScanFallbackLoader
= FALSE
;
1386 // scan the root directory for EFI executables
1387 if (ScanLoaderDir(Volume
, L
"\\", MatchPatterns
))
1388 ScanFallbackLoader
= FALSE
;
1390 // scan subdirectories of the EFI directory (as per the standard)
1391 DirIterOpen(Volume
->RootDir
, L
"EFI", &EfiDirIter
);
1392 while (DirIterNext(&EfiDirIter
, 1, NULL
, &EfiDirEntry
)) {
1393 if (StriCmp(EfiDirEntry
->FileName
, L
"tools") == 0 || EfiDirEntry
->FileName
[0] == '.')
1394 continue; // skip this, doesn't contain boot loaders or is scanned later
1395 SPrint(FileName
, 255, L
"EFI\\%s", EfiDirEntry
->FileName
);
1396 if (ScanLoaderDir(Volume
, FileName
, MatchPatterns
))
1397 ScanFallbackLoader
= FALSE
;
1399 Status
= DirIterClose(&EfiDirIter
);
1400 if (Status
!= EFI_NOT_FOUND
)
1401 CheckError(Status
, L
"while scanning the EFI directory");
1403 // Scan user-specified (or additional default) directories....
1405 while ((Directory
= FindCommaDelimited(GlobalConfig
.AlsoScan
, i
++)) != NULL
) {
1406 if (ShouldScan(Volume
, Directory
)) {
1407 SplitVolumeAndFilename(&Directory
, &VolName
);
1408 CleanUpPathNameSlashes(Directory
);
1409 Length
= StrLen(Directory
);
1410 if ((Length
> 0) && ScanLoaderDir(Volume
, Directory
, MatchPatterns
))
1411 ScanFallbackLoader
= FALSE
;
1412 MyFreePool(VolName
);
1413 } // if should scan dir
1414 MyFreePool(Directory
);
1417 // Don't scan the fallback loader if it's on the same volume and a duplicate of rEFInd itself....
1418 SelfPath
= DevicePathToStr(SelfLoadedImage
->FilePath
);
1419 CleanUpPathNameSlashes(SelfPath
);
1420 if ((Volume
->DeviceHandle
== SelfLoadedImage
->DeviceHandle
) && DuplicatesFallback(Volume
, SelfPath
))
1421 ScanFallbackLoader
= FALSE
;
1423 // If not a duplicate & if it exists & if it's not us, create an entry
1424 // for the fallback boot loader
1425 if (ScanFallbackLoader
&& FileExists(Volume
->RootDir
, FALLBACK_FULLNAME
) && ShouldScan(Volume
, L
"EFI\\BOOT"))
1426 AddLoaderEntry(FALLBACK_FULLNAME
, L
"Fallback boot loader", Volume
);
1428 } // static VOID ScanEfiFiles()
1430 // Scan internal disks for valid EFI boot loaders....
1431 static VOID
ScanInternal(VOID
) {
1434 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1435 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_INTERNAL
) {
1436 ScanEfiFiles(Volumes
[VolumeIndex
]);
1439 } // static VOID ScanInternal()
1441 // Scan external disks for valid EFI boot loaders....
1442 static VOID
ScanExternal(VOID
) {
1445 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1446 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_EXTERNAL
) {
1447 ScanEfiFiles(Volumes
[VolumeIndex
]);
1450 } // static VOID ScanExternal()
1452 // Scan internal disks for valid EFI boot loaders....
1453 static VOID
ScanOptical(VOID
) {
1456 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1457 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_OPTICAL
) {
1458 ScanEfiFiles(Volumes
[VolumeIndex
]);
1461 } // static VOID ScanOptical()
1464 // legacy boot functions
1467 static EFI_STATUS
ActivateMbrPartition(IN EFI_BLOCK_IO
*BlockIO
, IN UINTN PartitionIndex
)
1470 UINT8 SectorBuffer
[512];
1471 MBR_PARTITION_INFO
*MbrTable
, *EMbrTable
;
1472 UINT32 ExtBase
, ExtCurrent
, NextExtCurrent
;
1473 UINTN LogicalPartitionIndex
= 4;
1475 BOOLEAN HaveBootCode
;
1478 Status
= refit_call5_wrapper(BlockIO
->ReadBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, 0, 512, SectorBuffer
);
1479 if (EFI_ERROR(Status
))
1481 if (*((UINT16
*)(SectorBuffer
+ 510)) != 0xaa55)
1482 return EFI_NOT_FOUND
; // safety measure #1
1484 // add boot code if necessary
1485 HaveBootCode
= FALSE
;
1486 for (i
= 0; i
< MBR_BOOTCODE_SIZE
; i
++) {
1487 if (SectorBuffer
[i
] != 0) {
1488 HaveBootCode
= TRUE
;
1492 if (!HaveBootCode
) {
1493 // no boot code found in the MBR, add the syslinux MBR code
1494 SetMem(SectorBuffer
, MBR_BOOTCODE_SIZE
, 0);
1495 CopyMem(SectorBuffer
, syslinux_mbr
, SYSLINUX_MBR_SIZE
);
1498 // set the partition active
1499 MbrTable
= (MBR_PARTITION_INFO
*)(SectorBuffer
+ 446);
1501 for (i
= 0; i
< 4; i
++) {
1502 if (MbrTable
[i
].Flags
!= 0x00 && MbrTable
[i
].Flags
!= 0x80)
1503 return EFI_NOT_FOUND
; // safety measure #2
1504 if (i
== PartitionIndex
)
1505 MbrTable
[i
].Flags
= 0x80;
1506 else if (PartitionIndex
>= 4 && IS_EXTENDED_PART_TYPE(MbrTable
[i
].Type
)) {
1507 MbrTable
[i
].Flags
= 0x80;
1508 ExtBase
= MbrTable
[i
].StartLBA
;
1510 MbrTable
[i
].Flags
= 0x00;
1514 Status
= refit_call5_wrapper(BlockIO
->WriteBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, 0, 512, SectorBuffer
);
1515 if (EFI_ERROR(Status
))
1518 if (PartitionIndex
>= 4) {
1519 // we have to activate a logical partition, so walk the EMBR chain
1521 // NOTE: ExtBase was set above while looking at the MBR table
1522 for (ExtCurrent
= ExtBase
; ExtCurrent
; ExtCurrent
= NextExtCurrent
) {
1523 // read current EMBR
1524 Status
= refit_call5_wrapper(BlockIO
->ReadBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, ExtCurrent
, 512, SectorBuffer
);
1525 if (EFI_ERROR(Status
))
1527 if (*((UINT16
*)(SectorBuffer
+ 510)) != 0xaa55)
1528 return EFI_NOT_FOUND
; // safety measure #3
1530 // scan EMBR, set appropriate partition active
1531 EMbrTable
= (MBR_PARTITION_INFO
*)(SectorBuffer
+ 446);
1533 for (i
= 0; i
< 4; i
++) {
1534 if (EMbrTable
[i
].Flags
!= 0x00 && EMbrTable
[i
].Flags
!= 0x80)
1535 return EFI_NOT_FOUND
; // safety measure #4
1536 if (EMbrTable
[i
].StartLBA
== 0 || EMbrTable
[i
].Size
== 0)
1538 if (IS_EXTENDED_PART_TYPE(EMbrTable
[i
].Type
)) {
1539 // link to next EMBR
1540 NextExtCurrent
= ExtBase
+ EMbrTable
[i
].StartLBA
;
1541 EMbrTable
[i
].Flags
= (PartitionIndex
>= LogicalPartitionIndex
) ? 0x80 : 0x00;
1544 // logical partition
1545 EMbrTable
[i
].Flags
= (PartitionIndex
== LogicalPartitionIndex
) ? 0x80 : 0x00;
1546 LogicalPartitionIndex
++;
1550 // write current EMBR
1551 Status
= refit_call5_wrapper(BlockIO
->WriteBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, ExtCurrent
, 512, SectorBuffer
);
1552 if (EFI_ERROR(Status
))
1555 if (PartitionIndex
< LogicalPartitionIndex
)
1556 break; // stop the loop, no need to touch further EMBRs
1562 } /* static EFI_STATUS ActivateMbrPartition() */
1564 // early 2006 Core Duo / Core Solo models
1565 static UINT8 LegacyLoaderDevicePath1Data
[] = {
1566 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1567 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1568 0xFF, 0xFF, 0xF9, 0xFF, 0x00, 0x00, 0x00, 0x00,
1569 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1570 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1571 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1573 // mid-2006 Mac Pro (and probably other Core 2 models)
1574 static UINT8 LegacyLoaderDevicePath2Data
[] = {
1575 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1576 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1577 0xFF, 0xFF, 0xF7, 0xFF, 0x00, 0x00, 0x00, 0x00,
1578 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1579 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1580 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1582 // mid-2007 MBP ("Santa Rosa" based models)
1583 static UINT8 LegacyLoaderDevicePath3Data
[] = {
1584 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1585 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1586 0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
1587 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1588 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1589 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1592 static UINT8 LegacyLoaderDevicePath4Data
[] = {
1593 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1594 0x00, 0x00, 0xC0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1595 0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
1596 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1597 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1598 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1600 // late-2008 MB/MBP (NVidia chipset)
1601 static UINT8 LegacyLoaderDevicePath5Data
[] = {
1602 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1603 0x00, 0x40, 0xCB, 0xFF, 0x00, 0x00, 0x00, 0x00,
1604 0xFF, 0xBF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
1605 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1606 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1607 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1610 static EFI_DEVICE_PATH
*LegacyLoaderList
[] = {
1611 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath1Data
,
1612 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath2Data
,
1613 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath3Data
,
1614 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath4Data
,
1615 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath5Data
,
1619 #define MAX_DISCOVERED_PATHS (16)
1621 static VOID
StartLegacy(IN LEGACY_ENTRY
*Entry
)
1624 EG_IMAGE
*BootLogoImage
;
1625 UINTN ErrorInStep
= 0;
1626 EFI_DEVICE_PATH
*DiscoveredPathList
[MAX_DISCOVERED_PATHS
];
1628 BeginExternalScreen(TRUE
, L
"Booting Legacy OS (Mac mode)");
1630 BootLogoImage
= LoadOSIcon(Entry
->Volume
->OSIconName
, L
"legacy", TRUE
);
1631 if (BootLogoImage
!= NULL
)
1632 BltImageAlpha(BootLogoImage
,
1633 (UGAWidth
- BootLogoImage
->Width
) >> 1,
1634 (UGAHeight
- BootLogoImage
->Height
) >> 1,
1635 &StdBackgroundPixel
);
1637 if (Entry
->Volume
->IsMbrPartition
) {
1638 ActivateMbrPartition(Entry
->Volume
->WholeDiskBlockIO
, Entry
->Volume
->MbrPartitionIndex
);
1641 ExtractLegacyLoaderPaths(DiscoveredPathList
, MAX_DISCOVERED_PATHS
, LegacyLoaderList
);
1643 Status
= StartEFIImageList(DiscoveredPathList
, Entry
->LoadOptions
, TYPE_LEGACY
, L
"legacy loader", 0, &ErrorInStep
, TRUE
);
1644 if (Status
== EFI_NOT_FOUND
) {
1645 if (ErrorInStep
== 1) {
1646 Print(L
"\nPlease make sure that you have the latest firmware update installed.\n");
1647 } else if (ErrorInStep
== 3) {
1648 Print(L
"\nThe firmware refused to boot from the selected volume. Note that external\n"
1649 L
"hard drives are not well-supported by Apple's firmware for legacy OS booting.\n");
1652 FinishExternalScreen();
1653 } /* static VOID StartLegacy() */
1655 // Start a device on a non-Mac using the EFI_LEGACY_BIOS_PROTOCOL
1656 static VOID
StartLegacyUEFI(LEGACY_ENTRY
*Entry
)
1658 BeginExternalScreen(TRUE
, L
"Booting Legacy OS (UEFI mode)");
1660 BdsLibConnectDevicePath (Entry
->BdsOption
->DevicePath
);
1661 BdsLibDoLegacyBoot(Entry
->BdsOption
);
1663 // If we get here, it means that there was a failure....
1664 Print(L
"Failure booting legacy (BIOS) OS.");
1666 FinishExternalScreen();
1667 } // static VOID StartLegacyUEFI()
1669 static LEGACY_ENTRY
* AddLegacyEntry(IN CHAR16
*LoaderTitle
, IN REFIT_VOLUME
*Volume
)
1671 LEGACY_ENTRY
*Entry
, *SubEntry
;
1672 REFIT_MENU_SCREEN
*SubScreen
;
1673 CHAR16
*VolDesc
, *LegacyTitle
;
1674 CHAR16 ShortcutLetter
= 0;
1676 if (LoaderTitle
== NULL
) {
1677 if (Volume
->OSName
!= NULL
) {
1678 LoaderTitle
= Volume
->OSName
;
1679 if (LoaderTitle
[0] == 'W' || LoaderTitle
[0] == 'L')
1680 ShortcutLetter
= LoaderTitle
[0];
1682 LoaderTitle
= L
"Legacy OS";
1684 if (Volume
->VolName
!= NULL
)
1685 VolDesc
= Volume
->VolName
;
1687 VolDesc
= (Volume
->DiskKind
== DISK_KIND_OPTICAL
) ? L
"CD" : L
"HD";
1689 LegacyTitle
= AllocateZeroPool(256 * sizeof(CHAR16
));
1690 if (LegacyTitle
!= NULL
)
1691 SPrint(LegacyTitle
, 255, L
"Boot %s from %s", LoaderTitle
, VolDesc
);
1692 if (IsInSubstring(LegacyTitle
, GlobalConfig
.DontScanVolumes
)) {
1693 MyFreePool(LegacyTitle
);
1697 // prepare the menu entry
1698 Entry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1699 Entry
->me
.Title
= LegacyTitle
;
1700 Entry
->me
.Tag
= TAG_LEGACY
;
1702 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1703 Entry
->me
.Image
= LoadOSIcon(Volume
->OSIconName
, L
"legacy", FALSE
);
1704 Entry
->me
.BadgeImage
= Volume
->VolBadgeImage
;
1705 Entry
->Volume
= Volume
;
1706 Entry
->LoadOptions
= (Volume
->DiskKind
== DISK_KIND_OPTICAL
) ? L
"CD" :
1707 ((Volume
->DiskKind
== DISK_KIND_EXTERNAL
) ? L
"USB" : L
"HD");
1708 Entry
->Enabled
= TRUE
;
1710 // create the submenu
1711 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
1712 SubScreen
->Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1713 SPrint(SubScreen
->Title
, 255, L
"Boot Options for %s on %s", LoaderTitle
, VolDesc
);
1714 SubScreen
->TitleImage
= Entry
->me
.Image
;
1715 SubScreen
->Hint1
= StrDuplicate(SUBSCREEN_HINT1
);
1716 if (GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_EDITOR
) {
1717 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2_NO_EDITOR
);
1719 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2
);
1723 SubEntry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1724 SubEntry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1725 SPrint(SubEntry
->me
.Title
, 255, L
"Boot %s", LoaderTitle
);
1726 SubEntry
->me
.Tag
= TAG_LEGACY
;
1727 SubEntry
->Volume
= Entry
->Volume
;
1728 SubEntry
->LoadOptions
= Entry
->LoadOptions
;
1729 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
1731 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
1732 Entry
->me
.SubScreen
= SubScreen
;
1733 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1735 } /* static LEGACY_ENTRY * AddLegacyEntry() */
1738 // #ifdef __MAKEWITH_GNUEFI
1739 // static VOID ScanLegacyUEFI(IN UINTN DiskType){}
1741 // default volume badge icon based on disk kind
1742 static EG_IMAGE
* GetDiskBadge(IN UINTN DiskType
) {
1743 EG_IMAGE
* Badge
= NULL
;
1747 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_INTERNAL
);
1750 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_EXTERNAL
);
1753 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_OPTICAL
);
1757 } // static EG_IMAGE * GetDiskBadge()
1760 Create a rEFInd boot option from a Legacy BIOS protocol option.
1762 static LEGACY_ENTRY
* AddLegacyEntryUEFI(BDS_COMMON_OPTION
*BdsOption
, IN UINT16 DiskType
)
1764 LEGACY_ENTRY
*Entry
, *SubEntry
;
1765 REFIT_MENU_SCREEN
*SubScreen
;
1766 CHAR16 ShortcutLetter
= 0;
1767 CHAR16
*LegacyDescription
= StrDuplicate(BdsOption
->Description
);
1769 if (IsInSubstring(LegacyDescription
, GlobalConfig
.DontScanVolumes
))
1772 // Remove stray spaces, since many EFIs produce descriptions with lots of
1773 // extra spaces, especially at the end; this throws off centering of the
1774 // description on the screen....
1775 LimitStringLength(LegacyDescription
, 100);
1777 // prepare the menu entry
1778 Entry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1779 Entry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1780 SPrint(Entry
->me
.Title
, 255, L
"Boot legacy target %s", LegacyDescription
);
1781 Entry
->me
.Tag
= TAG_LEGACY_UEFI
;
1783 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1784 Entry
->me
.Image
= LoadOSIcon(L
"legacy", L
"legacy", TRUE
);
1785 Entry
->LoadOptions
= (DiskType
== BBS_CDROM
) ? L
"CD" :
1786 ((DiskType
== BBS_USB
) ? L
"USB" : L
"HD");
1787 Entry
->me
.BadgeImage
= GetDiskBadge(DiskType
);
1788 Entry
->BdsOption
= BdsOption
;
1789 Entry
->Enabled
= TRUE
;
1791 // create the submenu
1792 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
1793 SubScreen
->Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1794 SPrint(SubScreen
->Title
, 255, L
"No boot options for legacy target");
1795 SubScreen
->TitleImage
= Entry
->me
.Image
;
1796 SubScreen
->Hint1
= StrDuplicate(SUBSCREEN_HINT1
);
1797 if (GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_EDITOR
) {
1798 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2_NO_EDITOR
);
1800 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2
);
1804 SubEntry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1805 SubEntry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1806 SPrint(SubEntry
->me
.Title
, 255, L
"Boot %s", LegacyDescription
);
1807 SubEntry
->me
.Tag
= TAG_LEGACY_UEFI
;
1808 Entry
->BdsOption
= BdsOption
;
1809 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
1811 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
1812 Entry
->me
.SubScreen
= SubScreen
;
1813 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1815 } /* static LEGACY_ENTRY * AddLegacyEntryUEFI() */
1818 Scan for legacy BIOS targets on machines that implement EFI_LEGACY_BIOS_PROTOCOL.
1819 In testing, protocol has not been implemented on Macs but has been
1820 implemented on several Dell PCs and an ASUS motherboard.
1821 Restricts output to disks of the specified DiskType.
1823 static VOID
ScanLegacyUEFI(IN UINTN DiskType
)
1826 EFI_LEGACY_BIOS_PROTOCOL
*LegacyBios
;
1827 UINT16
*BootOrder
= NULL
;
1829 CHAR16 BootOption
[10];
1830 UINTN BootOrderSize
= 0;
1832 BDS_COMMON_OPTION
*BdsOption
;
1833 LIST_ENTRY TempList
;
1834 BBS_BBS_DEVICE_PATH
*BbsDevicePath
= NULL
;
1835 BOOLEAN SearchingForUsb
= FALSE
;
1837 InitializeListHead (&TempList
);
1838 ZeroMem (Buffer
, sizeof (Buffer
));
1840 // If LegacyBios protocol is not implemented on this platform, then
1841 //we do not support this type of legacy boot on this machine.
1842 Status
= refit_call3_wrapper(gBS
->LocateProtocol
, &gEfiLegacyBootProtocolGuid
, NULL
, (VOID
**) &LegacyBios
);
1843 if (EFI_ERROR (Status
))
1846 // EFI calls USB drives BBS_HARDDRIVE, but we want to distinguish them,
1847 // so we set DiskType inappropriately elsewhere in the program and
1848 // "translate" it here.
1849 if (DiskType
== BBS_USB
) {
1850 DiskType
= BBS_HARDDISK
;
1851 SearchingForUsb
= TRUE
;
1854 // Grab the boot order
1855 BootOrder
= BdsLibGetVariableAndSize(L
"BootOrder", &gEfiGlobalVariableGuid
, &BootOrderSize
);
1856 if (BootOrder
== NULL
) {
1861 while (Index
< BootOrderSize
/ sizeof (UINT16
))
1863 // Grab each boot option variable from the boot order, and convert
1864 // the variable into a BDS boot option
1865 UnicodeSPrint (BootOption
, sizeof (BootOption
), L
"Boot%04x", BootOrder
[Index
]);
1866 BdsOption
= BdsLibVariableToOption (&TempList
, BootOption
);
1868 if (BdsOption
!= NULL
) {
1869 BbsDevicePath
= (BBS_BBS_DEVICE_PATH
*)BdsOption
->DevicePath
;
1870 // Only add the entry if it is of a requested type (e.g. USB, HD)
1871 // Two checks necessary because some systems return EFI boot loaders
1872 // with a DeviceType value that would inappropriately include them
1873 // as legacy loaders....
1874 if ((BbsDevicePath
->DeviceType
== DiskType
) && (BdsOption
->DevicePath
->Type
== DEVICE_TYPE_BIOS
)) {
1875 // USB flash drives appear as hard disks with certain media flags set.
1876 // Look for this, and if present, pass it on with the (technically
1877 // incorrect, but internally useful) BBS_TYPE_USB flag set.
1878 if (DiskType
== BBS_HARDDISK
) {
1879 if (SearchingForUsb
&& (BbsDevicePath
->StatusFlag
& (BBS_MEDIA_PRESENT
| BBS_MEDIA_MAYBE_PRESENT
))) {
1880 AddLegacyEntryUEFI(BdsOption
, BBS_USB
);
1881 } else if (!SearchingForUsb
&& !(BbsDevicePath
->StatusFlag
& (BBS_MEDIA_PRESENT
| BBS_MEDIA_MAYBE_PRESENT
))) {
1882 AddLegacyEntryUEFI(BdsOption
, DiskType
);
1885 AddLegacyEntryUEFI(BdsOption
, DiskType
);
1888 } // if (BdsOption != NULL)
1891 } /* static VOID ScanLegacyUEFI() */
1892 //#endif // __MAKEWITH_GNUEFI
1894 static VOID
ScanLegacyVolume(REFIT_VOLUME
*Volume
, UINTN VolumeIndex
) {
1896 BOOLEAN ShowVolume
, HideIfOthersFound
;
1899 HideIfOthersFound
= FALSE
;
1900 if (Volume
->IsAppleLegacy
) {
1902 HideIfOthersFound
= TRUE
;
1903 } else if (Volume
->HasBootCode
) {
1905 if (Volume
->BlockIO
== Volume
->WholeDiskBlockIO
&&
1906 Volume
->BlockIOOffset
== 0 &&
1907 Volume
->OSName
== NULL
)
1908 // this is a whole disk (MBR) entry; hide if we have entries for partitions
1909 HideIfOthersFound
= TRUE
;
1911 if (HideIfOthersFound
) {
1912 // check for other bootable entries on the same disk
1913 for (VolumeIndex2
= 0; VolumeIndex2
< VolumesCount
; VolumeIndex2
++) {
1914 if (VolumeIndex2
!= VolumeIndex
&& Volumes
[VolumeIndex2
]->HasBootCode
&&
1915 Volumes
[VolumeIndex2
]->WholeDiskBlockIO
== Volume
->WholeDiskBlockIO
)
1921 AddLegacyEntry(NULL
, Volume
);
1922 } // static VOID ScanLegacyVolume()
1924 // Scan attached optical discs for legacy (BIOS) boot code
1925 // and add anything found to the list....
1926 static VOID
ScanLegacyDisc(VOID
)
1929 REFIT_VOLUME
*Volume
;
1931 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
) {
1932 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1933 Volume
= Volumes
[VolumeIndex
];
1934 if (Volume
->DiskKind
== DISK_KIND_OPTICAL
)
1935 ScanLegacyVolume(Volume
, VolumeIndex
);
1937 } else if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
1938 ScanLegacyUEFI(BBS_CDROM
);
1940 } /* static VOID ScanLegacyDisc() */
1942 // Scan internal hard disks for legacy (BIOS) boot code
1943 // and add anything found to the list....
1944 static VOID
ScanLegacyInternal(VOID
)
1947 REFIT_VOLUME
*Volume
;
1949 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
) {
1950 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1951 Volume
= Volumes
[VolumeIndex
];
1952 if (Volume
->DiskKind
== DISK_KIND_INTERNAL
)
1953 ScanLegacyVolume(Volume
, VolumeIndex
);
1955 } else if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
1956 // TODO: This actually picks up USB flash drives, too; try to find
1957 // a way to differentiate the two....
1958 ScanLegacyUEFI(BBS_HARDDISK
);
1960 } /* static VOID ScanLegacyInternal() */
1962 // Scan external disks for legacy (BIOS) boot code
1963 // and add anything found to the list....
1964 static VOID
ScanLegacyExternal(VOID
)
1967 REFIT_VOLUME
*Volume
;
1969 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
) {
1970 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1971 Volume
= Volumes
[VolumeIndex
];
1972 if (Volume
->DiskKind
== DISK_KIND_EXTERNAL
)
1973 ScanLegacyVolume(Volume
, VolumeIndex
);
1975 } else if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
1976 // TODO: This actually doesn't do anything useful; leaving in hopes of
1977 // fixing it later....
1978 ScanLegacyUEFI(BBS_USB
);
1980 } /* static VOID ScanLegacyExternal() */
1983 // pre-boot tool functions
1986 static VOID
StartTool(IN LOADER_ENTRY
*Entry
)
1988 BeginExternalScreen(Entry
->UseGraphicsMode
, Entry
->me
.Title
+ 6); // assumes "Start <title>" as assigned below
1989 StartEFIImage(Entry
->DevicePath
, Entry
->LoadOptions
, TYPE_EFI
,
1990 Basename(Entry
->LoaderPath
), Entry
->OSType
, NULL
, TRUE
);
1991 FinishExternalScreen();
1992 } /* static VOID StartTool() */
1994 static LOADER_ENTRY
* AddToolEntry(EFI_HANDLE DeviceHandle
, IN CHAR16
*LoaderPath
, IN CHAR16
*LoaderTitle
, IN EG_IMAGE
*Image
,
1995 IN CHAR16 ShortcutLetter
, IN BOOLEAN UseGraphicsMode
)
1997 LOADER_ENTRY
*Entry
;
1998 CHAR16
*TitleStr
= NULL
;
2000 Entry
= AllocateZeroPool(sizeof(LOADER_ENTRY
));
2002 TitleStr
= PoolPrint(L
"Start %s", LoaderTitle
);
2003 Entry
->me
.Title
= TitleStr
;
2004 Entry
->me
.Tag
= TAG_TOOL
;
2006 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
2007 Entry
->me
.Image
= Image
;
2008 Entry
->LoaderPath
= (LoaderPath
) ? StrDuplicate(LoaderPath
) : NULL
;
2009 Entry
->DevicePath
= FileDevicePath(DeviceHandle
, Entry
->LoaderPath
);
2010 Entry
->UseGraphicsMode
= UseGraphicsMode
;
2012 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
2014 } /* static LOADER_ENTRY * AddToolEntry() */
2017 // pre-boot driver functions
2020 static UINTN
ScanDriverDir(IN CHAR16
*Path
)
2023 REFIT_DIR_ITER DirIter
;
2025 EFI_FILE_INFO
*DirEntry
;
2026 CHAR16 FileName
[256];
2028 CleanUpPathNameSlashes(Path
);
2029 // look through contents of the directory
2030 DirIterOpen(SelfRootDir
, Path
, &DirIter
);
2031 while (DirIterNext(&DirIter
, 2, LOADER_MATCH_PATTERNS
, &DirEntry
)) {
2032 if (DirEntry
->FileName
[0] == '.')
2033 continue; // skip this
2035 SPrint(FileName
, 255, L
"%s\\%s", Path
, DirEntry
->FileName
);
2037 Status
= StartEFIImage(FileDevicePath(SelfLoadedImage
->DeviceHandle
, FileName
),
2038 L
"", TYPE_EFI
, DirEntry
->FileName
, 0, NULL
, FALSE
);
2040 Status
= DirIterClose(&DirIter
);
2041 if (Status
!= EFI_NOT_FOUND
) {
2042 SPrint(FileName
, 255, L
"while scanning the %s directory", Path
);
2043 CheckError(Status
, FileName
);
2048 #ifdef __MAKEWITH_GNUEFI
2049 static EFI_STATUS
ConnectAllDriversToAllControllers(VOID
)
2052 UINTN AllHandleCount
;
2053 EFI_HANDLE
*AllHandleBuffer
;
2056 EFI_HANDLE
*HandleBuffer
;
2062 Status
= LibLocateHandle(AllHandles
,
2067 if (EFI_ERROR(Status
))
2070 for (Index
= 0; Index
< AllHandleCount
; Index
++) {
2072 // Scan the handle database
2074 Status
= LibScanHandleDatabase(NULL
,
2076 AllHandleBuffer
[Index
],
2081 if (EFI_ERROR (Status
))
2085 if (HandleType
[Index
] & EFI_HANDLE_TYPE_DRIVER_BINDING_HANDLE
)
2087 if (HandleType
[Index
] & EFI_HANDLE_TYPE_IMAGE_HANDLE
)
2092 for (HandleIndex
= 0; HandleIndex
< HandleCount
; HandleIndex
++) {
2093 if (HandleType
[HandleIndex
] & EFI_HANDLE_TYPE_PARENT_HANDLE
)
2098 if (HandleType
[Index
] & EFI_HANDLE_TYPE_DEVICE_HANDLE
) {
2099 Status
= refit_call4_wrapper(BS
->ConnectController
,
2100 AllHandleBuffer
[Index
],
2108 MyFreePool (HandleBuffer
);
2109 MyFreePool (HandleType
);
2113 MyFreePool (AllHandleBuffer
);
2115 } /* EFI_STATUS ConnectAllDriversToAllControllers() */
2117 static EFI_STATUS
ConnectAllDriversToAllControllers(VOID
) {
2118 BdsLibConnectAllDriversToAllControllers();
2123 // Load all EFI drivers from rEFInd's "drivers" subdirectory and from the
2124 // directories specified by the user in the "scan_driver_dirs" configuration
2126 static VOID
LoadDrivers(VOID
)
2128 CHAR16
*Directory
, *SelfDirectory
;
2129 UINTN i
= 0, Length
, NumFound
= 0;
2131 // load drivers from the subdirectories of rEFInd's home directory specified
2132 // in the DRIVER_DIRS constant.
2133 while ((Directory
= FindCommaDelimited(DRIVER_DIRS
, i
++)) != NULL
) {
2134 SelfDirectory
= SelfDirPath
? StrDuplicate(SelfDirPath
) : NULL
;
2135 CleanUpPathNameSlashes(SelfDirectory
);
2136 MergeStrings(&SelfDirectory
, Directory
, L
'\\');
2137 NumFound
+= ScanDriverDir(SelfDirectory
);
2138 MyFreePool(Directory
);
2139 MyFreePool(SelfDirectory
);
2142 // Scan additional user-specified driver directories....
2144 while ((Directory
= FindCommaDelimited(GlobalConfig
.DriverDirs
, i
++)) != NULL
) {
2145 CleanUpPathNameSlashes(Directory
);
2146 Length
= StrLen(Directory
);
2148 NumFound
+= ScanDriverDir(Directory
);
2150 MyFreePool(Directory
);
2153 // connect all devices
2155 ConnectAllDriversToAllControllers();
2156 } /* static VOID LoadDrivers() */
2158 // Determine what (if any) type of legacy (BIOS) boot support is available
2159 static VOID
FindLegacyBootType(VOID
) {
2161 EFI_LEGACY_BIOS_PROTOCOL
*LegacyBios
;
2163 GlobalConfig
.LegacyType
= LEGACY_TYPE_NONE
;
2165 // UEFI-style legacy BIOS support is available only with some EFI implementations....
2166 Status
= refit_call3_wrapper(gBS
->LocateProtocol
, &gEfiLegacyBootProtocolGuid
, NULL
, (VOID
**) &LegacyBios
);
2167 if (!EFI_ERROR (Status
))
2168 GlobalConfig
.LegacyType
= LEGACY_TYPE_UEFI
;
2170 // Macs have their own system. If the firmware vendor code contains the
2171 // string "Apple", assume it's available. Note that this overrides the
2172 // UEFI type, and might yield false positives if the vendor string
2173 // contains "Apple" as part of something bigger, so this isn't 100%
2175 if (StriSubCmp(L
"Apple", ST
->FirmwareVendor
))
2176 GlobalConfig
.LegacyType
= LEGACY_TYPE_MAC
;
2177 } // static VOID FindLegacyBootType
2179 // Warn the user if legacy OS scans are enabled but the firmware can't support them....
2180 static VOID
WarnIfLegacyProblems(VOID
) {
2181 BOOLEAN found
= FALSE
;
2184 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_NONE
) {
2186 if (GlobalConfig
.ScanFor
[i
] == 'h' || GlobalConfig
.ScanFor
[i
] == 'b' || GlobalConfig
.ScanFor
[i
] == 'c' ||
2187 GlobalConfig
.ScanFor
[i
] == 'H' || GlobalConfig
.ScanFor
[i
] == 'B' || GlobalConfig
.ScanFor
[i
] == 'C')
2190 } while ((i
< NUM_SCAN_OPTIONS
) && (!found
));
2193 Print(L
"NOTE: refind.conf's 'scanfor' line specifies scanning for one or more legacy\n");
2194 Print(L
"(BIOS) boot options; however, this is not possible because your computer lacks\n");
2195 Print(L
"the necessary Compatibility Support Module (CSM) support or that support is\n");
2196 Print(L
"disabled in your firmware.\n");
2199 } // if no legacy support
2200 } // static VOID WarnIfLegacyProblems()
2202 // Locates boot loaders. NOTE: This assumes that GlobalConfig.LegacyType is set correctly.
2203 static VOID
ScanForBootloaders(VOID
) {
2206 BOOLEAN ScanForLegacy
= FALSE
;
2208 // Determine up-front if we'll be scanning for legacy loaders....
2209 for (i
= 0; i
< NUM_SCAN_OPTIONS
; i
++) {
2210 s
= GlobalConfig
.ScanFor
[i
];
2211 if ((s
== 'c') || (s
== 'C') || (s
== 'h') || (s
== 'H') || (s
== 'b') || (s
== 'B'))
2212 ScanForLegacy
= TRUE
;
2215 // If UEFI & scanning for legacy loaders, update NVRAM boot manager list
2216 if ((GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) && ScanForLegacy
) {
2217 BdsDeleteAllInvalidLegacyBootOptions();
2218 BdsAddNonExistingLegacyBootOptions();
2221 // scan for loaders and tools, add them to the menu
2222 for (i
= 0; i
< NUM_SCAN_OPTIONS
; i
++) {
2223 switch(GlobalConfig
.ScanFor
[i
]) {
2228 ScanLegacyInternal();
2231 ScanLegacyExternal();
2234 ScanUserConfigured(GlobalConfig
.ConfigFilename
);
2248 // assign shortcut keys
2249 for (i
= 0; i
< MainMenu
.EntryCount
&& MainMenu
.Entries
[i
]->Row
== 0 && i
< 9; i
++)
2250 MainMenu
.Entries
[i
]->ShortcutDigit
= (CHAR16
)('1' + i
);
2252 // wait for user ACK when there were errors
2253 FinishTextScreen(FALSE
);
2254 } // static VOID ScanForBootloaders()
2256 // Locate a single tool from the specified Locations using one of the
2257 // specified Names and add it to the menu.
2258 static VOID
FindTool(CHAR16
*Locations
, CHAR16
*Names
, CHAR16
*Description
, UINTN Icon
) {
2259 UINTN j
= 0, k
, VolumeIndex
;
2260 CHAR16
*DirName
, *FileName
, *PathName
, FullDescription
[256];
2262 while ((DirName
= FindCommaDelimited(Locations
, j
++)) != NULL
) {
2264 while ((FileName
= FindCommaDelimited(Names
, k
++)) != NULL
) {
2265 PathName
= StrDuplicate(DirName
);
2266 MergeStrings(&PathName
, FileName
, (StriCmp(PathName
, L
"\\") == 0) ? 0 : L
'\\');
2267 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
2268 if ((Volumes
[VolumeIndex
]->RootDir
!= NULL
) && (FileExists(Volumes
[VolumeIndex
]->RootDir
, PathName
))) {
2269 SPrint(FullDescription
, 255, L
"%s at %s on %s", Description
, PathName
, Volumes
[VolumeIndex
]->VolName
);
2270 AddToolEntry(Volumes
[VolumeIndex
]->DeviceHandle
, PathName
, FullDescription
, BuiltinIcon(Icon
), 'S', FALSE
);
2273 MyFreePool(PathName
);
2274 MyFreePool(FileName
);
2276 MyFreePool(DirName
);
2277 } // while Locations
2278 } // VOID FindTool()
2280 // Add the second-row tags containing built-in and external tools (EFI shell,
2282 static VOID
ScanForTools(VOID
) {
2283 CHAR16
*FileName
= NULL
, *VolName
= NULL
, *MokLocations
, Description
[256];
2284 REFIT_MENU_ENTRY
*TempMenuEntry
;
2285 UINTN i
, j
, VolumeIndex
;
2289 MokLocations
= StrDuplicate(MOK_LOCATIONS
);
2290 if (MokLocations
!= NULL
)
2291 MergeStrings(&MokLocations
, SelfDirPath
, L
',');
2293 for (i
= 0; i
< NUM_TOOLS
; i
++) {
2294 switch(GlobalConfig
.ShowTools
[i
]) {
2295 // NOTE: Be sure that FileName is NULL at the end of each case.
2297 TempMenuEntry
= CopyMenuEntry(&MenuEntryShutdown
);
2298 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_SHUTDOWN
);
2299 AddMenuEntry(&MainMenu
, TempMenuEntry
);
2303 TempMenuEntry
= CopyMenuEntry(&MenuEntryReset
);
2304 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_RESET
);
2305 AddMenuEntry(&MainMenu
, TempMenuEntry
);
2309 TempMenuEntry
= CopyMenuEntry(&MenuEntryAbout
);
2310 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT
);
2311 AddMenuEntry(&MainMenu
, TempMenuEntry
);
2315 TempMenuEntry
= CopyMenuEntry(&MenuEntryExit
);
2316 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_EXIT
);
2317 AddMenuEntry(&MainMenu
, TempMenuEntry
);
2321 if (EfivarGetRaw(&GlobalGuid
, L
"OsIndicationsSupported", &b
, &j
) == EFI_SUCCESS
) {
2323 if (osind
& EFI_OS_INDICATIONS_BOOT_TO_FW_UI
) {
2324 TempMenuEntry
= CopyMenuEntry(&MenuEntryFirmware
);
2325 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_FIRMWARE
);
2326 AddMenuEntry(&MainMenu
, TempMenuEntry
);
2333 while ((FileName
= FindCommaDelimited(SHELL_NAMES
, j
++)) != NULL
) {
2334 if (FileExists(SelfRootDir
, FileName
)) {
2335 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, L
"EFI Shell", BuiltinIcon(BUILTIN_ICON_TOOL_SHELL
),
2338 MyFreePool(FileName
);
2344 while ((FileName
= FindCommaDelimited(GPTSYNC_NAMES
, j
++)) != NULL
) {
2345 if (FileExists(SelfRootDir
, FileName
)) {
2346 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, L
"Hybrid MBR tool", BuiltinIcon(BUILTIN_ICON_TOOL_PART
),
2349 MyFreePool(FileName
);
2356 while ((FileName
= FindCommaDelimited(GDISK_NAMES
, j
++)) != NULL
) {
2357 if (FileExists(SelfRootDir
, FileName
)) {
2358 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, L
"disk partitioning tool",
2359 BuiltinIcon(BUILTIN_ICON_TOOL_PART
), 'G', FALSE
);
2361 MyFreePool(FileName
);
2366 case TAG_APPLE_RECOVERY
:
2367 FileName
= StrDuplicate(L
"\\com.apple.recovery.boot\\boot.efi");
2368 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
2369 if ((Volumes
[VolumeIndex
]->RootDir
!= NULL
) && (FileExists(Volumes
[VolumeIndex
]->RootDir
, FileName
))) {
2370 SPrint(Description
, 255, L
"Apple Recovery on %s", Volumes
[VolumeIndex
]->VolName
);
2371 AddToolEntry(Volumes
[VolumeIndex
]->DeviceHandle
, FileName
, Description
,
2372 BuiltinIcon(BUILTIN_ICON_TOOL_APPLE_RESCUE
), 'R', TRUE
);
2375 MyFreePool(FileName
);
2379 case TAG_WINDOWS_RECOVERY
:
2381 while ((FileName
= FindCommaDelimited(GlobalConfig
.WindowsRecoveryFiles
, j
++)) != NULL
) {
2382 SplitVolumeAndFilename(&FileName
, &VolName
);
2383 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
2384 if ((Volumes
[VolumeIndex
]->RootDir
!= NULL
) && (FileExists(Volumes
[VolumeIndex
]->RootDir
, FileName
)) &&
2385 ((VolName
== NULL
) || (StriCmp(VolName
, Volumes
[VolumeIndex
]->VolName
) == 0))) {
2386 SPrint(Description
, 255, L
"Microsoft Recovery on %s", Volumes
[VolumeIndex
]->VolName
);
2387 AddToolEntry(Volumes
[VolumeIndex
]->DeviceHandle
, FileName
, Description
,
2388 BuiltinIcon(BUILTIN_ICON_TOOL_WINDOWS_RESCUE
), 'R', TRUE
);
2392 MyFreePool(FileName
);
2394 MyFreePool(VolName
);
2399 FindTool(MokLocations
, MOK_NAMES
, L
"MOK utility", BUILTIN_ICON_TOOL_MOK_TOOL
);
2403 FindTool(MEMTEST_LOCATIONS
, MEMTEST_NAMES
, L
"Memory test utility", BUILTIN_ICON_TOOL_MEMTEST
);
2408 } // static VOID ScanForTools
2410 // Rescan for boot loaders
2411 static VOID
RescanAll(BOOLEAN DisplayMessage
) {
2419 egDisplayMessage(L
"Scanning for new boot loaders; please wait....", &BGColor
);
2420 FreeList((VOID
***) &(MainMenu
.Entries
), &MainMenu
.EntryCount
);
2421 MainMenu
.Entries
= NULL
;
2422 MainMenu
.EntryCount
= 0;
2423 ReadConfig(GlobalConfig
.ConfigFilename
);
2424 ConnectAllDriversToAllControllers();
2426 ScanForBootloaders();
2429 } // VOID RescanAll()
2431 #ifdef __MAKEWITH_TIANO
2433 // Minimal initialization function
2434 static VOID
InitializeLib(IN EFI_HANDLE ImageHandle
, IN EFI_SYSTEM_TABLE
*SystemTable
) {
2436 // gImageHandle = ImageHandle;
2437 gBS
= SystemTable
->BootServices
;
2438 // gRS = SystemTable->RuntimeServices;
2439 gRT
= SystemTable
->RuntimeServices
; // Some BDS functions need gRT to be set
2440 EfiGetSystemConfigurationTable (&gEfiDxeServicesTableGuid
, (VOID
**) &gDS
);
2442 // InitializeConsoleSim();
2447 // Set up our own Secure Boot extensions....
2448 // Returns TRUE on success, FALSE otherwise
2449 static BOOLEAN
SecureBootSetup(VOID
) {
2451 BOOLEAN Success
= FALSE
;
2453 if (secure_mode() && ShimLoaded()) {
2454 Status
= security_policy_install();
2455 if (Status
== EFI_SUCCESS
) {
2458 Print(L
"Failed to install MOK Secure Boot extensions");
2462 } // VOID SecureBootSetup()
2464 // Remove our own Secure Boot extensions....
2465 // Returns TRUE on success, FALSE otherwise
2466 static BOOLEAN
SecureBootUninstall(VOID
) {
2468 BOOLEAN Success
= TRUE
;
2470 if (secure_mode()) {
2471 Status
= security_policy_uninstall();
2472 if (Status
!= EFI_SUCCESS
) {
2474 BeginTextScreen(L
"Secure Boot Policy Failure");
2475 Print(L
"Failed to uninstall MOK Secure Boot extensions; forcing a reboot.");
2477 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);
2481 } // VOID SecureBootUninstall
2483 // Sets the global configuration filename; will be CONFIG_FILE_NAME unless the
2484 // "-c" command-line option is set, in which case that takes precedence.
2485 // If an error is encountered, leaves the value alone (it should be set to
2486 // CONFIG_FILE_NAME when GlobalConfig is initialized).
2487 static VOID
SetConfigFilename(EFI_HANDLE ImageHandle
) {
2488 EFI_LOADED_IMAGE
*Info
;
2489 CHAR16
*Options
, *FileName
;
2493 Status
= refit_call3_wrapper(BS
->HandleProtocol
, ImageHandle
, &LoadedImageProtocol
, (VOID
**) &Info
);
2494 if ((Status
== EFI_SUCCESS
) && (Info
->LoadOptionsSize
> 0)) {
2495 Options
= (CHAR16
*) Info
->LoadOptions
;
2496 Where
= FindSubString(L
" -c ", Options
);
2498 FileName
= StrDuplicate(&Options
[Where
+ 4]);
2499 Where
= FindSubString(L
" ", FileName
);
2501 FileName
[Where
] = L
'\0';
2503 if (FileExists(SelfDir
, FileName
)) {
2504 GlobalConfig
.ConfigFilename
= FileName
;
2506 Print(L
"Specified configuration file (%s) doesn't exist; using\n'refind.conf' default\n", FileName
);
2507 MyFreePool(FileName
);
2511 } // VOID SetConfigFilename()
2518 efi_main (EFI_HANDLE ImageHandle
, EFI_SYSTEM_TABLE
*SystemTable
)
2521 BOOLEAN MainLoopRunning
= TRUE
;
2522 BOOLEAN MokProtocol
;
2523 REFIT_MENU_ENTRY
*ChosenEntry
;
2525 CHAR16
*Selection
= NULL
;
2529 InitializeLib(ImageHandle
, SystemTable
);
2530 Status
= InitRefitLib(ImageHandle
);
2531 if (EFI_ERROR(Status
))
2534 // read configuration
2535 CopyMem(GlobalConfig
.ScanFor
, "ieom ", NUM_SCAN_OPTIONS
);
2536 FindLegacyBootType();
2537 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
)
2538 CopyMem(GlobalConfig
.ScanFor
, "ihebocm ", NUM_SCAN_OPTIONS
);
2539 SetConfigFilename(ImageHandle
);
2540 ReadConfig(GlobalConfig
.ConfigFilename
);
2543 WarnIfLegacyProblems();
2544 MainMenu
.TimeoutSeconds
= GlobalConfig
.Timeout
;
2546 // disable EFI watchdog timer
2547 refit_call4_wrapper(BS
->SetWatchdogTimer
, 0x0000, 0x0000, 0x0000, NULL
);
2549 // further bootstrap (now with config available)
2550 MokProtocol
= SecureBootSetup();
2553 ScanForBootloaders();
2557 if (GlobalConfig
.ScanDelay
> 0) {
2562 if (GlobalConfig
.ScanDelay
> 1)
2563 egDisplayMessage(L
"Pausing before disk scan; please wait....", &BGColor
);
2564 for (i
= 0; i
< GlobalConfig
.ScanDelay
; i
++)
2565 refit_call1_wrapper(BS
->Stall
, 1000000);
2566 RescanAll(GlobalConfig
.ScanDelay
> 1);
2569 if (GlobalConfig
.DefaultSelection
)
2570 Selection
= StrDuplicate(GlobalConfig
.DefaultSelection
);
2572 while (MainLoopRunning
) {
2573 MenuExit
= RunMainMenu(&MainMenu
, Selection
, &ChosenEntry
);
2575 // The Escape key triggers a re-scan operation....
2576 if (MenuExit
== MENU_EXIT_ESCAPE
) {
2582 switch (ChosenEntry
->Tag
) {
2584 case TAG_REBOOT
: // Reboot
2586 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);
2587 MainLoopRunning
= FALSE
; // just in case we get this far
2590 case TAG_SHUTDOWN
: // Shut Down
2592 refit_call4_wrapper(RT
->ResetSystem
, EfiResetShutdown
, EFI_SUCCESS
, 0, NULL
);
2593 MainLoopRunning
= FALSE
; // just in case we get this far
2596 case TAG_ABOUT
: // About rEFInd
2600 case TAG_LOADER
: // Boot OS via .EFI loader
2601 StartLoader((LOADER_ENTRY
*)ChosenEntry
);
2604 case TAG_LEGACY
: // Boot legacy OS
2605 StartLegacy((LEGACY_ENTRY
*)ChosenEntry
);
2608 case TAG_LEGACY_UEFI
: // Boot a legacy OS on a non-Mac
2609 StartLegacyUEFI((LEGACY_ENTRY
*)ChosenEntry
);
2612 case TAG_TOOL
: // Start a EFI tool
2613 StartTool((LOADER_ENTRY
*)ChosenEntry
);
2616 case TAG_EXIT
: // Terminate rEFInd
2617 if ((MokProtocol
) && !SecureBootUninstall()) {
2618 MainLoopRunning
= FALSE
; // just in case we get this far
2620 BeginTextScreen(L
" ");
2625 case TAG_FIRMWARE
: // Reboot into firmware's user interface
2626 RebootIntoFirmware();
2630 MyFreePool(Selection
);
2631 Selection
= (ChosenEntry
->Title
) ? StrDuplicate(ChosenEntry
->Title
) : NULL
;
2634 // If we end up here, things have gone wrong. Try to reboot, and if that
2635 // fails, go into an endless loop.
2636 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);