3 * Main code for the boot menu
5 * Copyright (c) 2006-2010 Christoph Pfisterer
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions are
12 * * Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
15 * * Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the
20 * * Neither the name of Christoph Pfisterer nor the names of the
21 * contributors may be used to endorse or promote products derived
22 * from this software without specific prior written permission.
24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
26 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
27 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
28 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
29 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
30 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
31 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
32 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
33 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
34 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37 * Modifications copyright (c) 2012-2014 Roderick W. Smith
39 * Modifications distributed under the terms of the GNU General Public
40 * License (GPL) version 3 (GPLv3), a copy of which must be distributed
41 * with this source code or binaries made from it.
53 #include "security_policy.h"
54 #include "../include/Handle.h"
55 #include "../include/refit_call_wrapper.h"
56 #include "driver_support.h"
57 #include "../include/syslinux_mbr.h"
59 #ifdef __MAKEWITH_GNUEFI
60 #ifndef EFI_SECURITY_VIOLATION
61 #define EFI_SECURITY_VIOLATION EFIERR (26)
64 #include "../EfiLib/BdsHelper.h"
65 #include "../EfiLib/legacy.h"
66 #endif // __MAKEWITH_GNUEFI
68 #ifndef EFI_OS_INDICATIONS_BOOT_TO_FW_UI
69 #define EFI_OS_INDICATIONS_BOOT_TO_FW_UI 0x0000000000000001ULL
75 #define MACOSX_LOADER_PATH L"System\\Library\\CoreServices\\boot.efi"
77 #define SHELL_NAMES L"\\EFI\\tools\\shell.efi,\\EFI\\tools\\shellx64.efi,\\shell.efi,\\shellx64.efi"
78 #define GPTSYNC_NAMES L"\\EFI\\tools\\gptsync.efi,\\EFI\\tools\\gptsync_x64.efi"
79 #define GDISK_NAMES L"\\EFI\\tools\\gdisk.efi,\\EFI\\tools\\gdisk_x64.efi"
80 #define MEMTEST_NAMES L"memtest86.efi,memtest86_x64.efi,memtest86x64.efi,bootx64.efi"
81 #define DRIVER_DIRS L"drivers,drivers_x64"
82 #define FALLBACK_FULLNAME L"EFI\\BOOT\\bootx64.efi"
83 #define FALLBACK_BASENAME L"bootx64.efi"
84 #define EFI_STUB_ARCH 0x8664
86 #define SHELL_NAMES L"\\EFI\\tools\\shell.efi,\\EFI\\tools\\shellia32.efi,\\shell.efi,\\shellia32.efi"
87 #define GPTSYNC_NAMES L"\\EFI\\tools\\gptsync.efi,\\EFI\\tools\\gptsync_ia32.efi"
88 #define GDISK_NAMES L"\\EFI\\tools\\gdisk.efi,\\EFI\\tools\\gdisk_ia32.efi"
89 #define MEMTEST_NAMES L"memtest86.efi,memtest86_ia32.efi,memtest86ia32.efi,bootia32.efi"
90 #define DRIVER_DIRS L"drivers,drivers_ia32"
91 #define FALLBACK_FULLNAME L"EFI\\BOOT\\bootia32.efi"
92 #define FALLBACK_BASENAME L"bootia32.efi"
93 #define EFI_STUB_ARCH 0x014c
95 #define SHELL_NAMES L"\\EFI\\tools\\shell.efi,\\shell.efi"
96 #define GPTSYNC_NAMES L"\\EFI\\tools\\gptsync.efi"
97 #define GDISK_NAMES L"\\EFI\\tools\\gdisk.efi"
98 #define MEMTEST_NAMES L"memtest86.efi"
99 #define DRIVER_DIRS L"drivers"
100 #define FALLBACK_FULLNAME L"EFI\\BOOT\\boot.efi" /* Not really correct */
101 #define FALLBACK_BASENAME L"boot.efi" /* Not really correct */
103 #define FAT_ARCH 0x0ef1fab9 /* ID for Apple "fat" binary */
105 // Filename patterns that identify EFI boot loaders. Note that a single case (either L"*.efi" or
106 // L"*.EFI") is fine for most systems; but Gigabyte's buggy Hybrid EFI does a case-sensitive
107 // comparison when it should do a case-insensitive comparison, so I'm doubling this up. It does
108 // no harm on other computers, AFAIK. In theory, every case variation should be done for
109 // completeness, but that's ridiculous....
110 #define LOADER_MATCH_PATTERNS L"*.efi,*.EFI"
112 // Patterns that identify Linux kernels. Added to the loader match pattern when the
113 // scan_all_linux_kernels option is set in the configuration file. Causes kernels WITHOUT
114 // a ".efi" extension to be found when scanning for boot loaders.
115 #define LINUX_MATCH_PATTERNS L"vmlinuz*,bzImage*"
117 // Default hint text for program-launch submenus
118 #define SUBSCREEN_HINT1 L"Use arrow keys to move cursor; Enter to boot;"
119 #define SUBSCREEN_HINT2 L"Insert or F2 to edit options; Esc to return to main menu"
120 #define SUBSCREEN_HINT2_NO_EDITOR L"Esc to return to main menu"
124 #define TYPE_LEGACY 2
126 static REFIT_MENU_ENTRY MenuEntryAbout
= { L
"About rEFInd", TAG_ABOUT
, 1, 0, 'A', NULL
, NULL
, NULL
};
127 static REFIT_MENU_ENTRY MenuEntryReset
= { L
"Reboot Computer", TAG_REBOOT
, 1, 0, 'R', NULL
, NULL
, NULL
};
128 static REFIT_MENU_ENTRY MenuEntryShutdown
= { L
"Shut Down Computer", TAG_SHUTDOWN
, 1, 0, 'U', NULL
, NULL
, NULL
};
129 static REFIT_MENU_ENTRY MenuEntryReturn
= { L
"Return to Main Menu", TAG_RETURN
, 1, 0, 0, NULL
, NULL
, NULL
};
130 static REFIT_MENU_ENTRY MenuEntryExit
= { L
"Exit rEFInd", TAG_EXIT
, 1, 0, 0, NULL
, NULL
, NULL
};
131 static REFIT_MENU_ENTRY MenuEntryFirmware
= { L
"Reboot to Computer Setup Utility", TAG_FIRMWARE
, 1, 0, 0, NULL
, NULL
, NULL
};
133 static REFIT_MENU_SCREEN MainMenu
= { L
"Main Menu", NULL
, 0, NULL
, 0, NULL
, 0, L
"Automatic boot",
134 L
"Use arrow keys to move cursor; Enter to boot;",
135 L
"Insert or F2 for more options; Esc to refresh" };
136 static REFIT_MENU_SCREEN AboutMenu
= { L
"About", NULL
, 0, NULL
, 0, NULL
, 0, NULL
, L
"Press Enter to return to main menu", L
"" };
138 REFIT_CONFIG GlobalConfig
= { FALSE
, FALSE
, 0, 0, 0, DONT_CHANGE_TEXT_MODE
, 20, 0, 0, GRAPHICS_FOR_OSX
, LEGACY_TYPE_MAC
, 0, 0,
139 { DEFAULT_BIG_ICON_SIZE
/ 4, DEFAULT_SMALL_ICON_SIZE
, DEFAULT_BIG_ICON_SIZE
}, BANNER_NOSCALE
,
140 NULL
, NULL
, CONFIG_FILE_NAME
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
141 { TAG_SHELL
, TAG_MEMTEST
, TAG_GDISK
, TAG_APPLE_RECOVERY
, TAG_WINDOWS_RECOVERY
, TAG_MOK_TOOL
,
142 TAG_ABOUT
, TAG_SHUTDOWN
, TAG_REBOOT
, TAG_FIRMWARE
, 0, 0, 0, 0, 0, 0 }
145 EFI_GUID GlobalGuid
= EFI_GLOBAL_VARIABLE
;
147 GPT_DATA
*gPartitions
= NULL
;
149 // Structure used to hold boot loader filenames and time stamps in
150 // a linked list; used to sort entries within a directory.
154 struct LOADER_LIST
*NextEntry
;
161 static VOID
AboutrEFInd(VOID
)
163 CHAR16
*FirmwareVendor
;
165 if (AboutMenu
.EntryCount
== 0) {
166 AboutMenu
.TitleImage
= BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT
);
167 AddMenuInfoLine(&AboutMenu
, L
"rEFInd Version 0.8.0");
168 AddMenuInfoLine(&AboutMenu
, L
"");
169 AddMenuInfoLine(&AboutMenu
, L
"Copyright (c) 2006-2010 Christoph Pfisterer");
170 AddMenuInfoLine(&AboutMenu
, L
"Copyright (c) 2012-2014 Roderick W. Smith");
171 AddMenuInfoLine(&AboutMenu
, L
"Portions Copyright (c) Intel Corporation and others");
172 AddMenuInfoLine(&AboutMenu
, L
"Distributed under the terms of the GNU GPLv3 license");
173 AddMenuInfoLine(&AboutMenu
, L
"");
174 AddMenuInfoLine(&AboutMenu
, L
"Running on:");
175 AddMenuInfoLine(&AboutMenu
, PoolPrint(L
" EFI Revision %d.%02d", ST
->Hdr
.Revision
>> 16, ST
->Hdr
.Revision
& ((1 << 16) - 1)));
177 AddMenuInfoLine(&AboutMenu
, L
" Platform: x86 (32 bit)");
178 #elif defined(EFIX64)
179 AddMenuInfoLine(&AboutMenu
, PoolPrint(L
" Platform: x86_64 (64 bit); Secure Boot %s",
180 secure_mode() ? L
"active" : L
"inactive"));
182 AddMenuInfoLine(&AboutMenu
, L
" Platform: unknown");
184 FirmwareVendor
= StrDuplicate(ST
->FirmwareVendor
);
185 LimitStringLength(FirmwareVendor
, 65); // More than ~65 causes empty info page on 800x600 display
186 AddMenuInfoLine(&AboutMenu
, PoolPrint(L
" Firmware: %s %d.%02d", FirmwareVendor
, ST
->FirmwareRevision
>> 16,
187 ST
->FirmwareRevision
& ((1 << 16) - 1)));
188 AddMenuInfoLine(&AboutMenu
, PoolPrint(L
" Screen Output: %s", egScreenDescription()));
189 AddMenuInfoLine(&AboutMenu
, L
"");
190 #if defined(__MAKEWITH_GNUEFI)
191 AddMenuInfoLine(&AboutMenu
, L
"Built with GNU-EFI");
193 AddMenuInfoLine(&AboutMenu
, L
"Built with TianoCore EDK2");
195 AddMenuInfoLine(&AboutMenu
, L
"");
196 AddMenuInfoLine(&AboutMenu
, L
"For more information, see the rEFInd Web site:");
197 AddMenuInfoLine(&AboutMenu
, L
"http://www.rodsbooks.com/refind/");
198 AddMenuEntry(&AboutMenu
, &MenuEntryReturn
);
201 RunMenu(&AboutMenu
, NULL
);
202 } /* VOID AboutrEFInd() */
204 static VOID
WarnSecureBootError(CHAR16
*Name
, BOOLEAN Verbose
) {
206 Name
= L
"the loader";
208 refit_call2_wrapper(ST
->ConOut
->SetAttribute
, ST
->ConOut
, ATTR_ERROR
);
209 Print(L
"Secure Boot validation failure loading %s!\n", Name
);
210 refit_call2_wrapper(ST
->ConOut
->SetAttribute
, ST
->ConOut
, ATTR_BASIC
);
211 if (Verbose
&& secure_mode()) {
212 Print(L
"\nThis computer is configured with Secure Boot active, but\n%s has failed validation.\n", Name
);
213 Print(L
"\nYou can:\n * Launch another boot loader\n");
214 Print(L
" * Disable Secure Boot in your firmware\n");
215 Print(L
" * Sign %s with a machine owner key (MOK)\n", Name
);
216 Print(L
" * Use a MOK utility (often present on the second row) to add a MOK with which\n");
217 Print(L
" %s has already been signed.\n", Name
);
218 Print(L
" * Use a MOK utility to register %s (\"enroll its hash\") without\n", Name
);
219 Print(L
" signing it.\n");
220 Print(L
"\nSee http://www.rodsbooks.com/refind/secureboot.html for more information\n");
223 } // VOID WarnSecureBootError()
225 // Returns TRUE if this file is a valid EFI loader file, and is proper ARCH
226 static BOOLEAN
IsValidLoader(EFI_FILE
*RootDir
, CHAR16
*FileName
) {
227 BOOLEAN IsValid
= TRUE
;
228 #if defined (EFIX64) | defined (EFI32)
230 EFI_FILE_HANDLE FileHandle
;
232 UINTN Size
= sizeof(Header
);
234 if ((RootDir
== NULL
) || (FileName
== NULL
)) {
235 // Assume valid here, because Macs produce NULL RootDir (& maybe FileName)
236 // when launching from a Firewire drive. This should be handled better, but
237 // fix would have to be in StartEFIImageList() and/or in FindVolumeAndFilename().
241 Status
= refit_call5_wrapper(RootDir
->Open
, RootDir
, &FileHandle
, FileName
, EFI_FILE_MODE_READ
, 0);
242 if (EFI_ERROR(Status
))
245 Status
= refit_call3_wrapper(FileHandle
->Read
, FileHandle
, &Size
, Header
);
246 refit_call1_wrapper(FileHandle
->Close
, FileHandle
);
248 IsValid
= !EFI_ERROR(Status
) &&
249 Size
== sizeof(Header
) &&
250 ((Header
[0] == 'M' && Header
[1] == 'Z' &&
251 (Size
= *(UINT32
*)&Header
[0x3c]) < 0x180 &&
252 Header
[Size
] == 'P' && Header
[Size
+1] == 'E' &&
253 Header
[Size
+2] == 0 && Header
[Size
+3] == 0 &&
254 *(UINT16
*)&Header
[Size
+4] == EFI_STUB_ARCH
) ||
255 (*(UINT32
*)&Header
== FAT_ARCH
));
258 } // BOOLEAN IsValidLoader()
260 // Launch an EFI binary.
261 static EFI_STATUS
StartEFIImageList(IN EFI_DEVICE_PATH
**DevicePaths
,
262 IN CHAR16
*LoadOptions
, IN UINTN LoaderType
,
263 IN CHAR16
*ImageTitle
, IN CHAR8 OSType
,
264 OUT UINTN
*ErrorInStep
,
267 EFI_STATUS Status
, ReturnStatus
;
268 EFI_HANDLE ChildImageHandle
;
269 EFI_LOADED_IMAGE
*ChildLoadedImage
= NULL
;
270 REFIT_VOLUME
*Volume
= NULL
;
271 UINTN DevicePathIndex
;
272 CHAR16 ErrorInfo
[256];
273 CHAR16
*FullLoadOptions
= NULL
;
274 CHAR16
*Filename
= NULL
;
277 if (ErrorInStep
!= NULL
)
281 if (LoadOptions
!= NULL
) {
282 FullLoadOptions
= StrDuplicate(LoadOptions
);
283 if ((LoaderType
== TYPE_EFI
) && (OSType
== 'M')) {
284 MergeStrings(&FullLoadOptions
, L
" ", 0);
285 // NOTE: That last space is also added by the EFI shell and seems to be significant
286 // when passing options to Apple's boot.efi...
288 } // if (LoadOptions != NULL)
290 Print(L
"Starting %s\nUsing load options '%s'\n", ImageTitle
, FullLoadOptions
? FullLoadOptions
: L
"");
292 // load the image into memory (and execute it, in the case of a shim/MOK image).
293 ReturnStatus
= Status
= EFI_NOT_FOUND
; // in case the list is empty
294 for (DevicePathIndex
= 0; DevicePaths
[DevicePathIndex
] != NULL
; DevicePathIndex
++) {
295 FindVolumeAndFilename(DevicePaths
[DevicePathIndex
], &Volume
, &Filename
);
296 // Some EFIs crash if attempting to load driver for invalid architecture, so
297 // protect for this condition; but sometimes Volume comes back NULL, so provide
298 // an exception. (TODO: Handle this special condition better.)
299 if ((LoaderType
== TYPE_LEGACY
) || (Volume
== NULL
) || IsValidLoader(Volume
->RootDir
, Filename
)) {
300 if (Filename
&& (LoaderType
!= TYPE_LEGACY
)) {
301 Temp
= PoolPrint(L
"\\%s %s", Filename
, FullLoadOptions
? FullLoadOptions
: L
"");
303 MyFreePool(FullLoadOptions
);
304 FullLoadOptions
= Temp
;
308 // NOTE: Below commented-out line could be more efficient if file were read ahead of
309 // time and passed as a pre-loaded image to LoadImage(), but it doesn't work on my
310 // 32-bit Mac Mini or my 64-bit Intel box when launching a Linux kernel; the
311 // kernel returns a "Failed to handle fs_proto" error message.
312 // TODO: Track down the cause of this error and fix it, if possible.
313 // ReturnStatus = Status = refit_call6_wrapper(BS->LoadImage, FALSE, SelfImageHandle, DevicePaths[DevicePathIndex],
314 // ImageData, ImageSize, &ChildImageHandle);
315 ReturnStatus
= Status
= refit_call6_wrapper(BS
->LoadImage
, FALSE
, SelfImageHandle
, DevicePaths
[DevicePathIndex
],
316 NULL
, 0, &ChildImageHandle
);
318 Print(L
"Invalid loader file!\n");
319 ReturnStatus
= EFI_LOAD_ERROR
;
321 if (ReturnStatus
!= EFI_NOT_FOUND
) {
325 if ((Status
== EFI_ACCESS_DENIED
) || (Status
== EFI_SECURITY_VIOLATION
)) {
326 WarnSecureBootError(ImageTitle
, Verbose
);
329 SPrint(ErrorInfo
, 255, L
"while loading %s", ImageTitle
);
330 if (CheckError(Status
, ErrorInfo
)) {
331 if (ErrorInStep
!= NULL
)
336 ReturnStatus
= Status
= refit_call3_wrapper(BS
->HandleProtocol
, ChildImageHandle
, &LoadedImageProtocol
,
337 (VOID
**) &ChildLoadedImage
);
338 if (CheckError(Status
, L
"while getting a LoadedImageProtocol handle")) {
339 if (ErrorInStep
!= NULL
)
343 ChildLoadedImage
->LoadOptions
= (VOID
*)FullLoadOptions
;
344 ChildLoadedImage
->LoadOptionsSize
= FullLoadOptions
? ((UINT32
)StrLen(FullLoadOptions
) + 1) * sizeof(CHAR16
) : 0;
345 // turn control over to the image
346 // TODO: (optionally) re-enable the EFI watchdog timer!
348 // close open file handles
350 ReturnStatus
= Status
= refit_call3_wrapper(BS
->StartImage
, ChildImageHandle
, NULL
, NULL
);
352 // control returns here when the child image calls Exit()
353 SPrint(ErrorInfo
, 255, L
"returned from %s", ImageTitle
);
354 if (CheckError(Status
, ErrorInfo
)) {
355 if (ErrorInStep
!= NULL
)
359 // re-open file handles
363 // unload the image, we don't care if it works or not...
364 Status
= refit_call1_wrapper(BS
->UnloadImage
, ChildImageHandle
);
367 MyFreePool(FullLoadOptions
);
369 } /* static EFI_STATUS StartEFIImageList() */
371 static EFI_STATUS
StartEFIImage(IN EFI_DEVICE_PATH
*DevicePath
,
372 IN CHAR16
*LoadOptions
, IN UINTN LoaderType
,
373 IN CHAR16
*ImageTitle
, IN CHAR8 OSType
,
374 OUT UINTN
*ErrorInStep
,
377 EFI_DEVICE_PATH
*DevicePaths
[2];
379 DevicePaths
[0] = DevicePath
;
380 DevicePaths
[1] = NULL
;
381 return StartEFIImageList(DevicePaths
, LoadOptions
, LoaderType
, ImageTitle
, OSType
, ErrorInStep
, Verbose
);
382 } /* static EFI_STATUS StartEFIImage() */
384 // From gummiboot: Retrieve a raw EFI variable.
385 // Returns EFI status
386 static EFI_STATUS
EfivarGetRaw(EFI_GUID
*vendor
, CHAR16
*name
, CHAR8
**buffer
, UINTN
*size
) {
391 l
= sizeof(CHAR16
*) * EFI_MAXIMUM_VARIABLE_SIZE
;
392 buf
= AllocatePool(l
);
394 return EFI_OUT_OF_RESOURCES
;
396 err
= refit_call5_wrapper(RT
->GetVariable
, name
, vendor
, NULL
, &l
, buf
);
397 if (EFI_ERROR(err
) == EFI_SUCCESS
) {
404 } // EFI_STATUS EfivarGetRaw()
406 // From gummiboot: Set an EFI variable
407 static EFI_STATUS
EfivarSetRaw(EFI_GUID
*vendor
, CHAR16
*name
, CHAR8
*buf
, UINTN size
, BOOLEAN persistent
) {
410 flags
= EFI_VARIABLE_BOOTSERVICE_ACCESS
|EFI_VARIABLE_RUNTIME_ACCESS
;
412 flags
|= EFI_VARIABLE_NON_VOLATILE
;
414 return refit_call5_wrapper(RT
->SetVariable
, name
, vendor
, flags
, size
, buf
);
415 } // EFI_STATUS EfivarSetRaw()
417 // From gummiboot: Reboot the computer into its built-in user interface
418 static EFI_STATUS
RebootIntoFirmware(VOID
) {
424 osind
= EFI_OS_INDICATIONS_BOOT_TO_FW_UI
;
426 err
= EfivarGetRaw(&GlobalGuid
, L
"OsIndications", &b
, &size
);
427 if (err
== EFI_SUCCESS
)
431 err
= EfivarSetRaw(&GlobalGuid
, L
"OsIndications", (CHAR8
*)&osind
, sizeof(UINT64
), TRUE
);
432 if (err
!= EFI_SUCCESS
)
435 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);
436 Print(L
"Error calling ResetSystem: %r", err
);
443 // EFI OS loader functions
446 static VOID
StartLoader(LOADER_ENTRY
*Entry
)
448 UINTN ErrorInStep
= 0;
450 BeginExternalScreen(Entry
->UseGraphicsMode
, L
"Booting OS");
451 StartEFIImage(Entry
->DevicePath
, Entry
->LoadOptions
, TYPE_EFI
,
452 Basename(Entry
->LoaderPath
), Entry
->OSType
, &ErrorInStep
, !Entry
->UseGraphicsMode
);
453 FinishExternalScreen();
456 // Locate an initrd or initramfs file that matches the kernel specified by LoaderPath.
457 // The matching file has a name that begins with "init" and includes the same version
458 // number string as is found in LoaderPath -- but not a longer version number string.
459 // For instance, if LoaderPath is \EFI\kernels\bzImage-3.3.0.efi, and if \EFI\kernels
460 // has a file called initramfs-3.3.0.img, this function will return the string
461 // '\EFI\kernels\initramfs-3.3.0.img'. If the directory ALSO contains the file
462 // initramfs-3.3.0-rc7.img or initramfs-13.3.0.img, those files will NOT match;
463 // however, initmine-3.3.0.img might match. (FindInitrd() returns the first match it
464 // finds). Thus, care should be taken to avoid placing duplicate matching files in
465 // the kernel's directory.
466 // If no matching init file can be found, returns NULL.
467 static CHAR16
* FindInitrd(IN CHAR16
*LoaderPath
, IN REFIT_VOLUME
*Volume
) {
468 CHAR16
*InitrdName
= NULL
, *FileName
, *KernelVersion
, *InitrdVersion
, *Path
;
469 REFIT_DIR_ITER DirIter
;
470 EFI_FILE_INFO
*DirEntry
;
472 FileName
= Basename(LoaderPath
);
473 KernelVersion
= FindNumbers(FileName
);
474 Path
= FindPath(LoaderPath
);
476 // Add trailing backslash for root directory; necessary on some systems, but must
477 // NOT be added to all directories, since on other systems, a trailing backslash on
478 // anything but the root directory causes them to flake out!
479 if (StrLen(Path
) == 0) {
480 MergeStrings(&Path
, L
"\\", 0);
482 DirIterOpen(Volume
->RootDir
, Path
, &DirIter
);
483 // Now add a trailing backslash if it was NOT added earlier, for consistency in
484 // building the InitrdName later....
485 if ((StrLen(Path
) > 0) && (Path
[StrLen(Path
) - 1] != L
'\\'))
486 MergeStrings(&Path
, L
"\\", 0);
487 while ((DirIterNext(&DirIter
, 2, L
"init*", &DirEntry
)) && (InitrdName
== NULL
)) {
488 InitrdVersion
= FindNumbers(DirEntry
->FileName
);
489 if (KernelVersion
!= NULL
) {
490 if (StriCmp(InitrdVersion
, KernelVersion
) == 0) {
491 InitrdName
= PoolPrint(L
"%s%s", Path
, DirEntry
->FileName
);
494 if (InitrdVersion
== NULL
) {
495 InitrdName
= PoolPrint(L
"%s%s", Path
, DirEntry
->FileName
);
498 MyFreePool(InitrdVersion
);
500 DirIterClose(&DirIter
);
502 // Note: Don't FreePool(FileName), since Basename returns a pointer WITHIN the string it's passed.
503 MyFreePool(KernelVersion
);
506 } // static CHAR16 * FindInitrd()
508 LOADER_ENTRY
* AddPreparedLoaderEntry(LOADER_ENTRY
*Entry
) {
509 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
512 } // LOADER_ENTRY * AddPreparedLoaderEntry()
514 // Creates a copy of a menu screen.
515 // Returns a pointer to the copy of the menu screen.
516 static REFIT_MENU_SCREEN
* CopyMenuScreen(REFIT_MENU_SCREEN
*Entry
) {
517 REFIT_MENU_SCREEN
*NewEntry
;
520 NewEntry
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
521 if ((Entry
!= NULL
) && (NewEntry
!= NULL
)) {
522 CopyMem(NewEntry
, Entry
, sizeof(REFIT_MENU_SCREEN
));
523 NewEntry
->Title
= (Entry
->Title
) ? StrDuplicate(Entry
->Title
) : NULL
;
524 NewEntry
->TimeoutText
= (Entry
->TimeoutText
) ? StrDuplicate(Entry
->TimeoutText
) : NULL
;
525 if (Entry
->TitleImage
!= NULL
) {
526 NewEntry
->TitleImage
= AllocatePool(sizeof(EG_IMAGE
));
527 if (NewEntry
->TitleImage
!= NULL
)
528 CopyMem(NewEntry
->TitleImage
, Entry
->TitleImage
, sizeof(EG_IMAGE
));
530 NewEntry
->InfoLines
= (CHAR16
**) AllocateZeroPool(Entry
->InfoLineCount
* (sizeof(CHAR16
*)));
531 for (i
= 0; i
< Entry
->InfoLineCount
&& NewEntry
->InfoLines
; i
++) {
532 NewEntry
->InfoLines
[i
] = (Entry
->InfoLines
[i
]) ? StrDuplicate(Entry
->InfoLines
[i
]) : NULL
;
534 NewEntry
->Entries
= (REFIT_MENU_ENTRY
**) AllocateZeroPool(Entry
->EntryCount
* (sizeof (REFIT_MENU_ENTRY
*)));
535 for (i
= 0; i
< Entry
->EntryCount
&& NewEntry
->Entries
; i
++) {
536 AddMenuEntry(NewEntry
, Entry
->Entries
[i
]);
538 NewEntry
->Hint1
= (Entry
->Hint1
) ? StrDuplicate(Entry
->Hint1
) : NULL
;
539 NewEntry
->Hint2
= (Entry
->Hint2
) ? StrDuplicate(Entry
->Hint2
) : NULL
;
542 } // static REFIT_MENU_SCREEN* CopyMenuScreen()
544 // Creates a copy of a menu entry. Intended to enable moving a stack-based
545 // menu entry (such as the ones for the "reboot" and "exit" functions) to
546 // to the heap. This enables easier deletion of the whole set of menu
547 // entries when re-scanning.
548 // Returns a pointer to the copy of the menu entry.
549 static REFIT_MENU_ENTRY
* CopyMenuEntry(REFIT_MENU_ENTRY
*Entry
) {
550 REFIT_MENU_ENTRY
*NewEntry
;
552 NewEntry
= AllocateZeroPool(sizeof(REFIT_MENU_ENTRY
));
553 if ((Entry
!= NULL
) && (NewEntry
!= NULL
)) {
554 CopyMem(NewEntry
, Entry
, sizeof(REFIT_MENU_ENTRY
));
555 NewEntry
->Title
= (Entry
->Title
) ? StrDuplicate(Entry
->Title
) : NULL
;
556 if (Entry
->BadgeImage
!= NULL
) {
557 NewEntry
->BadgeImage
= AllocatePool(sizeof(EG_IMAGE
));
558 if (NewEntry
->BadgeImage
!= NULL
)
559 CopyMem(NewEntry
->BadgeImage
, Entry
->BadgeImage
, sizeof(EG_IMAGE
));
561 if (Entry
->Image
!= NULL
) {
562 NewEntry
->Image
= AllocatePool(sizeof(EG_IMAGE
));
563 if (NewEntry
->Image
!= NULL
)
564 CopyMem(NewEntry
->Image
, Entry
->Image
, sizeof(EG_IMAGE
));
566 if (Entry
->SubScreen
!= NULL
) {
567 NewEntry
->SubScreen
= CopyMenuScreen(Entry
->SubScreen
);
571 } // REFIT_MENU_ENTRY* CopyMenuEntry()
573 // Creates a new LOADER_ENTRY data structure and populates it with
574 // default values from the specified Entry, or NULL values if Entry
575 // is unspecified (NULL).
576 // Returns a pointer to the new data structure, or NULL if it
577 // couldn't be allocated
578 LOADER_ENTRY
*InitializeLoaderEntry(IN LOADER_ENTRY
*Entry
) {
579 LOADER_ENTRY
*NewEntry
= NULL
;
581 NewEntry
= AllocateZeroPool(sizeof(LOADER_ENTRY
));
582 if (NewEntry
!= NULL
) {
583 NewEntry
->me
.Title
= NULL
;
584 NewEntry
->me
.Tag
= TAG_LOADER
;
585 NewEntry
->Enabled
= TRUE
;
586 NewEntry
->UseGraphicsMode
= FALSE
;
587 NewEntry
->OSType
= 0;
589 NewEntry
->LoaderPath
= (Entry
->LoaderPath
) ? StrDuplicate(Entry
->LoaderPath
) : NULL
;
590 NewEntry
->VolName
= (Entry
->VolName
) ? StrDuplicate(Entry
->VolName
) : NULL
;
591 NewEntry
->DevicePath
= Entry
->DevicePath
;
592 NewEntry
->UseGraphicsMode
= Entry
->UseGraphicsMode
;
593 NewEntry
->LoadOptions
= (Entry
->LoadOptions
) ? StrDuplicate(Entry
->LoadOptions
) : NULL
;
594 NewEntry
->InitrdPath
= (Entry
->InitrdPath
) ? StrDuplicate(Entry
->InitrdPath
) : NULL
;
598 } // LOADER_ENTRY *InitializeLoaderEntry()
600 // Adds InitrdPath to Options, but only if Options doesn't already include an
601 // initrd= line. Done to enable overriding the default initrd selection in a
602 // refind_linux.conf file's options list.
603 // Returns a pointer to a new string. The calling function is responsible for
604 // freeing its memory.
605 static CHAR16
*AddInitrdToOptions(CHAR16
*Options
, CHAR16
*InitrdPath
) {
606 CHAR16
*NewOptions
= NULL
;
609 NewOptions
= StrDuplicate(Options
);
610 if ((InitrdPath
!= NULL
) && !StriSubCmp(L
"initrd=", Options
)) {
611 MergeStrings(&NewOptions
, L
"initrd=", L
' ');
612 MergeStrings(&NewOptions
, InitrdPath
, 0);
615 } // CHAR16 *AddInitrdToOptions()
617 // Prepare a REFIT_MENU_SCREEN data structure for a subscreen entry. This sets up
618 // the default entry that launches the boot loader using the same options as the
619 // main Entry does. Subsequent options can be added by the calling function.
620 // If a subscreen already exists in the Entry that's passed to this function,
621 // it's left unchanged and a pointer to it is returned.
622 // Returns a pointer to the new subscreen data structure, or NULL if there
623 // were problems allocating memory.
624 REFIT_MENU_SCREEN
*InitializeSubScreen(IN LOADER_ENTRY
*Entry
) {
625 CHAR16
*FileName
, *MainOptions
= NULL
;
626 REFIT_MENU_SCREEN
*SubScreen
= NULL
;
627 LOADER_ENTRY
*SubEntry
;
629 FileName
= Basename(Entry
->LoaderPath
);
630 if (Entry
->me
.SubScreen
== NULL
) { // No subscreen yet; initialize default entry....
631 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
632 if (SubScreen
!= NULL
) {
633 SubScreen
->Title
= AllocateZeroPool(sizeof(CHAR16
) * 256);
634 SPrint(SubScreen
->Title
, 255, L
"Boot Options for %s on %s",
635 (Entry
->Title
!= NULL
) ? Entry
->Title
: FileName
, Entry
->VolName
);
636 SubScreen
->TitleImage
= Entry
->me
.Image
;
638 SubEntry
= InitializeLoaderEntry(Entry
);
639 if (SubEntry
!= NULL
) {
640 SubEntry
->me
.Title
= StrDuplicate(L
"Boot using default options");
641 MainOptions
= SubEntry
->LoadOptions
;
642 SubEntry
->LoadOptions
= AddInitrdToOptions(MainOptions
, SubEntry
->InitrdPath
);
643 MyFreePool(MainOptions
);
644 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
645 } // if (SubEntry != NULL)
646 SubScreen
->Hint1
= StrDuplicate(SUBSCREEN_HINT1
);
647 if (GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_EDITOR
) {
648 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2_NO_EDITOR
);
650 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2
);
652 } // if (SubScreen != NULL)
653 } else { // existing subscreen; less initialization, and just add new entry later....
654 SubScreen
= Entry
->me
.SubScreen
;
657 } // REFIT_MENU_SCREEN *InitializeSubScreen()
659 VOID
GenerateSubScreen(LOADER_ENTRY
*Entry
, IN REFIT_VOLUME
*Volume
) {
660 REFIT_MENU_SCREEN
*SubScreen
;
661 LOADER_ENTRY
*SubEntry
;
663 CHAR16 DiagsFileName
[256];
668 // create the submenu
669 if (StrLen(Entry
->Title
) == 0) {
670 MyFreePool(Entry
->Title
);
673 SubScreen
= InitializeSubScreen(Entry
);
675 // loader-specific submenu entries
676 if (Entry
->OSType
== 'M') { // entries for Mac OS X
678 SubEntry
= InitializeLoaderEntry(Entry
);
679 if (SubEntry
!= NULL
) {
680 SubEntry
->me
.Title
= L
"Boot Mac OS X with a 64-bit kernel";
681 SubEntry
->LoadOptions
= L
"arch=x86_64";
682 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
683 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
686 SubEntry
= InitializeLoaderEntry(Entry
);
687 if (SubEntry
!= NULL
) {
688 SubEntry
->me
.Title
= L
"Boot Mac OS X with a 32-bit kernel";
689 SubEntry
->LoadOptions
= L
"arch=i386";
690 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
691 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
695 if (!(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_SINGLEUSER
)) {
696 SubEntry
= InitializeLoaderEntry(Entry
);
697 if (SubEntry
!= NULL
) {
698 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode";
699 SubEntry
->UseGraphicsMode
= FALSE
;
700 SubEntry
->LoadOptions
= L
"-v";
701 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
705 SubEntry
= InitializeLoaderEntry(Entry
);
706 if (SubEntry
!= NULL
) {
707 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode (64-bit)";
708 SubEntry
->UseGraphicsMode
= FALSE
;
709 SubEntry
->LoadOptions
= L
"-v arch=x86_64";
710 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
713 SubEntry
= InitializeLoaderEntry(Entry
);
714 if (SubEntry
!= NULL
) {
715 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode (32-bit)";
716 SubEntry
->UseGraphicsMode
= FALSE
;
717 SubEntry
->LoadOptions
= L
"-v arch=i386";
718 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
722 SubEntry
= InitializeLoaderEntry(Entry
);
723 if (SubEntry
!= NULL
) {
724 SubEntry
->me
.Title
= L
"Boot Mac OS X in single user mode";
725 SubEntry
->UseGraphicsMode
= FALSE
;
726 SubEntry
->LoadOptions
= L
"-v -s";
727 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
729 } // single-user mode allowed
731 if (!(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_SAFEMODE
)) {
732 SubEntry
= InitializeLoaderEntry(Entry
);
733 if (SubEntry
!= NULL
) {
734 SubEntry
->me
.Title
= L
"Boot Mac OS X in safe mode";
735 SubEntry
->UseGraphicsMode
= FALSE
;
736 SubEntry
->LoadOptions
= L
"-v -x";
737 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
739 } // safe mode allowed
741 // check for Apple hardware diagnostics
742 StrCpy(DiagsFileName
, L
"System\\Library\\CoreServices\\.diagnostics\\diags.efi");
743 if (FileExists(Volume
->RootDir
, DiagsFileName
) && !(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_HWTEST
)) {
744 SubEntry
= InitializeLoaderEntry(Entry
);
745 if (SubEntry
!= NULL
) {
746 SubEntry
->me
.Title
= L
"Run Apple Hardware Test";
747 MyFreePool(SubEntry
->LoaderPath
);
748 SubEntry
->LoaderPath
= StrDuplicate(DiagsFileName
);
749 SubEntry
->DevicePath
= FileDevicePath(Volume
->DeviceHandle
, SubEntry
->LoaderPath
);
750 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
751 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
753 } // if diagnostics entry found
755 } else if (Entry
->OSType
== 'L') { // entries for Linux kernels with EFI stub loaders
756 File
= ReadLinuxOptionsFile(Entry
->LoaderPath
, Volume
);
758 InitrdName
= FindInitrd(Entry
->LoaderPath
, Volume
);
759 TokenCount
= ReadTokenLine(File
, &TokenList
);
760 // first entry requires special processing, since it was initially set
761 // up with a default title but correct options by InitializeSubScreen(),
763 if ((SubScreen
->Entries
!= NULL
) && (SubScreen
->Entries
[0] != NULL
)) {
764 MyFreePool(SubScreen
->Entries
[0]->Title
);
765 SubScreen
->Entries
[0]->Title
= TokenList
[0] ? StrDuplicate(TokenList
[0]) : StrDuplicate(L
"Boot Linux");
767 FreeTokenLine(&TokenList
, &TokenCount
);
768 while ((TokenCount
= ReadTokenLine(File
, &TokenList
)) > 1) {
769 SubEntry
= InitializeLoaderEntry(Entry
);
770 SubEntry
->me
.Title
= TokenList
[0] ? StrDuplicate(TokenList
[0]) : StrDuplicate(L
"Boot Linux");
771 MyFreePool(SubEntry
->LoadOptions
);
772 SubEntry
->LoadOptions
= AddInitrdToOptions(TokenList
[1], InitrdName
);
773 FreeTokenLine(&TokenList
, &TokenCount
);
774 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_LINUX
;
775 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
777 MyFreePool(InitrdName
);
781 } else if (Entry
->OSType
== 'E') { // entries for ELILO
782 SubEntry
= InitializeLoaderEntry(Entry
);
783 if (SubEntry
!= NULL
) {
784 SubEntry
->me
.Title
= L
"Run ELILO in interactive mode";
785 SubEntry
->LoadOptions
= L
"-p";
786 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
787 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
790 SubEntry
= InitializeLoaderEntry(Entry
);
791 if (SubEntry
!= NULL
) {
792 SubEntry
->me
.Title
= L
"Boot Linux for a 17\" iMac or a 15\" MacBook Pro (*)";
793 SubEntry
->UseGraphicsMode
= TRUE
;
794 SubEntry
->LoadOptions
= L
"-d 0 i17";
795 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
796 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
799 SubEntry
= InitializeLoaderEntry(Entry
);
800 if (SubEntry
!= NULL
) {
801 SubEntry
->me
.Title
= L
"Boot Linux for a 20\" iMac (*)";
802 SubEntry
->UseGraphicsMode
= TRUE
;
803 SubEntry
->LoadOptions
= L
"-d 0 i20";
804 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
805 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
808 SubEntry
= InitializeLoaderEntry(Entry
);
809 if (SubEntry
!= NULL
) {
810 SubEntry
->me
.Title
= L
"Boot Linux for a Mac Mini (*)";
811 SubEntry
->UseGraphicsMode
= TRUE
;
812 SubEntry
->LoadOptions
= L
"-d 0 mini";
813 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
814 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
817 AddMenuInfoLine(SubScreen
, L
"NOTE: This is an example. Entries");
818 AddMenuInfoLine(SubScreen
, L
"marked with (*) may not work.");
820 } else if (Entry
->OSType
== 'X') { // entries for xom.efi
821 // by default, skip the built-in selection and boot from hard disk only
822 Entry
->LoadOptions
= L
"-s -h";
824 SubEntry
= InitializeLoaderEntry(Entry
);
825 if (SubEntry
!= NULL
) {
826 SubEntry
->me
.Title
= L
"Boot Windows from Hard Disk";
827 SubEntry
->LoadOptions
= L
"-s -h";
828 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
829 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
832 SubEntry
= InitializeLoaderEntry(Entry
);
833 if (SubEntry
!= NULL
) {
834 SubEntry
->me
.Title
= L
"Boot Windows from CD-ROM";
835 SubEntry
->LoadOptions
= L
"-s -c";
836 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
837 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
840 SubEntry
= InitializeLoaderEntry(Entry
);
841 if (SubEntry
!= NULL
) {
842 SubEntry
->me
.Title
= L
"Run XOM in text mode";
843 SubEntry
->UseGraphicsMode
= FALSE
;
844 SubEntry
->LoadOptions
= L
"-v";
845 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
846 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
848 } // entries for xom.efi
849 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
850 Entry
->me
.SubScreen
= SubScreen
;
851 } // VOID GenerateSubScreen()
853 // Returns options for a Linux kernel. Reads them from an options file in the
854 // kernel's directory; and if present, adds an initrd= option for an initial
855 // RAM disk file with the same version number as the kernel file.
856 static CHAR16
* GetMainLinuxOptions(IN CHAR16
* LoaderPath
, IN REFIT_VOLUME
*Volume
) {
857 CHAR16
*Options
= NULL
, *InitrdName
, *FullOptions
= NULL
;
859 Options
= GetFirstOptionsFromFile(LoaderPath
, Volume
);
860 InitrdName
= FindInitrd(LoaderPath
, Volume
);
861 FullOptions
= AddInitrdToOptions(Options
, InitrdName
);
864 MyFreePool(InitrdName
);
865 return (FullOptions
);
866 } // static CHAR16 * GetMainLinuxOptions()
868 // Try to guess the name of the Linux distribution & add that name to
870 static VOID
GuessLinuxDistribution(CHAR16
**OSIconName
, REFIT_VOLUME
*Volume
, CHAR16
*LoaderPath
) {
874 UINTN TokenCount
= 0;
876 // If on Linux root fs, /etc/os-release file probably has clues....
877 if (FileExists(Volume
->RootDir
, L
"etc\\os-release") &&
878 (ReadFile(Volume
->RootDir
, L
"etc\\os-release", &File
, &FileSize
) == EFI_SUCCESS
)) {
880 TokenCount
= ReadTokenLine(&File
, &TokenList
);
881 if ((TokenCount
> 1) && ((StriCmp(TokenList
[0], L
"ID") == 0) || (StriCmp(TokenList
[0], L
"NAME") == 0))) {
882 MergeStrings(OSIconName
, TokenList
[1], L
',');
884 FreeTokenLine(&TokenList
, &TokenCount
);
885 } while (TokenCount
> 0);
886 MyFreePool(File
.Buffer
);
889 // Search for clues in the kernel's filename....
890 if (StriSubCmp(L
".fc", LoaderPath
))
891 MergeStrings(OSIconName
, L
"fedora", L
',');
892 if (StriSubCmp(L
".el", LoaderPath
))
893 MergeStrings(OSIconName
, L
"redhat", L
',');
894 } // VOID GuessLinuxDistribution()
896 // Sets a few defaults for a loader entry -- mainly the icon, but also the OS type
897 // code and shortcut letter. For Linux EFI stub loaders, also sets kernel options
898 // that will (with luck) work fairly automatically.
899 VOID
SetLoaderDefaults(LOADER_ENTRY
*Entry
, CHAR16
*LoaderPath
, REFIT_VOLUME
*Volume
) {
900 CHAR16
*FileName
, *PathOnly
, *NoExtension
, *OSIconName
= NULL
, *Temp
, *SubString
;
901 CHAR16 ShortcutLetter
= 0;
904 FileName
= Basename(LoaderPath
);
905 PathOnly
= FindPath(LoaderPath
);
906 NoExtension
= StripEfiExtension(FileName
);
908 // locate a custom icon for the loader
909 // Anything found here takes precedence over the "hints" in the OSIconName variable
910 if (!Entry
->me
.Image
)
911 Entry
->me
.Image
= egLoadIconAnyType(Volume
->RootDir
, PathOnly
, NoExtension
, GlobalConfig
.IconSizes
[ICON_SIZE_BIG
]);
912 if (!Entry
->me
.Image
)
913 Entry
->me
.Image
= egCopyImage(Volume
->VolIconImage
);
915 // Begin creating icon "hints" by using last part of directory path leading
917 Temp
= FindLastDirName(LoaderPath
);
918 MergeStrings(&OSIconName
, Temp
, L
',');
921 if (OSIconName
!= NULL
) {
922 ShortcutLetter
= OSIconName
[0];
925 // Add every "word" in the volume label, delimited by spaces, dashes (-), or
926 // underscores (_), to the list of hints to be used in searching for OS
928 if ((Volume
->VolName
) && (StrLen(Volume
->VolName
) > 0)) {
929 Temp
= SubString
= StrDuplicate(Volume
->VolName
);
931 Length
= StrLen(Temp
);
932 for (i
= 0; i
< Length
; i
++) {
933 if ((Temp
[i
] == L
' ') || (Temp
[i
] == L
'_') || (Temp
[i
] == L
'-')) {
935 if (StrLen(SubString
) > 0)
936 MergeStrings(&OSIconName
, SubString
, L
',');
937 SubString
= Temp
+ i
+ 1;
940 MergeStrings(&OSIconName
, SubString
, L
',');
945 // detect specific loaders
946 if (StriSubCmp(L
"bzImage", FileName
) || StriSubCmp(L
"vmlinuz", FileName
)) {
947 GuessLinuxDistribution(&OSIconName
, Volume
, LoaderPath
);
948 MergeStrings(&OSIconName
, L
"linux", L
',');
950 if (ShortcutLetter
== 0)
951 ShortcutLetter
= 'L';
952 Entry
->LoadOptions
= GetMainLinuxOptions(LoaderPath
, Volume
);
953 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_LINUX
;
954 } else if (StriSubCmp(L
"refit", LoaderPath
)) {
955 MergeStrings(&OSIconName
, L
"refit", L
',');
957 ShortcutLetter
= 'R';
958 } else if (StriSubCmp(L
"refind", LoaderPath
)) {
959 MergeStrings(&OSIconName
, L
"refind", L
',');
961 ShortcutLetter
= 'R';
962 } else if (StriCmp(LoaderPath
, MACOSX_LOADER_PATH
) == 0) {
963 if (Volume
->VolIconImage
!= NULL
) { // custom icon file found
964 Entry
->me
.Image
= Volume
->VolIconImage
;
966 MergeStrings(&OSIconName
, L
"mac", L
',');
968 ShortcutLetter
= 'M';
969 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
970 } else if (StriCmp(FileName
, L
"diags.efi") == 0) {
971 MergeStrings(&OSIconName
, L
"hwtest", L
',');
972 } else if (StriCmp(FileName
, L
"e.efi") == 0 || StriCmp(FileName
, L
"elilo.efi") == 0 || StriSubCmp(L
"elilo", FileName
)) {
973 MergeStrings(&OSIconName
, L
"elilo,linux", L
',');
975 if (ShortcutLetter
== 0)
976 ShortcutLetter
= 'L';
977 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
978 } else if (StriSubCmp(L
"grub", FileName
)) {
980 ShortcutLetter
= 'G';
981 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_GRUB
;
982 } else if (StriCmp(FileName
, L
"cdboot.efi") == 0 ||
983 StriCmp(FileName
, L
"bootmgr.efi") == 0 ||
984 StriCmp(FileName
, L
"bootmgfw.efi") == 0 ||
985 StriCmp(FileName
, L
"bkpbootmgfw.efi") == 0) {
986 MergeStrings(&OSIconName
, L
"win", L
',');
988 ShortcutLetter
= 'W';
989 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
990 } else if (StriCmp(FileName
, L
"xom.efi") == 0) {
991 MergeStrings(&OSIconName
, L
"xom,win", L
',');
992 Entry
->UseGraphicsMode
= TRUE
;
994 ShortcutLetter
= 'W';
995 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
998 if ((ShortcutLetter
>= 'a') && (ShortcutLetter
<= 'z'))
999 ShortcutLetter
= ShortcutLetter
- 'a' + 'A'; // convert lowercase to uppercase
1000 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1001 if (Entry
->me
.Image
== NULL
)
1002 Entry
->me
.Image
= LoadOSIcon(OSIconName
, L
"unknown", FALSE
);
1003 MyFreePool(PathOnly
);
1004 } // VOID SetLoaderDefaults()
1006 // Add a specified EFI boot loader to the list, using automatic settings
1007 // for icons, options, etc.
1008 LOADER_ENTRY
* AddLoaderEntry(IN CHAR16
*LoaderPath
, IN CHAR16
*LoaderTitle
, IN REFIT_VOLUME
*Volume
) {
1009 LOADER_ENTRY
*Entry
;
1011 CleanUpPathNameSlashes(LoaderPath
);
1012 Entry
= InitializeLoaderEntry(NULL
);
1013 if (Entry
!= NULL
) {
1014 Entry
->Title
= StrDuplicate((LoaderTitle
!= NULL
) ? LoaderTitle
: LoaderPath
);
1015 Entry
->me
.Title
= AllocateZeroPool(sizeof(CHAR16
) * 256);
1016 SPrint(Entry
->me
.Title
, 255, L
"Boot %s from %s ", (LoaderTitle
!= NULL
) ? LoaderTitle
: LoaderPath
, Volume
->VolName
);
1018 Entry
->me
.BadgeImage
= Volume
->VolBadgeImage
;
1019 if ((LoaderPath
!= NULL
) && (LoaderPath
[0] != L
'\\')) {
1020 Entry
->LoaderPath
= StrDuplicate(L
"\\");
1022 Entry
->LoaderPath
= NULL
;
1024 MergeStrings(&(Entry
->LoaderPath
), LoaderPath
, 0);
1025 Entry
->VolName
= Volume
->VolName
;
1026 Entry
->DevicePath
= FileDevicePath(Volume
->DeviceHandle
, Entry
->LoaderPath
);
1027 SetLoaderDefaults(Entry
, LoaderPath
, Volume
);
1028 GenerateSubScreen(Entry
, Volume
);
1029 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1033 } // LOADER_ENTRY * AddLoaderEntry()
1035 // Returns -1 if (Time1 < Time2), +1 if (Time1 > Time2), or 0 if
1036 // (Time1 == Time2). Precision is only to the nearest second; since
1037 // this is used for sorting boot loader entries, differences smaller
1038 // than this are likely to be meaningless (and unlikely!).
1039 INTN
TimeComp(IN EFI_TIME
*Time1
, IN EFI_TIME
*Time2
) {
1040 INT64 Time1InSeconds
, Time2InSeconds
;
1042 // Following values are overestimates; I'm assuming 31 days in every month.
1043 // This is fine for the purpose of this function, which is limited
1044 Time1InSeconds
= Time1
->Second
+ (Time1
->Minute
* 60) + (Time1
->Hour
* 3600) + (Time1
->Day
* 86400) +
1045 (Time1
->Month
* 2678400) + ((Time1
->Year
- 1998) * 32140800);
1046 Time2InSeconds
= Time2
->Second
+ (Time2
->Minute
* 60) + (Time2
->Hour
* 3600) + (Time2
->Day
* 86400) +
1047 (Time2
->Month
* 2678400) + ((Time2
->Year
- 1998) * 32140800);
1048 if (Time1InSeconds
< Time2InSeconds
)
1050 else if (Time1InSeconds
> Time2InSeconds
)
1054 } // INTN TimeComp()
1056 // Adds a loader list element, keeping it sorted by date. Returns the new
1057 // first element (the one with the most recent date).
1058 static struct LOADER_LIST
* AddLoaderListEntry(struct LOADER_LIST
*LoaderList
, struct LOADER_LIST
*NewEntry
) {
1059 struct LOADER_LIST
*LatestEntry
, *CurrentEntry
, *PrevEntry
= NULL
;
1061 LatestEntry
= CurrentEntry
= LoaderList
;
1062 if (LoaderList
== NULL
) {
1063 LatestEntry
= NewEntry
;
1065 while ((CurrentEntry
!= NULL
) && (TimeComp(&(NewEntry
->TimeStamp
), &(CurrentEntry
->TimeStamp
)) < 0)) {
1066 PrevEntry
= CurrentEntry
;
1067 CurrentEntry
= CurrentEntry
->NextEntry
;
1069 NewEntry
->NextEntry
= CurrentEntry
;
1070 if (PrevEntry
== NULL
) {
1071 LatestEntry
= NewEntry
;
1073 PrevEntry
->NextEntry
= NewEntry
;
1076 return (LatestEntry
);
1077 } // static VOID AddLoaderListEntry()
1079 // Delete the LOADER_LIST linked list
1080 static VOID
CleanUpLoaderList(struct LOADER_LIST
*LoaderList
) {
1081 struct LOADER_LIST
*Temp
;
1083 while (LoaderList
!= NULL
) {
1085 LoaderList
= LoaderList
->NextEntry
;
1086 MyFreePool(Temp
->FileName
);
1089 } // static VOID CleanUpLoaderList()
1091 // Returns FALSE if the specified file/volume matches the GlobalConfig.DontScanDirs
1092 // or GlobalConfig.DontScanVolumes specification, or if Path points to a volume
1093 // other than the one specified by Volume, or if the specified path is SelfDir.
1094 // Returns TRUE if none of these conditions is met -- that is, if the path is
1095 // eligible for scanning.
1096 static BOOLEAN
ShouldScan(REFIT_VOLUME
*Volume
, CHAR16
*Path
) {
1097 CHAR16
*VolName
= NULL
, *DontScanDir
, *PathCopy
= NULL
;
1099 BOOLEAN ScanIt
= TRUE
;
1101 if ((IsIn(Volume
->VolName
, GlobalConfig
.DontScanVolumes
)) || (IsIn(Volume
->PartName
, GlobalConfig
.DontScanVolumes
)))
1104 if ((StriCmp(Path
, SelfDirPath
) == 0) && (Volume
->DeviceHandle
== SelfVolume
->DeviceHandle
))
1107 // See if Path includes an explicit volume declaration that's NOT Volume....
1108 PathCopy
= StrDuplicate(Path
);
1109 if (SplitVolumeAndFilename(&PathCopy
, &VolName
)) {
1110 VolumeNumberToName(Volume
, &VolName
);
1111 if (VolName
&& StriCmp(VolName
, Volume
->VolName
) != 0) {
1114 } // if Path includes volume specification
1115 MyFreePool(PathCopy
);
1116 MyFreePool(VolName
);
1119 // See if Volume is in GlobalConfig.DontScanDirs....
1120 while (ScanIt
&& (DontScanDir
= FindCommaDelimited(GlobalConfig
.DontScanDirs
, i
++))) {
1121 SplitVolumeAndFilename(&DontScanDir
, &VolName
);
1122 CleanUpPathNameSlashes(DontScanDir
);
1123 VolumeNumberToName(Volume
, &VolName
);
1124 if (VolName
!= NULL
) {
1125 if ((StriCmp(VolName
, Volume
->VolName
) == 0) && (StriCmp(DontScanDir
, Path
) == 0))
1128 if (StriCmp(DontScanDir
, Path
) == 0)
1131 MyFreePool(DontScanDir
);
1132 MyFreePool(VolName
);
1138 } // BOOLEAN ShouldScan()
1140 // Returns TRUE if the file is byte-for-byte identical with the fallback file
1141 // on the volume AND if the file is not itself the fallback file; returns
1142 // FALSE if the file is not identical to the fallback file OR if the file
1143 // IS the fallback file. Intended for use in excluding the fallback boot
1144 // loader when it's a duplicate of another boot loader.
1145 static BOOLEAN
DuplicatesFallback(IN REFIT_VOLUME
*Volume
, IN CHAR16
*FileName
) {
1146 CHAR8
*FileContents
, *FallbackContents
;
1147 EFI_FILE_HANDLE FileHandle
, FallbackHandle
;
1148 EFI_FILE_INFO
*FileInfo
, *FallbackInfo
;
1149 UINTN FileSize
= 0, FallbackSize
= 0;
1151 BOOLEAN AreIdentical
= FALSE
;
1153 if (!FileExists(Volume
->RootDir
, FileName
) || !FileExists(Volume
->RootDir
, FALLBACK_FULLNAME
))
1156 CleanUpPathNameSlashes(FileName
);
1158 if (StriCmp(FileName
, FALLBACK_FULLNAME
) == 0)
1159 return FALSE
; // identical filenames, so not a duplicate....
1161 Status
= refit_call5_wrapper(Volume
->RootDir
->Open
, Volume
->RootDir
, &FileHandle
, FileName
, EFI_FILE_MODE_READ
, 0);
1162 if (Status
== EFI_SUCCESS
) {
1163 FileInfo
= LibFileInfo(FileHandle
);
1164 FileSize
= FileInfo
->FileSize
;
1169 Status
= refit_call5_wrapper(Volume
->RootDir
->Open
, Volume
->RootDir
, &FallbackHandle
, FALLBACK_FULLNAME
, EFI_FILE_MODE_READ
, 0);
1170 if (Status
== EFI_SUCCESS
) {
1171 FallbackInfo
= LibFileInfo(FallbackHandle
);
1172 FallbackSize
= FallbackInfo
->FileSize
;
1174 refit_call1_wrapper(FileHandle
->Close
, FileHandle
);
1178 if (FallbackSize
!= FileSize
) { // not same size, so can't be identical
1179 AreIdentical
= FALSE
;
1180 } else { // could be identical; do full check....
1181 FileContents
= AllocatePool(FileSize
);
1182 FallbackContents
= AllocatePool(FallbackSize
);
1183 if (FileContents
&& FallbackContents
) {
1184 Status
= refit_call3_wrapper(FileHandle
->Read
, FileHandle
, &FileSize
, FileContents
);
1185 if (Status
== EFI_SUCCESS
) {
1186 Status
= refit_call3_wrapper(FallbackHandle
->Read
, FallbackHandle
, &FallbackSize
, FallbackContents
);
1188 if (Status
== EFI_SUCCESS
) {
1189 AreIdentical
= (CompareMem(FileContents
, FallbackContents
, FileSize
) == 0);
1192 MyFreePool(FileContents
);
1193 MyFreePool(FallbackContents
);
1196 // BUG ALERT: Some systems (e.g., DUET, some Macs with large displays) crash if the
1197 // following two calls are reversed. Go figure....
1198 refit_call1_wrapper(FileHandle
->Close
, FallbackHandle
);
1199 refit_call1_wrapper(FileHandle
->Close
, FileHandle
);
1200 return AreIdentical
;
1201 } // BOOLEAN DuplicatesFallback()
1203 // Returns FALSE if two measures of file size are identical for a single file,
1204 // TRUE if not or if the file can't be opened and the other measure is non-0.
1205 // Despite the function's name, this isn't really a direct test of symbolic
1206 // link status, since EFI doesn't officially support symlinks. It does seem
1207 // to be a reliable indicator, though. (OTOH, some disk errors might cause a
1208 // file to fail to open, which would return a false positive -- but as I use
1209 // this function to exclude symbolic links from the list of boot loaders,
1210 // that would be fine, since such boot loaders wouldn't work.)
1211 static BOOLEAN
IsSymbolicLink(REFIT_VOLUME
*Volume
, CHAR16
*Path
, EFI_FILE_INFO
*DirEntry
) {
1212 EFI_FILE_HANDLE FileHandle
;
1213 EFI_FILE_INFO
*FileInfo
= NULL
;
1215 UINTN FileSize2
= 0;
1218 FileName
= StrDuplicate(Path
);
1219 MergeStrings(&FileName
, DirEntry
->FileName
, L
'\\');
1220 CleanUpPathNameSlashes(FileName
);
1222 Status
= refit_call5_wrapper(Volume
->RootDir
->Open
, Volume
->RootDir
, &FileHandle
, FileName
, EFI_FILE_MODE_READ
, 0);
1223 if (Status
== EFI_SUCCESS
) {
1224 FileInfo
= LibFileInfo(FileHandle
);
1225 if (FileInfo
!= NULL
)
1226 FileSize2
= FileInfo
->FileSize
;
1229 MyFreePool(FileName
);
1230 MyFreePool(FileInfo
);
1232 return (DirEntry
->FileSize
!= FileSize2
);
1233 } // BOOLEAN IsSymbolicLink()
1235 // Returns TRUE if a file with the same name as the original but with
1236 // ".efi.signed" is also present in the same directory. Ubuntu is using
1237 // this filename as a signed version of the original unsigned kernel, and
1238 // there's no point in cluttering the display with two kernels that will
1239 // behave identically on non-SB systems, or when one will fail when SB
1241 static BOOLEAN
HasSignedCounterpart(IN REFIT_VOLUME
*Volume
, IN CHAR16
*Path
, IN CHAR16
*Filename
) {
1242 CHAR16
*NewFile
= NULL
;
1243 BOOLEAN retval
= FALSE
;
1245 MergeStrings(&NewFile
, Path
, 0);
1246 MergeStrings(&NewFile
, Filename
, L
'\\');
1247 MergeStrings(&NewFile
, L
".efi.signed", 0);
1248 if (FileExists(Volume
->RootDir
, NewFile
))
1250 MyFreePool(NewFile
);
1253 } // BOOLEAN HasSignedCounterpart()
1255 // Scan an individual directory for EFI boot loader files and, if found,
1256 // add them to the list. Exception: Ignores FALLBACK_FULLNAME, which is picked
1257 // up in ScanEfiFiles(). Sorts the entries within the loader directory so that
1258 // the most recent one appears first in the list.
1259 // Returns TRUE if a duplicate for FALLBACK_FILENAME was found, FALSE if not.
1260 static BOOLEAN
ScanLoaderDir(IN REFIT_VOLUME
*Volume
, IN CHAR16
*Path
, IN CHAR16
*Pattern
)
1263 REFIT_DIR_ITER DirIter
;
1264 EFI_FILE_INFO
*DirEntry
;
1265 CHAR16 FileName
[256], *Extension
;
1266 struct LOADER_LIST
*LoaderList
= NULL
, *NewLoader
;
1267 BOOLEAN FoundFallbackDuplicate
= FALSE
;
1269 if ((!SelfDirPath
|| !Path
|| ((StriCmp(Path
, SelfDirPath
) == 0) && (Volume
->DeviceHandle
!= SelfVolume
->DeviceHandle
)) ||
1270 (StriCmp(Path
, SelfDirPath
) != 0)) &&
1271 (ShouldScan(Volume
, Path
))) {
1272 // look through contents of the directory
1273 DirIterOpen(Volume
->RootDir
, Path
, &DirIter
);
1274 while (DirIterNext(&DirIter
, 2, Pattern
, &DirEntry
)) {
1275 Extension
= FindExtension(DirEntry
->FileName
);
1276 if (DirEntry
->FileName
[0] == '.' ||
1277 StriCmp(Extension
, L
".icns") == 0 ||
1278 StriCmp(Extension
, L
".png") == 0 ||
1279 (StriCmp(DirEntry
->FileName
, FALLBACK_BASENAME
) == 0 && (StriCmp(Path
, L
"EFI\\BOOT") == 0)) ||
1280 StriSubCmp(L
"shell", DirEntry
->FileName
) ||
1281 IsSymbolicLink(Volume
, Path
, DirEntry
) || /* is symbolic link */
1282 HasSignedCounterpart(Volume
, Path
, DirEntry
->FileName
) || /* a file with same name plus ".efi.signed" is present */
1283 FilenameIn(Volume
, Path
, DirEntry
->FileName
, GlobalConfig
.DontScanFiles
))
1284 continue; // skip this
1287 SPrint(FileName
, 255, L
"\\%s\\%s", Path
, DirEntry
->FileName
);
1289 SPrint(FileName
, 255, L
"\\%s", DirEntry
->FileName
);
1290 CleanUpPathNameSlashes(FileName
);
1292 if(!IsValidLoader(Volume
->RootDir
, FileName
))
1295 NewLoader
= AllocateZeroPool(sizeof(struct LOADER_LIST
));
1296 if (NewLoader
!= NULL
) {
1297 NewLoader
->FileName
= StrDuplicate(FileName
);
1298 NewLoader
->TimeStamp
= DirEntry
->ModificationTime
;
1299 LoaderList
= AddLoaderListEntry(LoaderList
, NewLoader
);
1300 if (DuplicatesFallback(Volume
, FileName
))
1301 FoundFallbackDuplicate
= TRUE
;
1303 MyFreePool(Extension
);
1306 NewLoader
= LoaderList
;
1307 while (NewLoader
!= NULL
) {
1308 AddLoaderEntry(NewLoader
->FileName
, NULL
, Volume
);
1309 NewLoader
= NewLoader
->NextEntry
;
1312 CleanUpLoaderList(LoaderList
);
1313 Status
= DirIterClose(&DirIter
);
1314 // NOTE: EFI_INVALID_PARAMETER really is an error that should be reported;
1315 // but I've gotten reports from users who are getting this error occasionally
1316 // and I can't find anything wrong or reproduce the problem, so I'm putting
1317 // it down to buggy EFI implementations and ignoring that particular error....
1318 if ((Status
!= EFI_NOT_FOUND
) && (Status
!= EFI_INVALID_PARAMETER
)) {
1320 SPrint(FileName
, 255, L
"while scanning the %s directory", Path
);
1322 StrCpy(FileName
, L
"while scanning the root directory");
1323 CheckError(Status
, FileName
);
1324 } // if (Status != EFI_NOT_FOUND)
1325 } // if not scanning a blacklisted directory
1327 return FoundFallbackDuplicate
;
1328 } /* static VOID ScanLoaderDir() */
1330 static VOID
ScanEfiFiles(REFIT_VOLUME
*Volume
) {
1332 REFIT_DIR_ITER EfiDirIter
;
1333 EFI_FILE_INFO
*EfiDirEntry
;
1334 CHAR16 FileName
[256], *Directory
= NULL
, *MatchPatterns
, *VolName
= NULL
, *SelfPath
;
1336 BOOLEAN ScanFallbackLoader
= TRUE
;
1337 BOOLEAN FoundBRBackup
= FALSE
;
1339 if ((Volume
->RootDir
!= NULL
) && (Volume
->VolName
!= NULL
) && (Volume
->IsReadable
)) {
1340 MatchPatterns
= StrDuplicate(LOADER_MATCH_PATTERNS
);
1341 if (GlobalConfig
.ScanAllLinux
)
1342 MergeStrings(&MatchPatterns
, LINUX_MATCH_PATTERNS
, L
',');
1344 // check for Mac OS X boot loader
1345 if (ShouldScan(Volume
, L
"System\\Library\\CoreServices")) {
1346 StrCpy(FileName
, MACOSX_LOADER_PATH
);
1347 if (FileExists(Volume
->RootDir
, FileName
) && !FilenameIn(Volume
, Directory
, L
"boot.efi", GlobalConfig
.DontScanFiles
)) {
1348 AddLoaderEntry(FileName
, L
"Mac OS X", Volume
);
1349 if (DuplicatesFallback(Volume
, FileName
))
1350 ScanFallbackLoader
= FALSE
;
1354 StrCpy(FileName
, L
"System\\Library\\CoreServices\\xom.efi");
1355 if (FileExists(Volume
->RootDir
, FileName
) && !FilenameIn(Volume
, Directory
, L
"boot.efi", GlobalConfig
.DontScanFiles
)) {
1356 AddLoaderEntry(FileName
, L
"Windows XP (XoM)", Volume
);
1357 if (DuplicatesFallback(Volume
, FileName
))
1358 ScanFallbackLoader
= FALSE
;
1360 } // if should scan Mac directory
1362 // check for Microsoft boot loader/menu
1363 if (ShouldScan(Volume
, L
"EFI\\Microsoft\\Boot")) {
1364 StrCpy(FileName
, L
"EFI\\Microsoft\\Boot\\bkpbootmgfw.efi");
1365 if (FileExists(Volume
->RootDir
, FileName
) && !FilenameIn(Volume
, Directory
, L
"bkpbootmgfw.efi",
1366 GlobalConfig
.DontScanFiles
)) {
1367 AddLoaderEntry(FileName
, L
"Microsoft EFI boot (Boot Repair backup)", Volume
);
1368 FoundBRBackup
= TRUE
;
1369 if (DuplicatesFallback(Volume
, FileName
))
1370 ScanFallbackLoader
= FALSE
;
1372 StrCpy(FileName
, L
"EFI\\Microsoft\\Boot\\bootmgfw.efi");
1373 if (FileExists(Volume
->RootDir
, FileName
) && !FilenameIn(Volume
, Directory
, L
"bootmgfw.efi", GlobalConfig
.DontScanFiles
)) {
1375 AddLoaderEntry(FileName
, L
"Supposed Microsoft EFI boot (probably GRUB)", Volume
);
1377 AddLoaderEntry(FileName
, L
"Microsoft EFI boot", Volume
);
1378 if (DuplicatesFallback(Volume
, FileName
))
1379 ScanFallbackLoader
= FALSE
;
1383 // scan the root directory for EFI executables
1384 if (ScanLoaderDir(Volume
, L
"\\", MatchPatterns
))
1385 ScanFallbackLoader
= FALSE
;
1387 // scan subdirectories of the EFI directory (as per the standard)
1388 DirIterOpen(Volume
->RootDir
, L
"EFI", &EfiDirIter
);
1389 while (DirIterNext(&EfiDirIter
, 1, NULL
, &EfiDirEntry
)) {
1390 if (StriCmp(EfiDirEntry
->FileName
, L
"tools") == 0 || EfiDirEntry
->FileName
[0] == '.')
1391 continue; // skip this, doesn't contain boot loaders or is scanned later
1392 SPrint(FileName
, 255, L
"EFI\\%s", EfiDirEntry
->FileName
);
1393 if (ScanLoaderDir(Volume
, FileName
, MatchPatterns
))
1394 ScanFallbackLoader
= FALSE
;
1396 Status
= DirIterClose(&EfiDirIter
);
1397 if (Status
!= EFI_NOT_FOUND
)
1398 CheckError(Status
, L
"while scanning the EFI directory");
1400 // Scan user-specified (or additional default) directories....
1402 while ((Directory
= FindCommaDelimited(GlobalConfig
.AlsoScan
, i
++)) != NULL
) {
1403 if (ShouldScan(Volume
, Directory
)) {
1404 SplitVolumeAndFilename(&Directory
, &VolName
);
1405 CleanUpPathNameSlashes(Directory
);
1406 Length
= StrLen(Directory
);
1407 if ((Length
> 0) && ScanLoaderDir(Volume
, Directory
, MatchPatterns
))
1408 ScanFallbackLoader
= FALSE
;
1409 MyFreePool(VolName
);
1410 } // if should scan dir
1411 MyFreePool(Directory
);
1414 // Don't scan the fallback loader if it's on the same volume and a duplicate of rEFInd itself....
1415 SelfPath
= DevicePathToStr(SelfLoadedImage
->FilePath
);
1416 CleanUpPathNameSlashes(SelfPath
);
1417 if ((Volume
->DeviceHandle
== SelfLoadedImage
->DeviceHandle
) && DuplicatesFallback(Volume
, SelfPath
))
1418 ScanFallbackLoader
= FALSE
;
1420 // If not a duplicate & if it exists & if it's not us, create an entry
1421 // for the fallback boot loader
1422 if (ScanFallbackLoader
&& FileExists(Volume
->RootDir
, FALLBACK_FULLNAME
) && ShouldScan(Volume
, L
"EFI\\BOOT"))
1423 AddLoaderEntry(FALLBACK_FULLNAME
, L
"Fallback boot loader", Volume
);
1425 } // static VOID ScanEfiFiles()
1427 // Scan internal disks for valid EFI boot loaders....
1428 static VOID
ScanInternal(VOID
) {
1431 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1432 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_INTERNAL
) {
1433 ScanEfiFiles(Volumes
[VolumeIndex
]);
1436 } // static VOID ScanInternal()
1438 // Scan external disks for valid EFI boot loaders....
1439 static VOID
ScanExternal(VOID
) {
1442 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1443 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_EXTERNAL
) {
1444 ScanEfiFiles(Volumes
[VolumeIndex
]);
1447 } // static VOID ScanExternal()
1449 // Scan internal disks for valid EFI boot loaders....
1450 static VOID
ScanOptical(VOID
) {
1453 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1454 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_OPTICAL
) {
1455 ScanEfiFiles(Volumes
[VolumeIndex
]);
1458 } // static VOID ScanOptical()
1461 // legacy boot functions
1464 static EFI_STATUS
ActivateMbrPartition(IN EFI_BLOCK_IO
*BlockIO
, IN UINTN PartitionIndex
)
1467 UINT8 SectorBuffer
[512];
1468 MBR_PARTITION_INFO
*MbrTable
, *EMbrTable
;
1469 UINT32 ExtBase
, ExtCurrent
, NextExtCurrent
;
1470 UINTN LogicalPartitionIndex
= 4;
1472 BOOLEAN HaveBootCode
;
1475 Status
= refit_call5_wrapper(BlockIO
->ReadBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, 0, 512, SectorBuffer
);
1476 if (EFI_ERROR(Status
))
1478 if (*((UINT16
*)(SectorBuffer
+ 510)) != 0xaa55)
1479 return EFI_NOT_FOUND
; // safety measure #1
1481 // add boot code if necessary
1482 HaveBootCode
= FALSE
;
1483 for (i
= 0; i
< MBR_BOOTCODE_SIZE
; i
++) {
1484 if (SectorBuffer
[i
] != 0) {
1485 HaveBootCode
= TRUE
;
1489 if (!HaveBootCode
) {
1490 // no boot code found in the MBR, add the syslinux MBR code
1491 SetMem(SectorBuffer
, MBR_BOOTCODE_SIZE
, 0);
1492 CopyMem(SectorBuffer
, syslinux_mbr
, SYSLINUX_MBR_SIZE
);
1495 // set the partition active
1496 MbrTable
= (MBR_PARTITION_INFO
*)(SectorBuffer
+ 446);
1498 for (i
= 0; i
< 4; i
++) {
1499 if (MbrTable
[i
].Flags
!= 0x00 && MbrTable
[i
].Flags
!= 0x80)
1500 return EFI_NOT_FOUND
; // safety measure #2
1501 if (i
== PartitionIndex
)
1502 MbrTable
[i
].Flags
= 0x80;
1503 else if (PartitionIndex
>= 4 && IS_EXTENDED_PART_TYPE(MbrTable
[i
].Type
)) {
1504 MbrTable
[i
].Flags
= 0x80;
1505 ExtBase
= MbrTable
[i
].StartLBA
;
1507 MbrTable
[i
].Flags
= 0x00;
1511 Status
= refit_call5_wrapper(BlockIO
->WriteBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, 0, 512, SectorBuffer
);
1512 if (EFI_ERROR(Status
))
1515 if (PartitionIndex
>= 4) {
1516 // we have to activate a logical partition, so walk the EMBR chain
1518 // NOTE: ExtBase was set above while looking at the MBR table
1519 for (ExtCurrent
= ExtBase
; ExtCurrent
; ExtCurrent
= NextExtCurrent
) {
1520 // read current EMBR
1521 Status
= refit_call5_wrapper(BlockIO
->ReadBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, ExtCurrent
, 512, SectorBuffer
);
1522 if (EFI_ERROR(Status
))
1524 if (*((UINT16
*)(SectorBuffer
+ 510)) != 0xaa55)
1525 return EFI_NOT_FOUND
; // safety measure #3
1527 // scan EMBR, set appropriate partition active
1528 EMbrTable
= (MBR_PARTITION_INFO
*)(SectorBuffer
+ 446);
1530 for (i
= 0; i
< 4; i
++) {
1531 if (EMbrTable
[i
].Flags
!= 0x00 && EMbrTable
[i
].Flags
!= 0x80)
1532 return EFI_NOT_FOUND
; // safety measure #4
1533 if (EMbrTable
[i
].StartLBA
== 0 || EMbrTable
[i
].Size
== 0)
1535 if (IS_EXTENDED_PART_TYPE(EMbrTable
[i
].Type
)) {
1536 // link to next EMBR
1537 NextExtCurrent
= ExtBase
+ EMbrTable
[i
].StartLBA
;
1538 EMbrTable
[i
].Flags
= (PartitionIndex
>= LogicalPartitionIndex
) ? 0x80 : 0x00;
1541 // logical partition
1542 EMbrTable
[i
].Flags
= (PartitionIndex
== LogicalPartitionIndex
) ? 0x80 : 0x00;
1543 LogicalPartitionIndex
++;
1547 // write current EMBR
1548 Status
= refit_call5_wrapper(BlockIO
->WriteBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, ExtCurrent
, 512, SectorBuffer
);
1549 if (EFI_ERROR(Status
))
1552 if (PartitionIndex
< LogicalPartitionIndex
)
1553 break; // stop the loop, no need to touch further EMBRs
1559 } /* static EFI_STATUS ActivateMbrPartition() */
1561 // early 2006 Core Duo / Core Solo models
1562 static UINT8 LegacyLoaderDevicePath1Data
[] = {
1563 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1564 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1565 0xFF, 0xFF, 0xF9, 0xFF, 0x00, 0x00, 0x00, 0x00,
1566 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1567 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1568 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1570 // mid-2006 Mac Pro (and probably other Core 2 models)
1571 static UINT8 LegacyLoaderDevicePath2Data
[] = {
1572 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1573 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1574 0xFF, 0xFF, 0xF7, 0xFF, 0x00, 0x00, 0x00, 0x00,
1575 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1576 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1577 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1579 // mid-2007 MBP ("Santa Rosa" based models)
1580 static UINT8 LegacyLoaderDevicePath3Data
[] = {
1581 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1582 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1583 0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
1584 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1585 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1586 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1589 static UINT8 LegacyLoaderDevicePath4Data
[] = {
1590 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1591 0x00, 0x00, 0xC0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1592 0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
1593 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1594 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1595 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1597 // late-2008 MB/MBP (NVidia chipset)
1598 static UINT8 LegacyLoaderDevicePath5Data
[] = {
1599 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1600 0x00, 0x40, 0xCB, 0xFF, 0x00, 0x00, 0x00, 0x00,
1601 0xFF, 0xBF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
1602 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1603 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1604 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1607 static EFI_DEVICE_PATH
*LegacyLoaderList
[] = {
1608 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath1Data
,
1609 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath2Data
,
1610 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath3Data
,
1611 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath4Data
,
1612 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath5Data
,
1616 #define MAX_DISCOVERED_PATHS (16)
1618 static VOID
StartLegacy(IN LEGACY_ENTRY
*Entry
)
1621 EG_IMAGE
*BootLogoImage
;
1622 UINTN ErrorInStep
= 0;
1623 EFI_DEVICE_PATH
*DiscoveredPathList
[MAX_DISCOVERED_PATHS
];
1625 BeginExternalScreen(TRUE
, L
"Booting Legacy OS (Mac mode)");
1627 BootLogoImage
= LoadOSIcon(Entry
->Volume
->OSIconName
, L
"legacy", TRUE
);
1628 if (BootLogoImage
!= NULL
)
1629 BltImageAlpha(BootLogoImage
,
1630 (UGAWidth
- BootLogoImage
->Width
) >> 1,
1631 (UGAHeight
- BootLogoImage
->Height
) >> 1,
1632 &StdBackgroundPixel
);
1634 if (Entry
->Volume
->IsMbrPartition
) {
1635 ActivateMbrPartition(Entry
->Volume
->WholeDiskBlockIO
, Entry
->Volume
->MbrPartitionIndex
);
1638 ExtractLegacyLoaderPaths(DiscoveredPathList
, MAX_DISCOVERED_PATHS
, LegacyLoaderList
);
1640 Status
= StartEFIImageList(DiscoveredPathList
, Entry
->LoadOptions
, TYPE_LEGACY
, L
"legacy loader", 0, &ErrorInStep
, TRUE
);
1641 if (Status
== EFI_NOT_FOUND
) {
1642 if (ErrorInStep
== 1) {
1643 Print(L
"\nPlease make sure that you have the latest firmware update installed.\n");
1644 } else if (ErrorInStep
== 3) {
1645 Print(L
"\nThe firmware refused to boot from the selected volume. Note that external\n"
1646 L
"hard drives are not well-supported by Apple's firmware for legacy OS booting.\n");
1649 FinishExternalScreen();
1650 } /* static VOID StartLegacy() */
1652 // Start a device on a non-Mac using the EFI_LEGACY_BIOS_PROTOCOL
1653 #ifdef __MAKEWITH_TIANO
1654 static VOID
StartLegacyUEFI(LEGACY_ENTRY
*Entry
)
1656 BeginExternalScreen(TRUE
, L
"Booting Legacy OS (UEFI mode)");
1658 BdsLibConnectDevicePath (Entry
->BdsOption
->DevicePath
);
1659 BdsLibDoLegacyBoot(Entry
->BdsOption
);
1661 // If we get here, it means that there was a failure....
1662 Print(L
"Failure booting legacy (BIOS) OS.");
1664 FinishExternalScreen();
1665 } // static VOID StartLegacyUEFI()
1666 #endif // __MAKEWITH_TIANO
1668 static LEGACY_ENTRY
* AddLegacyEntry(IN CHAR16
*LoaderTitle
, IN REFIT_VOLUME
*Volume
)
1670 LEGACY_ENTRY
*Entry
, *SubEntry
;
1671 REFIT_MENU_SCREEN
*SubScreen
;
1672 CHAR16
*VolDesc
, *LegacyTitle
;
1673 CHAR16 ShortcutLetter
= 0;
1675 if (LoaderTitle
== NULL
) {
1676 if (Volume
->OSName
!= NULL
) {
1677 LoaderTitle
= Volume
->OSName
;
1678 if (LoaderTitle
[0] == 'W' || LoaderTitle
[0] == 'L')
1679 ShortcutLetter
= LoaderTitle
[0];
1681 LoaderTitle
= L
"Legacy OS";
1683 if (Volume
->VolName
!= NULL
)
1684 VolDesc
= Volume
->VolName
;
1686 VolDesc
= (Volume
->DiskKind
== DISK_KIND_OPTICAL
) ? L
"CD" : L
"HD";
1688 LegacyTitle
= AllocateZeroPool(256 * sizeof(CHAR16
));
1689 if (LegacyTitle
!= NULL
)
1690 SPrint(LegacyTitle
, 255, L
"Boot %s from %s", LoaderTitle
, VolDesc
);
1691 if (IsInSubstring(LegacyTitle
, GlobalConfig
.DontScanVolumes
)) {
1692 MyFreePool(LegacyTitle
);
1696 // prepare the menu entry
1697 Entry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1698 Entry
->me
.Title
= LegacyTitle
;
1699 Entry
->me
.Tag
= TAG_LEGACY
;
1701 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1702 Entry
->me
.Image
= LoadOSIcon(Volume
->OSIconName
, L
"legacy", FALSE
);
1703 Entry
->me
.BadgeImage
= Volume
->VolBadgeImage
;
1704 Entry
->Volume
= Volume
;
1705 Entry
->LoadOptions
= (Volume
->DiskKind
== DISK_KIND_OPTICAL
) ? L
"CD" :
1706 ((Volume
->DiskKind
== DISK_KIND_EXTERNAL
) ? L
"USB" : L
"HD");
1707 Entry
->Enabled
= TRUE
;
1709 // create the submenu
1710 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
1711 SubScreen
->Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1712 SPrint(SubScreen
->Title
, 255, L
"Boot Options for %s on %s", LoaderTitle
, VolDesc
);
1713 SubScreen
->TitleImage
= Entry
->me
.Image
;
1714 SubScreen
->Hint1
= StrDuplicate(SUBSCREEN_HINT1
);
1715 if (GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_EDITOR
) {
1716 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2_NO_EDITOR
);
1718 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2
);
1722 SubEntry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1723 SubEntry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1724 SPrint(SubEntry
->me
.Title
, 255, L
"Boot %s", LoaderTitle
);
1725 SubEntry
->me
.Tag
= TAG_LEGACY
;
1726 SubEntry
->Volume
= Entry
->Volume
;
1727 SubEntry
->LoadOptions
= Entry
->LoadOptions
;
1728 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
1730 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
1731 Entry
->me
.SubScreen
= SubScreen
;
1732 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1734 } /* static LEGACY_ENTRY * AddLegacyEntry() */
1737 #ifdef __MAKEWITH_GNUEFI
1738 static VOID
ScanLegacyUEFI(IN UINTN DiskType
){}
1740 // default volume badge icon based on disk kind
1741 static EG_IMAGE
* GetDiskBadge(IN UINTN DiskType
) {
1742 EG_IMAGE
* Badge
= NULL
;
1746 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_INTERNAL
);
1749 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_EXTERNAL
);
1752 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_OPTICAL
);
1756 } // static EG_IMAGE * GetDiskBadge()
1759 Create a rEFInd boot option from a Legacy BIOS protocol option.
1761 static LEGACY_ENTRY
* AddLegacyEntryUEFI(BDS_COMMON_OPTION
*BdsOption
, IN UINT16 DiskType
)
1763 LEGACY_ENTRY
*Entry
, *SubEntry
;
1764 REFIT_MENU_SCREEN
*SubScreen
;
1765 CHAR16 ShortcutLetter
= 0;
1766 CHAR16
*LegacyDescription
= BdsOption
->Description
;
1768 if (IsInSubstring(LegacyDescription
, GlobalConfig
.DontScanVolumes
))
1771 // prepare the menu entry
1772 Entry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1773 Entry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1774 SPrint(Entry
->me
.Title
, 255, L
"Boot legacy target %s", LegacyDescription
);
1775 Entry
->me
.Tag
= TAG_LEGACY_UEFI
;
1777 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1778 Entry
->me
.Image
= LoadOSIcon(L
"legacy", L
"legacy", TRUE
);
1779 Entry
->LoadOptions
= (DiskType
== BBS_CDROM
) ? L
"CD" :
1780 ((DiskType
== BBS_USB
) ? L
"USB" : L
"HD");
1781 Entry
->me
.BadgeImage
= GetDiskBadge(DiskType
);
1782 Entry
->BdsOption
= BdsOption
;
1783 Entry
->Enabled
= TRUE
;
1785 // create the submenu
1786 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
1787 SubScreen
->Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1788 SPrint(SubScreen
->Title
, 255, L
"No boot options for legacy target");
1789 SubScreen
->TitleImage
= Entry
->me
.Image
;
1790 SubScreen
->Hint1
= StrDuplicate(SUBSCREEN_HINT1
);
1791 if (GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_EDITOR
) {
1792 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2_NO_EDITOR
);
1794 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2
);
1798 SubEntry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1799 SubEntry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1800 SPrint(SubEntry
->me
.Title
, 255, L
"Boot %s", LegacyDescription
);
1801 SubEntry
->me
.Tag
= TAG_LEGACY_UEFI
;
1802 Entry
->BdsOption
= BdsOption
;
1803 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
1805 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
1806 Entry
->me
.SubScreen
= SubScreen
;
1807 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1809 } /* static LEGACY_ENTRY * AddLegacyEntryUEFI() */
1812 Scan for legacy BIOS targets on machines that implement EFI_LEGACY_BIOS_PROTOCOL.
1813 In testing, protocol has not been implemented on Macs but has been
1814 implemented on several Dell PCs and an ASUS motherboard.
1815 Restricts output to disks of the specified DiskType.
1817 static VOID
ScanLegacyUEFI(IN UINTN DiskType
)
1820 EFI_LEGACY_BIOS_PROTOCOL
*LegacyBios
;
1821 UINT16
*BootOrder
= NULL
;
1823 CHAR16 BootOption
[10];
1824 UINTN BootOrderSize
= 0;
1826 BDS_COMMON_OPTION
*BdsOption
;
1827 LIST_ENTRY TempList
;
1828 BBS_BBS_DEVICE_PATH
*BbsDevicePath
= NULL
;
1829 BOOLEAN SearchingForUsb
= FALSE
;
1831 InitializeListHead (&TempList
);
1832 ZeroMem (Buffer
, sizeof (Buffer
));
1834 // If LegacyBios protocol is not implemented on this platform, then
1835 //we do not support this type of legacy boot on this machine.
1836 Status
= gBS
->LocateProtocol(&gEfiLegacyBootProtocolGuid
, NULL
, (VOID
**) &LegacyBios
);
1837 if (EFI_ERROR (Status
))
1840 // EFI calls USB drives BBS_HARDDRIVE, but we want to distinguish them,
1841 // so we set DiskType inappropriately elsewhere in the program and
1842 // "translate" it here.
1843 if (DiskType
== BBS_USB
) {
1844 DiskType
= BBS_HARDDISK
;
1845 SearchingForUsb
= TRUE
;
1848 // Grab the boot order
1849 BootOrder
= BdsLibGetVariableAndSize(L
"BootOrder", &gEfiGlobalVariableGuid
, &BootOrderSize
);
1850 if (BootOrder
== NULL
) {
1855 while (Index
< BootOrderSize
/ sizeof (UINT16
))
1857 // Grab each boot option variable from the boot order, and convert
1858 // the variable into a BDS boot option
1859 UnicodeSPrint (BootOption
, sizeof (BootOption
), L
"Boot%04x", BootOrder
[Index
]);
1860 BdsOption
= BdsLibVariableToOption (&TempList
, BootOption
);
1862 if (BdsOption
!= NULL
) {
1863 BbsDevicePath
= (BBS_BBS_DEVICE_PATH
*)BdsOption
->DevicePath
;
1864 // Only add the entry if it is of a requested type (e.g. USB, HD)
1865 // Two checks necessary because some systems return EFI boot loaders
1866 // with a DeviceType value that would inappropriately include them
1867 // as legacy loaders....
1868 if ((BbsDevicePath
->DeviceType
== DiskType
) && (BdsOption
->DevicePath
->Type
== DEVICE_TYPE_BIOS
)) {
1869 // USB flash drives appear as hard disks with certain media flags set.
1870 // Look for this, and if present, pass it on with the (technically
1871 // incorrect, but internally useful) BBS_TYPE_USB flag set.
1872 if (DiskType
== BBS_HARDDISK
) {
1873 if (SearchingForUsb
&& (BbsDevicePath
->StatusFlag
& (BBS_MEDIA_PRESENT
| BBS_MEDIA_MAYBE_PRESENT
))) {
1874 AddLegacyEntryUEFI(BdsOption
, BBS_USB
);
1875 } else if (!SearchingForUsb
&& !(BbsDevicePath
->StatusFlag
& (BBS_MEDIA_PRESENT
| BBS_MEDIA_MAYBE_PRESENT
))) {
1876 AddLegacyEntryUEFI(BdsOption
, DiskType
);
1879 AddLegacyEntryUEFI(BdsOption
, DiskType
);
1882 } // if (BdsOption != NULL)
1885 } /* static VOID ScanLegacyUEFI() */
1886 #endif // __MAKEWITH_GNUEFI
1888 static VOID
ScanLegacyVolume(REFIT_VOLUME
*Volume
, UINTN VolumeIndex
) {
1890 BOOLEAN ShowVolume
, HideIfOthersFound
;
1893 HideIfOthersFound
= FALSE
;
1894 if (Volume
->IsAppleLegacy
) {
1896 HideIfOthersFound
= TRUE
;
1897 } else if (Volume
->HasBootCode
) {
1899 if (Volume
->BlockIO
== Volume
->WholeDiskBlockIO
&&
1900 Volume
->BlockIOOffset
== 0 &&
1901 Volume
->OSName
== NULL
)
1902 // this is a whole disk (MBR) entry; hide if we have entries for partitions
1903 HideIfOthersFound
= TRUE
;
1905 if (HideIfOthersFound
) {
1906 // check for other bootable entries on the same disk
1907 for (VolumeIndex2
= 0; VolumeIndex2
< VolumesCount
; VolumeIndex2
++) {
1908 if (VolumeIndex2
!= VolumeIndex
&& Volumes
[VolumeIndex2
]->HasBootCode
&&
1909 Volumes
[VolumeIndex2
]->WholeDiskBlockIO
== Volume
->WholeDiskBlockIO
)
1915 AddLegacyEntry(NULL
, Volume
);
1916 } // static VOID ScanLegacyVolume()
1918 // Scan attached optical discs for legacy (BIOS) boot code
1919 // and add anything found to the list....
1920 static VOID
ScanLegacyDisc(VOID
)
1923 REFIT_VOLUME
*Volume
;
1925 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
) {
1926 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1927 Volume
= Volumes
[VolumeIndex
];
1928 if (Volume
->DiskKind
== DISK_KIND_OPTICAL
)
1929 ScanLegacyVolume(Volume
, VolumeIndex
);
1931 } else if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
1932 ScanLegacyUEFI(BBS_CDROM
);
1934 } /* static VOID ScanLegacyDisc() */
1936 // Scan internal hard disks for legacy (BIOS) boot code
1937 // and add anything found to the list....
1938 static VOID
ScanLegacyInternal(VOID
)
1941 REFIT_VOLUME
*Volume
;
1943 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
) {
1944 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1945 Volume
= Volumes
[VolumeIndex
];
1946 if (Volume
->DiskKind
== DISK_KIND_INTERNAL
)
1947 ScanLegacyVolume(Volume
, VolumeIndex
);
1949 } else if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
1950 // TODO: This actually picks up USB flash drives, too; try to find
1951 // a way to differentiate the two....
1952 ScanLegacyUEFI(BBS_HARDDISK
);
1954 } /* static VOID ScanLegacyInternal() */
1956 // Scan external disks for legacy (BIOS) boot code
1957 // and add anything found to the list....
1958 static VOID
ScanLegacyExternal(VOID
)
1961 REFIT_VOLUME
*Volume
;
1963 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
) {
1964 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1965 Volume
= Volumes
[VolumeIndex
];
1966 if (Volume
->DiskKind
== DISK_KIND_EXTERNAL
)
1967 ScanLegacyVolume(Volume
, VolumeIndex
);
1969 } else if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
1970 // TODO: This actually doesn't do anything useful; leaving in hopes of
1971 // fixing it later....
1972 ScanLegacyUEFI(BBS_USB
);
1974 } /* static VOID ScanLegacyExternal() */
1977 // pre-boot tool functions
1980 static VOID
StartTool(IN LOADER_ENTRY
*Entry
)
1982 BeginExternalScreen(Entry
->UseGraphicsMode
, Entry
->me
.Title
+ 6); // assumes "Start <title>" as assigned below
1983 StartEFIImage(Entry
->DevicePath
, Entry
->LoadOptions
, TYPE_EFI
,
1984 Basename(Entry
->LoaderPath
), Entry
->OSType
, NULL
, TRUE
);
1985 FinishExternalScreen();
1986 } /* static VOID StartTool() */
1988 static LOADER_ENTRY
* AddToolEntry(EFI_HANDLE DeviceHandle
, IN CHAR16
*LoaderPath
, IN CHAR16
*LoaderTitle
, IN EG_IMAGE
*Image
,
1989 IN CHAR16 ShortcutLetter
, IN BOOLEAN UseGraphicsMode
)
1991 LOADER_ENTRY
*Entry
;
1992 CHAR16
*TitleStr
= NULL
;
1994 Entry
= AllocateZeroPool(sizeof(LOADER_ENTRY
));
1996 TitleStr
= PoolPrint(L
"Start %s", LoaderTitle
);
1997 Entry
->me
.Title
= TitleStr
;
1998 Entry
->me
.Tag
= TAG_TOOL
;
2000 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
2001 Entry
->me
.Image
= Image
;
2002 Entry
->LoaderPath
= (LoaderPath
) ? StrDuplicate(LoaderPath
) : NULL
;
2003 Entry
->DevicePath
= FileDevicePath(DeviceHandle
, Entry
->LoaderPath
);
2004 Entry
->UseGraphicsMode
= UseGraphicsMode
;
2006 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
2008 } /* static LOADER_ENTRY * AddToolEntry() */
2011 // pre-boot driver functions
2014 static UINTN
ScanDriverDir(IN CHAR16
*Path
)
2017 REFIT_DIR_ITER DirIter
;
2019 EFI_FILE_INFO
*DirEntry
;
2020 CHAR16 FileName
[256];
2022 CleanUpPathNameSlashes(Path
);
2023 // look through contents of the directory
2024 DirIterOpen(SelfRootDir
, Path
, &DirIter
);
2025 while (DirIterNext(&DirIter
, 2, LOADER_MATCH_PATTERNS
, &DirEntry
)) {
2026 if (DirEntry
->FileName
[0] == '.')
2027 continue; // skip this
2029 SPrint(FileName
, 255, L
"%s\\%s", Path
, DirEntry
->FileName
);
2031 Status
= StartEFIImage(FileDevicePath(SelfLoadedImage
->DeviceHandle
, FileName
),
2032 L
"", TYPE_EFI
, DirEntry
->FileName
, 0, NULL
, FALSE
);
2034 Status
= DirIterClose(&DirIter
);
2035 if (Status
!= EFI_NOT_FOUND
) {
2036 SPrint(FileName
, 255, L
"while scanning the %s directory", Path
);
2037 CheckError(Status
, FileName
);
2042 #ifdef __MAKEWITH_GNUEFI
2043 static EFI_STATUS
ConnectAllDriversToAllControllers(VOID
)
2046 UINTN AllHandleCount
;
2047 EFI_HANDLE
*AllHandleBuffer
;
2050 EFI_HANDLE
*HandleBuffer
;
2056 Status
= LibLocateHandle(AllHandles
,
2061 if (EFI_ERROR(Status
))
2064 for (Index
= 0; Index
< AllHandleCount
; Index
++) {
2066 // Scan the handle database
2068 Status
= LibScanHandleDatabase(NULL
,
2070 AllHandleBuffer
[Index
],
2075 if (EFI_ERROR (Status
))
2079 if (HandleType
[Index
] & EFI_HANDLE_TYPE_DRIVER_BINDING_HANDLE
)
2081 if (HandleType
[Index
] & EFI_HANDLE_TYPE_IMAGE_HANDLE
)
2086 for (HandleIndex
= 0; HandleIndex
< HandleCount
; HandleIndex
++) {
2087 if (HandleType
[HandleIndex
] & EFI_HANDLE_TYPE_PARENT_HANDLE
)
2092 if (HandleType
[Index
] & EFI_HANDLE_TYPE_DEVICE_HANDLE
) {
2093 Status
= refit_call4_wrapper(BS
->ConnectController
,
2094 AllHandleBuffer
[Index
],
2102 MyFreePool (HandleBuffer
);
2103 MyFreePool (HandleType
);
2107 MyFreePool (AllHandleBuffer
);
2109 } /* EFI_STATUS ConnectAllDriversToAllControllers() */
2111 static EFI_STATUS
ConnectAllDriversToAllControllers(VOID
) {
2112 BdsLibConnectAllDriversToAllControllers();
2117 // Load all EFI drivers from rEFInd's "drivers" subdirectory and from the
2118 // directories specified by the user in the "scan_driver_dirs" configuration
2120 static VOID
LoadDrivers(VOID
)
2122 CHAR16
*Directory
, *SelfDirectory
;
2123 UINTN i
= 0, Length
, NumFound
= 0;
2125 // load drivers from the subdirectories of rEFInd's home directory specified
2126 // in the DRIVER_DIRS constant.
2127 while ((Directory
= FindCommaDelimited(DRIVER_DIRS
, i
++)) != NULL
) {
2128 SelfDirectory
= SelfDirPath
? StrDuplicate(SelfDirPath
) : NULL
;
2129 CleanUpPathNameSlashes(SelfDirectory
);
2130 MergeStrings(&SelfDirectory
, Directory
, L
'\\');
2131 NumFound
+= ScanDriverDir(SelfDirectory
);
2132 MyFreePool(Directory
);
2133 MyFreePool(SelfDirectory
);
2136 // Scan additional user-specified driver directories....
2138 while ((Directory
= FindCommaDelimited(GlobalConfig
.DriverDirs
, i
++)) != NULL
) {
2139 CleanUpPathNameSlashes(Directory
);
2140 Length
= StrLen(Directory
);
2142 NumFound
+= ScanDriverDir(Directory
);
2144 MyFreePool(Directory
);
2147 // connect all devices
2149 ConnectAllDriversToAllControllers();
2150 } /* static VOID LoadDrivers() */
2152 // Determine what (if any) type of legacy (BIOS) boot support is available
2153 static VOID
FindLegacyBootType(VOID
) {
2154 #ifdef __MAKEWITH_TIANO
2156 EFI_LEGACY_BIOS_PROTOCOL
*LegacyBios
;
2159 GlobalConfig
.LegacyType
= LEGACY_TYPE_NONE
;
2161 // UEFI-style legacy BIOS support is available only with the TianoCore EDK2
2162 // build environment, and then only with some EFI implementations....
2163 #ifdef __MAKEWITH_TIANO
2164 Status
= gBS
->LocateProtocol (&gEfiLegacyBootProtocolGuid
, NULL
, (VOID
**) &LegacyBios
);
2165 if (!EFI_ERROR (Status
))
2166 GlobalConfig
.LegacyType
= LEGACY_TYPE_UEFI
;
2169 // Macs have their own system. If the firmware vendor code contains the
2170 // string "Apple", assume it's available. Note that this overrides the
2171 // UEFI type, and might yield false positives if the vendor string
2172 // contains "Apple" as part of something bigger, so this isn't 100%
2174 if (StriSubCmp(L
"Apple", ST
->FirmwareVendor
))
2175 GlobalConfig
.LegacyType
= LEGACY_TYPE_MAC
;
2176 } // static VOID FindLegacyBootType
2178 // Warn the user if legacy OS scans are enabled but the firmware or this
2179 // application can't support them....
2180 static VOID
WarnIfLegacyProblems() {
2181 BOOLEAN found
= FALSE
;
2184 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_NONE
) {
2186 if (GlobalConfig
.ScanFor
[i
] == 'h' || GlobalConfig
.ScanFor
[i
] == 'b' || GlobalConfig
.ScanFor
[i
] == 'c')
2189 } while ((i
< NUM_SCAN_OPTIONS
) && (!found
));
2191 Print(L
"NOTE: refind.conf's 'scanfor' line specifies scanning for one or more legacy\n");
2192 Print(L
"(BIOS) boot options; however, this is not possible because ");
2193 #ifdef __MAKEWITH_TIANO
2194 Print(L
"your computer lacks\n");
2195 Print(L
"the necessary Compatibility Support Module (CSM) support.\n");
2197 Print(L
"this program was\n");
2198 Print(L
"compiled without the necessary support. Please visit\n");
2199 Print(L
"http://www.rodsbooks.com/refind/getting.html and download and install a rEFInd\n");
2200 Print(L
"binary built with the TianoCore EDK2 to enable legacy boot support.\n");
2204 } // if no legacy support
2205 } // static VOID WarnIfLegacyProblems()
2207 // Locates boot loaders. NOTE: This assumes that GlobalConfig.LegacyType is set correctly.
2208 static VOID
ScanForBootloaders(VOID
) {
2210 #ifdef __MAKEWITH_TIANO
2212 BOOLEAN ScanForLegacy
= FALSE
;
2214 // Determine up-front if we'll be scanning for legacy loaders....
2215 for (i
= 0; i
< NUM_SCAN_OPTIONS
; i
++) {
2216 s
= GlobalConfig
.ScanFor
[i
];
2217 if ((s
== 'c') || (s
== 'C') || (s
== 'h') || (s
== 'H') || (s
== 'b') || (s
== 'B'))
2218 ScanForLegacy
= TRUE
;
2221 // If UEFI & scanning for legacy loaders, update NVRAM boot manager list
2222 if ((GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) && ScanForLegacy
) {
2223 BdsDeleteAllInvalidLegacyBootOptions();
2224 BdsAddNonExistingLegacyBootOptions();
2228 // scan for loaders and tools, add them to the menu
2229 for (i
= 0; i
< NUM_SCAN_OPTIONS
; i
++) {
2230 switch(GlobalConfig
.ScanFor
[i
]) {
2235 ScanLegacyInternal();
2238 ScanLegacyExternal();
2241 ScanUserConfigured(GlobalConfig
.ConfigFilename
);
2255 // assign shortcut keys
2256 for (i
= 0; i
< MainMenu
.EntryCount
&& MainMenu
.Entries
[i
]->Row
== 0 && i
< 9; i
++)
2257 MainMenu
.Entries
[i
]->ShortcutDigit
= (CHAR16
)('1' + i
);
2259 // wait for user ACK when there were errors
2260 FinishTextScreen(FALSE
);
2261 } // static VOID ScanForBootloaders()
2263 // Locate a single tool from the specified Locations using one of the
2264 // specified Names and add it to the menu.
2265 static VOID
FindTool(CHAR16
*Locations
, CHAR16
*Names
, CHAR16
*Description
, UINTN Icon
) {
2266 UINTN j
= 0, k
, VolumeIndex
;
2267 CHAR16
*DirName
, *FileName
, *PathName
, FullDescription
[256];
2269 while ((DirName
= FindCommaDelimited(Locations
, j
++)) != NULL
) {
2271 while ((FileName
= FindCommaDelimited(Names
, k
++)) != NULL
) {
2272 PathName
= StrDuplicate(DirName
);
2273 MergeStrings(&PathName
, FileName
, (StriCmp(PathName
, L
"\\") == 0) ? 0 : L
'\\');
2274 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
2275 if ((Volumes
[VolumeIndex
]->RootDir
!= NULL
) && (FileExists(Volumes
[VolumeIndex
]->RootDir
, PathName
))) {
2276 SPrint(FullDescription
, 255, L
"%s at %s on %s", Description
, PathName
, Volumes
[VolumeIndex
]->VolName
);
2277 AddToolEntry(Volumes
[VolumeIndex
]->DeviceHandle
, PathName
, FullDescription
, BuiltinIcon(Icon
), 'S', FALSE
);
2280 MyFreePool(PathName
);
2281 MyFreePool(FileName
);
2283 MyFreePool(DirName
);
2284 } // while Locations
2285 } // VOID FindTool()
2287 // Add the second-row tags containing built-in and external tools (EFI shell,
2289 static VOID
ScanForTools(VOID
) {
2290 CHAR16
*FileName
= NULL
, *VolName
= NULL
, *MokLocations
, Description
[256];
2291 REFIT_MENU_ENTRY
*TempMenuEntry
;
2292 UINTN i
, j
, VolumeIndex
;
2296 MokLocations
= StrDuplicate(MOK_LOCATIONS
);
2297 if (MokLocations
!= NULL
)
2298 MergeStrings(&MokLocations
, SelfDirPath
, L
',');
2300 for (i
= 0; i
< NUM_TOOLS
; i
++) {
2301 switch(GlobalConfig
.ShowTools
[i
]) {
2302 // NOTE: Be sure that FileName is NULL at the end of each case.
2304 TempMenuEntry
= CopyMenuEntry(&MenuEntryShutdown
);
2305 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_SHUTDOWN
);
2306 AddMenuEntry(&MainMenu
, TempMenuEntry
);
2310 TempMenuEntry
= CopyMenuEntry(&MenuEntryReset
);
2311 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_RESET
);
2312 AddMenuEntry(&MainMenu
, TempMenuEntry
);
2316 TempMenuEntry
= CopyMenuEntry(&MenuEntryAbout
);
2317 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT
);
2318 AddMenuEntry(&MainMenu
, TempMenuEntry
);
2322 TempMenuEntry
= CopyMenuEntry(&MenuEntryExit
);
2323 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_EXIT
);
2324 AddMenuEntry(&MainMenu
, TempMenuEntry
);
2328 if (EfivarGetRaw(&GlobalGuid
, L
"OsIndicationsSupported", &b
, &j
) == EFI_SUCCESS
) {
2330 if (osind
& EFI_OS_INDICATIONS_BOOT_TO_FW_UI
) {
2331 TempMenuEntry
= CopyMenuEntry(&MenuEntryFirmware
);
2332 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_FIRMWARE
);
2333 AddMenuEntry(&MainMenu
, TempMenuEntry
);
2340 while ((FileName
= FindCommaDelimited(SHELL_NAMES
, j
++)) != NULL
) {
2341 if (FileExists(SelfRootDir
, FileName
)) {
2342 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, L
"EFI Shell", BuiltinIcon(BUILTIN_ICON_TOOL_SHELL
),
2345 MyFreePool(FileName
);
2351 while ((FileName
= FindCommaDelimited(GPTSYNC_NAMES
, j
++)) != NULL
) {
2352 if (FileExists(SelfRootDir
, FileName
)) {
2353 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, L
"Hybrid MBR tool", BuiltinIcon(BUILTIN_ICON_TOOL_PART
),
2356 MyFreePool(FileName
);
2363 while ((FileName
= FindCommaDelimited(GDISK_NAMES
, j
++)) != NULL
) {
2364 if (FileExists(SelfRootDir
, FileName
)) {
2365 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, L
"disk partitioning tool",
2366 BuiltinIcon(BUILTIN_ICON_TOOL_PART
), 'G', FALSE
);
2368 MyFreePool(FileName
);
2373 case TAG_APPLE_RECOVERY
:
2374 FileName
= StrDuplicate(L
"\\com.apple.recovery.boot\\boot.efi");
2375 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
2376 if ((Volumes
[VolumeIndex
]->RootDir
!= NULL
) && (FileExists(Volumes
[VolumeIndex
]->RootDir
, FileName
))) {
2377 SPrint(Description
, 255, L
"Apple Recovery on %s", Volumes
[VolumeIndex
]->VolName
);
2378 AddToolEntry(Volumes
[VolumeIndex
]->DeviceHandle
, FileName
, Description
,
2379 BuiltinIcon(BUILTIN_ICON_TOOL_APPLE_RESCUE
), 'R', TRUE
);
2382 MyFreePool(FileName
);
2386 case TAG_WINDOWS_RECOVERY
:
2388 while ((FileName
= FindCommaDelimited(GlobalConfig
.WindowsRecoveryFiles
, j
++)) != NULL
) {
2389 SplitVolumeAndFilename(&FileName
, &VolName
);
2390 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
2391 if ((Volumes
[VolumeIndex
]->RootDir
!= NULL
) && (FileExists(Volumes
[VolumeIndex
]->RootDir
, FileName
)) &&
2392 ((VolName
== NULL
) || (StriCmp(VolName
, Volumes
[VolumeIndex
]->VolName
) == 0))) {
2393 SPrint(Description
, 255, L
"Microsoft Recovery on %s", Volumes
[VolumeIndex
]->VolName
);
2394 AddToolEntry(Volumes
[VolumeIndex
]->DeviceHandle
, FileName
, Description
,
2395 BuiltinIcon(BUILTIN_ICON_TOOL_WINDOWS_RESCUE
), 'R', TRUE
);
2399 MyFreePool(FileName
);
2401 MyFreePool(VolName
);
2406 FindTool(MokLocations
, MOK_NAMES
, L
"MOK utility", BUILTIN_ICON_TOOL_MOK_TOOL
);
2410 FindTool(MEMTEST_LOCATIONS
, MEMTEST_NAMES
, L
"Memory test utility", BUILTIN_ICON_TOOL_MEMTEST
);
2415 } // static VOID ScanForTools
2417 // Rescan for boot loaders
2418 VOID
RescanAll(VOID
) {
2425 egDisplayMessage(L
"Scanning for new boot loaders; please wait....", &BGColor
);
2426 FreeList((VOID
***) &(MainMenu
.Entries
), &MainMenu
.EntryCount
);
2427 MainMenu
.Entries
= NULL
;
2428 MainMenu
.EntryCount
= 0;
2429 ReadConfig(GlobalConfig
.ConfigFilename
);
2430 ConnectAllDriversToAllControllers();
2432 ScanForBootloaders();
2435 } // VOID RescanAll()
2437 #ifdef __MAKEWITH_TIANO
2439 // Minimal initialization function
2440 static VOID
InitializeLib(IN EFI_HANDLE ImageHandle
, IN EFI_SYSTEM_TABLE
*SystemTable
) {
2442 // gImageHandle = ImageHandle;
2443 gBS
= SystemTable
->BootServices
;
2444 // gRS = SystemTable->RuntimeServices;
2445 gRT
= SystemTable
->RuntimeServices
; // Some BDS functions need gRT to be set
2446 EfiGetSystemConfigurationTable (&gEfiDxeServicesTableGuid
, (VOID
**) &gDS
);
2448 InitializeConsoleSim();
2453 // Set up our own Secure Boot extensions....
2454 // Returns TRUE on success, FALSE otherwise
2455 static BOOLEAN
SecureBootSetup(VOID
) {
2457 BOOLEAN Success
= FALSE
;
2459 if (secure_mode() && ShimLoaded()) {
2460 Status
= security_policy_install();
2461 if (Status
== EFI_SUCCESS
) {
2464 Print(L
"Failed to install MOK Secure Boot extensions");
2468 } // VOID SecureBootSetup()
2470 // Remove our own Secure Boot extensions....
2471 // Returns TRUE on success, FALSE otherwise
2472 static BOOLEAN
SecureBootUninstall(VOID
) {
2474 BOOLEAN Success
= TRUE
;
2476 if (secure_mode()) {
2477 Status
= security_policy_uninstall();
2478 if (Status
!= EFI_SUCCESS
) {
2480 BeginTextScreen(L
"Secure Boot Policy Failure");
2481 Print(L
"Failed to uninstall MOK Secure Boot extensions; forcing a reboot.");
2483 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);
2487 } // VOID SecureBootUninstall
2489 // Sets the global configuration filename; will be CONFIG_FILE_NAME unless the
2490 // "-c" command-line option is set, in which case that takes precedence.
2491 // If an error is encountered, leaves the value alone (it should be set to
2492 // CONFIG_FILE_NAME when GlobalConfig is initialized).
2493 static VOID
SetConfigFilename(EFI_HANDLE ImageHandle
) {
2494 EFI_LOADED_IMAGE
*Info
;
2495 CHAR16
*Options
, *FileName
;
2499 Status
= refit_call3_wrapper(BS
->HandleProtocol
, ImageHandle
, &LoadedImageProtocol
, (VOID
**) &Info
);
2500 if ((Status
== EFI_SUCCESS
) && (Info
->LoadOptionsSize
> 0)) {
2501 Options
= (CHAR16
*) Info
->LoadOptions
;
2502 Where
= FindSubString(L
" -c ", Options
);
2504 FileName
= StrDuplicate(&Options
[Where
+ 4]);
2505 Where
= FindSubString(L
" ", FileName
);
2507 FileName
[Where
] = L
'\0';
2509 if (FileExists(SelfDir
, FileName
)) {
2510 GlobalConfig
.ConfigFilename
= FileName
;
2512 Print(L
"Specified configuration file (%s) doesn't exist; using\n'refind.conf' default\n", FileName
);
2513 MyFreePool(FileName
);
2517 } // VOID SetConfigFilename()
2524 efi_main (EFI_HANDLE ImageHandle
, EFI_SYSTEM_TABLE
*SystemTable
)
2527 BOOLEAN MainLoopRunning
= TRUE
;
2528 BOOLEAN MokProtocol
;
2529 REFIT_MENU_ENTRY
*ChosenEntry
;
2531 CHAR16
*Selection
= NULL
;
2535 InitializeLib(ImageHandle
, SystemTable
);
2536 Status
= InitRefitLib(ImageHandle
);
2537 if (EFI_ERROR(Status
))
2540 // read configuration
2541 CopyMem(GlobalConfig
.ScanFor
, "ieom ", NUM_SCAN_OPTIONS
);
2542 FindLegacyBootType();
2543 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
)
2544 CopyMem(GlobalConfig
.ScanFor
, "ihebocm ", NUM_SCAN_OPTIONS
);
2545 SetConfigFilename(ImageHandle
);
2546 ReadConfig(GlobalConfig
.ConfigFilename
);
2550 WarnIfLegacyProblems();
2551 MainMenu
.TimeoutSeconds
= GlobalConfig
.Timeout
;
2553 // disable EFI watchdog timer
2554 refit_call4_wrapper(BS
->SetWatchdogTimer
, 0x0000, 0x0000, 0x0000, NULL
);
2556 // further bootstrap (now with config available)
2557 MokProtocol
= SecureBootSetup();
2560 ScanForBootloaders();
2564 if (GlobalConfig
.ScanDelay
> 0) {
2569 egDisplayMessage(L
"Pausing before disk scan; please wait....", &BGColor
);
2570 for (i
= 0; i
< GlobalConfig
.ScanDelay
; i
++)
2571 refit_call1_wrapper(BS
->Stall
, 1000000);
2575 if (GlobalConfig
.DefaultSelection
)
2576 Selection
= StrDuplicate(GlobalConfig
.DefaultSelection
);
2578 while (MainLoopRunning
) {
2579 MenuExit
= RunMainMenu(&MainMenu
, Selection
, &ChosenEntry
);
2581 // The Escape key triggers a re-scan operation....
2582 if (MenuExit
== MENU_EXIT_ESCAPE
) {
2588 switch (ChosenEntry
->Tag
) {
2590 case TAG_REBOOT
: // Reboot
2592 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);
2593 MainLoopRunning
= FALSE
; // just in case we get this far
2596 case TAG_SHUTDOWN
: // Shut Down
2598 refit_call4_wrapper(RT
->ResetSystem
, EfiResetShutdown
, EFI_SUCCESS
, 0, NULL
);
2599 MainLoopRunning
= FALSE
; // just in case we get this far
2602 case TAG_ABOUT
: // About rEFInd
2606 case TAG_LOADER
: // Boot OS via .EFI loader
2607 StartLoader((LOADER_ENTRY
*)ChosenEntry
);
2610 case TAG_LEGACY
: // Boot legacy OS
2611 StartLegacy((LEGACY_ENTRY
*)ChosenEntry
);
2614 #ifdef __MAKEWITH_TIANO
2615 case TAG_LEGACY_UEFI
: // Boot a legacy OS on a non-Mac
2616 StartLegacyUEFI((LEGACY_ENTRY
*)ChosenEntry
);
2620 case TAG_TOOL
: // Start a EFI tool
2621 StartTool((LOADER_ENTRY
*)ChosenEntry
);
2624 case TAG_EXIT
: // Terminate rEFInd
2625 if ((MokProtocol
) && !SecureBootUninstall()) {
2626 MainLoopRunning
= FALSE
; // just in case we get this far
2628 BeginTextScreen(L
" ");
2633 case TAG_FIRMWARE
: // Reboot into firmware's user interface
2634 RebootIntoFirmware();
2638 MyFreePool(Selection
);
2639 Selection
= (ChosenEntry
->Title
) ? StrDuplicate(ChosenEntry
->Title
) : NULL
;
2642 // If we end up here, things have gone wrong. Try to reboot, and if that
2643 // fails, go into an endless loop.
2644 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);