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.7.9.5");
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 // UINTN ExitDataSize = 0;
1657 // CHAR16 *ExitData = NULL;
1658 // EFI_STATUS Status;
1660 BeginExternalScreen(TRUE
, L
"Booting Legacy OS (UEFI mode)");
1661 // Print(L"Launching from '%s'\n", DevicePathToStr(Entry->BdsOption->DevicePath));
1664 // Status = BdsLibBootViaBootOption(Entry->BdsOption, Entry->BdsOption->DevicePath, &ExitDataSize, &ExitData);
1665 // Print(L"BdsLibBootViaBootOption() returned %d\n", Status);
1666 BdsLibConnectDevicePath (Entry
->BdsOption
->DevicePath
);
1667 BdsLibDoLegacyBoot(Entry
->BdsOption
);
1669 // If we get here, it means that there was a failure....
1670 Print(L
"Failure booting legacy (BIOS) OS.");
1672 FinishExternalScreen();
1673 } // static VOID StartLegacyUEFI()
1674 #endif // __MAKEWITH_TIANO
1676 static LEGACY_ENTRY
* AddLegacyEntry(IN CHAR16
*LoaderTitle
, IN REFIT_VOLUME
*Volume
)
1678 LEGACY_ENTRY
*Entry
, *SubEntry
;
1679 REFIT_MENU_SCREEN
*SubScreen
;
1681 CHAR16 ShortcutLetter
= 0;
1683 if (LoaderTitle
== NULL
) {
1684 if (Volume
->OSName
!= NULL
) {
1685 LoaderTitle
= Volume
->OSName
;
1686 if (LoaderTitle
[0] == 'W' || LoaderTitle
[0] == 'L')
1687 ShortcutLetter
= LoaderTitle
[0];
1689 LoaderTitle
= L
"Legacy OS";
1691 if (Volume
->VolName
!= NULL
)
1692 VolDesc
= Volume
->VolName
;
1694 VolDesc
= (Volume
->DiskKind
== DISK_KIND_OPTICAL
) ? L
"CD" : L
"HD";
1696 // prepare the menu entry
1697 Entry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1698 Entry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1699 SPrint(Entry
->me
.Title
, 255, L
"Boot %s from %s", LoaderTitle
, VolDesc
);
1700 Entry
->me
.Tag
= TAG_LEGACY
;
1702 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1703 Entry
->me
.Image
= LoadOSIcon(Volume
->OSIconName
, L
"legacy", FALSE
);
1704 Entry
->me
.BadgeImage
= Volume
->VolBadgeImage
;
1705 Entry
->Volume
= Volume
;
1706 Entry
->LoadOptions
= (Volume
->DiskKind
== DISK_KIND_OPTICAL
) ? L
"CD" :
1707 ((Volume
->DiskKind
== DISK_KIND_EXTERNAL
) ? L
"USB" : L
"HD");
1708 Entry
->Enabled
= TRUE
;
1710 // create the submenu
1711 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
1712 SubScreen
->Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1713 SPrint(SubScreen
->Title
, 255, L
"Boot Options for %s on %s", LoaderTitle
, VolDesc
);
1714 SubScreen
->TitleImage
= Entry
->me
.Image
;
1715 SubScreen
->Hint1
= StrDuplicate(SUBSCREEN_HINT1
);
1716 if (GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_EDITOR
) {
1717 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2_NO_EDITOR
);
1719 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2
);
1723 SubEntry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1724 SubEntry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1725 SPrint(SubEntry
->me
.Title
, 255, L
"Boot %s", LoaderTitle
);
1726 SubEntry
->me
.Tag
= TAG_LEGACY
;
1727 SubEntry
->Volume
= Entry
->Volume
;
1728 SubEntry
->LoadOptions
= Entry
->LoadOptions
;
1729 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
1731 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
1732 Entry
->me
.SubScreen
= SubScreen
;
1733 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1735 } /* static LEGACY_ENTRY * AddLegacyEntry() */
1738 #ifdef __MAKEWITH_GNUEFI
1739 static VOID
ScanLegacyUEFI(IN UINTN DiskType
){}
1741 // default volume badge icon based on disk kind
1742 static EG_IMAGE
* GetDiskBadge(IN UINTN DiskType
) {
1743 EG_IMAGE
* Badge
= NULL
;
1747 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_INTERNAL
);
1750 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_EXTERNAL
);
1753 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_OPTICAL
);
1757 } // static EG_IMAGE * GetDiskBadge()
1760 Create a rEFInd boot option from a Legacy BIOS protocol option.
1762 static LEGACY_ENTRY
* AddLegacyEntryUEFI(BDS_COMMON_OPTION
*BdsOption
, IN UINT16 DiskType
)
1764 LEGACY_ENTRY
*Entry
, *SubEntry
;
1765 REFIT_MENU_SCREEN
*SubScreen
;
1766 CHAR16 ShortcutLetter
= 0;
1767 CHAR16
*LegacyDescription
= BdsOption
->Description
;
1769 // prepare the menu entry
1770 Entry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1771 Entry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1772 SPrint(Entry
->me
.Title
, 255, L
"Boot legacy target %s", LegacyDescription
);
1773 Entry
->me
.Tag
= TAG_LEGACY_UEFI
;
1775 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1776 Entry
->me
.Image
= LoadOSIcon(L
"legacy", L
"legacy", TRUE
);
1777 Entry
->LoadOptions
= (DiskType
== BBS_CDROM
) ? L
"CD" :
1778 ((DiskType
== BBS_USB
) ? L
"USB" : L
"HD");
1779 Entry
->me
.BadgeImage
= GetDiskBadge(DiskType
);
1780 Entry
->BdsOption
= BdsOption
;
1781 Entry
->Enabled
= TRUE
;
1783 // create the submenu
1784 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
1785 SubScreen
->Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1786 SPrint(SubScreen
->Title
, 255, L
"No boot options for legacy target");
1787 SubScreen
->TitleImage
= Entry
->me
.Image
;
1788 SubScreen
->Hint1
= StrDuplicate(SUBSCREEN_HINT1
);
1789 if (GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_EDITOR
) {
1790 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2_NO_EDITOR
);
1792 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2
);
1796 SubEntry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1797 SubEntry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1798 SPrint(SubEntry
->me
.Title
, 255, L
"Boot %s", LegacyDescription
);
1799 SubEntry
->me
.Tag
= TAG_LEGACY_UEFI
;
1800 Entry
->BdsOption
= BdsOption
;
1801 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
1803 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
1804 Entry
->me
.SubScreen
= SubScreen
;
1805 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1807 } /* static LEGACY_ENTRY * AddLegacyEntryUEFI() */
1810 Scan for legacy BIOS targets on machines that implement EFI_LEGACY_BIOS_PROTOCOL.
1811 In testing, protocol has not been implemented on Macs but has been
1812 implemented on several Dell PCs and an ASUS motherboard.
1813 Restricts output to disks of the specified DiskType.
1815 static VOID
ScanLegacyUEFI(IN UINTN DiskType
)
1818 EFI_LEGACY_BIOS_PROTOCOL
*LegacyBios
;
1819 UINT16
*BootOrder
= NULL
;
1821 CHAR16 BootOption
[10];
1822 UINTN BootOrderSize
= 0;
1824 BDS_COMMON_OPTION
*BdsOption
;
1825 LIST_ENTRY TempList
;
1826 BBS_BBS_DEVICE_PATH
* BbsDevicePath
= NULL
;
1828 InitializeListHead (&TempList
);
1829 ZeroMem (Buffer
, sizeof (Buffer
));
1831 // If LegacyBios protocol is not implemented on this platform, then
1832 //we do not support this type of legacy boot on this machine.
1833 Status
= gBS
->LocateProtocol(&gEfiLegacyBootProtocolGuid
, NULL
, (VOID
**) &LegacyBios
);
1834 if (EFI_ERROR (Status
))
1837 // Grab the boot order
1838 BootOrder
= BdsLibGetVariableAndSize(L
"BootOrder", &gEfiGlobalVariableGuid
, &BootOrderSize
);
1839 if (BootOrder
== NULL
) {
1844 while (Index
< BootOrderSize
/ sizeof (UINT16
))
1846 // Grab each boot option variable from the boot order, and convert
1847 // the variable into a BDS boot option
1848 UnicodeSPrint (BootOption
, sizeof (BootOption
), L
"Boot%04x", BootOrder
[Index
]);
1849 BdsOption
= BdsLibVariableToOption (&TempList
, BootOption
);
1851 if (BdsOption
!= NULL
) {
1852 BbsDevicePath
= (BBS_BBS_DEVICE_PATH
*)BdsOption
->DevicePath
;
1854 // Only add the entry if it is of a requested type (e.g. USB, HD)
1856 // Two checks necessary because some systems return EFI boot loaders
1857 // with a DeviceType value that would inappropriately include them
1858 // as legacy loaders....
1859 if ((BbsDevicePath
->DeviceType
== DiskType
) && (BdsOption
->DevicePath
->Type
== DEVICE_TYPE_BIOS
)) {
1860 AddLegacyEntryUEFI(BdsOption
, BbsDevicePath
->DeviceType
);
1865 } /* static VOID ScanLegacyUEFI() */
1866 #endif // __MAKEWITH_GNUEFI
1868 static VOID
ScanLegacyVolume(REFIT_VOLUME
*Volume
, UINTN VolumeIndex
) {
1870 BOOLEAN ShowVolume
, HideIfOthersFound
;
1873 HideIfOthersFound
= FALSE
;
1874 if (Volume
->IsAppleLegacy
) {
1876 HideIfOthersFound
= TRUE
;
1877 } else if (Volume
->HasBootCode
) {
1879 if (Volume
->BlockIO
== Volume
->WholeDiskBlockIO
&&
1880 Volume
->BlockIOOffset
== 0 &&
1881 Volume
->OSName
== NULL
)
1882 // this is a whole disk (MBR) entry; hide if we have entries for partitions
1883 HideIfOthersFound
= TRUE
;
1885 if (HideIfOthersFound
) {
1886 // check for other bootable entries on the same disk
1887 for (VolumeIndex2
= 0; VolumeIndex2
< VolumesCount
; VolumeIndex2
++) {
1888 if (VolumeIndex2
!= VolumeIndex
&& Volumes
[VolumeIndex2
]->HasBootCode
&&
1889 Volumes
[VolumeIndex2
]->WholeDiskBlockIO
== Volume
->WholeDiskBlockIO
)
1895 AddLegacyEntry(NULL
, Volume
);
1896 } // static VOID ScanLegacyVolume()
1898 // Scan attached optical discs for legacy (BIOS) boot code
1899 // and add anything found to the list....
1900 static VOID
ScanLegacyDisc(VOID
)
1903 REFIT_VOLUME
*Volume
;
1905 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
) {
1906 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1907 Volume
= Volumes
[VolumeIndex
];
1908 if (Volume
->DiskKind
== DISK_KIND_OPTICAL
)
1909 ScanLegacyVolume(Volume
, VolumeIndex
);
1911 } else if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
1912 ScanLegacyUEFI(BBS_CDROM
);
1914 } /* static VOID ScanLegacyDisc() */
1916 // Scan internal hard disks for legacy (BIOS) boot code
1917 // and add anything found to the list....
1918 static VOID
ScanLegacyInternal(VOID
)
1921 REFIT_VOLUME
*Volume
;
1923 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
) {
1924 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1925 Volume
= Volumes
[VolumeIndex
];
1926 if (Volume
->DiskKind
== DISK_KIND_INTERNAL
)
1927 ScanLegacyVolume(Volume
, VolumeIndex
);
1929 } else if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
1930 ScanLegacyUEFI(BBS_HARDDISK
);
1932 } /* static VOID ScanLegacyInternal() */
1934 // Scan external disks for legacy (BIOS) boot code
1935 // and add anything found to the list....
1936 static VOID
ScanLegacyExternal(VOID
)
1939 REFIT_VOLUME
*Volume
;
1941 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
) {
1942 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1943 Volume
= Volumes
[VolumeIndex
];
1944 if (Volume
->DiskKind
== DISK_KIND_EXTERNAL
)
1945 ScanLegacyVolume(Volume
, VolumeIndex
);
1947 } else if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
1948 ScanLegacyUEFI(BBS_USB
);
1950 } /* static VOID ScanLegacyExternal() */
1953 // pre-boot tool functions
1956 static VOID
StartTool(IN LOADER_ENTRY
*Entry
)
1958 BeginExternalScreen(Entry
->UseGraphicsMode
, Entry
->me
.Title
+ 6); // assumes "Start <title>" as assigned below
1959 StartEFIImage(Entry
->DevicePath
, Entry
->LoadOptions
, TYPE_EFI
,
1960 Basename(Entry
->LoaderPath
), Entry
->OSType
, NULL
, TRUE
);
1961 FinishExternalScreen();
1962 } /* static VOID StartTool() */
1964 static LOADER_ENTRY
* AddToolEntry(EFI_HANDLE DeviceHandle
, IN CHAR16
*LoaderPath
, IN CHAR16
*LoaderTitle
, IN EG_IMAGE
*Image
,
1965 IN CHAR16 ShortcutLetter
, IN BOOLEAN UseGraphicsMode
)
1967 LOADER_ENTRY
*Entry
;
1968 CHAR16
*TitleStr
= NULL
;
1970 Entry
= AllocateZeroPool(sizeof(LOADER_ENTRY
));
1972 TitleStr
= PoolPrint(L
"Start %s", LoaderTitle
);
1973 Entry
->me
.Title
= TitleStr
;
1974 Entry
->me
.Tag
= TAG_TOOL
;
1976 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1977 Entry
->me
.Image
= Image
;
1978 Entry
->LoaderPath
= (LoaderPath
) ? StrDuplicate(LoaderPath
) : NULL
;
1979 Entry
->DevicePath
= FileDevicePath(DeviceHandle
, Entry
->LoaderPath
);
1980 Entry
->UseGraphicsMode
= UseGraphicsMode
;
1982 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1984 } /* static LOADER_ENTRY * AddToolEntry() */
1987 // pre-boot driver functions
1990 static UINTN
ScanDriverDir(IN CHAR16
*Path
)
1993 REFIT_DIR_ITER DirIter
;
1995 EFI_FILE_INFO
*DirEntry
;
1996 CHAR16 FileName
[256];
1998 CleanUpPathNameSlashes(Path
);
1999 // look through contents of the directory
2000 DirIterOpen(SelfRootDir
, Path
, &DirIter
);
2001 while (DirIterNext(&DirIter
, 2, LOADER_MATCH_PATTERNS
, &DirEntry
)) {
2002 if (DirEntry
->FileName
[0] == '.')
2003 continue; // skip this
2005 SPrint(FileName
, 255, L
"%s\\%s", Path
, DirEntry
->FileName
);
2007 Status
= StartEFIImage(FileDevicePath(SelfLoadedImage
->DeviceHandle
, FileName
),
2008 L
"", TYPE_EFI
, DirEntry
->FileName
, 0, NULL
, FALSE
);
2010 Status
= DirIterClose(&DirIter
);
2011 if (Status
!= EFI_NOT_FOUND
) {
2012 SPrint(FileName
, 255, L
"while scanning the %s directory", Path
);
2013 CheckError(Status
, FileName
);
2018 #ifdef __MAKEWITH_GNUEFI
2019 static EFI_STATUS
ConnectAllDriversToAllControllers(VOID
)
2022 UINTN AllHandleCount
;
2023 EFI_HANDLE
*AllHandleBuffer
;
2026 EFI_HANDLE
*HandleBuffer
;
2032 Status
= LibLocateHandle(AllHandles
,
2037 if (EFI_ERROR(Status
))
2040 for (Index
= 0; Index
< AllHandleCount
; Index
++) {
2042 // Scan the handle database
2044 Status
= LibScanHandleDatabase(NULL
,
2046 AllHandleBuffer
[Index
],
2051 if (EFI_ERROR (Status
))
2055 if (HandleType
[Index
] & EFI_HANDLE_TYPE_DRIVER_BINDING_HANDLE
)
2057 if (HandleType
[Index
] & EFI_HANDLE_TYPE_IMAGE_HANDLE
)
2062 for (HandleIndex
= 0; HandleIndex
< HandleCount
; HandleIndex
++) {
2063 if (HandleType
[HandleIndex
] & EFI_HANDLE_TYPE_PARENT_HANDLE
)
2068 if (HandleType
[Index
] & EFI_HANDLE_TYPE_DEVICE_HANDLE
) {
2069 Status
= refit_call4_wrapper(BS
->ConnectController
,
2070 AllHandleBuffer
[Index
],
2078 MyFreePool (HandleBuffer
);
2079 MyFreePool (HandleType
);
2083 MyFreePool (AllHandleBuffer
);
2085 } /* EFI_STATUS ConnectAllDriversToAllControllers() */
2087 static EFI_STATUS
ConnectAllDriversToAllControllers(VOID
) {
2088 BdsLibConnectAllDriversToAllControllers();
2093 // Load all EFI drivers from rEFInd's "drivers" subdirectory and from the
2094 // directories specified by the user in the "scan_driver_dirs" configuration
2096 static VOID
LoadDrivers(VOID
)
2098 CHAR16
*Directory
, *SelfDirectory
;
2099 UINTN i
= 0, Length
, NumFound
= 0;
2101 // load drivers from the subdirectories of rEFInd's home directory specified
2102 // in the DRIVER_DIRS constant.
2103 while ((Directory
= FindCommaDelimited(DRIVER_DIRS
, i
++)) != NULL
) {
2104 SelfDirectory
= SelfDirPath
? StrDuplicate(SelfDirPath
) : NULL
;
2105 CleanUpPathNameSlashes(SelfDirectory
);
2106 MergeStrings(&SelfDirectory
, Directory
, L
'\\');
2107 NumFound
+= ScanDriverDir(SelfDirectory
);
2108 MyFreePool(Directory
);
2109 MyFreePool(SelfDirectory
);
2112 // Scan additional user-specified driver directories....
2114 while ((Directory
= FindCommaDelimited(GlobalConfig
.DriverDirs
, i
++)) != NULL
) {
2115 CleanUpPathNameSlashes(Directory
);
2116 Length
= StrLen(Directory
);
2118 NumFound
+= ScanDriverDir(Directory
);
2120 MyFreePool(Directory
);
2123 // connect all devices
2125 ConnectAllDriversToAllControllers();
2126 } /* static VOID LoadDrivers() */
2128 // Determine what (if any) type of legacy (BIOS) boot support is available
2129 static VOID
FindLegacyBootType(VOID
) {
2130 #ifdef __MAKEWITH_TIANO
2132 EFI_LEGACY_BIOS_PROTOCOL
*LegacyBios
;
2135 GlobalConfig
.LegacyType
= LEGACY_TYPE_NONE
;
2137 // UEFI-style legacy BIOS support is available only with the TianoCore EDK2
2138 // build environment, and then only with some EFI implementations....
2139 #ifdef __MAKEWITH_TIANO
2140 Status
= gBS
->LocateProtocol (&gEfiLegacyBootProtocolGuid
, NULL
, (VOID
**) &LegacyBios
);
2141 if (!EFI_ERROR (Status
))
2142 GlobalConfig
.LegacyType
= LEGACY_TYPE_UEFI
;
2145 // Macs have their own system. If the firmware vendor code contains the
2146 // string "Apple", assume it's available. Note that this overrides the
2147 // UEFI type, and might yield false positives if the vendor string
2148 // contains "Apple" as part of something bigger, so this isn't 100%
2150 if (StriSubCmp(L
"Apple", ST
->FirmwareVendor
))
2151 GlobalConfig
.LegacyType
= LEGACY_TYPE_MAC
;
2152 } // static VOID FindLegacyBootType
2154 // Warn the user if legacy OS scans are enabled but the firmware or this
2155 // application can't support them....
2156 static VOID
WarnIfLegacyProblems() {
2157 BOOLEAN found
= FALSE
;
2160 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_NONE
) {
2162 if (GlobalConfig
.ScanFor
[i
] == 'h' || GlobalConfig
.ScanFor
[i
] == 'b' || GlobalConfig
.ScanFor
[i
] == 'c')
2165 } while ((i
< NUM_SCAN_OPTIONS
) && (!found
));
2167 Print(L
"NOTE: refind.conf's 'scanfor' line specifies scanning for one or more legacy\n");
2168 Print(L
"(BIOS) boot options; however, this is not possible because ");
2169 #ifdef __MAKEWITH_TIANO
2170 Print(L
"your computer lacks\n");
2171 Print(L
"the necessary Compatibility Support Module (CSM) support.\n");
2173 Print(L
"this program was\n");
2174 Print(L
"compiled without the necessary support. Please visit\n");
2175 Print(L
"http://www.rodsbooks.com/refind/getting.html and download and install a rEFInd\n");
2176 Print(L
"binary built with the TianoCore EDK2 to enable legacy boot support.\n");
2180 } // if no legacy support
2181 } // static VOID WarnIfLegacyProblems()
2183 // Locates boot loaders. NOTE: This assumes that GlobalConfig.LegacyType is set correctly.
2184 static VOID
ScanForBootloaders(VOID
) {
2187 // if (GlobalConfig.LegacyType == LEGACY_TYPE_UEFI) {
2188 // Print(L"About to call BdsDeleteAllInvalidLegacyBootOptions()\n");
2189 // BdsDeleteAllInvalidLegacyBootOptions();
2190 // Print(L"About to call BdsAddNonExistingLegacyBootOptions()\n");
2191 // BdsAddNonExistingLegacyBootOptions();
2192 // Print(L"About to call BdsUpdateLegacyDevOrder()\n");
2193 // // BdsUpdateLegacyDevOrder(); // EXTREME CAUTION: HOSED ONE FIRMWARE!
2194 // Print(L"Done with legacy boot updates!\n");
2200 // scan for loaders and tools, add them to the menu
2201 for (i
= 0; i
< NUM_SCAN_OPTIONS
; i
++) {
2202 switch(GlobalConfig
.ScanFor
[i
]) {
2207 ScanLegacyInternal();
2210 ScanLegacyExternal();
2213 ScanUserConfigured(GlobalConfig
.ConfigFilename
);
2227 // assign shortcut keys
2228 for (i
= 0; i
< MainMenu
.EntryCount
&& MainMenu
.Entries
[i
]->Row
== 0 && i
< 9; i
++)
2229 MainMenu
.Entries
[i
]->ShortcutDigit
= (CHAR16
)('1' + i
);
2231 // wait for user ACK when there were errors
2232 FinishTextScreen(FALSE
);
2233 } // static VOID ScanForBootloaders()
2235 // Locate a single tool from the specified Locations using one of the
2236 // specified Names and add it to the menu.
2237 static VOID
FindTool(CHAR16
*Locations
, CHAR16
*Names
, CHAR16
*Description
, UINTN Icon
) {
2238 UINTN j
= 0, k
, VolumeIndex
;
2239 CHAR16
*DirName
, *FileName
, *PathName
, FullDescription
[256];
2241 while ((DirName
= FindCommaDelimited(Locations
, j
++)) != NULL
) {
2243 while ((FileName
= FindCommaDelimited(Names
, k
++)) != NULL
) {
2244 PathName
= StrDuplicate(DirName
);
2245 MergeStrings(&PathName
, FileName
, (StriCmp(PathName
, L
"\\") == 0) ? 0 : L
'\\');
2246 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
2247 if ((Volumes
[VolumeIndex
]->RootDir
!= NULL
) && (FileExists(Volumes
[VolumeIndex
]->RootDir
, PathName
))) {
2248 SPrint(FullDescription
, 255, L
"%s at %s on %s", Description
, PathName
, Volumes
[VolumeIndex
]->VolName
);
2249 AddToolEntry(Volumes
[VolumeIndex
]->DeviceHandle
, PathName
, FullDescription
, BuiltinIcon(Icon
), 'S', FALSE
);
2252 MyFreePool(PathName
);
2253 MyFreePool(FileName
);
2255 MyFreePool(DirName
);
2256 } // while Locations
2257 } // VOID FindTool()
2259 // Add the second-row tags containing built-in and external tools (EFI shell,
2261 static VOID
ScanForTools(VOID
) {
2262 CHAR16
*FileName
= NULL
, *VolName
= NULL
, *MokLocations
, Description
[256];
2263 REFIT_MENU_ENTRY
*TempMenuEntry
;
2264 UINTN i
, j
, VolumeIndex
;
2268 MokLocations
= StrDuplicate(MOK_LOCATIONS
);
2269 if (MokLocations
!= NULL
)
2270 MergeStrings(&MokLocations
, SelfDirPath
, L
',');
2272 for (i
= 0; i
< NUM_TOOLS
; i
++) {
2273 switch(GlobalConfig
.ShowTools
[i
]) {
2274 // NOTE: Be sure that FileName is NULL at the end of each case.
2276 TempMenuEntry
= CopyMenuEntry(&MenuEntryShutdown
);
2277 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_SHUTDOWN
);
2278 AddMenuEntry(&MainMenu
, TempMenuEntry
);
2282 TempMenuEntry
= CopyMenuEntry(&MenuEntryReset
);
2283 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_RESET
);
2284 AddMenuEntry(&MainMenu
, TempMenuEntry
);
2288 TempMenuEntry
= CopyMenuEntry(&MenuEntryAbout
);
2289 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT
);
2290 AddMenuEntry(&MainMenu
, TempMenuEntry
);
2294 TempMenuEntry
= CopyMenuEntry(&MenuEntryExit
);
2295 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_EXIT
);
2296 AddMenuEntry(&MainMenu
, TempMenuEntry
);
2300 if (EfivarGetRaw(&GlobalGuid
, L
"OsIndicationsSupported", &b
, &j
) == EFI_SUCCESS
) {
2302 if (osind
& EFI_OS_INDICATIONS_BOOT_TO_FW_UI
) {
2303 TempMenuEntry
= CopyMenuEntry(&MenuEntryFirmware
);
2304 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_FIRMWARE
);
2305 AddMenuEntry(&MainMenu
, TempMenuEntry
);
2312 while ((FileName
= FindCommaDelimited(SHELL_NAMES
, j
++)) != NULL
) {
2313 if (FileExists(SelfRootDir
, FileName
)) {
2314 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, L
"EFI Shell", BuiltinIcon(BUILTIN_ICON_TOOL_SHELL
),
2317 MyFreePool(FileName
);
2323 while ((FileName
= FindCommaDelimited(GPTSYNC_NAMES
, j
++)) != NULL
) {
2324 if (FileExists(SelfRootDir
, FileName
)) {
2325 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, L
"Hybrid MBR tool", BuiltinIcon(BUILTIN_ICON_TOOL_PART
),
2328 MyFreePool(FileName
);
2335 while ((FileName
= FindCommaDelimited(GDISK_NAMES
, j
++)) != NULL
) {
2336 if (FileExists(SelfRootDir
, FileName
)) {
2337 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, L
"disk partitioning tool",
2338 BuiltinIcon(BUILTIN_ICON_TOOL_PART
), 'G', FALSE
);
2340 MyFreePool(FileName
);
2345 case TAG_APPLE_RECOVERY
:
2346 FileName
= StrDuplicate(L
"\\com.apple.recovery.boot\\boot.efi");
2347 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
2348 if ((Volumes
[VolumeIndex
]->RootDir
!= NULL
) && (FileExists(Volumes
[VolumeIndex
]->RootDir
, FileName
))) {
2349 SPrint(Description
, 255, L
"Apple Recovery on %s", Volumes
[VolumeIndex
]->VolName
);
2350 AddToolEntry(Volumes
[VolumeIndex
]->DeviceHandle
, FileName
, Description
,
2351 BuiltinIcon(BUILTIN_ICON_TOOL_APPLE_RESCUE
), 'R', TRUE
);
2354 MyFreePool(FileName
);
2358 case TAG_WINDOWS_RECOVERY
:
2360 while ((FileName
= FindCommaDelimited(GlobalConfig
.WindowsRecoveryFiles
, j
++)) != NULL
) {
2361 SplitVolumeAndFilename(&FileName
, &VolName
);
2362 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
2363 if ((Volumes
[VolumeIndex
]->RootDir
!= NULL
) && (FileExists(Volumes
[VolumeIndex
]->RootDir
, FileName
)) &&
2364 ((VolName
== NULL
) || (StriCmp(VolName
, Volumes
[VolumeIndex
]->VolName
) == 0))) {
2365 SPrint(Description
, 255, L
"Microsoft Recovery on %s", Volumes
[VolumeIndex
]->VolName
);
2366 AddToolEntry(Volumes
[VolumeIndex
]->DeviceHandle
, FileName
, Description
,
2367 BuiltinIcon(BUILTIN_ICON_TOOL_WINDOWS_RESCUE
), 'R', TRUE
);
2371 MyFreePool(FileName
);
2373 MyFreePool(VolName
);
2378 FindTool(MokLocations
, MOK_NAMES
, L
"MOK utility", BUILTIN_ICON_TOOL_MOK_TOOL
);
2382 FindTool(MEMTEST_LOCATIONS
, MEMTEST_NAMES
, L
"Memory test utility", BUILTIN_ICON_TOOL_MEMTEST
);
2387 } // static VOID ScanForTools
2389 // Rescan for boot loaders
2390 VOID
RescanAll(VOID
) {
2397 egDisplayMessage(L
"Scanning for new boot loaders; please wait....", &BGColor
);
2398 FreeList((VOID
***) &(MainMenu
.Entries
), &MainMenu
.EntryCount
);
2399 MainMenu
.Entries
= NULL
;
2400 MainMenu
.EntryCount
= 0;
2401 ReadConfig(GlobalConfig
.ConfigFilename
);
2402 ConnectAllDriversToAllControllers();
2404 ScanForBootloaders();
2407 } // VOID RescanAll()
2409 #ifdef __MAKEWITH_TIANO
2411 // Minimal initialization function
2412 static VOID
InitializeLib(IN EFI_HANDLE ImageHandle
, IN EFI_SYSTEM_TABLE
*SystemTable
) {
2414 // gImageHandle = ImageHandle;
2415 gBS
= SystemTable
->BootServices
;
2416 // gRS = SystemTable->RuntimeServices;
2417 gRT
= SystemTable
->RuntimeServices
; // Some BDS functions need gRT to be set
2418 EfiGetSystemConfigurationTable (&gEfiDxeServicesTableGuid
, (VOID
**) &gDS
);
2420 InitializeConsoleSim();
2425 // Set up our own Secure Boot extensions....
2426 // Returns TRUE on success, FALSE otherwise
2427 static BOOLEAN
SecureBootSetup(VOID
) {
2429 BOOLEAN Success
= FALSE
;
2431 if (secure_mode() && ShimLoaded()) {
2432 Status
= security_policy_install();
2433 if (Status
== EFI_SUCCESS
) {
2436 Print(L
"Failed to install MOK Secure Boot extensions");
2440 } // VOID SecureBootSetup()
2442 // Remove our own Secure Boot extensions....
2443 // Returns TRUE on success, FALSE otherwise
2444 static BOOLEAN
SecureBootUninstall(VOID
) {
2446 BOOLEAN Success
= TRUE
;
2448 if (secure_mode()) {
2449 Status
= security_policy_uninstall();
2450 if (Status
!= EFI_SUCCESS
) {
2452 BeginTextScreen(L
"Secure Boot Policy Failure");
2453 Print(L
"Failed to uninstall MOK Secure Boot extensions; forcing a reboot.");
2455 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);
2459 } // VOID SecureBootUninstall
2461 // Sets the global configuration filename; will be CONFIG_FILE_NAME unless the
2462 // "-c" command-line option is set, in which case that takes precedence.
2463 // If an error is encountered, leaves the value alone (it should be set to
2464 // CONFIG_FILE_NAME when GlobalConfig is initialized).
2465 static VOID
SetConfigFilename(EFI_HANDLE ImageHandle
) {
2466 EFI_LOADED_IMAGE
*Info
;
2467 CHAR16
*Options
, *FileName
;
2471 Status
= refit_call3_wrapper(BS
->HandleProtocol
, ImageHandle
, &LoadedImageProtocol
, (VOID
**) &Info
);
2472 if ((Status
== EFI_SUCCESS
) && (Info
->LoadOptionsSize
> 0)) {
2473 Options
= (CHAR16
*) Info
->LoadOptions
;
2474 Where
= FindSubString(L
" -c ", Options
);
2476 FileName
= StrDuplicate(&Options
[Where
+ 4]);
2477 Where
= FindSubString(L
" ", FileName
);
2479 FileName
[Where
] = L
'\0';
2481 if (FileExists(SelfDir
, FileName
)) {
2482 GlobalConfig
.ConfigFilename
= FileName
;
2484 Print(L
"Specified configuration file (%s) doesn't exist; using\n'refind.conf' default\n", FileName
);
2485 MyFreePool(FileName
);
2489 } // VOID SetConfigFilename()
2496 efi_main (EFI_HANDLE ImageHandle
, EFI_SYSTEM_TABLE
*SystemTable
)
2499 BOOLEAN MainLoopRunning
= TRUE
;
2500 BOOLEAN MokProtocol
;
2501 REFIT_MENU_ENTRY
*ChosenEntry
;
2503 CHAR16
*Selection
= NULL
;
2507 InitializeLib(ImageHandle
, SystemTable
);
2508 Status
= InitRefitLib(ImageHandle
);
2509 if (EFI_ERROR(Status
))
2512 // read configuration
2513 CopyMem(GlobalConfig
.ScanFor
, "ieom ", NUM_SCAN_OPTIONS
);
2514 FindLegacyBootType();
2515 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
)
2516 CopyMem(GlobalConfig
.ScanFor
, "ihebocm ", NUM_SCAN_OPTIONS
);
2517 SetConfigFilename(ImageHandle
);
2518 ReadConfig(GlobalConfig
.ConfigFilename
);
2522 WarnIfLegacyProblems();
2523 MainMenu
.TimeoutSeconds
= GlobalConfig
.Timeout
;
2525 // disable EFI watchdog timer
2526 refit_call4_wrapper(BS
->SetWatchdogTimer
, 0x0000, 0x0000, 0x0000, NULL
);
2528 // further bootstrap (now with config available)
2529 MokProtocol
= SecureBootSetup();
2532 ScanForBootloaders();
2536 if (GlobalConfig
.ScanDelay
> 0) {
2541 egDisplayMessage(L
"Pausing before disk scan; please wait....", &BGColor
);
2542 for (i
= 0; i
< GlobalConfig
.ScanDelay
; i
++)
2543 refit_call1_wrapper(BS
->Stall
, 1000000);
2547 if (GlobalConfig
.DefaultSelection
)
2548 Selection
= StrDuplicate(GlobalConfig
.DefaultSelection
);
2550 while (MainLoopRunning
) {
2551 MenuExit
= RunMainMenu(&MainMenu
, Selection
, &ChosenEntry
);
2553 // The Escape key triggers a re-scan operation....
2554 if (MenuExit
== MENU_EXIT_ESCAPE
) {
2560 switch (ChosenEntry
->Tag
) {
2562 case TAG_REBOOT
: // Reboot
2564 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);
2565 MainLoopRunning
= FALSE
; // just in case we get this far
2568 case TAG_SHUTDOWN
: // Shut Down
2570 refit_call4_wrapper(RT
->ResetSystem
, EfiResetShutdown
, EFI_SUCCESS
, 0, NULL
);
2571 MainLoopRunning
= FALSE
; // just in case we get this far
2574 case TAG_ABOUT
: // About rEFInd
2578 case TAG_LOADER
: // Boot OS via .EFI loader
2579 StartLoader((LOADER_ENTRY
*)ChosenEntry
);
2582 case TAG_LEGACY
: // Boot legacy OS
2583 StartLegacy((LEGACY_ENTRY
*)ChosenEntry
);
2586 #ifdef __MAKEWITH_TIANO
2587 case TAG_LEGACY_UEFI
: // Boot a legacy OS on a non-Mac
2588 StartLegacyUEFI((LEGACY_ENTRY
*)ChosenEntry
);
2592 case TAG_TOOL
: // Start a EFI tool
2593 StartTool((LOADER_ENTRY
*)ChosenEntry
);
2596 case TAG_EXIT
: // Terminate rEFInd
2597 if ((MokProtocol
) && !SecureBootUninstall()) {
2598 MainLoopRunning
= FALSE
; // just in case we get this far
2600 BeginTextScreen(L
" ");
2605 case TAG_FIRMWARE
: // Reboot into firmware's user interface
2606 RebootIntoFirmware();
2610 MyFreePool(Selection
);
2611 Selection
= (ChosenEntry
->Title
) ? StrDuplicate(ChosenEntry
->Title
) : NULL
;
2614 // If we end up here, things have gone wrong. Try to reboot, and if that
2615 // fails, go into an endless loop.
2616 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);