3 * Main code for the boot menu
5 * Copyright (c) 2006-2010 Christoph Pfisterer
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions are
12 * * Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
15 * * Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the
20 * * Neither the name of Christoph Pfisterer nor the names of the
21 * contributors may be used to endorse or promote products derived
22 * from this software without specific prior written permission.
24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
26 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
27 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
28 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
29 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
30 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
31 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
32 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
33 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
34 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37 * Modifications copyright (c) 2012-2014 Roderick W. Smith
39 * Modifications distributed under the terms of the GNU General Public
40 * License (GPL) version 3 (GPLv3), a copy of which must be distributed
41 * with this source code or binaries made from it.
52 #include "security_policy.h"
53 #include "../include/Handle.h"
54 #include "../include/refit_call_wrapper.h"
55 #include "driver_support.h"
56 #include "../include/syslinux_mbr.h"
58 #ifdef __MAKEWITH_GNUEFI
59 #ifndef EFI_SECURITY_VIOLATION
60 #define EFI_SECURITY_VIOLATION EFIERR (26)
63 #include "../EfiLib/BdsHelper.h"
64 #include "../EfiLib/legacy.h"
65 #endif // __MAKEWITH_GNUEFI
67 #ifndef EFI_OS_INDICATIONS_BOOT_TO_FW_UI
68 #define EFI_OS_INDICATIONS_BOOT_TO_FW_UI 0x0000000000000001ULL
74 #define MACOSX_LOADER_PATH L"System\\Library\\CoreServices\\boot.efi"
76 #define SHELL_NAMES L"\\EFI\\tools\\shell.efi,\\EFI\\tools\\shellx64.efi,\\shell.efi,\\shellx64.efi"
77 #define GPTSYNC_NAMES L"\\EFI\\tools\\gptsync.efi,\\EFI\\tools\\gptsync_x64.efi"
78 #define GDISK_NAMES L"\\EFI\\tools\\gdisk.efi,\\EFI\\tools\\gdisk_x64.efi"
79 #define MEMTEST_NAMES L"memtest86.efi,memtest86_x64.efi,memtest86x64.efi,bootx64.efi"
80 #define DRIVER_DIRS L"drivers,drivers_x64"
81 #define FALLBACK_FULLNAME L"EFI\\BOOT\\bootx64.efi"
82 #define FALLBACK_BASENAME L"bootx64.efi"
83 #define EFI_STUB_ARCH 0x8664
85 #define SHELL_NAMES L"\\EFI\\tools\\shell.efi,\\EFI\\tools\\shellia32.efi,\\shell.efi,\\shellia32.efi"
86 #define GPTSYNC_NAMES L"\\EFI\\tools\\gptsync.efi,\\EFI\\tools\\gptsync_ia32.efi"
87 #define GDISK_NAMES L"\\EFI\\tools\\gdisk.efi,\\EFI\\tools\\gdisk_ia32.efi"
88 #define MEMTEST_NAMES L"memtest86.efi,memtest86_ia32.efi,memtest86ia32.efi,bootia32.efi"
89 #define DRIVER_DIRS L"drivers,drivers_ia32"
90 #define FALLBACK_FULLNAME L"EFI\\BOOT\\bootia32.efi"
91 #define FALLBACK_BASENAME L"bootia32.efi"
92 #define EFI_STUB_ARCH 0x014c
94 #define SHELL_NAMES L"\\EFI\\tools\\shell.efi,\\shell.efi"
95 #define GPTSYNC_NAMES L"\\EFI\\tools\\gptsync.efi"
96 #define GDISK_NAMES L"\\EFI\\tools\\gdisk.efi"
97 #define MEMTEST_NAMES L"memtest86.efi"
98 #define DRIVER_DIRS L"drivers"
99 #define FALLBACK_FULLNAME L"EFI\\BOOT\\boot.efi" /* Not really correct */
100 #define FALLBACK_BASENAME L"boot.efi" /* Not really correct */
102 #define FAT_ARCH 0x0ef1fab9 /* ID for Apple "fat" binary */
104 // Filename patterns that identify EFI boot loaders. Note that a single case (either L"*.efi" or
105 // L"*.EFI") is fine for most systems; but Gigabyte's buggy Hybrid EFI does a case-sensitive
106 // comparison when it should do a case-insensitive comparison, so I'm doubling this up. It does
107 // no harm on other computers, AFAIK. In theory, every case variation should be done for
108 // completeness, but that's ridiculous....
109 #define LOADER_MATCH_PATTERNS L"*.efi,*.EFI"
111 // Patterns that identify Linux kernels. Added to the loader match pattern when the
112 // scan_all_linux_kernels option is set in the configuration file. Causes kernels WITHOUT
113 // a ".efi" extension to be found when scanning for boot loaders.
114 #define LINUX_MATCH_PATTERNS L"vmlinuz*,bzImage*"
116 // Default hint text for program-launch submenus
117 #define SUBSCREEN_HINT1 L"Use arrow keys to move cursor; Enter to boot;"
118 #define SUBSCREEN_HINT2 L"Insert or F2 to edit options; Esc to return to main menu"
119 #define SUBSCREEN_HINT2_NO_EDITOR L"Esc to return to main menu"
123 #define TYPE_LEGACY 2
125 static REFIT_MENU_ENTRY MenuEntryAbout
= { L
"About rEFInd", TAG_ABOUT
, 1, 0, 'A', NULL
, NULL
, NULL
};
126 static REFIT_MENU_ENTRY MenuEntryReset
= { L
"Reboot Computer", TAG_REBOOT
, 1, 0, 'R', NULL
, NULL
, NULL
};
127 static REFIT_MENU_ENTRY MenuEntryShutdown
= { L
"Shut Down Computer", TAG_SHUTDOWN
, 1, 0, 'U', NULL
, NULL
, NULL
};
128 static REFIT_MENU_ENTRY MenuEntryReturn
= { L
"Return to Main Menu", TAG_RETURN
, 1, 0, 0, NULL
, NULL
, NULL
};
129 static REFIT_MENU_ENTRY MenuEntryExit
= { L
"Exit rEFInd", TAG_EXIT
, 1, 0, 0, NULL
, NULL
, NULL
};
130 static REFIT_MENU_ENTRY MenuEntryFirmware
= { L
"Reboot to Computer Setup Utility", TAG_FIRMWARE
, 1, 0, 0, NULL
, NULL
, NULL
};
132 static REFIT_MENU_SCREEN MainMenu
= { L
"Main Menu", NULL
, 0, NULL
, 0, NULL
, 0, L
"Automatic boot",
133 L
"Use arrow keys to move cursor; Enter to boot;",
134 L
"Insert or F2 for more options; Esc to refresh" };
135 static REFIT_MENU_SCREEN AboutMenu
= { L
"About", NULL
, 0, NULL
, 0, NULL
, 0, NULL
, L
"Press Enter to return to main menu", L
"" };
137 REFIT_CONFIG GlobalConfig
= { FALSE
, FALSE
, 0, 0, 0, DONT_CHANGE_TEXT_MODE
, 20, 0, 0, GRAPHICS_FOR_OSX
, LEGACY_TYPE_MAC
, 0, 0,
138 { DEFAULT_BIG_ICON_SIZE
/ 4, DEFAULT_SMALL_ICON_SIZE
, DEFAULT_BIG_ICON_SIZE
}, BANNER_NOSCALE
,
139 NULL
, NULL
, CONFIG_FILE_NAME
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
140 { TAG_SHELL
, TAG_MEMTEST
, TAG_GDISK
, TAG_APPLE_RECOVERY
, TAG_WINDOWS_RECOVERY
, TAG_MOK_TOOL
,
141 TAG_ABOUT
, TAG_SHUTDOWN
, TAG_REBOOT
, TAG_FIRMWARE
, 0, 0, 0, 0, 0, 0 }
144 EFI_GUID GlobalGuid
= EFI_GLOBAL_VARIABLE
;
146 // Structure used to hold boot loader filenames and time stamps in
147 // a linked list; used to sort entries within a directory.
151 struct LOADER_LIST
*NextEntry
;
158 static VOID
AboutrEFInd(VOID
)
160 if (AboutMenu
.EntryCount
== 0) {
161 AboutMenu
.TitleImage
= BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT
);
162 AddMenuInfoLine(&AboutMenu
, L
"rEFInd Version 0.7.9.2");
163 AddMenuInfoLine(&AboutMenu
, L
"");
164 AddMenuInfoLine(&AboutMenu
, L
"Copyright (c) 2006-2010 Christoph Pfisterer");
165 AddMenuInfoLine(&AboutMenu
, L
"Copyright (c) 2012-2014 Roderick W. Smith");
166 AddMenuInfoLine(&AboutMenu
, L
"Portions Copyright (c) Intel Corporation and others");
167 AddMenuInfoLine(&AboutMenu
, L
"Distributed under the terms of the GNU GPLv3 license");
168 AddMenuInfoLine(&AboutMenu
, L
"");
169 AddMenuInfoLine(&AboutMenu
, L
"Running on:");
170 AddMenuInfoLine(&AboutMenu
, PoolPrint(L
" EFI Revision %d.%02d", ST
->Hdr
.Revision
>> 16, ST
->Hdr
.Revision
& ((1 << 16) - 1)));
172 AddMenuInfoLine(&AboutMenu
, L
" Platform: x86 (32 bit)");
173 #elif defined(EFIX64)
174 AddMenuInfoLine(&AboutMenu
, PoolPrint(L
" Platform: x86_64 (64 bit); Secure Boot %s",
175 secure_mode() ? L
"active" : L
"inactive"));
177 AddMenuInfoLine(&AboutMenu
, L
" Platform: unknown");
179 AddMenuInfoLine(&AboutMenu
, PoolPrint(L
" Firmware: %s %d.%02d", ST
->FirmwareVendor
, ST
->FirmwareRevision
>> 16,
180 ST
->FirmwareRevision
& ((1 << 16) - 1)));
181 AddMenuInfoLine(&AboutMenu
, PoolPrint(L
" Screen Output: %s", egScreenDescription()));
182 AddMenuInfoLine(&AboutMenu
, L
"");
183 #if defined(__MAKEWITH_GNUEFI)
184 AddMenuInfoLine(&AboutMenu
, L
"Built with GNU-EFI");
186 AddMenuInfoLine(&AboutMenu
, L
"Built with TianoCore EDK2");
188 AddMenuInfoLine(&AboutMenu
, L
"");
189 AddMenuInfoLine(&AboutMenu
, L
"For more information, see the rEFInd Web site:");
190 AddMenuInfoLine(&AboutMenu
, L
"http://www.rodsbooks.com/refind/");
191 AddMenuEntry(&AboutMenu
, &MenuEntryReturn
);
194 RunMenu(&AboutMenu
, NULL
);
195 } /* VOID AboutrEFInd() */
197 static VOID
WarnSecureBootError(CHAR16
*Name
, BOOLEAN Verbose
) {
199 Name
= L
"the loader";
201 refit_call2_wrapper(ST
->ConOut
->SetAttribute
, ST
->ConOut
, ATTR_ERROR
);
202 Print(L
"Secure Boot validation failure loading %s!\n", Name
);
203 refit_call2_wrapper(ST
->ConOut
->SetAttribute
, ST
->ConOut
, ATTR_BASIC
);
204 if (Verbose
&& secure_mode()) {
205 Print(L
"\nThis computer is configured with Secure Boot active, but\n%s has failed validation.\n", Name
);
206 Print(L
"\nYou can:\n * Launch another boot loader\n");
207 Print(L
" * Disable Secure Boot in your firmware\n");
208 Print(L
" * Sign %s with a machine owner key (MOK)\n", Name
);
209 Print(L
" * Use a MOK utility (often present on the second row) to add a MOK with which\n");
210 Print(L
" %s has already been signed.\n", Name
);
211 Print(L
" * Use a MOK utility to register %s (\"enroll its hash\") without\n", Name
);
212 Print(L
" signing it.\n");
213 Print(L
"\nSee http://www.rodsbooks.com/refind/secureboot.html for more information\n");
216 } // VOID WarnSecureBootError()
218 // Returns TRUE if this file is a valid EFI loader file, and is proper ARCH
219 static BOOLEAN
IsValidLoader(EFI_FILE
*RootDir
, CHAR16
*FileName
) {
220 BOOLEAN IsValid
= TRUE
;
221 #if defined (EFIX64) | defined (EFI32)
223 EFI_FILE_HANDLE FileHandle
;
225 UINTN Size
= sizeof(Header
);
227 if ((RootDir
== NULL
) || (FileName
== NULL
)) {
228 // Assume valid here, because Macs produce NULL RootDir (& maybe FileName)
229 // when launching from a Firewire drive. This should be handled better, but
230 // fix would have to be in StartEFIImageList() and/or in FindVolumeAndFilename().
234 Status
= refit_call5_wrapper(RootDir
->Open
, RootDir
, &FileHandle
, FileName
, EFI_FILE_MODE_READ
, 0);
235 if (EFI_ERROR(Status
))
238 Status
= refit_call3_wrapper(FileHandle
->Read
, FileHandle
, &Size
, Header
);
239 refit_call1_wrapper(FileHandle
->Close
, FileHandle
);
241 IsValid
= !EFI_ERROR(Status
) &&
242 Size
== sizeof(Header
) &&
243 ((Header
[0] == 'M' && Header
[1] == 'Z' &&
244 (Size
= *(UINT32
*)&Header
[0x3c]) < 0x180 &&
245 Header
[Size
] == 'P' && Header
[Size
+1] == 'E' &&
246 Header
[Size
+2] == 0 && Header
[Size
+3] == 0 &&
247 *(UINT16
*)&Header
[Size
+4] == EFI_STUB_ARCH
) ||
248 (*(UINT32
*)&Header
== FAT_ARCH
));
251 } // BOOLEAN IsValidLoader()
253 // Launch an EFI binary.
254 static EFI_STATUS
StartEFIImageList(IN EFI_DEVICE_PATH
**DevicePaths
,
255 IN CHAR16
*LoadOptions
, IN UINTN LoaderType
,
256 IN CHAR16
*ImageTitle
, IN CHAR8 OSType
,
257 OUT UINTN
*ErrorInStep
,
260 EFI_STATUS Status
, ReturnStatus
;
261 EFI_HANDLE ChildImageHandle
;
262 EFI_LOADED_IMAGE
*ChildLoadedImage
= NULL
;
263 REFIT_VOLUME
*Volume
= NULL
;
264 UINTN DevicePathIndex
;
265 CHAR16 ErrorInfo
[256];
266 CHAR16
*FullLoadOptions
= NULL
;
267 CHAR16
*Filename
= NULL
;
270 if (ErrorInStep
!= NULL
)
274 if (LoadOptions
!= NULL
) {
275 FullLoadOptions
= StrDuplicate(LoadOptions
);
276 if ((LoaderType
== TYPE_EFI
) && (OSType
== 'M')) {
277 MergeStrings(&FullLoadOptions
, L
" ", 0);
278 // NOTE: That last space is also added by the EFI shell and seems to be significant
279 // when passing options to Apple's boot.efi...
281 } // if (LoadOptions != NULL)
283 Print(L
"Starting %s\nUsing load options '%s'\n", ImageTitle
, FullLoadOptions
? FullLoadOptions
: L
"");
285 // load the image into memory (and execute it, in the case of a shim/MOK image).
286 ReturnStatus
= Status
= EFI_NOT_FOUND
; // in case the list is empty
287 for (DevicePathIndex
= 0; DevicePaths
[DevicePathIndex
] != NULL
; DevicePathIndex
++) {
288 FindVolumeAndFilename(DevicePaths
[DevicePathIndex
], &Volume
, &Filename
);
289 // Some EFIs crash if attempting to load driver for invalid architecture, so
290 // protect for this condition; but sometimes Volume comes back NULL, so provide
291 // an exception. (TODO: Handle this special condition better.)
292 if ((LoaderType
== TYPE_LEGACY
) || (Volume
== NULL
) || IsValidLoader(Volume
->RootDir
, Filename
)) {
293 if (Filename
&& (LoaderType
!= TYPE_LEGACY
)) {
294 Temp
= PoolPrint(L
"\\%s %s", Filename
, FullLoadOptions
? FullLoadOptions
: L
"");
296 MyFreePool(FullLoadOptions
);
297 FullLoadOptions
= Temp
;
301 // NOTE: Below commented-out line could be more efficient if file were read ahead of
302 // time and passed as a pre-loaded image to LoadImage(), but it doesn't work on my
303 // 32-bit Mac Mini or my 64-bit Intel box when launching a Linux kernel; the
304 // kernel returns a "Failed to handle fs_proto" error message.
305 // TODO: Track down the cause of this error and fix it, if possible.
306 // ReturnStatus = Status = refit_call6_wrapper(BS->LoadImage, FALSE, SelfImageHandle, DevicePaths[DevicePathIndex],
307 // ImageData, ImageSize, &ChildImageHandle);
308 ReturnStatus
= Status
= refit_call6_wrapper(BS
->LoadImage
, FALSE
, SelfImageHandle
, DevicePaths
[DevicePathIndex
],
309 NULL
, 0, &ChildImageHandle
);
311 Print(L
"Invalid loader file!\n");
312 ReturnStatus
= EFI_LOAD_ERROR
;
314 if (ReturnStatus
!= EFI_NOT_FOUND
) {
318 if ((Status
== EFI_ACCESS_DENIED
) || (Status
== EFI_SECURITY_VIOLATION
)) {
319 WarnSecureBootError(ImageTitle
, Verbose
);
322 SPrint(ErrorInfo
, 255, L
"while loading %s", ImageTitle
);
323 if (CheckError(Status
, ErrorInfo
)) {
324 if (ErrorInStep
!= NULL
)
329 ReturnStatus
= Status
= refit_call3_wrapper(BS
->HandleProtocol
, ChildImageHandle
, &LoadedImageProtocol
,
330 (VOID
**) &ChildLoadedImage
);
331 if (CheckError(Status
, L
"while getting a LoadedImageProtocol handle")) {
332 if (ErrorInStep
!= NULL
)
336 ChildLoadedImage
->LoadOptions
= (VOID
*)FullLoadOptions
;
337 ChildLoadedImage
->LoadOptionsSize
= FullLoadOptions
? ((UINT32
)StrLen(FullLoadOptions
) + 1) * sizeof(CHAR16
) : 0;
338 // turn control over to the image
339 // TODO: (optionally) re-enable the EFI watchdog timer!
341 // close open file handles
343 ReturnStatus
= Status
= refit_call3_wrapper(BS
->StartImage
, ChildImageHandle
, NULL
, NULL
);
345 // control returns here when the child image calls Exit()
346 SPrint(ErrorInfo
, 255, L
"returned from %s", ImageTitle
);
347 if (CheckError(Status
, ErrorInfo
)) {
348 if (ErrorInStep
!= NULL
)
352 // re-open file handles
356 // unload the image, we don't care if it works or not...
357 Status
= refit_call1_wrapper(BS
->UnloadImage
, ChildImageHandle
);
360 MyFreePool(FullLoadOptions
);
362 } /* static EFI_STATUS StartEFIImageList() */
364 static EFI_STATUS
StartEFIImage(IN EFI_DEVICE_PATH
*DevicePath
,
365 IN CHAR16
*LoadOptions
, IN UINTN LoaderType
,
366 IN CHAR16
*ImageTitle
, IN CHAR8 OSType
,
367 OUT UINTN
*ErrorInStep
,
370 EFI_DEVICE_PATH
*DevicePaths
[2];
372 DevicePaths
[0] = DevicePath
;
373 DevicePaths
[1] = NULL
;
374 return StartEFIImageList(DevicePaths
, LoadOptions
, LoaderType
, ImageTitle
, OSType
, ErrorInStep
, Verbose
);
375 } /* static EFI_STATUS StartEFIImage() */
377 // From gummiboot: Retrieve a raw EFI variable.
378 // Returns EFI status
379 static EFI_STATUS
EfivarGetRaw(EFI_GUID
*vendor
, CHAR16
*name
, CHAR8
**buffer
, UINTN
*size
) {
384 l
= sizeof(CHAR16
*) * EFI_MAXIMUM_VARIABLE_SIZE
;
385 buf
= AllocatePool(l
);
387 return EFI_OUT_OF_RESOURCES
;
389 err
= refit_call5_wrapper(RT
->GetVariable
, name
, vendor
, NULL
, &l
, buf
);
390 if (EFI_ERROR(err
) == EFI_SUCCESS
) {
397 } // EFI_STATUS EfivarGetRaw()
399 // From gummiboot: Set an EFI variable
400 static EFI_STATUS
EfivarSetRaw(EFI_GUID
*vendor
, CHAR16
*name
, CHAR8
*buf
, UINTN size
, BOOLEAN persistent
) {
403 flags
= EFI_VARIABLE_BOOTSERVICE_ACCESS
|EFI_VARIABLE_RUNTIME_ACCESS
;
405 flags
|= EFI_VARIABLE_NON_VOLATILE
;
407 return refit_call5_wrapper(RT
->SetVariable
, name
, vendor
, flags
, size
, buf
);
408 } // EFI_STATUS EfivarSetRaw()
410 // From gummiboot: Reboot the computer into its built-in user interface
411 static EFI_STATUS
RebootIntoFirmware(VOID
) {
417 osind
= EFI_OS_INDICATIONS_BOOT_TO_FW_UI
;
419 err
= EfivarGetRaw(&GlobalGuid
, L
"OsIndications", &b
, &size
);
420 if (err
== EFI_SUCCESS
)
424 err
= EfivarSetRaw(&GlobalGuid
, L
"OsIndications", (CHAR8
*)&osind
, sizeof(UINT64
), TRUE
);
425 if (err
!= EFI_SUCCESS
)
428 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);
429 Print(L
"Error calling ResetSystem: %r", err
);
436 // EFI OS loader functions
439 static VOID
StartLoader(LOADER_ENTRY
*Entry
)
441 UINTN ErrorInStep
= 0;
443 BeginExternalScreen(Entry
->UseGraphicsMode
, L
"Booting OS");
444 StartEFIImage(Entry
->DevicePath
, Entry
->LoadOptions
, TYPE_EFI
,
445 Basename(Entry
->LoaderPath
), Entry
->OSType
, &ErrorInStep
, !Entry
->UseGraphicsMode
);
446 FinishExternalScreen();
449 // Locate an initrd or initramfs file that matches the kernel specified by LoaderPath.
450 // The matching file has a name that begins with "init" and includes the same version
451 // number string as is found in LoaderPath -- but not a longer version number string.
452 // For instance, if LoaderPath is \EFI\kernels\bzImage-3.3.0.efi, and if \EFI\kernels
453 // has a file called initramfs-3.3.0.img, this function will return the string
454 // '\EFI\kernels\initramfs-3.3.0.img'. If the directory ALSO contains the file
455 // initramfs-3.3.0-rc7.img or initramfs-13.3.0.img, those files will NOT match;
456 // however, initmine-3.3.0.img might match. (FindInitrd() returns the first match it
457 // finds). Thus, care should be taken to avoid placing duplicate matching files in
458 // the kernel's directory.
459 // If no matching init file can be found, returns NULL.
460 static CHAR16
* FindInitrd(IN CHAR16
*LoaderPath
, IN REFIT_VOLUME
*Volume
) {
461 CHAR16
*InitrdName
= NULL
, *FileName
, *KernelVersion
, *InitrdVersion
, *Path
;
462 REFIT_DIR_ITER DirIter
;
463 EFI_FILE_INFO
*DirEntry
;
465 FileName
= Basename(LoaderPath
);
466 KernelVersion
= FindNumbers(FileName
);
467 Path
= FindPath(LoaderPath
);
469 // Add trailing backslash for root directory; necessary on some systems, but must
470 // NOT be added to all directories, since on other systems, a trailing backslash on
471 // anything but the root directory causes them to flake out!
472 if (StrLen(Path
) == 0) {
473 MergeStrings(&Path
, L
"\\", 0);
475 DirIterOpen(Volume
->RootDir
, Path
, &DirIter
);
476 // Now add a trailing backslash if it was NOT added earlier, for consistency in
477 // building the InitrdName later....
478 if ((StrLen(Path
) > 0) && (Path
[StrLen(Path
) - 1] != L
'\\'))
479 MergeStrings(&Path
, L
"\\", 0);
480 while ((DirIterNext(&DirIter
, 2, L
"init*", &DirEntry
)) && (InitrdName
== NULL
)) {
481 InitrdVersion
= FindNumbers(DirEntry
->FileName
);
482 if (KernelVersion
!= NULL
) {
483 if (StriCmp(InitrdVersion
, KernelVersion
) == 0) {
484 InitrdName
= PoolPrint(L
"%s%s", Path
, DirEntry
->FileName
);
487 if (InitrdVersion
== NULL
) {
488 InitrdName
= PoolPrint(L
"%s%s", Path
, DirEntry
->FileName
);
491 MyFreePool(InitrdVersion
);
493 DirIterClose(&DirIter
);
495 // Note: Don't FreePool(FileName), since Basename returns a pointer WITHIN the string it's passed.
496 MyFreePool(KernelVersion
);
499 } // static CHAR16 * FindInitrd()
501 LOADER_ENTRY
* AddPreparedLoaderEntry(LOADER_ENTRY
*Entry
) {
502 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
505 } // LOADER_ENTRY * AddPreparedLoaderEntry()
507 // Creates a copy of a menu screen.
508 // Returns a pointer to the copy of the menu screen.
509 static REFIT_MENU_SCREEN
* CopyMenuScreen(REFIT_MENU_SCREEN
*Entry
) {
510 REFIT_MENU_SCREEN
*NewEntry
;
513 NewEntry
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
514 if ((Entry
!= NULL
) && (NewEntry
!= NULL
)) {
515 CopyMem(NewEntry
, Entry
, sizeof(REFIT_MENU_SCREEN
));
516 NewEntry
->Title
= (Entry
->Title
) ? StrDuplicate(Entry
->Title
) : NULL
;
517 NewEntry
->TimeoutText
= (Entry
->TimeoutText
) ? StrDuplicate(Entry
->TimeoutText
) : NULL
;
518 if (Entry
->TitleImage
!= NULL
) {
519 NewEntry
->TitleImage
= AllocatePool(sizeof(EG_IMAGE
));
520 if (NewEntry
->TitleImage
!= NULL
)
521 CopyMem(NewEntry
->TitleImage
, Entry
->TitleImage
, sizeof(EG_IMAGE
));
523 NewEntry
->InfoLines
= (CHAR16
**) AllocateZeroPool(Entry
->InfoLineCount
* (sizeof(CHAR16
*)));
524 for (i
= 0; i
< Entry
->InfoLineCount
&& NewEntry
->InfoLines
; i
++) {
525 NewEntry
->InfoLines
[i
] = (Entry
->InfoLines
[i
]) ? StrDuplicate(Entry
->InfoLines
[i
]) : NULL
;
527 NewEntry
->Entries
= (REFIT_MENU_ENTRY
**) AllocateZeroPool(Entry
->EntryCount
* (sizeof (REFIT_MENU_ENTRY
*)));
528 for (i
= 0; i
< Entry
->EntryCount
&& NewEntry
->Entries
; i
++) {
529 AddMenuEntry(NewEntry
, Entry
->Entries
[i
]);
531 NewEntry
->Hint1
= (Entry
->Hint1
) ? StrDuplicate(Entry
->Hint1
) : NULL
;
532 NewEntry
->Hint2
= (Entry
->Hint2
) ? StrDuplicate(Entry
->Hint2
) : NULL
;
535 } // static REFIT_MENU_SCREEN* CopyMenuScreen()
537 // Creates a copy of a menu entry. Intended to enable moving a stack-based
538 // menu entry (such as the ones for the "reboot" and "exit" functions) to
539 // to the heap. This enables easier deletion of the whole set of menu
540 // entries when re-scanning.
541 // Returns a pointer to the copy of the menu entry.
542 static REFIT_MENU_ENTRY
* CopyMenuEntry(REFIT_MENU_ENTRY
*Entry
) {
543 REFIT_MENU_ENTRY
*NewEntry
;
545 NewEntry
= AllocateZeroPool(sizeof(REFIT_MENU_ENTRY
));
546 if ((Entry
!= NULL
) && (NewEntry
!= NULL
)) {
547 CopyMem(NewEntry
, Entry
, sizeof(REFIT_MENU_ENTRY
));
548 NewEntry
->Title
= (Entry
->Title
) ? StrDuplicate(Entry
->Title
) : NULL
;
549 if (Entry
->BadgeImage
!= NULL
) {
550 NewEntry
->BadgeImage
= AllocatePool(sizeof(EG_IMAGE
));
551 if (NewEntry
->BadgeImage
!= NULL
)
552 CopyMem(NewEntry
->BadgeImage
, Entry
->BadgeImage
, sizeof(EG_IMAGE
));
554 if (Entry
->Image
!= NULL
) {
555 NewEntry
->Image
= AllocatePool(sizeof(EG_IMAGE
));
556 if (NewEntry
->Image
!= NULL
)
557 CopyMem(NewEntry
->Image
, Entry
->Image
, sizeof(EG_IMAGE
));
559 if (Entry
->SubScreen
!= NULL
) {
560 NewEntry
->SubScreen
= CopyMenuScreen(Entry
->SubScreen
);
564 } // REFIT_MENU_ENTRY* CopyMenuEntry()
566 // Creates a new LOADER_ENTRY data structure and populates it with
567 // default values from the specified Entry, or NULL values if Entry
568 // is unspecified (NULL).
569 // Returns a pointer to the new data structure, or NULL if it
570 // couldn't be allocated
571 LOADER_ENTRY
*InitializeLoaderEntry(IN LOADER_ENTRY
*Entry
) {
572 LOADER_ENTRY
*NewEntry
= NULL
;
574 NewEntry
= AllocateZeroPool(sizeof(LOADER_ENTRY
));
575 if (NewEntry
!= NULL
) {
576 NewEntry
->me
.Title
= NULL
;
577 NewEntry
->me
.Tag
= TAG_LOADER
;
578 NewEntry
->Enabled
= TRUE
;
579 NewEntry
->UseGraphicsMode
= FALSE
;
580 NewEntry
->OSType
= 0;
582 NewEntry
->LoaderPath
= (Entry
->LoaderPath
) ? StrDuplicate(Entry
->LoaderPath
) : NULL
;
583 NewEntry
->VolName
= (Entry
->VolName
) ? StrDuplicate(Entry
->VolName
) : NULL
;
584 NewEntry
->DevicePath
= Entry
->DevicePath
;
585 NewEntry
->UseGraphicsMode
= Entry
->UseGraphicsMode
;
586 NewEntry
->LoadOptions
= (Entry
->LoadOptions
) ? StrDuplicate(Entry
->LoadOptions
) : NULL
;
587 NewEntry
->InitrdPath
= (Entry
->InitrdPath
) ? StrDuplicate(Entry
->InitrdPath
) : NULL
;
591 } // LOADER_ENTRY *InitializeLoaderEntry()
593 // Adds InitrdPath to Options, but only if Options doesn't already include an
594 // initrd= line. Done to enable overriding the default initrd selection in a
595 // refind_linux.conf file's options list.
596 // Returns a pointer to a new string. The calling function is responsible for
597 // freeing its memory.
598 static CHAR16
*AddInitrdToOptions(CHAR16
*Options
, CHAR16
*InitrdPath
) {
599 CHAR16
*NewOptions
= NULL
;
602 NewOptions
= StrDuplicate(Options
);
603 if ((InitrdPath
!= NULL
) && !StriSubCmp(L
"initrd=", Options
)) {
604 MergeStrings(&NewOptions
, L
"initrd=", L
' ');
605 MergeStrings(&NewOptions
, InitrdPath
, 0);
608 } // CHAR16 *AddInitrdToOptions()
610 // Prepare a REFIT_MENU_SCREEN data structure for a subscreen entry. This sets up
611 // the default entry that launches the boot loader using the same options as the
612 // main Entry does. Subsequent options can be added by the calling function.
613 // If a subscreen already exists in the Entry that's passed to this function,
614 // it's left unchanged and a pointer to it is returned.
615 // Returns a pointer to the new subscreen data structure, or NULL if there
616 // were problems allocating memory.
617 REFIT_MENU_SCREEN
*InitializeSubScreen(IN LOADER_ENTRY
*Entry
) {
618 CHAR16
*FileName
, *MainOptions
= NULL
;
619 REFIT_MENU_SCREEN
*SubScreen
= NULL
;
620 LOADER_ENTRY
*SubEntry
;
622 FileName
= Basename(Entry
->LoaderPath
);
623 if (Entry
->me
.SubScreen
== NULL
) { // No subscreen yet; initialize default entry....
624 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
625 if (SubScreen
!= NULL
) {
626 SubScreen
->Title
= AllocateZeroPool(sizeof(CHAR16
) * 256);
627 SPrint(SubScreen
->Title
, 255, L
"Boot Options for %s on %s",
628 (Entry
->Title
!= NULL
) ? Entry
->Title
: FileName
, Entry
->VolName
);
629 SubScreen
->TitleImage
= Entry
->me
.Image
;
631 SubEntry
= InitializeLoaderEntry(Entry
);
632 if (SubEntry
!= NULL
) {
633 SubEntry
->me
.Title
= StrDuplicate(L
"Boot using default options");
634 MainOptions
= SubEntry
->LoadOptions
;
635 SubEntry
->LoadOptions
= AddInitrdToOptions(MainOptions
, SubEntry
->InitrdPath
);
636 MyFreePool(MainOptions
);
637 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
638 } // if (SubEntry != NULL)
639 SubScreen
->Hint1
= StrDuplicate(SUBSCREEN_HINT1
);
640 if (GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_EDITOR
) {
641 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2_NO_EDITOR
);
643 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2
);
645 } // if (SubScreen != NULL)
646 } else { // existing subscreen; less initialization, and just add new entry later....
647 SubScreen
= Entry
->me
.SubScreen
;
650 } // REFIT_MENU_SCREEN *InitializeSubScreen()
652 VOID
GenerateSubScreen(LOADER_ENTRY
*Entry
, IN REFIT_VOLUME
*Volume
) {
653 REFIT_MENU_SCREEN
*SubScreen
;
654 LOADER_ENTRY
*SubEntry
;
656 CHAR16 DiagsFileName
[256];
661 // create the submenu
662 if (StrLen(Entry
->Title
) == 0) {
663 MyFreePool(Entry
->Title
);
666 SubScreen
= InitializeSubScreen(Entry
);
668 // loader-specific submenu entries
669 if (Entry
->OSType
== 'M') { // entries for Mac OS X
671 SubEntry
= InitializeLoaderEntry(Entry
);
672 if (SubEntry
!= NULL
) {
673 SubEntry
->me
.Title
= L
"Boot Mac OS X with a 64-bit kernel";
674 SubEntry
->LoadOptions
= L
"arch=x86_64";
675 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
676 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
679 SubEntry
= InitializeLoaderEntry(Entry
);
680 if (SubEntry
!= NULL
) {
681 SubEntry
->me
.Title
= L
"Boot Mac OS X with a 32-bit kernel";
682 SubEntry
->LoadOptions
= L
"arch=i386";
683 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
684 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
688 if (!(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_SINGLEUSER
)) {
689 SubEntry
= InitializeLoaderEntry(Entry
);
690 if (SubEntry
!= NULL
) {
691 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode";
692 SubEntry
->UseGraphicsMode
= FALSE
;
693 SubEntry
->LoadOptions
= L
"-v";
694 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
698 SubEntry
= InitializeLoaderEntry(Entry
);
699 if (SubEntry
!= NULL
) {
700 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode (64-bit)";
701 SubEntry
->UseGraphicsMode
= FALSE
;
702 SubEntry
->LoadOptions
= L
"-v arch=x86_64";
703 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
706 SubEntry
= InitializeLoaderEntry(Entry
);
707 if (SubEntry
!= NULL
) {
708 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode (32-bit)";
709 SubEntry
->UseGraphicsMode
= FALSE
;
710 SubEntry
->LoadOptions
= L
"-v arch=i386";
711 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
715 SubEntry
= InitializeLoaderEntry(Entry
);
716 if (SubEntry
!= NULL
) {
717 SubEntry
->me
.Title
= L
"Boot Mac OS X in single user mode";
718 SubEntry
->UseGraphicsMode
= FALSE
;
719 SubEntry
->LoadOptions
= L
"-v -s";
720 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
722 } // single-user mode allowed
724 if (!(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_SAFEMODE
)) {
725 SubEntry
= InitializeLoaderEntry(Entry
);
726 if (SubEntry
!= NULL
) {
727 SubEntry
->me
.Title
= L
"Boot Mac OS X in safe mode";
728 SubEntry
->UseGraphicsMode
= FALSE
;
729 SubEntry
->LoadOptions
= L
"-v -x";
730 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
732 } // safe mode allowed
734 // check for Apple hardware diagnostics
735 StrCpy(DiagsFileName
, L
"System\\Library\\CoreServices\\.diagnostics\\diags.efi");
736 if (FileExists(Volume
->RootDir
, DiagsFileName
) && !(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_HWTEST
)) {
737 SubEntry
= InitializeLoaderEntry(Entry
);
738 if (SubEntry
!= NULL
) {
739 SubEntry
->me
.Title
= L
"Run Apple Hardware Test";
740 MyFreePool(SubEntry
->LoaderPath
);
741 SubEntry
->LoaderPath
= StrDuplicate(DiagsFileName
);
742 SubEntry
->DevicePath
= FileDevicePath(Volume
->DeviceHandle
, SubEntry
->LoaderPath
);
743 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
744 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
746 } // if diagnostics entry found
748 } else if (Entry
->OSType
== 'L') { // entries for Linux kernels with EFI stub loaders
749 File
= ReadLinuxOptionsFile(Entry
->LoaderPath
, Volume
);
751 InitrdName
= FindInitrd(Entry
->LoaderPath
, Volume
);
752 TokenCount
= ReadTokenLine(File
, &TokenList
);
753 // first entry requires special processing, since it was initially set
754 // up with a default title but correct options by InitializeSubScreen(),
756 if ((SubScreen
->Entries
!= NULL
) && (SubScreen
->Entries
[0] != NULL
)) {
757 MyFreePool(SubScreen
->Entries
[0]->Title
);
758 SubScreen
->Entries
[0]->Title
= TokenList
[0] ? StrDuplicate(TokenList
[0]) : StrDuplicate(L
"Boot Linux");
760 FreeTokenLine(&TokenList
, &TokenCount
);
761 while ((TokenCount
= ReadTokenLine(File
, &TokenList
)) > 1) {
762 SubEntry
= InitializeLoaderEntry(Entry
);
763 SubEntry
->me
.Title
= TokenList
[0] ? StrDuplicate(TokenList
[0]) : StrDuplicate(L
"Boot Linux");
764 MyFreePool(SubEntry
->LoadOptions
);
765 SubEntry
->LoadOptions
= AddInitrdToOptions(TokenList
[1], InitrdName
);
766 FreeTokenLine(&TokenList
, &TokenCount
);
767 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_LINUX
;
768 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
770 MyFreePool(InitrdName
);
774 } else if (Entry
->OSType
== 'E') { // entries for ELILO
775 SubEntry
= InitializeLoaderEntry(Entry
);
776 if (SubEntry
!= NULL
) {
777 SubEntry
->me
.Title
= L
"Run ELILO in interactive mode";
778 SubEntry
->LoadOptions
= L
"-p";
779 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
780 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
783 SubEntry
= InitializeLoaderEntry(Entry
);
784 if (SubEntry
!= NULL
) {
785 SubEntry
->me
.Title
= L
"Boot Linux for a 17\" iMac or a 15\" MacBook Pro (*)";
786 SubEntry
->UseGraphicsMode
= TRUE
;
787 SubEntry
->LoadOptions
= L
"-d 0 i17";
788 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
789 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
792 SubEntry
= InitializeLoaderEntry(Entry
);
793 if (SubEntry
!= NULL
) {
794 SubEntry
->me
.Title
= L
"Boot Linux for a 20\" iMac (*)";
795 SubEntry
->UseGraphicsMode
= TRUE
;
796 SubEntry
->LoadOptions
= L
"-d 0 i20";
797 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
798 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
801 SubEntry
= InitializeLoaderEntry(Entry
);
802 if (SubEntry
!= NULL
) {
803 SubEntry
->me
.Title
= L
"Boot Linux for a Mac Mini (*)";
804 SubEntry
->UseGraphicsMode
= TRUE
;
805 SubEntry
->LoadOptions
= L
"-d 0 mini";
806 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
807 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
810 AddMenuInfoLine(SubScreen
, L
"NOTE: This is an example. Entries");
811 AddMenuInfoLine(SubScreen
, L
"marked with (*) may not work.");
813 } else if (Entry
->OSType
== 'X') { // entries for xom.efi
814 // by default, skip the built-in selection and boot from hard disk only
815 Entry
->LoadOptions
= L
"-s -h";
817 SubEntry
= InitializeLoaderEntry(Entry
);
818 if (SubEntry
!= NULL
) {
819 SubEntry
->me
.Title
= L
"Boot Windows from Hard Disk";
820 SubEntry
->LoadOptions
= L
"-s -h";
821 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
822 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
825 SubEntry
= InitializeLoaderEntry(Entry
);
826 if (SubEntry
!= NULL
) {
827 SubEntry
->me
.Title
= L
"Boot Windows from CD-ROM";
828 SubEntry
->LoadOptions
= L
"-s -c";
829 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
830 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
833 SubEntry
= InitializeLoaderEntry(Entry
);
834 if (SubEntry
!= NULL
) {
835 SubEntry
->me
.Title
= L
"Run XOM in text mode";
836 SubEntry
->UseGraphicsMode
= FALSE
;
837 SubEntry
->LoadOptions
= L
"-v";
838 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
839 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
841 } // entries for xom.efi
842 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
843 Entry
->me
.SubScreen
= SubScreen
;
844 } // VOID GenerateSubScreen()
846 // Returns options for a Linux kernel. Reads them from an options file in the
847 // kernel's directory; and if present, adds an initrd= option for an initial
848 // RAM disk file with the same version number as the kernel file.
849 static CHAR16
* GetMainLinuxOptions(IN CHAR16
* LoaderPath
, IN REFIT_VOLUME
*Volume
) {
850 CHAR16
*Options
= NULL
, *InitrdName
, *FullOptions
= NULL
;
852 Options
= GetFirstOptionsFromFile(LoaderPath
, Volume
);
853 InitrdName
= FindInitrd(LoaderPath
, Volume
);
854 FullOptions
= AddInitrdToOptions(Options
, InitrdName
);
857 MyFreePool(InitrdName
);
858 return (FullOptions
);
859 } // static CHAR16 * GetMainLinuxOptions()
861 // Try to guess the name of the Linux distribution & add that name to
863 static VOID
GuessLinuxDistribution(CHAR16
**OSIconName
, REFIT_VOLUME
*Volume
, CHAR16
*LoaderPath
) {
867 UINTN TokenCount
= 0;
869 // If on Linux root fs, /etc/os-release file probably has clues....
870 if (FileExists(Volume
->RootDir
, L
"etc\\os-release") &&
871 (ReadFile(Volume
->RootDir
, L
"etc\\os-release", &File
, &FileSize
) == EFI_SUCCESS
)) {
873 TokenCount
= ReadTokenLine(&File
, &TokenList
);
874 if ((TokenCount
> 1) && ((StriCmp(TokenList
[0], L
"ID") == 0) || (StriCmp(TokenList
[0], L
"NAME") == 0))) {
875 MergeStrings(OSIconName
, TokenList
[1], L
',');
877 FreeTokenLine(&TokenList
, &TokenCount
);
878 } while (TokenCount
> 0);
879 MyFreePool(File
.Buffer
);
882 // Search for clues in the kernel's filename....
883 if (StriSubCmp(L
".fc", LoaderPath
))
884 MergeStrings(OSIconName
, L
"fedora", L
',');
885 if (StriSubCmp(L
".el", LoaderPath
))
886 MergeStrings(OSIconName
, L
"redhat", L
',');
887 } // VOID GuessLinuxDistribution()
889 // Sets a few defaults for a loader entry -- mainly the icon, but also the OS type
890 // code and shortcut letter. For Linux EFI stub loaders, also sets kernel options
891 // that will (with luck) work fairly automatically.
892 VOID
SetLoaderDefaults(LOADER_ENTRY
*Entry
, CHAR16
*LoaderPath
, REFIT_VOLUME
*Volume
) {
893 CHAR16
*FileName
, *PathOnly
, *NoExtension
, *OSIconName
= NULL
, *Temp
, *SubString
;
894 CHAR16 ShortcutLetter
= 0;
897 FileName
= Basename(LoaderPath
);
898 PathOnly
= FindPath(LoaderPath
);
899 NoExtension
= StripEfiExtension(FileName
);
901 // locate a custom icon for the loader
902 // Anything found here takes precedence over the "hints" in the OSIconName variable
903 if (!Entry
->me
.Image
)
904 Entry
->me
.Image
= egLoadIconAnyType(Volume
->RootDir
, PathOnly
, NoExtension
, GlobalConfig
.IconSizes
[ICON_SIZE_BIG
]);
905 if (!Entry
->me
.Image
)
906 Entry
->me
.Image
= egCopyImage(Volume
->VolIconImage
);
908 // Begin creating icon "hints" by using last part of directory path leading
910 Temp
= FindLastDirName(LoaderPath
);
911 MergeStrings(&OSIconName
, Temp
, L
',');
914 if (OSIconName
!= NULL
) {
915 ShortcutLetter
= OSIconName
[0];
918 // Add every "word" in the volume label, delimited by spaces, dashes (-), or
919 // underscores (_), to the list of hints to be used in searching for OS
921 if ((Volume
->VolName
) && (StrLen(Volume
->VolName
) > 0)) {
922 Temp
= SubString
= StrDuplicate(Volume
->VolName
);
924 Length
= StrLen(Temp
);
925 for (i
= 0; i
< Length
; i
++) {
926 if ((Temp
[i
] == L
' ') || (Temp
[i
] == L
'_') || (Temp
[i
] == L
'-')) {
928 if (StrLen(SubString
) > 0)
929 MergeStrings(&OSIconName
, SubString
, L
',');
930 SubString
= Temp
+ i
+ 1;
933 MergeStrings(&OSIconName
, SubString
, L
',');
938 // detect specific loaders
939 if (StriSubCmp(L
"bzImage", FileName
) || StriSubCmp(L
"vmlinuz", FileName
)) {
940 GuessLinuxDistribution(&OSIconName
, Volume
, LoaderPath
);
941 MergeStrings(&OSIconName
, L
"linux", L
',');
943 if (ShortcutLetter
== 0)
944 ShortcutLetter
= 'L';
945 Entry
->LoadOptions
= GetMainLinuxOptions(LoaderPath
, Volume
);
946 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_LINUX
;
947 } else if (StriSubCmp(L
"refit", LoaderPath
)) {
948 MergeStrings(&OSIconName
, L
"refit", L
',');
950 ShortcutLetter
= 'R';
951 } else if (StriSubCmp(L
"refind", LoaderPath
)) {
952 MergeStrings(&OSIconName
, L
"refind", L
',');
954 ShortcutLetter
= 'R';
955 } else if (StriCmp(LoaderPath
, MACOSX_LOADER_PATH
) == 0) {
956 if (Volume
->VolIconImage
!= NULL
) { // custom icon file found
957 Entry
->me
.Image
= Volume
->VolIconImage
;
959 MergeStrings(&OSIconName
, L
"mac", L
',');
961 ShortcutLetter
= 'M';
962 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
963 } else if (StriCmp(FileName
, L
"diags.efi") == 0) {
964 MergeStrings(&OSIconName
, L
"hwtest", L
',');
965 } else if (StriCmp(FileName
, L
"e.efi") == 0 || StriCmp(FileName
, L
"elilo.efi") == 0 || StriSubCmp(L
"elilo", FileName
)) {
966 MergeStrings(&OSIconName
, L
"elilo,linux", L
',');
968 if (ShortcutLetter
== 0)
969 ShortcutLetter
= 'L';
970 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
971 } else if (StriSubCmp(L
"grub", FileName
)) {
973 ShortcutLetter
= 'G';
974 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_GRUB
;
975 } else if (StriCmp(FileName
, L
"cdboot.efi") == 0 ||
976 StriCmp(FileName
, L
"bootmgr.efi") == 0 ||
977 StriCmp(FileName
, L
"bootmgfw.efi") == 0 ||
978 StriCmp(FileName
, L
"bkpbootmgfw.efi") == 0) {
979 MergeStrings(&OSIconName
, L
"win", L
',');
981 ShortcutLetter
= 'W';
982 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
983 } else if (StriCmp(FileName
, L
"xom.efi") == 0) {
984 MergeStrings(&OSIconName
, L
"xom,win", L
',');
985 Entry
->UseGraphicsMode
= TRUE
;
987 ShortcutLetter
= 'W';
988 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
991 if ((ShortcutLetter
>= 'a') && (ShortcutLetter
<= 'z'))
992 ShortcutLetter
= ShortcutLetter
- 'a' + 'A'; // convert lowercase to uppercase
993 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
994 if (Entry
->me
.Image
== NULL
)
995 Entry
->me
.Image
= LoadOSIcon(OSIconName
, L
"unknown", FALSE
);
996 MyFreePool(PathOnly
);
997 } // VOID SetLoaderDefaults()
999 // Add a specified EFI boot loader to the list, using automatic settings
1000 // for icons, options, etc.
1001 LOADER_ENTRY
* AddLoaderEntry(IN CHAR16
*LoaderPath
, IN CHAR16
*LoaderTitle
, IN REFIT_VOLUME
*Volume
) {
1002 LOADER_ENTRY
*Entry
;
1004 CleanUpPathNameSlashes(LoaderPath
);
1005 Entry
= InitializeLoaderEntry(NULL
);
1006 if (Entry
!= NULL
) {
1007 Entry
->Title
= StrDuplicate((LoaderTitle
!= NULL
) ? LoaderTitle
: LoaderPath
);
1008 Entry
->me
.Title
= AllocateZeroPool(sizeof(CHAR16
) * 256);
1009 SPrint(Entry
->me
.Title
, 255, L
"Boot %s from %s ", (LoaderTitle
!= NULL
) ? LoaderTitle
: LoaderPath
, Volume
->VolName
);
1011 Entry
->me
.BadgeImage
= Volume
->VolBadgeImage
;
1012 if ((LoaderPath
!= NULL
) && (LoaderPath
[0] != L
'\\')) {
1013 Entry
->LoaderPath
= StrDuplicate(L
"\\");
1015 Entry
->LoaderPath
= NULL
;
1017 MergeStrings(&(Entry
->LoaderPath
), LoaderPath
, 0);
1018 Entry
->VolName
= Volume
->VolName
;
1019 Entry
->DevicePath
= FileDevicePath(Volume
->DeviceHandle
, Entry
->LoaderPath
);
1020 SetLoaderDefaults(Entry
, LoaderPath
, Volume
);
1021 GenerateSubScreen(Entry
, Volume
);
1022 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1026 } // LOADER_ENTRY * AddLoaderEntry()
1028 // Returns -1 if (Time1 < Time2), +1 if (Time1 > Time2), or 0 if
1029 // (Time1 == Time2). Precision is only to the nearest second; since
1030 // this is used for sorting boot loader entries, differences smaller
1031 // than this are likely to be meaningless (and unlikely!).
1032 INTN
TimeComp(IN EFI_TIME
*Time1
, IN EFI_TIME
*Time2
) {
1033 INT64 Time1InSeconds
, Time2InSeconds
;
1035 // Following values are overestimates; I'm assuming 31 days in every month.
1036 // This is fine for the purpose of this function, which is limited
1037 Time1InSeconds
= Time1
->Second
+ (Time1
->Minute
* 60) + (Time1
->Hour
* 3600) + (Time1
->Day
* 86400) +
1038 (Time1
->Month
* 2678400) + ((Time1
->Year
- 1998) * 32140800);
1039 Time2InSeconds
= Time2
->Second
+ (Time2
->Minute
* 60) + (Time2
->Hour
* 3600) + (Time2
->Day
* 86400) +
1040 (Time2
->Month
* 2678400) + ((Time2
->Year
- 1998) * 32140800);
1041 if (Time1InSeconds
< Time2InSeconds
)
1043 else if (Time1InSeconds
> Time2InSeconds
)
1047 } // INTN TimeComp()
1049 // Adds a loader list element, keeping it sorted by date. Returns the new
1050 // first element (the one with the most recent date).
1051 static struct LOADER_LIST
* AddLoaderListEntry(struct LOADER_LIST
*LoaderList
, struct LOADER_LIST
*NewEntry
) {
1052 struct LOADER_LIST
*LatestEntry
, *CurrentEntry
, *PrevEntry
= NULL
;
1054 LatestEntry
= CurrentEntry
= LoaderList
;
1055 if (LoaderList
== NULL
) {
1056 LatestEntry
= NewEntry
;
1058 while ((CurrentEntry
!= NULL
) && (TimeComp(&(NewEntry
->TimeStamp
), &(CurrentEntry
->TimeStamp
)) < 0)) {
1059 PrevEntry
= CurrentEntry
;
1060 CurrentEntry
= CurrentEntry
->NextEntry
;
1062 NewEntry
->NextEntry
= CurrentEntry
;
1063 if (PrevEntry
== NULL
) {
1064 LatestEntry
= NewEntry
;
1066 PrevEntry
->NextEntry
= NewEntry
;
1069 return (LatestEntry
);
1070 } // static VOID AddLoaderListEntry()
1072 // Delete the LOADER_LIST linked list
1073 static VOID
CleanUpLoaderList(struct LOADER_LIST
*LoaderList
) {
1074 struct LOADER_LIST
*Temp
;
1076 while (LoaderList
!= NULL
) {
1078 LoaderList
= LoaderList
->NextEntry
;
1079 MyFreePool(Temp
->FileName
);
1082 } // static VOID CleanUpLoaderList()
1084 // Returns FALSE if the specified file/volume matches the GlobalConfig.DontScanDirs
1085 // or GlobalConfig.DontScanVolumes specification, or if Path points to a volume
1086 // other than the one specified by Volume, or if the specified path is SelfDir.
1087 // Returns TRUE if none of these conditions is met -- that is, if the path is
1088 // eligible for scanning.
1089 static BOOLEAN
ShouldScan(REFIT_VOLUME
*Volume
, CHAR16
*Path
) {
1090 CHAR16
*VolName
= NULL
, *DontScanDir
, *PathCopy
= NULL
;
1092 BOOLEAN ScanIt
= TRUE
;
1094 if (IsIn(Volume
->VolName
, GlobalConfig
.DontScanVolumes
))
1097 if ((StriCmp(Path
, SelfDirPath
) == 0) && (Volume
->DeviceHandle
== SelfVolume
->DeviceHandle
))
1100 // See if Path includes an explicit volume declaration that's NOT Volume....
1101 PathCopy
= StrDuplicate(Path
);
1102 if (SplitVolumeAndFilename(&PathCopy
, &VolName
)) {
1103 VolumeNumberToName(Volume
, &VolName
);
1104 if (VolName
&& StriCmp(VolName
, Volume
->VolName
) != 0) {
1107 } // if Path includes volume specification
1108 MyFreePool(PathCopy
);
1109 MyFreePool(VolName
);
1112 // See if Volume is in GlobalConfig.DontScanDirs....
1113 while (ScanIt
&& (DontScanDir
= FindCommaDelimited(GlobalConfig
.DontScanDirs
, i
++))) {
1114 SplitVolumeAndFilename(&DontScanDir
, &VolName
);
1115 CleanUpPathNameSlashes(DontScanDir
);
1116 VolumeNumberToName(Volume
, &VolName
);
1117 if (VolName
!= NULL
) {
1118 if ((StriCmp(VolName
, Volume
->VolName
) == 0) && (StriCmp(DontScanDir
, Path
) == 0))
1121 if (StriCmp(DontScanDir
, Path
) == 0)
1124 MyFreePool(DontScanDir
);
1125 MyFreePool(VolName
);
1131 } // BOOLEAN ShouldScan()
1133 // Returns TRUE if the file is byte-for-byte identical with the fallback file
1134 // on the volume AND if the file is not itself the fallback file; returns
1135 // FALSE if the file is not identical to the fallback file OR if the file
1136 // IS the fallback file. Intended for use in excluding the fallback boot
1137 // loader when it's a duplicate of another boot loader.
1138 static BOOLEAN
DuplicatesFallback(IN REFIT_VOLUME
*Volume
, IN CHAR16
*FileName
) {
1139 CHAR8
*FileContents
, *FallbackContents
;
1140 EFI_FILE_HANDLE FileHandle
, FallbackHandle
;
1141 EFI_FILE_INFO
*FileInfo
, *FallbackInfo
;
1142 UINTN FileSize
= 0, FallbackSize
= 0;
1144 BOOLEAN AreIdentical
= FALSE
;
1146 if (!FileExists(Volume
->RootDir
, FileName
) || !FileExists(Volume
->RootDir
, FALLBACK_FULLNAME
))
1149 CleanUpPathNameSlashes(FileName
);
1151 if (StriCmp(FileName
, FALLBACK_FULLNAME
) == 0)
1152 return FALSE
; // identical filenames, so not a duplicate....
1154 Status
= refit_call5_wrapper(Volume
->RootDir
->Open
, Volume
->RootDir
, &FileHandle
, FileName
, EFI_FILE_MODE_READ
, 0);
1155 if (Status
== EFI_SUCCESS
) {
1156 FileInfo
= LibFileInfo(FileHandle
);
1157 FileSize
= FileInfo
->FileSize
;
1162 Status
= refit_call5_wrapper(Volume
->RootDir
->Open
, Volume
->RootDir
, &FallbackHandle
, FALLBACK_FULLNAME
, EFI_FILE_MODE_READ
, 0);
1163 if (Status
== EFI_SUCCESS
) {
1164 FallbackInfo
= LibFileInfo(FallbackHandle
);
1165 FallbackSize
= FallbackInfo
->FileSize
;
1167 refit_call1_wrapper(FileHandle
->Close
, FileHandle
);
1171 if (FallbackSize
!= FileSize
) { // not same size, so can't be identical
1172 AreIdentical
= FALSE
;
1173 } else { // could be identical; do full check....
1174 FileContents
= AllocatePool(FileSize
);
1175 FallbackContents
= AllocatePool(FallbackSize
);
1176 if (FileContents
&& FallbackContents
) {
1177 Status
= refit_call3_wrapper(FileHandle
->Read
, FileHandle
, &FileSize
, FileContents
);
1178 if (Status
== EFI_SUCCESS
) {
1179 Status
= refit_call3_wrapper(FallbackHandle
->Read
, FallbackHandle
, &FallbackSize
, FallbackContents
);
1181 if (Status
== EFI_SUCCESS
) {
1182 AreIdentical
= (CompareMem(FileContents
, FallbackContents
, FileSize
) == 0);
1185 MyFreePool(FileContents
);
1186 MyFreePool(FallbackContents
);
1189 // BUG ALERT: Some systems (e.g., DUET, some Macs with large displays) crash if the
1190 // following two calls are reversed. Go figure....
1191 refit_call1_wrapper(FileHandle
->Close
, FallbackHandle
);
1192 refit_call1_wrapper(FileHandle
->Close
, FileHandle
);
1193 return AreIdentical
;
1194 } // BOOLEAN DuplicatesFallback()
1196 // Returns FALSE if two measures of file size are identical for a single file,
1197 // TRUE if not or if the file can't be opened and the other measure is non-0.
1198 // Despite the function's name, this isn't really a direct test of symbolic
1199 // link status, since EFI doesn't officially support symlinks. It does seem
1200 // to be a reliable indicator, though. (OTOH, some disk errors might cause a
1201 // file to fail to open, which would return a false positive -- but as I use
1202 // this function to exclude symbolic links from the list of boot loaders,
1203 // that would be fine, since such boot loaders wouldn't work.)
1204 static BOOLEAN
IsSymbolicLink(REFIT_VOLUME
*Volume
, CHAR16
*Path
, EFI_FILE_INFO
*DirEntry
) {
1205 EFI_FILE_HANDLE FileHandle
;
1206 EFI_FILE_INFO
*FileInfo
= NULL
;
1208 UINTN FileSize2
= 0;
1211 FileName
= StrDuplicate(Path
);
1212 MergeStrings(&FileName
, DirEntry
->FileName
, L
'\\');
1213 CleanUpPathNameSlashes(FileName
);
1215 Status
= refit_call5_wrapper(Volume
->RootDir
->Open
, Volume
->RootDir
, &FileHandle
, FileName
, EFI_FILE_MODE_READ
, 0);
1216 if (Status
== EFI_SUCCESS
) {
1217 FileInfo
= LibFileInfo(FileHandle
);
1218 if (FileInfo
!= NULL
)
1219 FileSize2
= FileInfo
->FileSize
;
1222 MyFreePool(FileName
);
1223 MyFreePool(FileInfo
);
1225 return (DirEntry
->FileSize
!= FileSize2
);
1226 } // BOOLEAN IsSymbolicLink()
1228 // Returns TRUE if a file with the same name as the original but with
1229 // ".efi.signed" is also present in the same directory. Ubuntu is using
1230 // this filename as a signed version of the original unsigned kernel, and
1231 // there's no point in cluttering the display with two kernels that will
1232 // behave identically on non-SB systems, or when one will fail when SB
1234 static BOOLEAN
HasSignedCounterpart(IN REFIT_VOLUME
*Volume
, IN CHAR16
*Path
, IN CHAR16
*Filename
) {
1235 CHAR16
*NewFile
= NULL
;
1236 BOOLEAN retval
= FALSE
;
1238 MergeStrings(&NewFile
, Path
, 0);
1239 MergeStrings(&NewFile
, Filename
, L
'\\');
1240 MergeStrings(&NewFile
, L
".efi.signed", 0);
1241 if (FileExists(Volume
->RootDir
, NewFile
))
1243 MyFreePool(NewFile
);
1246 } // BOOLEAN HasSignedCounterpart()
1248 // Scan an individual directory for EFI boot loader files and, if found,
1249 // add them to the list. Exception: Ignores FALLBACK_FULLNAME, which is picked
1250 // up in ScanEfiFiles(). Sorts the entries within the loader directory so that
1251 // the most recent one appears first in the list.
1252 // Returns TRUE if a duplicate for FALLBACK_FILENAME was found, FALSE if not.
1253 static BOOLEAN
ScanLoaderDir(IN REFIT_VOLUME
*Volume
, IN CHAR16
*Path
, IN CHAR16
*Pattern
)
1256 REFIT_DIR_ITER DirIter
;
1257 EFI_FILE_INFO
*DirEntry
;
1258 CHAR16 FileName
[256], *Extension
;
1259 struct LOADER_LIST
*LoaderList
= NULL
, *NewLoader
;
1260 BOOLEAN FoundFallbackDuplicate
= FALSE
;
1262 if ((!SelfDirPath
|| !Path
|| ((StriCmp(Path
, SelfDirPath
) == 0) && (Volume
->DeviceHandle
!= SelfVolume
->DeviceHandle
)) ||
1263 (StriCmp(Path
, SelfDirPath
) != 0)) &&
1264 (ShouldScan(Volume
, Path
))) {
1265 // look through contents of the directory
1266 DirIterOpen(Volume
->RootDir
, Path
, &DirIter
);
1267 while (DirIterNext(&DirIter
, 2, Pattern
, &DirEntry
)) {
1268 Extension
= FindExtension(DirEntry
->FileName
);
1269 if (DirEntry
->FileName
[0] == '.' ||
1270 StriCmp(Extension
, L
".icns") == 0 ||
1271 StriCmp(Extension
, L
".png") == 0 ||
1272 (StriCmp(DirEntry
->FileName
, FALLBACK_BASENAME
) == 0 && (StriCmp(Path
, L
"EFI\\BOOT") == 0)) ||
1273 StriSubCmp(L
"shell", DirEntry
->FileName
) ||
1274 IsSymbolicLink(Volume
, Path
, DirEntry
) || /* is symbolic link */
1275 HasSignedCounterpart(Volume
, Path
, DirEntry
->FileName
) || /* a file with same name plus ".efi.signed" is present */
1276 FilenameIn(Volume
, Path
, DirEntry
->FileName
, GlobalConfig
.DontScanFiles
))
1277 continue; // skip this
1280 SPrint(FileName
, 255, L
"\\%s\\%s", Path
, DirEntry
->FileName
);
1282 SPrint(FileName
, 255, L
"\\%s", DirEntry
->FileName
);
1283 CleanUpPathNameSlashes(FileName
);
1285 if(!IsValidLoader(Volume
->RootDir
, FileName
))
1288 NewLoader
= AllocateZeroPool(sizeof(struct LOADER_LIST
));
1289 if (NewLoader
!= NULL
) {
1290 NewLoader
->FileName
= StrDuplicate(FileName
);
1291 NewLoader
->TimeStamp
= DirEntry
->ModificationTime
;
1292 LoaderList
= AddLoaderListEntry(LoaderList
, NewLoader
);
1293 if (DuplicatesFallback(Volume
, FileName
))
1294 FoundFallbackDuplicate
= TRUE
;
1296 MyFreePool(Extension
);
1299 NewLoader
= LoaderList
;
1300 while (NewLoader
!= NULL
) {
1301 AddLoaderEntry(NewLoader
->FileName
, NULL
, Volume
);
1302 NewLoader
= NewLoader
->NextEntry
;
1305 CleanUpLoaderList(LoaderList
);
1306 Status
= DirIterClose(&DirIter
);
1307 // NOTE: EFI_INVALID_PARAMETER really is an error that should be reported;
1308 // but I've gotten reports from users who are getting this error occasionally
1309 // and I can't find anything wrong or reproduce the problem, so I'm putting
1310 // it down to buggy EFI implementations and ignoring that particular error....
1311 if ((Status
!= EFI_NOT_FOUND
) && (Status
!= EFI_INVALID_PARAMETER
)) {
1313 SPrint(FileName
, 255, L
"while scanning the %s directory", Path
);
1315 StrCpy(FileName
, L
"while scanning the root directory");
1316 CheckError(Status
, FileName
);
1317 } // if (Status != EFI_NOT_FOUND)
1318 } // if not scanning a blacklisted directory
1320 return FoundFallbackDuplicate
;
1321 } /* static VOID ScanLoaderDir() */
1323 static VOID
ScanEfiFiles(REFIT_VOLUME
*Volume
) {
1325 REFIT_DIR_ITER EfiDirIter
;
1326 EFI_FILE_INFO
*EfiDirEntry
;
1327 CHAR16 FileName
[256], *Directory
= NULL
, *MatchPatterns
, *VolName
= NULL
, *SelfPath
;
1329 BOOLEAN ScanFallbackLoader
= TRUE
;
1330 BOOLEAN FoundBRBackup
= FALSE
;
1332 MatchPatterns
= StrDuplicate(LOADER_MATCH_PATTERNS
);
1333 if (GlobalConfig
.ScanAllLinux
)
1334 MergeStrings(&MatchPatterns
, LINUX_MATCH_PATTERNS
, L
',');
1336 if ((Volume
->RootDir
!= NULL
) && (Volume
->VolName
!= NULL
) && (Volume
->IsReadable
)) {
1337 // check for Mac OS X boot loader
1338 if (ShouldScan(Volume
, L
"System\\Library\\CoreServices")) {
1339 StrCpy(FileName
, MACOSX_LOADER_PATH
);
1340 if (FileExists(Volume
->RootDir
, FileName
) && !FilenameIn(Volume
, Directory
, L
"boot.efi", GlobalConfig
.DontScanFiles
)) {
1341 AddLoaderEntry(FileName
, L
"Mac OS X", Volume
);
1342 if (DuplicatesFallback(Volume
, FileName
))
1343 ScanFallbackLoader
= FALSE
;
1347 StrCpy(FileName
, L
"System\\Library\\CoreServices\\xom.efi");
1348 if (FileExists(Volume
->RootDir
, FileName
) && !FilenameIn(Volume
, Directory
, L
"boot.efi", GlobalConfig
.DontScanFiles
)) {
1349 AddLoaderEntry(FileName
, L
"Windows XP (XoM)", Volume
);
1350 if (DuplicatesFallback(Volume
, FileName
))
1351 ScanFallbackLoader
= FALSE
;
1353 } // if should scan Mac directory
1355 // check for Microsoft boot loader/menu
1356 if (ShouldScan(Volume
, L
"EFI\\Microsoft\\Boot")) {
1357 StrCpy(FileName
, L
"EFI\\Microsoft\\Boot\\bkpbootmgfw.efi");
1358 if (FileExists(Volume
->RootDir
, FileName
) && !FilenameIn(Volume
, Directory
, L
"bkpbootmgfw.efi",
1359 GlobalConfig
.DontScanFiles
)) {
1360 AddLoaderEntry(FileName
, L
"Microsoft EFI boot (Boot Repair backup)", Volume
);
1361 FoundBRBackup
= TRUE
;
1362 if (DuplicatesFallback(Volume
, FileName
))
1363 ScanFallbackLoader
= FALSE
;
1365 StrCpy(FileName
, L
"EFI\\Microsoft\\Boot\\bootmgfw.efi");
1366 if (FileExists(Volume
->RootDir
, FileName
) && !FilenameIn(Volume
, Directory
, L
"bootmgfw.efi", GlobalConfig
.DontScanFiles
)) {
1368 AddLoaderEntry(FileName
, L
"Supposed Microsoft EFI boot (probably GRUB)", Volume
);
1370 AddLoaderEntry(FileName
, L
"Microsoft EFI boot", Volume
);
1371 if (DuplicatesFallback(Volume
, FileName
))
1372 ScanFallbackLoader
= FALSE
;
1376 // scan the root directory for EFI executables
1377 if (ScanLoaderDir(Volume
, L
"\\", MatchPatterns
))
1378 ScanFallbackLoader
= FALSE
;
1380 // scan subdirectories of the EFI directory (as per the standard)
1381 DirIterOpen(Volume
->RootDir
, L
"EFI", &EfiDirIter
);
1382 while (DirIterNext(&EfiDirIter
, 1, NULL
, &EfiDirEntry
)) {
1383 if (StriCmp(EfiDirEntry
->FileName
, L
"tools") == 0 || EfiDirEntry
->FileName
[0] == '.')
1384 continue; // skip this, doesn't contain boot loaders or is scanned later
1385 SPrint(FileName
, 255, L
"EFI\\%s", EfiDirEntry
->FileName
);
1386 if (ScanLoaderDir(Volume
, FileName
, MatchPatterns
))
1387 ScanFallbackLoader
= FALSE
;
1389 Status
= DirIterClose(&EfiDirIter
);
1390 if (Status
!= EFI_NOT_FOUND
)
1391 CheckError(Status
, L
"while scanning the EFI directory");
1393 // Scan user-specified (or additional default) directories....
1395 while ((Directory
= FindCommaDelimited(GlobalConfig
.AlsoScan
, i
++)) != NULL
) {
1396 if (ShouldScan(Volume
, Directory
)) {
1397 SplitVolumeAndFilename(&Directory
, &VolName
);
1398 CleanUpPathNameSlashes(Directory
);
1399 Length
= StrLen(Directory
);
1400 if ((Length
> 0) && ScanLoaderDir(Volume
, Directory
, MatchPatterns
))
1401 ScanFallbackLoader
= FALSE
;
1402 MyFreePool(VolName
);
1403 } // if should scan dir
1404 MyFreePool(Directory
);
1407 // Don't scan the fallback loader if it's on the same volume and a duplicate of rEFInd itself....
1408 SelfPath
= DevicePathToStr(SelfLoadedImage
->FilePath
);
1409 CleanUpPathNameSlashes(SelfPath
);
1410 if ((Volume
->DeviceHandle
== SelfLoadedImage
->DeviceHandle
) && DuplicatesFallback(Volume
, SelfPath
))
1411 ScanFallbackLoader
= FALSE
;
1413 // If not a duplicate & if it exists & if it's not us, create an entry
1414 // for the fallback boot loader
1415 if (ScanFallbackLoader
&& FileExists(Volume
->RootDir
, FALLBACK_FULLNAME
) && ShouldScan(Volume
, L
"EFI\\BOOT"))
1416 AddLoaderEntry(FALLBACK_FULLNAME
, L
"Fallback boot loader", Volume
);
1418 } // static VOID ScanEfiFiles()
1420 // Scan internal disks for valid EFI boot loaders....
1421 static VOID
ScanInternal(VOID
) {
1424 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1425 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_INTERNAL
) {
1426 ScanEfiFiles(Volumes
[VolumeIndex
]);
1429 } // static VOID ScanInternal()
1431 // Scan external disks for valid EFI boot loaders....
1432 static VOID
ScanExternal(VOID
) {
1435 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1436 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_EXTERNAL
) {
1437 ScanEfiFiles(Volumes
[VolumeIndex
]);
1440 } // static VOID ScanExternal()
1442 // Scan internal disks for valid EFI boot loaders....
1443 static VOID
ScanOptical(VOID
) {
1446 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1447 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_OPTICAL
) {
1448 ScanEfiFiles(Volumes
[VolumeIndex
]);
1451 } // static VOID ScanOptical()
1454 // legacy boot functions
1457 static EFI_STATUS
ActivateMbrPartition(IN EFI_BLOCK_IO
*BlockIO
, IN UINTN PartitionIndex
)
1460 UINT8 SectorBuffer
[512];
1461 MBR_PARTITION_INFO
*MbrTable
, *EMbrTable
;
1462 UINT32 ExtBase
, ExtCurrent
, NextExtCurrent
;
1463 UINTN LogicalPartitionIndex
= 4;
1465 BOOLEAN HaveBootCode
;
1468 Status
= refit_call5_wrapper(BlockIO
->ReadBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, 0, 512, SectorBuffer
);
1469 if (EFI_ERROR(Status
))
1471 if (*((UINT16
*)(SectorBuffer
+ 510)) != 0xaa55)
1472 return EFI_NOT_FOUND
; // safety measure #1
1474 // add boot code if necessary
1475 HaveBootCode
= FALSE
;
1476 for (i
= 0; i
< MBR_BOOTCODE_SIZE
; i
++) {
1477 if (SectorBuffer
[i
] != 0) {
1478 HaveBootCode
= TRUE
;
1482 if (!HaveBootCode
) {
1483 // no boot code found in the MBR, add the syslinux MBR code
1484 SetMem(SectorBuffer
, MBR_BOOTCODE_SIZE
, 0);
1485 CopyMem(SectorBuffer
, syslinux_mbr
, SYSLINUX_MBR_SIZE
);
1488 // set the partition active
1489 MbrTable
= (MBR_PARTITION_INFO
*)(SectorBuffer
+ 446);
1491 for (i
= 0; i
< 4; i
++) {
1492 if (MbrTable
[i
].Flags
!= 0x00 && MbrTable
[i
].Flags
!= 0x80)
1493 return EFI_NOT_FOUND
; // safety measure #2
1494 if (i
== PartitionIndex
)
1495 MbrTable
[i
].Flags
= 0x80;
1496 else if (PartitionIndex
>= 4 && IS_EXTENDED_PART_TYPE(MbrTable
[i
].Type
)) {
1497 MbrTable
[i
].Flags
= 0x80;
1498 ExtBase
= MbrTable
[i
].StartLBA
;
1500 MbrTable
[i
].Flags
= 0x00;
1504 Status
= refit_call5_wrapper(BlockIO
->WriteBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, 0, 512, SectorBuffer
);
1505 if (EFI_ERROR(Status
))
1508 if (PartitionIndex
>= 4) {
1509 // we have to activate a logical partition, so walk the EMBR chain
1511 // NOTE: ExtBase was set above while looking at the MBR table
1512 for (ExtCurrent
= ExtBase
; ExtCurrent
; ExtCurrent
= NextExtCurrent
) {
1513 // read current EMBR
1514 Status
= refit_call5_wrapper(BlockIO
->ReadBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, ExtCurrent
, 512, SectorBuffer
);
1515 if (EFI_ERROR(Status
))
1517 if (*((UINT16
*)(SectorBuffer
+ 510)) != 0xaa55)
1518 return EFI_NOT_FOUND
; // safety measure #3
1520 // scan EMBR, set appropriate partition active
1521 EMbrTable
= (MBR_PARTITION_INFO
*)(SectorBuffer
+ 446);
1523 for (i
= 0; i
< 4; i
++) {
1524 if (EMbrTable
[i
].Flags
!= 0x00 && EMbrTable
[i
].Flags
!= 0x80)
1525 return EFI_NOT_FOUND
; // safety measure #4
1526 if (EMbrTable
[i
].StartLBA
== 0 || EMbrTable
[i
].Size
== 0)
1528 if (IS_EXTENDED_PART_TYPE(EMbrTable
[i
].Type
)) {
1529 // link to next EMBR
1530 NextExtCurrent
= ExtBase
+ EMbrTable
[i
].StartLBA
;
1531 EMbrTable
[i
].Flags
= (PartitionIndex
>= LogicalPartitionIndex
) ? 0x80 : 0x00;
1534 // logical partition
1535 EMbrTable
[i
].Flags
= (PartitionIndex
== LogicalPartitionIndex
) ? 0x80 : 0x00;
1536 LogicalPartitionIndex
++;
1540 // write current EMBR
1541 Status
= refit_call5_wrapper(BlockIO
->WriteBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, ExtCurrent
, 512, SectorBuffer
);
1542 if (EFI_ERROR(Status
))
1545 if (PartitionIndex
< LogicalPartitionIndex
)
1546 break; // stop the loop, no need to touch further EMBRs
1552 } /* static EFI_STATUS ActivateMbrPartition() */
1554 // early 2006 Core Duo / Core Solo models
1555 static UINT8 LegacyLoaderDevicePath1Data
[] = {
1556 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1557 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1558 0xFF, 0xFF, 0xF9, 0xFF, 0x00, 0x00, 0x00, 0x00,
1559 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1560 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1561 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1563 // mid-2006 Mac Pro (and probably other Core 2 models)
1564 static UINT8 LegacyLoaderDevicePath2Data
[] = {
1565 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1566 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1567 0xFF, 0xFF, 0xF7, 0xFF, 0x00, 0x00, 0x00, 0x00,
1568 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1569 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1570 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1572 // mid-2007 MBP ("Santa Rosa" based models)
1573 static UINT8 LegacyLoaderDevicePath3Data
[] = {
1574 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1575 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1576 0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
1577 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1578 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1579 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1582 static UINT8 LegacyLoaderDevicePath4Data
[] = {
1583 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1584 0x00, 0x00, 0xC0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1585 0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
1586 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1587 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1588 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1590 // late-2008 MB/MBP (NVidia chipset)
1591 static UINT8 LegacyLoaderDevicePath5Data
[] = {
1592 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1593 0x00, 0x40, 0xCB, 0xFF, 0x00, 0x00, 0x00, 0x00,
1594 0xFF, 0xBF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
1595 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1596 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1597 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1600 static EFI_DEVICE_PATH
*LegacyLoaderList
[] = {
1601 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath1Data
,
1602 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath2Data
,
1603 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath3Data
,
1604 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath4Data
,
1605 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath5Data
,
1609 #define MAX_DISCOVERED_PATHS (16)
1611 static VOID
StartLegacy(IN LEGACY_ENTRY
*Entry
)
1614 EG_IMAGE
*BootLogoImage
;
1615 UINTN ErrorInStep
= 0;
1616 EFI_DEVICE_PATH
*DiscoveredPathList
[MAX_DISCOVERED_PATHS
];
1618 BeginExternalScreen(TRUE
, L
"Booting Legacy OS (Mac mode)");
1620 BootLogoImage
= LoadOSIcon(Entry
->Volume
->OSIconName
, L
"legacy", TRUE
);
1621 if (BootLogoImage
!= NULL
)
1622 BltImageAlpha(BootLogoImage
,
1623 (UGAWidth
- BootLogoImage
->Width
) >> 1,
1624 (UGAHeight
- BootLogoImage
->Height
) >> 1,
1625 &StdBackgroundPixel
);
1627 if (Entry
->Volume
->IsMbrPartition
) {
1628 ActivateMbrPartition(Entry
->Volume
->WholeDiskBlockIO
, Entry
->Volume
->MbrPartitionIndex
);
1631 ExtractLegacyLoaderPaths(DiscoveredPathList
, MAX_DISCOVERED_PATHS
, LegacyLoaderList
);
1633 Status
= StartEFIImageList(DiscoveredPathList
, Entry
->LoadOptions
, TYPE_LEGACY
, L
"legacy loader", 0, &ErrorInStep
, TRUE
);
1634 if (Status
== EFI_NOT_FOUND
) {
1635 if (ErrorInStep
== 1) {
1636 Print(L
"\nPlease make sure that you have the latest firmware update installed.\n");
1637 } else if (ErrorInStep
== 3) {
1638 Print(L
"\nThe firmware refused to boot from the selected volume. Note that external\n"
1639 L
"hard drives are not well-supported by Apple's firmware for legacy OS booting.\n");
1642 FinishExternalScreen();
1643 } /* static VOID StartLegacy() */
1645 // Start a device on a non-Mac using the EFI_LEGACY_BIOS_PROTOCOL
1646 #ifdef __MAKEWITH_TIANO
1647 static VOID
StartLegacyUEFI(LEGACY_ENTRY
*Entry
)
1649 // UINTN ExitDataSize = 0;
1650 // CHAR16 *ExitData = NULL;
1651 // EFI_STATUS Status;
1653 BeginExternalScreen(TRUE
, L
"Booting Legacy OS (UEFI mode)");
1654 // Print(L"Launching from '%s'\n", DevicePathToStr(Entry->BdsOption->DevicePath));
1657 // Status = BdsLibBootViaBootOption(Entry->BdsOption, Entry->BdsOption->DevicePath, &ExitDataSize, &ExitData);
1658 // Print(L"BdsLibBootViaBootOption() returned %d\n", Status);
1659 BdsLibConnectDevicePath (Entry
->BdsOption
->DevicePath
);
1660 BdsLibDoLegacyBoot(Entry
->BdsOption
);
1662 // If we get here, it means that there was a failure....
1663 Print(L
"Failure booting legacy (BIOS) OS.");
1665 FinishExternalScreen();
1666 } // static VOID StartLegacyUEFI()
1667 #endif // __MAKEWITH_TIANO
1669 static LEGACY_ENTRY
* AddLegacyEntry(IN CHAR16
*LoaderTitle
, IN REFIT_VOLUME
*Volume
)
1671 LEGACY_ENTRY
*Entry
, *SubEntry
;
1672 REFIT_MENU_SCREEN
*SubScreen
;
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 // prepare the menu entry
1690 Entry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1691 Entry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1692 SPrint(Entry
->me
.Title
, 255, L
"Boot %s from %s", LoaderTitle
, VolDesc
);
1693 Entry
->me
.Tag
= TAG_LEGACY
;
1695 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1696 Entry
->me
.Image
= LoadOSIcon(Volume
->OSIconName
, L
"legacy", FALSE
);
1697 Entry
->me
.BadgeImage
= Volume
->VolBadgeImage
;
1698 Entry
->Volume
= Volume
;
1699 Entry
->LoadOptions
= (Volume
->DiskKind
== DISK_KIND_OPTICAL
) ? L
"CD" :
1700 ((Volume
->DiskKind
== DISK_KIND_EXTERNAL
) ? L
"USB" : L
"HD");
1701 Entry
->Enabled
= TRUE
;
1703 // create the submenu
1704 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
1705 SubScreen
->Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1706 SPrint(SubScreen
->Title
, 255, L
"Boot Options for %s on %s", LoaderTitle
, VolDesc
);
1707 SubScreen
->TitleImage
= Entry
->me
.Image
;
1708 SubScreen
->Hint1
= StrDuplicate(SUBSCREEN_HINT1
);
1709 if (GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_EDITOR
) {
1710 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2_NO_EDITOR
);
1712 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2
);
1716 SubEntry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1717 SubEntry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1718 SPrint(SubEntry
->me
.Title
, 255, L
"Boot %s", LoaderTitle
);
1719 SubEntry
->me
.Tag
= TAG_LEGACY
;
1720 SubEntry
->Volume
= Entry
->Volume
;
1721 SubEntry
->LoadOptions
= Entry
->LoadOptions
;
1722 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
1724 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
1725 Entry
->me
.SubScreen
= SubScreen
;
1726 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1728 } /* static LEGACY_ENTRY * AddLegacyEntry() */
1731 #ifdef __MAKEWITH_GNUEFI
1732 static VOID
ScanLegacyUEFI(IN UINTN DiskType
){}
1734 // default volume badge icon based on disk kind
1735 static EG_IMAGE
* GetDiskBadge(IN UINTN DiskType
) {
1736 EG_IMAGE
* Badge
= NULL
;
1740 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_INTERNAL
);
1743 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_EXTERNAL
);
1746 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_OPTICAL
);
1750 } // static EG_IMAGE * GetDiskBadge()
1753 Create a rEFInd boot option from a Legacy BIOS protocol option.
1755 static LEGACY_ENTRY
* AddLegacyEntryUEFI(BDS_COMMON_OPTION
*BdsOption
, IN UINT16 DiskType
)
1757 LEGACY_ENTRY
*Entry
, *SubEntry
;
1758 REFIT_MENU_SCREEN
*SubScreen
;
1759 CHAR16 ShortcutLetter
= 0;
1760 CHAR16
*LegacyDescription
= BdsOption
->Description
;
1762 // prepare the menu entry
1763 Entry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1764 Entry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1765 SPrint(Entry
->me
.Title
, 255, L
"Boot legacy target %s", LegacyDescription
);
1766 Entry
->me
.Tag
= TAG_LEGACY_UEFI
;
1768 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1769 Entry
->me
.Image
= LoadOSIcon(L
"legacy", L
"legacy", TRUE
);
1770 Entry
->LoadOptions
= (DiskType
== BBS_CDROM
) ? L
"CD" :
1771 ((DiskType
== BBS_USB
) ? L
"USB" : L
"HD");
1772 Entry
->me
.BadgeImage
= GetDiskBadge(DiskType
);
1773 Entry
->BdsOption
= BdsOption
;
1774 Entry
->Enabled
= TRUE
;
1776 // create the submenu
1777 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
1778 SubScreen
->Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1779 SPrint(SubScreen
->Title
, 255, L
"No boot options for legacy target");
1780 SubScreen
->TitleImage
= Entry
->me
.Image
;
1781 SubScreen
->Hint1
= StrDuplicate(SUBSCREEN_HINT1
);
1782 if (GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_EDITOR
) {
1783 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2_NO_EDITOR
);
1785 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2
);
1789 SubEntry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1790 SubEntry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1791 SPrint(SubEntry
->me
.Title
, 255, L
"Boot %s", LegacyDescription
);
1792 SubEntry
->me
.Tag
= TAG_LEGACY_UEFI
;
1793 Entry
->BdsOption
= BdsOption
;
1794 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
1796 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
1797 Entry
->me
.SubScreen
= SubScreen
;
1798 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1800 } /* static LEGACY_ENTRY * AddLegacyEntryUEFI() */
1803 Scan for legacy BIOS targets on machines that implement EFI_LEGACY_BIOS_PROTOCOL.
1804 In testing, protocol has not been implemented on Macs but has been
1805 implemented on several Dell PCs and an ASUS motherboard.
1806 Restricts output to disks of the specified DiskType.
1808 static VOID
ScanLegacyUEFI(IN UINTN DiskType
)
1811 EFI_LEGACY_BIOS_PROTOCOL
*LegacyBios
;
1812 UINT16
*BootOrder
= NULL
;
1814 CHAR16 BootOption
[10];
1815 UINTN BootOrderSize
= 0;
1817 BDS_COMMON_OPTION
*BdsOption
;
1818 LIST_ENTRY TempList
;
1819 BBS_BBS_DEVICE_PATH
* BbsDevicePath
= NULL
;
1821 InitializeListHead (&TempList
);
1822 ZeroMem (Buffer
, sizeof (Buffer
));
1824 // If LegacyBios protocol is not implemented on this platform, then
1825 //we do not support this type of legacy boot on this machine.
1826 Status
= gBS
->LocateProtocol(&gEfiLegacyBootProtocolGuid
, NULL
, (VOID
**) &LegacyBios
);
1827 if (EFI_ERROR (Status
))
1830 // Grab the boot order
1831 BootOrder
= BdsLibGetVariableAndSize(L
"BootOrder", &gEfiGlobalVariableGuid
, &BootOrderSize
);
1832 if (BootOrder
== NULL
) {
1837 while (Index
< BootOrderSize
/ sizeof (UINT16
))
1839 // Grab each boot option variable from the boot order, and convert
1840 // the variable into a BDS boot option
1841 UnicodeSPrint (BootOption
, sizeof (BootOption
), L
"Boot%04x", BootOrder
[Index
]);
1842 BdsOption
= BdsLibVariableToOption (&TempList
, BootOption
);
1844 if (BdsOption
!= NULL
) {
1845 BbsDevicePath
= (BBS_BBS_DEVICE_PATH
*)BdsOption
->DevicePath
;
1847 // Only add the entry if it is of a requested type (e.g. USB, HD)
1849 // Two checks necessary because some systems return EFI boot loaders
1850 // with a DeviceType value that would inappropriately include them
1851 // as legacy loaders....
1852 if ((BbsDevicePath
->DeviceType
== DiskType
) && (BdsOption
->DevicePath
->Type
== DEVICE_TYPE_BIOS
)) {
1853 AddLegacyEntryUEFI(BdsOption
, BbsDevicePath
->DeviceType
);
1858 } /* static VOID ScanLegacyUEFI() */
1859 #endif // __MAKEWITH_GNUEFI
1861 static VOID
ScanLegacyVolume(REFIT_VOLUME
*Volume
, UINTN VolumeIndex
) {
1863 BOOLEAN ShowVolume
, HideIfOthersFound
;
1866 HideIfOthersFound
= FALSE
;
1867 if (Volume
->IsAppleLegacy
) {
1869 HideIfOthersFound
= TRUE
;
1870 } else if (Volume
->HasBootCode
) {
1872 if (Volume
->BlockIO
== Volume
->WholeDiskBlockIO
&&
1873 Volume
->BlockIOOffset
== 0 &&
1874 Volume
->OSName
== NULL
)
1875 // this is a whole disk (MBR) entry; hide if we have entries for partitions
1876 HideIfOthersFound
= TRUE
;
1878 if (HideIfOthersFound
) {
1879 // check for other bootable entries on the same disk
1880 for (VolumeIndex2
= 0; VolumeIndex2
< VolumesCount
; VolumeIndex2
++) {
1881 if (VolumeIndex2
!= VolumeIndex
&& Volumes
[VolumeIndex2
]->HasBootCode
&&
1882 Volumes
[VolumeIndex2
]->WholeDiskBlockIO
== Volume
->WholeDiskBlockIO
)
1888 AddLegacyEntry(NULL
, Volume
);
1889 } // static VOID ScanLegacyVolume()
1891 // Scan attached optical discs for legacy (BIOS) boot code
1892 // and add anything found to the list....
1893 static VOID
ScanLegacyDisc(VOID
)
1896 REFIT_VOLUME
*Volume
;
1898 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
) {
1899 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1900 Volume
= Volumes
[VolumeIndex
];
1901 if (Volume
->DiskKind
== DISK_KIND_OPTICAL
)
1902 ScanLegacyVolume(Volume
, VolumeIndex
);
1904 } else if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
1905 ScanLegacyUEFI(BBS_CDROM
);
1907 } /* static VOID ScanLegacyDisc() */
1909 // Scan internal hard disks for legacy (BIOS) boot code
1910 // and add anything found to the list....
1911 static VOID
ScanLegacyInternal(VOID
)
1914 REFIT_VOLUME
*Volume
;
1916 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
) {
1917 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1918 Volume
= Volumes
[VolumeIndex
];
1919 if (Volume
->DiskKind
== DISK_KIND_INTERNAL
)
1920 ScanLegacyVolume(Volume
, VolumeIndex
);
1922 } else if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
1923 ScanLegacyUEFI(BBS_HARDDISK
);
1925 } /* static VOID ScanLegacyInternal() */
1927 // Scan external disks for legacy (BIOS) boot code
1928 // and add anything found to the list....
1929 static VOID
ScanLegacyExternal(VOID
)
1932 REFIT_VOLUME
*Volume
;
1934 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
) {
1935 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1936 Volume
= Volumes
[VolumeIndex
];
1937 if (Volume
->DiskKind
== DISK_KIND_EXTERNAL
)
1938 ScanLegacyVolume(Volume
, VolumeIndex
);
1940 } else if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
1941 ScanLegacyUEFI(BBS_USB
);
1943 } /* static VOID ScanLegacyExternal() */
1946 // pre-boot tool functions
1949 static VOID
StartTool(IN LOADER_ENTRY
*Entry
)
1951 BeginExternalScreen(Entry
->UseGraphicsMode
, Entry
->me
.Title
+ 6); // assumes "Start <title>" as assigned below
1952 StartEFIImage(Entry
->DevicePath
, Entry
->LoadOptions
, TYPE_EFI
,
1953 Basename(Entry
->LoaderPath
), Entry
->OSType
, NULL
, TRUE
);
1954 FinishExternalScreen();
1955 } /* static VOID StartTool() */
1957 static LOADER_ENTRY
* AddToolEntry(EFI_HANDLE DeviceHandle
, IN CHAR16
*LoaderPath
, IN CHAR16
*LoaderTitle
, IN EG_IMAGE
*Image
,
1958 IN CHAR16 ShortcutLetter
, IN BOOLEAN UseGraphicsMode
)
1960 LOADER_ENTRY
*Entry
;
1961 CHAR16
*TitleStr
= NULL
;
1963 Entry
= AllocateZeroPool(sizeof(LOADER_ENTRY
));
1965 TitleStr
= PoolPrint(L
"Start %s", LoaderTitle
);
1966 Entry
->me
.Title
= TitleStr
;
1967 Entry
->me
.Tag
= TAG_TOOL
;
1969 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1970 Entry
->me
.Image
= Image
;
1971 Entry
->LoaderPath
= (LoaderPath
) ? StrDuplicate(LoaderPath
) : NULL
;
1972 Entry
->DevicePath
= FileDevicePath(DeviceHandle
, Entry
->LoaderPath
);
1973 Entry
->UseGraphicsMode
= UseGraphicsMode
;
1975 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1977 } /* static LOADER_ENTRY * AddToolEntry() */
1980 // pre-boot driver functions
1983 static UINTN
ScanDriverDir(IN CHAR16
*Path
)
1986 REFIT_DIR_ITER DirIter
;
1988 EFI_FILE_INFO
*DirEntry
;
1989 CHAR16 FileName
[256];
1991 CleanUpPathNameSlashes(Path
);
1992 // look through contents of the directory
1993 DirIterOpen(SelfRootDir
, Path
, &DirIter
);
1994 while (DirIterNext(&DirIter
, 2, LOADER_MATCH_PATTERNS
, &DirEntry
)) {
1995 if (DirEntry
->FileName
[0] == '.')
1996 continue; // skip this
1998 SPrint(FileName
, 255, L
"%s\\%s", Path
, DirEntry
->FileName
);
2000 Status
= StartEFIImage(FileDevicePath(SelfLoadedImage
->DeviceHandle
, FileName
),
2001 L
"", TYPE_EFI
, DirEntry
->FileName
, 0, NULL
, FALSE
);
2003 Status
= DirIterClose(&DirIter
);
2004 if (Status
!= EFI_NOT_FOUND
) {
2005 SPrint(FileName
, 255, L
"while scanning the %s directory", Path
);
2006 CheckError(Status
, FileName
);
2011 #ifdef __MAKEWITH_GNUEFI
2012 static EFI_STATUS
ConnectAllDriversToAllControllers(VOID
)
2015 UINTN AllHandleCount
;
2016 EFI_HANDLE
*AllHandleBuffer
;
2019 EFI_HANDLE
*HandleBuffer
;
2025 Status
= LibLocateHandle(AllHandles
,
2030 if (EFI_ERROR(Status
))
2033 for (Index
= 0; Index
< AllHandleCount
; Index
++) {
2035 // Scan the handle database
2037 Status
= LibScanHandleDatabase(NULL
,
2039 AllHandleBuffer
[Index
],
2044 if (EFI_ERROR (Status
))
2048 if (HandleType
[Index
] & EFI_HANDLE_TYPE_DRIVER_BINDING_HANDLE
)
2050 if (HandleType
[Index
] & EFI_HANDLE_TYPE_IMAGE_HANDLE
)
2055 for (HandleIndex
= 0; HandleIndex
< HandleCount
; HandleIndex
++) {
2056 if (HandleType
[HandleIndex
] & EFI_HANDLE_TYPE_PARENT_HANDLE
)
2061 if (HandleType
[Index
] & EFI_HANDLE_TYPE_DEVICE_HANDLE
) {
2062 Status
= refit_call4_wrapper(BS
->ConnectController
,
2063 AllHandleBuffer
[Index
],
2071 MyFreePool (HandleBuffer
);
2072 MyFreePool (HandleType
);
2076 MyFreePool (AllHandleBuffer
);
2078 } /* EFI_STATUS ConnectAllDriversToAllControllers() */
2080 static EFI_STATUS
ConnectAllDriversToAllControllers(VOID
) {
2081 BdsLibConnectAllDriversToAllControllers();
2086 // Load all EFI drivers from rEFInd's "drivers" subdirectory and from the
2087 // directories specified by the user in the "scan_driver_dirs" configuration
2089 static VOID
LoadDrivers(VOID
)
2091 CHAR16
*Directory
, *SelfDirectory
;
2092 UINTN i
= 0, Length
, NumFound
= 0;
2094 // load drivers from the subdirectories of rEFInd's home directory specified
2095 // in the DRIVER_DIRS constant.
2096 while ((Directory
= FindCommaDelimited(DRIVER_DIRS
, i
++)) != NULL
) {
2097 SelfDirectory
= SelfDirPath
? StrDuplicate(SelfDirPath
) : NULL
;
2098 CleanUpPathNameSlashes(SelfDirectory
);
2099 MergeStrings(&SelfDirectory
, Directory
, L
'\\');
2100 NumFound
+= ScanDriverDir(SelfDirectory
);
2101 MyFreePool(Directory
);
2102 MyFreePool(SelfDirectory
);
2105 // Scan additional user-specified driver directories....
2107 while ((Directory
= FindCommaDelimited(GlobalConfig
.DriverDirs
, i
++)) != NULL
) {
2108 CleanUpPathNameSlashes(Directory
);
2109 Length
= StrLen(Directory
);
2111 NumFound
+= ScanDriverDir(Directory
);
2113 MyFreePool(Directory
);
2116 // connect all devices
2118 ConnectAllDriversToAllControllers();
2119 } /* static VOID LoadDrivers() */
2121 // Determine what (if any) type of legacy (BIOS) boot support is available
2122 static VOID
FindLegacyBootType(VOID
) {
2123 #ifdef __MAKEWITH_TIANO
2125 EFI_LEGACY_BIOS_PROTOCOL
*LegacyBios
;
2128 GlobalConfig
.LegacyType
= LEGACY_TYPE_NONE
;
2130 // UEFI-style legacy BIOS support is available only with the TianoCore EDK2
2131 // build environment, and then only with some EFI implementations....
2132 #ifdef __MAKEWITH_TIANO
2133 Status
= gBS
->LocateProtocol (&gEfiLegacyBootProtocolGuid
, NULL
, (VOID
**) &LegacyBios
);
2134 if (!EFI_ERROR (Status
))
2135 GlobalConfig
.LegacyType
= LEGACY_TYPE_UEFI
;
2138 // Macs have their own system. If the firmware vendor code contains the
2139 // string "Apple", assume it's available. Note that this overrides the
2140 // UEFI type, and might yield false positives if the vendor string
2141 // contains "Apple" as part of something bigger, so this isn't 100%
2143 if (StriSubCmp(L
"Apple", ST
->FirmwareVendor
))
2144 GlobalConfig
.LegacyType
= LEGACY_TYPE_MAC
;
2145 } // static VOID FindLegacyBootType
2147 // Warn the user if legacy OS scans are enabled but the firmware or this
2148 // application can't support them....
2149 static VOID
WarnIfLegacyProblems() {
2150 BOOLEAN found
= FALSE
;
2153 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_NONE
) {
2155 if (GlobalConfig
.ScanFor
[i
] == 'h' || GlobalConfig
.ScanFor
[i
] == 'b' || GlobalConfig
.ScanFor
[i
] == 'c')
2158 } while ((i
< NUM_SCAN_OPTIONS
) && (!found
));
2160 Print(L
"NOTE: refind.conf's 'scanfor' line specifies scanning for one or more legacy\n");
2161 Print(L
"(BIOS) boot options; however, this is not possible because ");
2162 #ifdef __MAKEWITH_TIANO
2163 Print(L
"your computer lacks\n");
2164 Print(L
"the necessary Compatibility Support Module (CSM) support.\n");
2166 Print(L
"this program was\n");
2167 Print(L
"compiled without the necessary support. Please visit\n");
2168 Print(L
"http://www.rodsbooks.com/refind/getting.html and download and install a rEFInd\n");
2169 Print(L
"binary built with the TianoCore EDK2 to enable legacy boot support.\n");
2173 } // if no legacy support
2174 } // static VOID WarnIfLegacyProblems()
2176 // Locates boot loaders. NOTE: This assumes that GlobalConfig.LegacyType is set correctly.
2177 static VOID
ScanForBootloaders(VOID
) {
2180 // if (GlobalConfig.LegacyType == LEGACY_TYPE_UEFI) {
2181 // Print(L"About to call BdsDeleteAllInvalidLegacyBootOptions()\n");
2182 // BdsDeleteAllInvalidLegacyBootOptions();
2183 // Print(L"About to call BdsAddNonExistingLegacyBootOptions()\n");
2184 // BdsAddNonExistingLegacyBootOptions();
2185 // Print(L"About to call BdsUpdateLegacyDevOrder()\n");
2186 // // BdsUpdateLegacyDevOrder(); // EXTREME CAUTION: HOSED ONE FIRMWARE!
2187 // Print(L"Done with legacy boot updates!\n");
2193 // scan for loaders and tools, add them to the menu
2194 for (i
= 0; i
< NUM_SCAN_OPTIONS
; i
++) {
2195 switch(GlobalConfig
.ScanFor
[i
]) {
2200 ScanLegacyInternal();
2203 ScanLegacyExternal();
2206 ScanUserConfigured(GlobalConfig
.ConfigFilename
);
2220 // assign shortcut keys
2221 for (i
= 0; i
< MainMenu
.EntryCount
&& MainMenu
.Entries
[i
]->Row
== 0 && i
< 9; i
++)
2222 MainMenu
.Entries
[i
]->ShortcutDigit
= (CHAR16
)('1' + i
);
2224 // wait for user ACK when there were errors
2225 FinishTextScreen(FALSE
);
2226 } // static VOID ScanForBootloaders()
2228 // Locate a single tool from the specified Locations using one of the
2229 // specified Names and add it to the menu.
2230 static VOID
FindTool(CHAR16
*Locations
, CHAR16
*Names
, CHAR16
*Description
, UINTN Icon
) {
2231 UINTN j
= 0, k
, VolumeIndex
;
2232 CHAR16
*DirName
, *FileName
, *PathName
, FullDescription
[256];
2234 while ((DirName
= FindCommaDelimited(Locations
, j
++)) != NULL
) {
2236 while ((FileName
= FindCommaDelimited(Names
, k
++)) != NULL
) {
2237 PathName
= StrDuplicate(DirName
);
2238 MergeStrings(&PathName
, FileName
, (StriCmp(PathName
, L
"\\") == 0) ? 0 : L
'\\');
2239 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
2240 if ((Volumes
[VolumeIndex
]->RootDir
!= NULL
) && (FileExists(Volumes
[VolumeIndex
]->RootDir
, PathName
))) {
2241 SPrint(FullDescription
, 255, L
"%s at %s on %s", Description
, PathName
, Volumes
[VolumeIndex
]->VolName
);
2242 AddToolEntry(Volumes
[VolumeIndex
]->DeviceHandle
, PathName
, FullDescription
, BuiltinIcon(Icon
), 'S', FALSE
);
2245 MyFreePool(PathName
);
2246 MyFreePool(FileName
);
2248 MyFreePool(DirName
);
2249 } // while Locations
2250 } // VOID FindTool()
2252 // Add the second-row tags containing built-in and external tools (EFI shell,
2254 static VOID
ScanForTools(VOID
) {
2255 CHAR16
*FileName
= NULL
, *VolName
= NULL
, *MokLocations
, Description
[256];
2256 REFIT_MENU_ENTRY
*TempMenuEntry
;
2257 UINTN i
, j
, VolumeIndex
;
2261 MokLocations
= StrDuplicate(MOK_LOCATIONS
);
2262 if (MokLocations
!= NULL
)
2263 MergeStrings(&MokLocations
, SelfDirPath
, L
',');
2265 for (i
= 0; i
< NUM_TOOLS
; i
++) {
2266 switch(GlobalConfig
.ShowTools
[i
]) {
2267 // NOTE: Be sure that FileName is NULL at the end of each case.
2269 TempMenuEntry
= CopyMenuEntry(&MenuEntryShutdown
);
2270 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_SHUTDOWN
);
2271 AddMenuEntry(&MainMenu
, TempMenuEntry
);
2275 TempMenuEntry
= CopyMenuEntry(&MenuEntryReset
);
2276 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_RESET
);
2277 AddMenuEntry(&MainMenu
, TempMenuEntry
);
2281 TempMenuEntry
= CopyMenuEntry(&MenuEntryAbout
);
2282 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT
);
2283 AddMenuEntry(&MainMenu
, TempMenuEntry
);
2287 TempMenuEntry
= CopyMenuEntry(&MenuEntryExit
);
2288 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_EXIT
);
2289 AddMenuEntry(&MainMenu
, TempMenuEntry
);
2293 if (EfivarGetRaw(&GlobalGuid
, L
"OsIndicationsSupported", &b
, &j
) == EFI_SUCCESS
) {
2295 if (osind
& EFI_OS_INDICATIONS_BOOT_TO_FW_UI
) {
2296 TempMenuEntry
= CopyMenuEntry(&MenuEntryFirmware
);
2297 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_FIRMWARE
);
2298 AddMenuEntry(&MainMenu
, TempMenuEntry
);
2305 while ((FileName
= FindCommaDelimited(SHELL_NAMES
, j
++)) != NULL
) {
2306 if (FileExists(SelfRootDir
, FileName
)) {
2307 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, L
"EFI Shell", BuiltinIcon(BUILTIN_ICON_TOOL_SHELL
),
2310 MyFreePool(FileName
);
2316 while ((FileName
= FindCommaDelimited(GPTSYNC_NAMES
, j
++)) != NULL
) {
2317 if (FileExists(SelfRootDir
, FileName
)) {
2318 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, L
"Hybrid MBR tool", BuiltinIcon(BUILTIN_ICON_TOOL_PART
),
2321 MyFreePool(FileName
);
2328 while ((FileName
= FindCommaDelimited(GDISK_NAMES
, j
++)) != NULL
) {
2329 if (FileExists(SelfRootDir
, FileName
)) {
2330 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, L
"disk partitioning tool",
2331 BuiltinIcon(BUILTIN_ICON_TOOL_PART
), 'G', FALSE
);
2333 MyFreePool(FileName
);
2338 case TAG_APPLE_RECOVERY
:
2339 FileName
= StrDuplicate(L
"\\com.apple.recovery.boot\\boot.efi");
2340 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
2341 if ((Volumes
[VolumeIndex
]->RootDir
!= NULL
) && (FileExists(Volumes
[VolumeIndex
]->RootDir
, FileName
))) {
2342 SPrint(Description
, 255, L
"Apple Recovery on %s", Volumes
[VolumeIndex
]->VolName
);
2343 AddToolEntry(Volumes
[VolumeIndex
]->DeviceHandle
, FileName
, Description
,
2344 BuiltinIcon(BUILTIN_ICON_TOOL_APPLE_RESCUE
), 'R', TRUE
);
2347 MyFreePool(FileName
);
2351 case TAG_WINDOWS_RECOVERY
:
2353 while ((FileName
= FindCommaDelimited(GlobalConfig
.WindowsRecoveryFiles
, j
++)) != NULL
) {
2354 SplitVolumeAndFilename(&FileName
, &VolName
);
2355 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
2356 if ((Volumes
[VolumeIndex
]->RootDir
!= NULL
) && (FileExists(Volumes
[VolumeIndex
]->RootDir
, FileName
)) &&
2357 ((VolName
== NULL
) || (StriCmp(VolName
, Volumes
[VolumeIndex
]->VolName
) == 0))) {
2358 SPrint(Description
, 255, L
"Microsoft Recovery on %s", Volumes
[VolumeIndex
]->VolName
);
2359 AddToolEntry(Volumes
[VolumeIndex
]->DeviceHandle
, FileName
, Description
,
2360 BuiltinIcon(BUILTIN_ICON_TOOL_WINDOWS_RESCUE
), 'R', TRUE
);
2364 MyFreePool(FileName
);
2366 MyFreePool(VolName
);
2371 FindTool(MokLocations
, MOK_NAMES
, L
"MOK utility", BUILTIN_ICON_TOOL_MOK_TOOL
);
2375 FindTool(MEMTEST_LOCATIONS
, MEMTEST_NAMES
, L
"Memory test utility", BUILTIN_ICON_TOOL_MEMTEST
);
2380 } // static VOID ScanForTools
2382 // Rescan for boot loaders
2383 VOID
RescanAll(VOID
) {
2390 egDisplayMessage(L
"Scanning for new boot loaders; please wait....", &BGColor
);
2391 FreeList((VOID
***) &(MainMenu
.Entries
), &MainMenu
.EntryCount
);
2392 MainMenu
.Entries
= NULL
;
2393 MainMenu
.EntryCount
= 0;
2394 ReadConfig(GlobalConfig
.ConfigFilename
);
2395 ConnectAllDriversToAllControllers();
2397 ScanForBootloaders();
2400 } // VOID RescanAll()
2402 #ifdef __MAKEWITH_TIANO
2404 // Minimal initialization function
2405 static VOID
InitializeLib(IN EFI_HANDLE ImageHandle
, IN EFI_SYSTEM_TABLE
*SystemTable
) {
2407 // gImageHandle = ImageHandle;
2408 gBS
= SystemTable
->BootServices
;
2409 // gRS = SystemTable->RuntimeServices;
2410 gRT
= SystemTable
->RuntimeServices
; // Some BDS functions need gRT to be set
2411 EfiGetSystemConfigurationTable (&gEfiDxeServicesTableGuid
, (VOID
**) &gDS
);
2413 InitializeConsoleSim();
2418 // Set up our own Secure Boot extensions....
2419 // Returns TRUE on success, FALSE otherwise
2420 static BOOLEAN
SecureBootSetup(VOID
) {
2422 BOOLEAN Success
= FALSE
;
2424 if (secure_mode() && ShimLoaded()) {
2425 Status
= security_policy_install();
2426 if (Status
== EFI_SUCCESS
) {
2429 Print(L
"Failed to install MOK Secure Boot extensions");
2433 } // VOID SecureBootSetup()
2435 // Remove our own Secure Boot extensions....
2436 // Returns TRUE on success, FALSE otherwise
2437 static BOOLEAN
SecureBootUninstall(VOID
) {
2439 BOOLEAN Success
= TRUE
;
2441 if (secure_mode()) {
2442 Status
= security_policy_uninstall();
2443 if (Status
!= EFI_SUCCESS
) {
2445 BeginTextScreen(L
"Secure Boot Policy Failure");
2446 Print(L
"Failed to uninstall MOK Secure Boot extensions; forcing a reboot.");
2448 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);
2452 } // VOID SecureBootUninstall
2454 // Sets the global configuration filename; will be CONFIG_FILE_NAME unless the
2455 // "-c" command-line option is set, in which case that takes precedence.
2456 // If an error is encountered, leaves the value alone (it should be set to
2457 // CONFIG_FILE_NAME when GlobalConfig is initialized).
2458 static VOID
SetConfigFilename(EFI_HANDLE ImageHandle
) {
2459 EFI_LOADED_IMAGE
*Info
;
2460 CHAR16
*Options
, *FileName
;
2464 Status
= refit_call3_wrapper(BS
->HandleProtocol
, ImageHandle
, &LoadedImageProtocol
, (VOID
**) &Info
);
2465 if ((Status
== EFI_SUCCESS
) && (Info
->LoadOptionsSize
> 0)) {
2466 Options
= (CHAR16
*) Info
->LoadOptions
;
2467 Where
= FindSubString(L
" -c ", Options
);
2469 FileName
= StrDuplicate(&Options
[Where
+ 4]);
2470 Where
= FindSubString(L
" ", FileName
);
2472 FileName
[Where
] = L
'\0';
2474 if (FileExists(SelfDir
, FileName
)) {
2475 GlobalConfig
.ConfigFilename
= FileName
;
2477 Print(L
"Specified configuration file (%s) doesn't exist; using\n'refind.conf' default\n", FileName
);
2478 MyFreePool(FileName
);
2482 } // VOID SetConfigFilename()
2489 efi_main (EFI_HANDLE ImageHandle
, EFI_SYSTEM_TABLE
*SystemTable
)
2492 BOOLEAN MainLoopRunning
= TRUE
;
2493 BOOLEAN MokProtocol
;
2494 REFIT_MENU_ENTRY
*ChosenEntry
;
2496 CHAR16
*Selection
= NULL
;
2500 InitializeLib(ImageHandle
, SystemTable
);
2501 Status
= InitRefitLib(ImageHandle
);
2502 if (EFI_ERROR(Status
))
2505 // read configuration
2506 CopyMem(GlobalConfig
.ScanFor
, "ieom ", NUM_SCAN_OPTIONS
);
2507 FindLegacyBootType();
2508 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
)
2509 CopyMem(GlobalConfig
.ScanFor
, "ihebocm ", NUM_SCAN_OPTIONS
);
2510 SetConfigFilename(ImageHandle
);
2511 ReadConfig(GlobalConfig
.ConfigFilename
);
2515 WarnIfLegacyProblems();
2516 MainMenu
.TimeoutSeconds
= GlobalConfig
.Timeout
;
2518 // disable EFI watchdog timer
2519 refit_call4_wrapper(BS
->SetWatchdogTimer
, 0x0000, 0x0000, 0x0000, NULL
);
2521 // further bootstrap (now with config available)
2522 MokProtocol
= SecureBootSetup();
2524 ScanForBootloaders();
2528 if (GlobalConfig
.ScanDelay
> 0) {
2533 egDisplayMessage(L
"Pausing before disk scan; please wait....", &BGColor
);
2534 for (i
= 0; i
< GlobalConfig
.ScanDelay
; i
++)
2535 refit_call1_wrapper(BS
->Stall
, 1000000);
2539 if (GlobalConfig
.DefaultSelection
)
2540 Selection
= StrDuplicate(GlobalConfig
.DefaultSelection
);
2542 while (MainLoopRunning
) {
2543 MenuExit
= RunMainMenu(&MainMenu
, Selection
, &ChosenEntry
);
2545 // The Escape key triggers a re-scan operation....
2546 if (MenuExit
== MENU_EXIT_ESCAPE
) {
2552 switch (ChosenEntry
->Tag
) {
2554 case TAG_REBOOT
: // Reboot
2556 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);
2557 MainLoopRunning
= FALSE
; // just in case we get this far
2560 case TAG_SHUTDOWN
: // Shut Down
2562 refit_call4_wrapper(RT
->ResetSystem
, EfiResetShutdown
, EFI_SUCCESS
, 0, NULL
);
2563 MainLoopRunning
= FALSE
; // just in case we get this far
2566 case TAG_ABOUT
: // About rEFInd
2570 case TAG_LOADER
: // Boot OS via .EFI loader
2571 StartLoader((LOADER_ENTRY
*)ChosenEntry
);
2574 case TAG_LEGACY
: // Boot legacy OS
2575 StartLegacy((LEGACY_ENTRY
*)ChosenEntry
);
2578 #ifdef __MAKEWITH_TIANO
2579 case TAG_LEGACY_UEFI
: // Boot a legacy OS on a non-Mac
2580 StartLegacyUEFI((LEGACY_ENTRY
*)ChosenEntry
);
2584 case TAG_TOOL
: // Start a EFI tool
2585 StartTool((LOADER_ENTRY
*)ChosenEntry
);
2588 case TAG_EXIT
: // Terminate rEFInd
2589 if ((MokProtocol
) && !SecureBootUninstall()) {
2590 MainLoopRunning
= FALSE
; // just in case we get this far
2592 BeginTextScreen(L
" ");
2597 case TAG_FIRMWARE
: // Reboot into firmware's user interface
2598 RebootIntoFirmware();
2602 MyFreePool(Selection
);
2603 Selection
= (ChosenEntry
->Title
) ? StrDuplicate(ChosenEntry
->Title
) : NULL
;
2606 // If we end up here, things have gone wrong. Try to reboot, and if that
2607 // fails, go into an endless loop.
2608 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);