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 Roderick W. Smith
39 * Modifications distributed under the terms of the GNU General Public
40 * License (GPL) version 3 (GPLv3), a copy of which must be distributed
41 * with this source code or binaries made from it.
52 #include "security_policy.h"
53 #include "../include/Handle.h"
54 #include "../include/refit_call_wrapper.h"
55 #include "driver_support.h"
56 #include "../include/syslinux_mbr.h"
58 #ifdef __MAKEWITH_GNUEFI
59 #define EFI_SECURITY_VIOLATION EFIERR (26)
61 #include "../EfiLib/BdsHelper.h"
62 #endif // __MAKEWITH_GNUEFI
64 #ifndef EFI_OS_INDICATIONS_BOOT_TO_FW_UI
65 #define EFI_OS_INDICATIONS_BOOT_TO_FW_UI 0x0000000000000001ULL
71 #define MACOSX_LOADER_PATH L"System\\Library\\CoreServices\\boot.efi"
73 #define SHELL_NAMES L"\\EFI\\tools\\shell.efi,\\EFI\\tools\\shellx64.efi,\\shell.efi,\\shellx64.efi"
74 #define GPTSYNC_NAMES L"\\EFI\\tools\\gptsync.efi,\\EFI\\tools\\gptsync_x64.efi"
75 #define DRIVER_DIRS L"drivers,drivers_x64"
76 #define FALLBACK_FULLNAME L"EFI\\BOOT\\bootx64.efi"
77 #define FALLBACK_BASENAME L"bootx64.efi"
79 #define SHELL_NAMES L"\\EFI\\tools\\shell.efi,\\EFI\\tools\\shellia32.efi,\\shell.efi,\\shellia32.efi"
80 #define GPTSYNC_NAMES L"\\EFI\\tools\\gptsync.efi,\\EFI\\tools\\gptsync_ia32.efi"
81 #define DRIVER_DIRS L"drivers,drivers_ia32"
82 #define FALLBACK_FULLNAME L"EFI\\BOOT\\bootia32.efi"
83 #define FALLBACK_BASENAME L"bootia32.efi"
85 #define SHELL_NAMES L"\\EFI\\tools\\shell.efi,\\shell.efi"
86 #define GPTSYNC_NAMES L"\\EFI\\tools\\gptsync.efi"
87 #define DRIVER_DIRS L"drivers"
88 #define FALLBACK_FULLNAME L"EFI\\BOOT\\boot.efi" /* Not really correct */
89 #define FALLBACK_BASENAME L"boot.efi" /* Not really correct */
92 // Filename patterns that identify EFI boot loaders. Note that a single case (either L"*.efi" or
93 // L"*.EFI") is fine for most systems; but Gigabyte's buggy Hybrid EFI does a case-sensitive
94 // comparison when it should do a case-insensitive comparison, so I'm doubling this up. It does
95 // no harm on other computers, AFAIK. In theory, every case variation should be done for
96 // completeness, but that's ridiculous....
97 #define LOADER_MATCH_PATTERNS L"*.efi,*.EFI"
99 // Patterns that identify Linux kernels. Added to the loader match pattern when the
100 // scan_all_linux_kernels option is set in the configuration file. Causes kernels WITHOUT
101 // a ".efi" extension to be found when scanning for boot loaders.
102 #define LINUX_MATCH_PATTERNS L"vmlinuz*,bzImage*"
104 // Default hint text for program-launch submenus
105 #define SUBSCREEN_HINT1 L"Use arrow keys to move cursor; Enter to boot;"
106 #define SUBSCREEN_HINT2 L"Insert or F2 to edit options; Esc to return to main menu"
107 #define SUBSCREEN_HINT2_NO_EDITOR L"Esc to return to main menu"
109 static REFIT_MENU_ENTRY MenuEntryAbout
= { L
"About rEFInd", TAG_ABOUT
, 1, 0, 'A', NULL
, NULL
, NULL
};
110 static REFIT_MENU_ENTRY MenuEntryReset
= { L
"Reboot Computer", TAG_REBOOT
, 1, 0, 'R', NULL
, NULL
, NULL
};
111 static REFIT_MENU_ENTRY MenuEntryShutdown
= { L
"Shut Down Computer", TAG_SHUTDOWN
, 1, 0, 'U', NULL
, NULL
, NULL
};
112 static REFIT_MENU_ENTRY MenuEntryReturn
= { L
"Return to Main Menu", TAG_RETURN
, 1, 0, 0, NULL
, NULL
, NULL
};
113 static REFIT_MENU_ENTRY MenuEntryExit
= { L
"Exit rEFInd", TAG_EXIT
, 1, 0, 0, NULL
, NULL
, NULL
};
114 static REFIT_MENU_ENTRY MenuEntryFirmware
= { L
"Reboot to Firmware User Interface", TAG_FIRMWARE
, 1, 0, 0, NULL
, NULL
, NULL
};
116 static REFIT_MENU_SCREEN MainMenu
= { L
"Main Menu", NULL
, 0, NULL
, 0, NULL
, 0, L
"Automatic boot",
117 L
"Use arrow keys to move cursor; Enter to boot;",
118 L
"Insert or F2 for more options; Esc to refresh" };
119 static REFIT_MENU_SCREEN AboutMenu
= { L
"About", NULL
, 0, NULL
, 0, NULL
, 0, NULL
, L
"Press Enter to return to main menu", L
"" };
121 REFIT_CONFIG GlobalConfig
= { FALSE
, FALSE
, 0, 0, 0, DONT_CHANGE_TEXT_MODE
, 20, 0, 0, GRAPHICS_FOR_OSX
, LEGACY_TYPE_MAC
, 0, 0,
122 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
123 { TAG_SHELL
, TAG_APPLE_RECOVERY
, TAG_MOK_TOOL
, TAG_ABOUT
, TAG_SHUTDOWN
, TAG_REBOOT
, TAG_FIRMWARE
,
127 const EFI_GUID GlobalGuid
= EFI_GLOBAL_VARIABLE
;
129 // Structure used to hold boot loader filenames and time stamps in
130 // a linked list; used to sort entries within a directory.
134 struct LOADER_LIST
*NextEntry
;
141 static VOID
AboutrEFInd(VOID
)
143 CHAR16
*TempStr
; // Note: Don't deallocate; moved to menu structure
145 if (AboutMenu
.EntryCount
== 0) {
146 AboutMenu
.TitleImage
= BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT
);
147 AddMenuInfoLine(&AboutMenu
, L
"rEFInd Version 0.6.9.2");
148 AddMenuInfoLine(&AboutMenu
, L
"");
149 AddMenuInfoLine(&AboutMenu
, L
"Copyright (c) 2006-2010 Christoph Pfisterer");
150 AddMenuInfoLine(&AboutMenu
, L
"Copyright (c) 2012-2013 Roderick W. Smith");
151 AddMenuInfoLine(&AboutMenu
, L
"Portions Copyright (c) Intel Corporation and others");
152 AddMenuInfoLine(&AboutMenu
, L
"Distributed under the terms of the GNU GPLv3 license");
153 AddMenuInfoLine(&AboutMenu
, L
"");
154 AddMenuInfoLine(&AboutMenu
, L
"Running on:");
155 TempStr
= AllocateZeroPool(256 * sizeof(CHAR16
));
156 SPrint(TempStr
, 255, L
" EFI Revision %d.%02d", ST
->Hdr
.Revision
>> 16, ST
->Hdr
.Revision
& ((1 << 16) - 1));
157 AddMenuInfoLine(&AboutMenu
, TempStr
);
159 AddMenuInfoLine(&AboutMenu
, L
" Platform: x86 (32 bit)");
160 #elif defined(EFIX64)
161 TempStr
= AllocateZeroPool(256 * sizeof(CHAR16
));
162 SPrint(TempStr
, 255, L
" Platform: x86_64 (64 bit); Secure Boot %s", secure_mode() ? L
"active" : L
"inactive");
163 AddMenuInfoLine(&AboutMenu
, TempStr
);
165 AddMenuInfoLine(&AboutMenu
, L
" Platform: unknown");
167 TempStr
= AllocateZeroPool(256 * sizeof(CHAR16
));
168 SPrint(TempStr
, 255, L
" Firmware: %s %d.%02d",
169 ST
->FirmwareVendor
, ST
->FirmwareRevision
>> 16, ST
->FirmwareRevision
& ((1 << 16) - 1));
170 AddMenuInfoLine(&AboutMenu
, TempStr
);
171 TempStr
= AllocateZeroPool(256 * sizeof(CHAR16
));
172 SPrint(TempStr
, 255, L
" Screen Output: %s", egScreenDescription());
173 AddMenuInfoLine(&AboutMenu
, TempStr
);
174 AddMenuInfoLine(&AboutMenu
, L
"");
175 #if defined(__MAKEWITH_GNUEFI)
176 AddMenuInfoLine(&AboutMenu
, L
"Built with GNU-EFI");
178 AddMenuInfoLine(&AboutMenu
, L
"Built with TianoCore EDK2");
180 AddMenuInfoLine(&AboutMenu
, L
"");
181 AddMenuInfoLine(&AboutMenu
, L
"For more information, see the rEFInd Web site:");
182 AddMenuInfoLine(&AboutMenu
, L
"http://www.rodsbooks.com/refind/");
183 AddMenuEntry(&AboutMenu
, &MenuEntryReturn
);
186 RunMenu(&AboutMenu
, NULL
);
187 } /* VOID AboutrEFInd() */
189 static VOID
WarnSecureBootError(CHAR16
*Name
, BOOLEAN Verbose
) {
191 Name
= L
"the loader";
193 refit_call2_wrapper(ST
->ConOut
->SetAttribute
, ST
->ConOut
, ATTR_ERROR
);
194 Print(L
"Secure Boot validation failure loading %s!\n", Name
);
195 refit_call2_wrapper(ST
->ConOut
->SetAttribute
, ST
->ConOut
, ATTR_BASIC
);
196 if (Verbose
&& secure_mode()) {
197 Print(L
"\nThis computer is configured with Secure Boot active, but\n%s has failed validation.\n", Name
);
198 Print(L
"\nYou can:\n * Launch another boot loader\n");
199 Print(L
" * Disable Secure Boot in your firmware\n");
200 Print(L
" * Sign %s with a machine owner key (MOK)\n", Name
);
201 Print(L
" * Use a MOK utility (often present on the second row) to add a MOK with which\n");
202 Print(L
" %s has already been signed.\n", Name
);
203 Print(L
" * Use a MOK utility to register %s (\"enroll its hash\") without\n", Name
);
204 Print(L
" signing it.\n");
205 Print(L
"\nSee http://www.rodsbooks.com/refind/secureboot.html for more information\n");
208 } // VOID WarnSecureBootError()
210 // Launch an EFI binary.
211 static EFI_STATUS
StartEFIImageList(IN EFI_DEVICE_PATH
**DevicePaths
,
212 IN CHAR16
*LoadOptions
, IN CHAR16
*LoadOptionsPrefix
,
213 IN CHAR16
*ImageTitle
, IN CHAR8 OSType
,
214 OUT UINTN
*ErrorInStep
,
217 EFI_STATUS Status
, ReturnStatus
;
218 EFI_HANDLE ChildImageHandle
;
219 EFI_LOADED_IMAGE
*ChildLoadedImage
= NULL
;
220 UINTN DevicePathIndex
;
221 CHAR16 ErrorInfo
[256];
222 CHAR16
*FullLoadOptions
= NULL
;
224 if (ErrorInStep
!= NULL
)
228 if (LoadOptions
!= NULL
) {
229 if (LoadOptionsPrefix
!= NULL
) {
230 MergeStrings(&FullLoadOptions
, LoadOptions
, L
' ');
232 MergeStrings(&FullLoadOptions
, L
" ", 0);
233 // NOTE: That last space is also added by the EFI shell and seems to be significant
234 // when passing options to Apple's boot.efi...
237 MergeStrings(&FullLoadOptions
, LoadOptions
, 0);
239 } else { // LoadOptions == NULL
240 // NOTE: We provide a non-null string when no options are specified for safety;
241 // some systems (at least DUET) can hang when launching some programs (such as
242 // an EFI shell) without this.
243 FullLoadOptions
= StrDuplicate(L
" ");
246 Print(L
"Starting %s\nUsing load options '%s'\n", ImageTitle
, FullLoadOptions
);
248 // load the image into memory (and execute it, in the case of a shim/MOK image).
249 ReturnStatus
= Status
= EFI_NOT_FOUND
; // in case the list is empty
250 for (DevicePathIndex
= 0; DevicePaths
[DevicePathIndex
] != NULL
; DevicePathIndex
++) {
251 // NOTE: Below commented-out line could be more efficient if file were read ahead of
252 // time and passed as a pre-loaded image to LoadImage(), but it doesn't work on my
253 // 32-bit Mac Mini or my 64-bit Intel box when launching a Linux kernel; the
254 // kernel returns a "Failed to handle fs_proto" error message.
255 // TODO: Track down the cause of this error and fix it, if possible.
256 // ReturnStatus = Status = refit_call6_wrapper(BS->LoadImage, FALSE, SelfImageHandle, DevicePaths[DevicePathIndex],
257 // ImageData, ImageSize, &ChildImageHandle);
258 ReturnStatus
= Status
= refit_call6_wrapper(BS
->LoadImage
, FALSE
, SelfImageHandle
, DevicePaths
[DevicePathIndex
],
259 NULL
, 0, &ChildImageHandle
);
260 if (ReturnStatus
!= EFI_NOT_FOUND
) {
264 if ((Status
== EFI_ACCESS_DENIED
) || (Status
== EFI_SECURITY_VIOLATION
)) {
265 WarnSecureBootError(ImageTitle
, Verbose
);
268 SPrint(ErrorInfo
, 255, L
"while loading %s", ImageTitle
);
269 if (CheckError(Status
, ErrorInfo
)) {
270 if (ErrorInStep
!= NULL
)
275 ReturnStatus
= Status
= refit_call3_wrapper(BS
->HandleProtocol
, ChildImageHandle
, &LoadedImageProtocol
,
276 (VOID
**) &ChildLoadedImage
);
277 if (CheckError(Status
, L
"while getting a LoadedImageProtocol handle")) {
278 if (ErrorInStep
!= NULL
)
282 ChildLoadedImage
->LoadOptions
= (VOID
*)FullLoadOptions
;
283 ChildLoadedImage
->LoadOptionsSize
= ((UINT32
)StrLen(FullLoadOptions
) + 1) * sizeof(CHAR16
);
284 // turn control over to the image
285 // TODO: (optionally) re-enable the EFI watchdog timer!
287 // close open file handles
289 ReturnStatus
= Status
= refit_call3_wrapper(BS
->StartImage
, ChildImageHandle
, NULL
, NULL
);
291 // control returns here when the child image calls Exit()
292 SPrint(ErrorInfo
, 255, L
"returned from %s", ImageTitle
);
293 if (CheckError(Status
, ErrorInfo
)) {
294 if (ErrorInStep
!= NULL
)
298 // re-open file handles
302 // unload the image, we don't care if it works or not...
303 Status
= refit_call1_wrapper(BS
->UnloadImage
, ChildImageHandle
);
306 MyFreePool(FullLoadOptions
);
308 } /* static EFI_STATUS StartEFIImageList() */
310 static EFI_STATUS
StartEFIImage(IN EFI_DEVICE_PATH
*DevicePath
,
311 IN CHAR16
*LoadOptions
, IN CHAR16
*LoadOptionsPrefix
,
312 IN CHAR16
*ImageTitle
, IN CHAR8 OSType
,
313 OUT UINTN
*ErrorInStep
,
316 EFI_DEVICE_PATH
*DevicePaths
[2];
318 DevicePaths
[0] = DevicePath
;
319 DevicePaths
[1] = NULL
;
320 return StartEFIImageList(DevicePaths
, LoadOptions
, LoadOptionsPrefix
, ImageTitle
, OSType
, ErrorInStep
, Verbose
);
321 } /* static EFI_STATUS StartEFIImage() */
323 // From gummiboot: Retrieve a raw EFI variable.
324 // Returns EFI status
325 static EFI_STATUS
EfivarGetRaw(const EFI_GUID
*vendor
, CHAR16
*name
, CHAR8
**buffer
, UINTN
*size
) {
330 l
= sizeof(CHAR16
*) * EFI_MAXIMUM_VARIABLE_SIZE
;
331 buf
= AllocatePool(l
);
333 return EFI_OUT_OF_RESOURCES
;
335 err
= uefi_call_wrapper(RT
->GetVariable
, 5, name
, vendor
, NULL
, &l
, buf
);
336 if (EFI_ERROR(err
) == EFI_SUCCESS
) {
343 } // EFI_STATUS EfivarGetRaw()
345 // From gummiboot: Set an EFI variable
346 static EFI_STATUS
EfivarSetRaw(const EFI_GUID
*vendor
, CHAR16
*name
, CHAR8
*buf
, UINTN size
, BOOLEAN persistent
) {
349 flags
= EFI_VARIABLE_BOOTSERVICE_ACCESS
|EFI_VARIABLE_RUNTIME_ACCESS
;
351 flags
|= EFI_VARIABLE_NON_VOLATILE
;
353 return uefi_call_wrapper(RT
->SetVariable
, 5, name
, vendor
, flags
, size
, buf
);
354 } // EFI_STATUS EfivarSetRaw()
356 // From gummiboot: Reboot the computer into its built-in user interface
357 static EFI_STATUS
RebootIntoFirmware(VOID
) {
363 osind
= EFI_OS_INDICATIONS_BOOT_TO_FW_UI
;
365 err
= EfivarGetRaw(&GlobalGuid
, L
"OsIndications", &b
, &size
);
366 if (err
== EFI_SUCCESS
)
370 err
= EfivarSetRaw(&GlobalGuid
, L
"OsIndications", (CHAR8
*)&osind
, sizeof(UINT64
), TRUE
);
371 if (err
!= EFI_SUCCESS
)
374 uefi_call_wrapper(RT
->ResetSystem
, 4, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);
375 Print(L
"Error calling ResetSystem: %r", err
);
377 // uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
383 // EFI OS loader functions
386 static VOID
StartLoader(IN LOADER_ENTRY
*Entry
)
388 UINTN ErrorInStep
= 0;
390 BeginExternalScreen(Entry
->UseGraphicsMode
, L
"Booting OS");
391 StartEFIImage(Entry
->DevicePath
, Entry
->LoadOptions
, Basename(Entry
->LoaderPath
),
392 Basename(Entry
->LoaderPath
), Entry
->OSType
, &ErrorInStep
, !Entry
->UseGraphicsMode
);
393 FinishExternalScreen();
396 // Locate an initrd or initramfs file that matches the kernel specified by LoaderPath.
397 // The matching file has a name that begins with "init" and includes the same version
398 // number string as is found in LoaderPath -- but not a longer version number string.
399 // For instance, if LoaderPath is \EFI\kernels\bzImage-3.3.0.efi, and if \EFI\kernels
400 // has a file called initramfs-3.3.0.img, this function will return the string
401 // '\EFI\kernels\initramfs-3.3.0.img'. If the directory ALSO contains the file
402 // initramfs-3.3.0-rc7.img or initramfs-13.3.0.img, those files will NOT match;
403 // however, initmine-3.3.0.img might match. (FindInitrd() returns the first match it
404 // finds). Thus, care should be taken to avoid placing duplicate matching files in
405 // the kernel's directory.
406 // If no matching init file can be found, returns NULL.
407 static CHAR16
* FindInitrd(IN CHAR16
*LoaderPath
, IN REFIT_VOLUME
*Volume
) {
408 CHAR16
*InitrdName
= NULL
, *FileName
, *KernelVersion
, *InitrdVersion
, *Path
;
409 REFIT_DIR_ITER DirIter
;
410 EFI_FILE_INFO
*DirEntry
;
412 FileName
= Basename(LoaderPath
);
413 KernelVersion
= FindNumbers(FileName
);
414 Path
= FindPath(LoaderPath
);
416 // Add trailing backslash for root directory; necessary on some systems, but must
417 // NOT be added to all directories, since on other systems, a trailing backslash on
418 // anything but the root directory causes them to flake out!
419 if (StrLen(Path
) == 0) {
420 MergeStrings(&Path
, L
"\\", 0);
422 DirIterOpen(Volume
->RootDir
, Path
, &DirIter
);
423 // Now add a trailing backslash if it was NOT added earlier, for consistency in
424 // building the InitrdName later....
425 if ((StrLen(Path
) > 0) && (Path
[StrLen(Path
) - 1] != L
'\\'))
426 MergeStrings(&Path
, L
"\\", 0);
427 while ((DirIterNext(&DirIter
, 2, L
"init*", &DirEntry
)) && (InitrdName
== NULL
)) {
428 InitrdVersion
= FindNumbers(DirEntry
->FileName
);
429 if (KernelVersion
!= NULL
) {
430 if (StriCmp(InitrdVersion
, KernelVersion
) == 0) {
431 MergeStrings(&InitrdName
, Path
, 0);
432 MergeStrings(&InitrdName
, DirEntry
->FileName
, 0);
435 if (InitrdVersion
== NULL
) {
436 MergeStrings(&InitrdName
, Path
, 0);
437 MergeStrings(&InitrdName
, DirEntry
->FileName
, 0);
440 MyFreePool(InitrdVersion
);
442 DirIterClose(&DirIter
);
444 // Note: Don't FreePool(FileName), since Basename returns a pointer WITHIN the string it's passed.
445 MyFreePool(KernelVersion
);
448 } // static CHAR16 * FindInitrd()
450 LOADER_ENTRY
* AddPreparedLoaderEntry(LOADER_ENTRY
*Entry
) {
451 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
454 } // LOADER_ENTRY * AddPreparedLoaderEntry()
456 // Creates a copy of a menu screen.
457 // Returns a pointer to the copy of the menu screen.
458 static REFIT_MENU_SCREEN
* CopyMenuScreen(REFIT_MENU_SCREEN
*Entry
) {
459 REFIT_MENU_SCREEN
*NewEntry
;
462 NewEntry
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
463 if ((Entry
!= NULL
) && (NewEntry
!= NULL
)) {
464 CopyMem(NewEntry
, Entry
, sizeof(REFIT_MENU_SCREEN
));
465 NewEntry
->Title
= (Entry
->Title
) ? StrDuplicate(Entry
->Title
) : NULL
;
466 NewEntry
->TimeoutText
= (Entry
->TimeoutText
) ? StrDuplicate(Entry
->TimeoutText
) : NULL
;
467 if (Entry
->TitleImage
!= NULL
) {
468 NewEntry
->TitleImage
= AllocatePool(sizeof(EG_IMAGE
));
469 if (NewEntry
->TitleImage
!= NULL
)
470 CopyMem(NewEntry
->TitleImage
, Entry
->TitleImage
, sizeof(EG_IMAGE
));
472 NewEntry
->InfoLines
= (CHAR16
**) AllocateZeroPool(Entry
->InfoLineCount
* (sizeof(CHAR16
*)));
473 for (i
= 0; i
< Entry
->InfoLineCount
&& NewEntry
->InfoLines
; i
++) {
474 NewEntry
->InfoLines
[i
] = (Entry
->InfoLines
[i
]) ? StrDuplicate(Entry
->InfoLines
[i
]) : NULL
;
476 NewEntry
->Entries
= (REFIT_MENU_ENTRY
**) AllocateZeroPool(Entry
->EntryCount
* (sizeof (REFIT_MENU_ENTRY
*)));
477 for (i
= 0; i
< Entry
->EntryCount
&& NewEntry
->Entries
; i
++) {
478 AddMenuEntry(NewEntry
, Entry
->Entries
[i
]);
480 NewEntry
->Hint1
= (Entry
->Hint1
) ? StrDuplicate(Entry
->Hint1
) : NULL
;
481 NewEntry
->Hint2
= (Entry
->Hint2
) ? StrDuplicate(Entry
->Hint2
) : NULL
;
484 } // static REFIT_MENU_SCREEN* CopyMenuScreen()
486 // Creates a copy of a menu entry. Intended to enable moving a stack-based
487 // menu entry (such as the ones for the "reboot" and "exit" functions) to
488 // to the heap. This enables easier deletion of the whole set of menu
489 // entries when re-scanning.
490 // Returns a pointer to the copy of the menu entry.
491 static REFIT_MENU_ENTRY
* CopyMenuEntry(REFIT_MENU_ENTRY
*Entry
) {
492 REFIT_MENU_ENTRY
*NewEntry
;
494 NewEntry
= AllocateZeroPool(sizeof(REFIT_MENU_ENTRY
));
495 if ((Entry
!= NULL
) && (NewEntry
!= NULL
)) {
496 CopyMem(NewEntry
, Entry
, sizeof(REFIT_MENU_ENTRY
));
497 NewEntry
->Title
= (Entry
->Title
) ? StrDuplicate(Entry
->Title
) : NULL
;
498 if (Entry
->BadgeImage
!= NULL
) {
499 NewEntry
->BadgeImage
= AllocatePool(sizeof(EG_IMAGE
));
500 if (NewEntry
->BadgeImage
!= NULL
)
501 CopyMem(NewEntry
->BadgeImage
, Entry
->BadgeImage
, sizeof(EG_IMAGE
));
503 if (Entry
->Image
!= NULL
) {
504 NewEntry
->Image
= AllocatePool(sizeof(EG_IMAGE
));
505 if (NewEntry
->Image
!= NULL
)
506 CopyMem(NewEntry
->Image
, Entry
->Image
, sizeof(EG_IMAGE
));
508 if (Entry
->SubScreen
!= NULL
) {
509 NewEntry
->SubScreen
= CopyMenuScreen(Entry
->SubScreen
);
513 } // REFIT_MENU_ENTRY* CopyMenuEntry()
515 // Creates a new LOADER_ENTRY data structure and populates it with
516 // default values from the specified Entry, or NULL values if Entry
517 // is unspecified (NULL).
518 // Returns a pointer to the new data structure, or NULL if it
519 // couldn't be allocated
520 LOADER_ENTRY
*InitializeLoaderEntry(IN LOADER_ENTRY
*Entry
) {
521 LOADER_ENTRY
*NewEntry
= NULL
;
523 NewEntry
= AllocateZeroPool(sizeof(LOADER_ENTRY
));
524 if (NewEntry
!= NULL
) {
525 NewEntry
->me
.Title
= NULL
;
526 NewEntry
->me
.Tag
= TAG_LOADER
;
527 NewEntry
->Enabled
= TRUE
;
528 NewEntry
->UseGraphicsMode
= FALSE
;
529 NewEntry
->OSType
= 0;
531 NewEntry
->LoaderPath
= (Entry
->LoaderPath
) ? StrDuplicate(Entry
->LoaderPath
) : NULL
;
532 NewEntry
->VolName
= (Entry
->VolName
) ? StrDuplicate(Entry
->VolName
) : NULL
;
533 NewEntry
->DevicePath
= Entry
->DevicePath
;
534 NewEntry
->UseGraphicsMode
= Entry
->UseGraphicsMode
;
535 NewEntry
->LoadOptions
= (Entry
->LoadOptions
) ? StrDuplicate(Entry
->LoadOptions
) : NULL
;
536 NewEntry
->InitrdPath
= (Entry
->InitrdPath
) ? StrDuplicate(Entry
->InitrdPath
) : NULL
;
540 } // LOADER_ENTRY *InitializeLoaderEntry()
542 // Adds InitrdPath to Options, but only if Options doesn't already include an
543 // initrd= line. Done to enable overriding the default initrd selection in a
544 // refind_linux.conf file's options list.
545 // Returns a pointer to a new string. The calling function is responsible for
546 // freeing its memory.
547 static CHAR16
*AddInitrdToOptions(CHAR16
*Options
, CHAR16
*InitrdPath
) {
548 CHAR16
*NewOptions
= NULL
;
551 NewOptions
= StrDuplicate(Options
);
552 if ((InitrdPath
!= NULL
) && !StriSubCmp(L
"initrd=", Options
)) {
553 MergeStrings(&NewOptions
, L
"initrd=", L
' ');
554 MergeStrings(&NewOptions
, InitrdPath
, 0);
557 } // CHAR16 *AddInitrdToOptions()
559 // Prepare a REFIT_MENU_SCREEN data structure for a subscreen entry. This sets up
560 // the default entry that launches the boot loader using the same options as the
561 // main Entry does. Subsequent options can be added by the calling function.
562 // If a subscreen already exists in the Entry that's passed to this function,
563 // it's left unchanged and a pointer to it is returned.
564 // Returns a pointer to the new subscreen data structure, or NULL if there
565 // were problems allocating memory.
566 REFIT_MENU_SCREEN
*InitializeSubScreen(IN LOADER_ENTRY
*Entry
) {
567 CHAR16
*FileName
, *MainOptions
= NULL
;
568 REFIT_MENU_SCREEN
*SubScreen
= NULL
;
569 LOADER_ENTRY
*SubEntry
;
571 FileName
= Basename(Entry
->LoaderPath
);
572 if (Entry
->me
.SubScreen
== NULL
) { // No subscreen yet; initialize default entry....
573 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
574 if (SubScreen
!= NULL
) {
575 SubScreen
->Title
= AllocateZeroPool(sizeof(CHAR16
) * 256);
576 SPrint(SubScreen
->Title
, 255, L
"Boot Options for %s on %s",
577 (Entry
->Title
!= NULL
) ? Entry
->Title
: FileName
, Entry
->VolName
);
578 SubScreen
->TitleImage
= Entry
->me
.Image
;
580 SubEntry
= InitializeLoaderEntry(Entry
);
581 if (SubEntry
!= NULL
) {
582 SubEntry
->me
.Title
= StrDuplicate(L
"Boot using default options");
583 MainOptions
= SubEntry
->LoadOptions
;
584 SubEntry
->LoadOptions
= AddInitrdToOptions(MainOptions
, SubEntry
->InitrdPath
);
585 MyFreePool(MainOptions
);
586 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
587 } // if (SubEntry != NULL)
588 SubScreen
->Hint1
= StrDuplicate(SUBSCREEN_HINT1
);
589 if (GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_EDITOR
) {
590 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2_NO_EDITOR
);
592 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2
);
594 } // if (SubScreen != NULL)
595 } else { // existing subscreen; less initialization, and just add new entry later....
596 SubScreen
= Entry
->me
.SubScreen
;
599 } // REFIT_MENU_SCREEN *InitializeSubScreen()
601 VOID
GenerateSubScreen(LOADER_ENTRY
*Entry
, IN REFIT_VOLUME
*Volume
) {
602 REFIT_MENU_SCREEN
*SubScreen
;
603 LOADER_ENTRY
*SubEntry
;
605 CHAR16 DiagsFileName
[256];
610 // create the submenu
611 if (StrLen(Entry
->Title
) == 0) {
612 MyFreePool(Entry
->Title
);
615 SubScreen
= InitializeSubScreen(Entry
);
617 // loader-specific submenu entries
618 if (Entry
->OSType
== 'M') { // entries for Mac OS X
620 SubEntry
= InitializeLoaderEntry(Entry
);
621 if (SubEntry
!= NULL
) {
622 SubEntry
->me
.Title
= L
"Boot Mac OS X with a 64-bit kernel";
623 SubEntry
->LoadOptions
= L
"arch=x86_64";
624 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
625 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
628 SubEntry
= InitializeLoaderEntry(Entry
);
629 if (SubEntry
!= NULL
) {
630 SubEntry
->me
.Title
= L
"Boot Mac OS X with a 32-bit kernel";
631 SubEntry
->LoadOptions
= L
"arch=i386";
632 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
633 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
637 if (!(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_SINGLEUSER
)) {
638 SubEntry
= InitializeLoaderEntry(Entry
);
639 if (SubEntry
!= NULL
) {
640 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode";
641 SubEntry
->UseGraphicsMode
= FALSE
;
642 SubEntry
->LoadOptions
= L
"-v";
643 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
647 SubEntry
= InitializeLoaderEntry(Entry
);
648 if (SubEntry
!= NULL
) {
649 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode (64-bit)";
650 SubEntry
->UseGraphicsMode
= FALSE
;
651 SubEntry
->LoadOptions
= L
"-v arch=x86_64";
652 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
655 SubEntry
= InitializeLoaderEntry(Entry
);
656 if (SubEntry
!= NULL
) {
657 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode (32-bit)";
658 SubEntry
->UseGraphicsMode
= FALSE
;
659 SubEntry
->LoadOptions
= L
"-v arch=i386";
660 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
664 SubEntry
= InitializeLoaderEntry(Entry
);
665 if (SubEntry
!= NULL
) {
666 SubEntry
->me
.Title
= L
"Boot Mac OS X in single user mode";
667 SubEntry
->UseGraphicsMode
= FALSE
;
668 SubEntry
->LoadOptions
= L
"-v -s";
669 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
671 } // single-user mode allowed
673 if (!(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_SAFEMODE
)) {
674 SubEntry
= InitializeLoaderEntry(Entry
);
675 if (SubEntry
!= NULL
) {
676 SubEntry
->me
.Title
= L
"Boot Mac OS X in safe mode";
677 SubEntry
->UseGraphicsMode
= FALSE
;
678 SubEntry
->LoadOptions
= L
"-v -x";
679 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
681 } // safe mode allowed
683 // check for Apple hardware diagnostics
684 StrCpy(DiagsFileName
, L
"System\\Library\\CoreServices\\.diagnostics\\diags.efi");
685 if (FileExists(Volume
->RootDir
, DiagsFileName
) && !(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_HWTEST
)) {
686 SubEntry
= InitializeLoaderEntry(Entry
);
687 if (SubEntry
!= NULL
) {
688 SubEntry
->me
.Title
= L
"Run Apple Hardware Test";
689 MyFreePool(SubEntry
->LoaderPath
);
690 SubEntry
->LoaderPath
= StrDuplicate(DiagsFileName
);
691 SubEntry
->DevicePath
= FileDevicePath(Volume
->DeviceHandle
, SubEntry
->LoaderPath
);
692 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
693 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
695 } // if diagnostics entry found
697 } else if (Entry
->OSType
== 'L') { // entries for Linux kernels with EFI stub loaders
698 File
= ReadLinuxOptionsFile(Entry
->LoaderPath
, Volume
);
700 InitrdName
= FindInitrd(Entry
->LoaderPath
, Volume
);
701 TokenCount
= ReadTokenLine(File
, &TokenList
);
702 // first entry requires special processing, since it was initially set
703 // up with a default title but correct options by InitializeSubScreen(),
705 if ((SubScreen
->Entries
!= NULL
) && (SubScreen
->Entries
[0] != NULL
)) {
706 MyFreePool(SubScreen
->Entries
[0]->Title
);
707 SubScreen
->Entries
[0]->Title
= TokenList
[0] ? StrDuplicate(TokenList
[0]) : StrDuplicate(L
"Boot Linux");
709 FreeTokenLine(&TokenList
, &TokenCount
);
710 while ((TokenCount
= ReadTokenLine(File
, &TokenList
)) > 1) {
711 SubEntry
= InitializeLoaderEntry(Entry
);
712 SubEntry
->me
.Title
= TokenList
[0] ? StrDuplicate(TokenList
[0]) : StrDuplicate(L
"Boot Linux");
713 MyFreePool(SubEntry
->LoadOptions
);
714 SubEntry
->LoadOptions
= AddInitrdToOptions(TokenList
[1], InitrdName
);
715 FreeTokenLine(&TokenList
, &TokenCount
);
716 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_LINUX
;
717 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
719 MyFreePool(InitrdName
);
721 } // if Linux options file exists
723 } else if (Entry
->OSType
== 'E') { // entries for ELILO
724 SubEntry
= InitializeLoaderEntry(Entry
);
725 if (SubEntry
!= NULL
) {
726 SubEntry
->me
.Title
= L
"Run ELILO in interactive mode";
727 SubEntry
->LoadOptions
= L
"-p";
728 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
729 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
732 SubEntry
= InitializeLoaderEntry(Entry
);
733 if (SubEntry
!= NULL
) {
734 SubEntry
->me
.Title
= L
"Boot Linux for a 17\" iMac or a 15\" MacBook Pro (*)";
735 SubEntry
->UseGraphicsMode
= TRUE
;
736 SubEntry
->LoadOptions
= L
"-d 0 i17";
737 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
738 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
741 SubEntry
= InitializeLoaderEntry(Entry
);
742 if (SubEntry
!= NULL
) {
743 SubEntry
->me
.Title
= L
"Boot Linux for a 20\" iMac (*)";
744 SubEntry
->UseGraphicsMode
= TRUE
;
745 SubEntry
->LoadOptions
= L
"-d 0 i20";
746 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
747 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
750 SubEntry
= InitializeLoaderEntry(Entry
);
751 if (SubEntry
!= NULL
) {
752 SubEntry
->me
.Title
= L
"Boot Linux for a Mac Mini (*)";
753 SubEntry
->UseGraphicsMode
= TRUE
;
754 SubEntry
->LoadOptions
= L
"-d 0 mini";
755 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
756 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
759 AddMenuInfoLine(SubScreen
, L
"NOTE: This is an example. Entries");
760 AddMenuInfoLine(SubScreen
, L
"marked with (*) may not work.");
762 } else if (Entry
->OSType
== 'X') { // entries for xom.efi
763 // by default, skip the built-in selection and boot from hard disk only
764 Entry
->LoadOptions
= L
"-s -h";
766 SubEntry
= InitializeLoaderEntry(Entry
);
767 if (SubEntry
!= NULL
) {
768 SubEntry
->me
.Title
= L
"Boot Windows from Hard Disk";
769 SubEntry
->LoadOptions
= L
"-s -h";
770 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
771 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
774 SubEntry
= InitializeLoaderEntry(Entry
);
775 if (SubEntry
!= NULL
) {
776 SubEntry
->me
.Title
= L
"Boot Windows from CD-ROM";
777 SubEntry
->LoadOptions
= L
"-s -c";
778 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
779 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
782 SubEntry
= InitializeLoaderEntry(Entry
);
783 if (SubEntry
!= NULL
) {
784 SubEntry
->me
.Title
= L
"Run XOM in text mode";
785 SubEntry
->UseGraphicsMode
= FALSE
;
786 SubEntry
->LoadOptions
= L
"-v";
787 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
788 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
790 } // entries for xom.efi
791 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
792 Entry
->me
.SubScreen
= SubScreen
;
793 } // VOID GenerateSubScreen()
795 // Returns options for a Linux kernel. Reads them from an options file in the
796 // kernel's directory; and if present, adds an initrd= option for an initial
797 // RAM disk file with the same version number as the kernel file.
798 static CHAR16
* GetMainLinuxOptions(IN CHAR16
* LoaderPath
, IN REFIT_VOLUME
*Volume
) {
799 CHAR16
*Options
= NULL
, *InitrdName
, *FullOptions
= NULL
;
801 Options
= GetFirstOptionsFromFile(LoaderPath
, Volume
);
802 InitrdName
= FindInitrd(LoaderPath
, Volume
);
803 FullOptions
= AddInitrdToOptions(Options
, InitrdName
);
806 MyFreePool(InitrdName
);
807 return (FullOptions
);
808 } // static CHAR16 * GetMainLinuxOptions()
810 // Sets a few defaults for a loader entry -- mainly the icon, but also the OS type
811 // code and shortcut letter. For Linux EFI stub loaders, also sets kernel options
812 // that will (with luck) work fairly automatically.
813 VOID
SetLoaderDefaults(LOADER_ENTRY
*Entry
, CHAR16
*LoaderPath
, REFIT_VOLUME
*Volume
) {
814 CHAR16
*FileName
, *PathOnly
, *NoExtension
, *OSIconName
= NULL
, *Temp
, *SubString
;
815 CHAR16 ShortcutLetter
= 0;
818 FileName
= Basename(LoaderPath
);
819 PathOnly
= FindPath(LoaderPath
);
820 NoExtension
= StripEfiExtension(FileName
);
822 // locate a custom icon for the loader
823 // Anything found here takes precedence over the "hints" in the OSIconName variable
824 if (!Entry
->me
.Image
)
825 Entry
->me
.Image
= egLoadIconAnyType(Volume
->RootDir
, PathOnly
, NoExtension
, 128);
826 if (!Entry
->me
.Image
)
827 Entry
->me
.Image
= egCopyImage(Volume
->VolIconImage
);
829 // Begin creating icon "hints" by using last part of directory path leading
831 Temp
= FindLastDirName(LoaderPath
);
832 MergeStrings(&OSIconName
, Temp
, L
',');
835 if (OSIconName
!= NULL
) {
836 ShortcutLetter
= OSIconName
[0];
839 // Add every "word" in the volume label, delimited by spaces, dashes (-), or
840 // underscores (_), to the list of hints to be used in searching for OS
842 if ((Volume
->VolName
) && (StrLen(Volume
->VolName
) > 0)) {
843 Temp
= SubString
= StrDuplicate(Volume
->VolName
);
845 Length
= StrLen(Temp
);
846 for (i
= 0; i
< Length
; i
++) {
847 if ((Temp
[i
] == L
' ') || (Temp
[i
] == L
'_') || (Temp
[i
] == L
'-')) {
849 if (StrLen(SubString
) > 0)
850 MergeStrings(&OSIconName
, SubString
, L
',');
851 SubString
= Temp
+ i
+ 1;
854 MergeStrings(&OSIconName
, SubString
, L
',');
859 // detect specific loaders
860 if (StriSubCmp(L
"bzImage", LoaderPath
) || StriSubCmp(L
"vmlinuz", LoaderPath
)) {
861 MergeStrings(&OSIconName
, L
"linux", L
',');
863 if (ShortcutLetter
== 0)
864 ShortcutLetter
= 'L';
865 Entry
->LoadOptions
= GetMainLinuxOptions(LoaderPath
, Volume
);
866 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_LINUX
;
867 } else if (StriSubCmp(L
"refit", LoaderPath
)) {
868 MergeStrings(&OSIconName
, L
"refit", L
',');
870 ShortcutLetter
= 'R';
871 } else if (StriCmp(LoaderPath
, MACOSX_LOADER_PATH
) == 0) {
872 if (Volume
->VolIconImage
!= NULL
) { // custom icon file found
873 Entry
->me
.Image
= Volume
->VolIconImage
;
875 MergeStrings(&OSIconName
, L
"mac", L
',');
877 ShortcutLetter
= 'M';
878 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
879 } else if (StriCmp(FileName
, L
"diags.efi") == 0) {
880 MergeStrings(&OSIconName
, L
"hwtest", L
',');
881 } else if (StriCmp(FileName
, L
"e.efi") == 0 || StriCmp(FileName
, L
"elilo.efi") == 0 || StriSubCmp(L
"elilo", FileName
)) {
882 MergeStrings(&OSIconName
, L
"elilo,linux", L
',');
884 if (ShortcutLetter
== 0)
885 ShortcutLetter
= 'L';
886 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
887 } else if (StriSubCmp(L
"grub", FileName
)) {
889 ShortcutLetter
= 'G';
890 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_GRUB
;
891 } else if (StriCmp(FileName
, L
"cdboot.efi") == 0 ||
892 StriCmp(FileName
, L
"bootmgr.efi") == 0 ||
893 StriCmp(FileName
, L
"bootmgfw.efi") == 0) {
894 MergeStrings(&OSIconName
, L
"win", L
',');
896 ShortcutLetter
= 'W';
897 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
898 } else if (StriCmp(FileName
, L
"xom.efi") == 0) {
899 MergeStrings(&OSIconName
, L
"xom,win", L
',');
900 Entry
->UseGraphicsMode
= TRUE
;
902 ShortcutLetter
= 'W';
903 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
906 if ((ShortcutLetter
>= 'a') && (ShortcutLetter
<= 'z'))
907 ShortcutLetter
= ShortcutLetter
- 'a' + 'A'; // convert lowercase to uppercase
908 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
909 if (Entry
->me
.Image
== NULL
)
910 Entry
->me
.Image
= LoadOSIcon(OSIconName
, L
"unknown", FALSE
);
911 MyFreePool(PathOnly
);
912 } // VOID SetLoaderDefaults()
914 // Add a specified EFI boot loader to the list, using automatic settings
915 // for icons, options, etc.
916 LOADER_ENTRY
* AddLoaderEntry(IN CHAR16
*LoaderPath
, IN CHAR16
*LoaderTitle
, IN REFIT_VOLUME
*Volume
) {
919 CleanUpPathNameSlashes(LoaderPath
);
920 Entry
= InitializeLoaderEntry(NULL
);
922 Entry
->Title
= StrDuplicate((LoaderTitle
!= NULL
) ? LoaderTitle
: LoaderPath
);
923 Entry
->me
.Title
= AllocateZeroPool(sizeof(CHAR16
) * 256);
924 SPrint(Entry
->me
.Title
, 255, L
"Boot %s from %s", (LoaderTitle
!= NULL
) ? LoaderTitle
: LoaderPath
, Volume
->VolName
);
926 Entry
->me
.BadgeImage
= Volume
->VolBadgeImage
;
927 if ((LoaderPath
!= NULL
) && (LoaderPath
[0] != L
'\\')) {
928 Entry
->LoaderPath
= StrDuplicate(L
"\\");
930 Entry
->LoaderPath
= NULL
;
932 MergeStrings(&(Entry
->LoaderPath
), LoaderPath
, 0);
933 Entry
->VolName
= Volume
->VolName
;
934 Entry
->DevicePath
= FileDevicePath(Volume
->DeviceHandle
, Entry
->LoaderPath
);
935 SetLoaderDefaults(Entry
, LoaderPath
, Volume
);
936 GenerateSubScreen(Entry
, Volume
);
937 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
941 } // LOADER_ENTRY * AddLoaderEntry()
943 // Returns -1 if (Time1 < Time2), +1 if (Time1 > Time2), or 0 if
944 // (Time1 == Time2). Precision is only to the nearest second; since
945 // this is used for sorting boot loader entries, differences smaller
946 // than this are likely to be meaningless (and unlikely!).
947 INTN
TimeComp(IN EFI_TIME
*Time1
, IN EFI_TIME
*Time2
) {
948 INT64 Time1InSeconds
, Time2InSeconds
;
950 // Following values are overestimates; I'm assuming 31 days in every month.
951 // This is fine for the purpose of this function, which is limited
952 Time1InSeconds
= Time1
->Second
+ (Time1
->Minute
* 60) + (Time1
->Hour
* 3600) + (Time1
->Day
* 86400) +
953 (Time1
->Month
* 2678400) + ((Time1
->Year
- 1998) * 32140800);
954 Time2InSeconds
= Time2
->Second
+ (Time2
->Minute
* 60) + (Time2
->Hour
* 3600) + (Time2
->Day
* 86400) +
955 (Time2
->Month
* 2678400) + ((Time2
->Year
- 1998) * 32140800);
956 if (Time1InSeconds
< Time2InSeconds
)
958 else if (Time1InSeconds
> Time2InSeconds
)
964 // Adds a loader list element, keeping it sorted by date. Returns the new
965 // first element (the one with the most recent date).
966 static struct LOADER_LIST
* AddLoaderListEntry(struct LOADER_LIST
*LoaderList
, struct LOADER_LIST
*NewEntry
) {
967 struct LOADER_LIST
*LatestEntry
, *CurrentEntry
, *PrevEntry
= NULL
;
969 LatestEntry
= CurrentEntry
= LoaderList
;
970 if (LoaderList
== NULL
) {
971 LatestEntry
= NewEntry
;
973 while ((CurrentEntry
!= NULL
) && (TimeComp(&(NewEntry
->TimeStamp
), &(CurrentEntry
->TimeStamp
)) < 0)) {
974 PrevEntry
= CurrentEntry
;
975 CurrentEntry
= CurrentEntry
->NextEntry
;
977 NewEntry
->NextEntry
= CurrentEntry
;
978 if (PrevEntry
== NULL
) {
979 LatestEntry
= NewEntry
;
981 PrevEntry
->NextEntry
= NewEntry
;
984 return (LatestEntry
);
985 } // static VOID AddLoaderListEntry()
987 // Delete the LOADER_LIST linked list
988 static VOID
CleanUpLoaderList(struct LOADER_LIST
*LoaderList
) {
989 struct LOADER_LIST
*Temp
;
991 while (LoaderList
!= NULL
) {
993 LoaderList
= LoaderList
->NextEntry
;
994 MyFreePool(Temp
->FileName
);
997 } // static VOID CleanUpLoaderList()
999 // Returns FALSE if the specified file/volume matches the GlobalConfig.DontScanDirs
1000 // or GlobalConfig.DontScanVolumes specification, or if Path points to a volume
1001 // other than the one specified by Volume, or if the specified path is SelfDir.
1002 // Returns TRUE if none of these conditions is met -- that is, if the path is
1003 // eligible for scanning.
1004 static BOOLEAN
ShouldScan(REFIT_VOLUME
*Volume
, CHAR16
*Path
) {
1005 CHAR16
*VolName
= NULL
, *DontScanDir
;
1006 UINTN i
= 0, VolNum
;
1007 BOOLEAN ScanIt
= TRUE
;
1009 if (IsIn(Volume
->VolName
, GlobalConfig
.DontScanVolumes
))
1012 if ((StriCmp(Path
, SelfDirPath
) == 0) && (Volume
->DeviceHandle
== SelfVolume
->DeviceHandle
))
1015 while ((DontScanDir
= FindCommaDelimited(GlobalConfig
.DontScanDirs
, i
++)) && ScanIt
) {
1016 SplitVolumeAndFilename(&DontScanDir
, &VolName
);
1017 CleanUpPathNameSlashes(DontScanDir
);
1018 if (VolName
!= NULL
) {
1019 if ((StriCmp(VolName
, Volume
->VolName
) == 0) && (StriCmp(DontScanDir
, Path
) == 0))
1021 if ((StrLen(VolName
) > 2) && (VolName
[0] == L
'f') && (VolName
[1] == L
's') && (VolName
[2] >= L
'0') && (VolName
[2] <= L
'9')) {
1022 VolNum
= Atoi(VolName
+ 2);
1023 if ((VolNum
== Volume
->VolNumber
) && (StriCmp(DontScanDir
, Path
) == 0))
1027 if (StriCmp(DontScanDir
, Path
) == 0)
1030 MyFreePool(DontScanDir
);
1034 } // BOOLEAN ShouldScan()
1036 // Returns TRUE if the file is byte-for-byte identical with the fallback file
1037 // on the volume AND if the file is not itself the fallback file; returns
1038 // FALSE if the file is not identical to the fallback file OR if the file
1039 // IS the fallback file. Intended for use in excluding the fallback boot
1040 // loader when it's a duplicate of another boot loader.
1041 static BOOLEAN
DuplicatesFallback(IN REFIT_VOLUME
*Volume
, IN CHAR16
*FileName
) {
1042 CHAR8
*FileContents
, *FallbackContents
;
1043 EFI_FILE_HANDLE FileHandle
, FallbackHandle
;
1044 EFI_FILE_INFO
*FileInfo
, *FallbackInfo
;
1045 UINTN FileSize
= 0, FallbackSize
= 0;
1047 BOOLEAN AreIdentical
= FALSE
;
1049 if (!FileExists(Volume
->RootDir
, FileName
) || !FileExists(Volume
->RootDir
, FALLBACK_FULLNAME
))
1052 CleanUpPathNameSlashes(FileName
);
1054 if (StriCmp(FileName
, FALLBACK_FULLNAME
) == 0)
1055 return FALSE
; // identical filenames, so not a duplicate....
1057 Status
= refit_call5_wrapper(Volume
->RootDir
->Open
, Volume
->RootDir
, &FileHandle
, FileName
, EFI_FILE_MODE_READ
, 0);
1058 if (Status
== EFI_SUCCESS
) {
1059 FileInfo
= LibFileInfo(FileHandle
);
1060 FileSize
= FileInfo
->FileSize
;
1065 Status
= refit_call5_wrapper(Volume
->RootDir
->Open
, Volume
->RootDir
, &FallbackHandle
, FALLBACK_FULLNAME
, EFI_FILE_MODE_READ
, 0);
1066 if (Status
== EFI_SUCCESS
) {
1067 FallbackInfo
= LibFileInfo(FallbackHandle
);
1068 FallbackSize
= FallbackInfo
->FileSize
;
1070 refit_call1_wrapper(FileHandle
->Close
, FileHandle
);
1074 if (FallbackSize
!= FileSize
) { // not same size, so can't be identical
1075 AreIdentical
= FALSE
;
1076 } else { // could be identical; do full check....
1077 FileContents
= AllocatePool(FileSize
);
1078 FallbackContents
= AllocatePool(FallbackSize
);
1079 if (FileContents
&& FallbackContents
) {
1080 Status
= refit_call3_wrapper(FileHandle
->Read
, FileHandle
, &FileSize
, FileContents
);
1081 if (Status
== EFI_SUCCESS
) {
1082 Status
= refit_call3_wrapper(FallbackHandle
->Read
, FallbackHandle
, &FallbackSize
, FallbackContents
);
1084 if (Status
== EFI_SUCCESS
) {
1085 AreIdentical
= (CompareMem(FileContents
, FallbackContents
, FileSize
) == 0);
1088 MyFreePool(FileContents
);
1089 MyFreePool(FallbackContents
);
1092 // BUG ALERT: Some systems (e.g., DUET, some Macs with large displays) crash if the
1093 // following two calls are reversed. Go figure....
1094 refit_call1_wrapper(FileHandle
->Close
, FallbackHandle
);
1095 refit_call1_wrapper(FileHandle
->Close
, FileHandle
);
1096 return AreIdentical
;
1097 } // BOOLEAN DuplicatesFallback()
1099 // Scan an individual directory for EFI boot loader files and, if found,
1100 // add them to the list. Exception: Ignores FALLBACK_FULLNAME, which is picked
1101 // up in ScanEfiFiles(). Sorts the entries within the loader directory so that
1102 // the most recent one appears first in the list.
1103 // Returns TRUE if a duplicate for FALLBACK_FILENAME was found, FALSE if not.
1104 static BOOLEAN
ScanLoaderDir(IN REFIT_VOLUME
*Volume
, IN CHAR16
*Path
, IN CHAR16
*Pattern
)
1107 REFIT_DIR_ITER DirIter
;
1108 EFI_FILE_INFO
*DirEntry
;
1109 CHAR16 FileName
[256], *Extension
;
1110 struct LOADER_LIST
*LoaderList
= NULL
, *NewLoader
;
1111 BOOLEAN FoundFallbackDuplicate
= FALSE
;
1113 if ((!SelfDirPath
|| !Path
|| ((StriCmp(Path
, SelfDirPath
) == 0) && (Volume
->DeviceHandle
!= SelfVolume
->DeviceHandle
)) ||
1114 (StriCmp(Path
, SelfDirPath
) != 0)) &&
1115 (ShouldScan(Volume
, Path
))) {
1116 // look through contents of the directory
1117 DirIterOpen(Volume
->RootDir
, Path
, &DirIter
);
1118 while (DirIterNext(&DirIter
, 2, Pattern
, &DirEntry
)) {
1119 Extension
= FindExtension(DirEntry
->FileName
);
1120 if (DirEntry
->FileName
[0] == '.' ||
1121 StriCmp(Extension
, L
".icns") == 0 ||
1122 StriCmp(Extension
, L
".png") == 0 ||
1123 (StriCmp(DirEntry
->FileName
, FALLBACK_BASENAME
) == 0 && (StriCmp(Path
, L
"EFI\\BOOT") == 0)) ||
1124 StriSubCmp(L
"shell", DirEntry
->FileName
) ||
1125 IsIn(DirEntry
->FileName
, GlobalConfig
.DontScanFiles
))
1126 continue; // skip this
1129 SPrint(FileName
, 255, L
"\\%s\\%s", Path
, DirEntry
->FileName
);
1131 SPrint(FileName
, 255, L
"\\%s", DirEntry
->FileName
);
1132 CleanUpPathNameSlashes(FileName
);
1133 NewLoader
= AllocateZeroPool(sizeof(struct LOADER_LIST
));
1134 if (NewLoader
!= NULL
) {
1135 NewLoader
->FileName
= StrDuplicate(FileName
);
1136 NewLoader
->TimeStamp
= DirEntry
->ModificationTime
;
1137 LoaderList
= AddLoaderListEntry(LoaderList
, NewLoader
);
1138 if (DuplicatesFallback(Volume
, FileName
))
1139 FoundFallbackDuplicate
= TRUE
;
1141 MyFreePool(Extension
);
1144 NewLoader
= LoaderList
;
1145 while (NewLoader
!= NULL
) {
1146 AddLoaderEntry(NewLoader
->FileName
, NULL
, Volume
);
1147 NewLoader
= NewLoader
->NextEntry
;
1150 CleanUpLoaderList(LoaderList
);
1151 Status
= DirIterClose(&DirIter
);
1152 // NOTE: EFI_INVALID_PARAMETER really is an error that should be reported;
1153 // but I've gotten reports from users who are getting this error occasionally
1154 // and I can't find anything wrong or reproduce the problem, so I'm putting
1155 // it down to buggy EFI implementations and ignoring that particular error....
1156 if ((Status
!= EFI_NOT_FOUND
) && (Status
!= EFI_INVALID_PARAMETER
)) {
1158 SPrint(FileName
, 255, L
"while scanning the %s directory", Path
);
1160 StrCpy(FileName
, L
"while scanning the root directory");
1161 CheckError(Status
, FileName
);
1162 } // if (Status != EFI_NOT_FOUND)
1163 } // if not scanning a blacklisted directory
1165 return FoundFallbackDuplicate
;
1166 } /* static VOID ScanLoaderDir() */
1168 static VOID
ScanEfiFiles(REFIT_VOLUME
*Volume
) {
1170 REFIT_DIR_ITER EfiDirIter
;
1171 EFI_FILE_INFO
*EfiDirEntry
;
1172 CHAR16 FileName
[256], *Directory
, *MatchPatterns
, *VolName
= NULL
, *SelfPath
;
1174 BOOLEAN ScanFallbackLoader
= TRUE
;
1176 MatchPatterns
= StrDuplicate(LOADER_MATCH_PATTERNS
);
1177 if (GlobalConfig
.ScanAllLinux
)
1178 MergeStrings(&MatchPatterns
, LINUX_MATCH_PATTERNS
, L
',');
1180 if ((Volume
->RootDir
!= NULL
) && (Volume
->VolName
!= NULL
)) {
1181 // check for Mac OS X boot loader
1182 if (ShouldScan(Volume
, L
"System\\Library\\CoreServices")) {
1183 StrCpy(FileName
, MACOSX_LOADER_PATH
);
1184 if (FileExists(Volume
->RootDir
, FileName
) && !IsIn(L
"boot.efi", GlobalConfig
.DontScanFiles
)) {
1185 AddLoaderEntry(FileName
, L
"Mac OS X", Volume
);
1186 if (DuplicatesFallback(Volume
, FileName
))
1187 ScanFallbackLoader
= FALSE
;
1191 StrCpy(FileName
, L
"System\\Library\\CoreServices\\xom.efi");
1192 if (FileExists(Volume
->RootDir
, FileName
) && !IsIn(L
"boot.efi", GlobalConfig
.DontScanFiles
)) {
1193 AddLoaderEntry(FileName
, L
"Windows XP (XoM)", Volume
);
1194 if (DuplicatesFallback(Volume
, FileName
))
1195 ScanFallbackLoader
= FALSE
;
1197 } // if should scan Mac directory
1199 // check for Microsoft boot loader/menu
1200 StrCpy(FileName
, L
"EFI\\Microsoft\\Boot\\Bootmgfw.efi");
1201 if (FileExists(Volume
->RootDir
, FileName
) && ShouldScan(Volume
, L
"EFI\\Microsoft\\Boot") &&
1202 !IsIn(L
"bootmgfw.efi", GlobalConfig
.DontScanFiles
)) {
1203 AddLoaderEntry(FileName
, L
"Microsoft EFI boot", Volume
);
1204 if (DuplicatesFallback(Volume
, FileName
))
1205 ScanFallbackLoader
= FALSE
;
1208 // scan the root directory for EFI executables
1209 if (ScanLoaderDir(Volume
, L
"\\", MatchPatterns
))
1210 ScanFallbackLoader
= FALSE
;
1212 // scan subdirectories of the EFI directory (as per the standard)
1213 DirIterOpen(Volume
->RootDir
, L
"EFI", &EfiDirIter
);
1214 while (DirIterNext(&EfiDirIter
, 1, NULL
, &EfiDirEntry
)) {
1215 if (StriCmp(EfiDirEntry
->FileName
, L
"tools") == 0 || EfiDirEntry
->FileName
[0] == '.')
1216 continue; // skip this, doesn't contain boot loaders or is scanned later
1217 SPrint(FileName
, 255, L
"EFI\\%s", EfiDirEntry
->FileName
);
1218 if (ScanLoaderDir(Volume
, FileName
, MatchPatterns
))
1219 ScanFallbackLoader
= FALSE
;
1221 Status
= DirIterClose(&EfiDirIter
);
1222 if (Status
!= EFI_NOT_FOUND
)
1223 CheckError(Status
, L
"while scanning the EFI directory");
1225 // Scan user-specified (or additional default) directories....
1227 while ((Directory
= FindCommaDelimited(GlobalConfig
.AlsoScan
, i
++)) != NULL
) {
1228 SplitVolumeAndFilename(&Directory
, &VolName
);
1229 CleanUpPathNameSlashes(Directory
);
1230 Length
= StrLen(Directory
);
1231 if ((Length
> 0) && ScanLoaderDir(Volume
, Directory
, MatchPatterns
))
1232 ScanFallbackLoader
= FALSE
;
1233 MyFreePool(Directory
);
1234 MyFreePool(VolName
);
1237 // Don't scan the fallback loader if it's on the same volume and a duplicate of rEFInd itself....
1238 SelfPath
= DevicePathToStr(SelfLoadedImage
->FilePath
);
1239 CleanUpPathNameSlashes(SelfPath
);
1240 if ((Volume
->DeviceHandle
== SelfLoadedImage
->DeviceHandle
) && DuplicatesFallback(Volume
, SelfPath
))
1241 ScanFallbackLoader
= FALSE
;
1243 // If not a duplicate & if it exists & if it's not us, create an entry
1244 // for the fallback boot loader
1245 if (ScanFallbackLoader
&& FileExists(Volume
->RootDir
, FALLBACK_FULLNAME
) && ShouldScan(Volume
, L
"EFI\\BOOT"))
1246 AddLoaderEntry(FALLBACK_FULLNAME
, L
"Fallback boot loader", Volume
);
1248 } // static VOID ScanEfiFiles()
1250 // Scan internal disks for valid EFI boot loaders....
1251 static VOID
ScanInternal(VOID
) {
1254 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1255 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_INTERNAL
) {
1256 ScanEfiFiles(Volumes
[VolumeIndex
]);
1259 } // static VOID ScanInternal()
1261 // Scan external disks for valid EFI boot loaders....
1262 static VOID
ScanExternal(VOID
) {
1265 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1266 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_EXTERNAL
) {
1267 ScanEfiFiles(Volumes
[VolumeIndex
]);
1270 } // static VOID ScanExternal()
1272 // Scan internal disks for valid EFI boot loaders....
1273 static VOID
ScanOptical(VOID
) {
1276 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1277 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_OPTICAL
) {
1278 ScanEfiFiles(Volumes
[VolumeIndex
]);
1281 } // static VOID ScanOptical()
1284 // legacy boot functions
1287 static EFI_STATUS
ActivateMbrPartition(IN EFI_BLOCK_IO
*BlockIO
, IN UINTN PartitionIndex
)
1290 UINT8 SectorBuffer
[512];
1291 MBR_PARTITION_INFO
*MbrTable
, *EMbrTable
;
1292 UINT32 ExtBase
, ExtCurrent
, NextExtCurrent
;
1293 UINTN LogicalPartitionIndex
= 4;
1295 BOOLEAN HaveBootCode
;
1298 Status
= refit_call5_wrapper(BlockIO
->ReadBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, 0, 512, SectorBuffer
);
1299 if (EFI_ERROR(Status
))
1301 if (*((UINT16
*)(SectorBuffer
+ 510)) != 0xaa55)
1302 return EFI_NOT_FOUND
; // safety measure #1
1304 // add boot code if necessary
1305 HaveBootCode
= FALSE
;
1306 for (i
= 0; i
< MBR_BOOTCODE_SIZE
; i
++) {
1307 if (SectorBuffer
[i
] != 0) {
1308 HaveBootCode
= TRUE
;
1312 if (!HaveBootCode
) {
1313 // no boot code found in the MBR, add the syslinux MBR code
1314 SetMem(SectorBuffer
, MBR_BOOTCODE_SIZE
, 0);
1315 CopyMem(SectorBuffer
, syslinux_mbr
, SYSLINUX_MBR_SIZE
);
1318 // set the partition active
1319 MbrTable
= (MBR_PARTITION_INFO
*)(SectorBuffer
+ 446);
1321 for (i
= 0; i
< 4; i
++) {
1322 if (MbrTable
[i
].Flags
!= 0x00 && MbrTable
[i
].Flags
!= 0x80)
1323 return EFI_NOT_FOUND
; // safety measure #2
1324 if (i
== PartitionIndex
)
1325 MbrTable
[i
].Flags
= 0x80;
1326 else if (PartitionIndex
>= 4 && IS_EXTENDED_PART_TYPE(MbrTable
[i
].Type
)) {
1327 MbrTable
[i
].Flags
= 0x80;
1328 ExtBase
= MbrTable
[i
].StartLBA
;
1330 MbrTable
[i
].Flags
= 0x00;
1334 Status
= refit_call5_wrapper(BlockIO
->WriteBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, 0, 512, SectorBuffer
);
1335 if (EFI_ERROR(Status
))
1338 if (PartitionIndex
>= 4) {
1339 // we have to activate a logical partition, so walk the EMBR chain
1341 // NOTE: ExtBase was set above while looking at the MBR table
1342 for (ExtCurrent
= ExtBase
; ExtCurrent
; ExtCurrent
= NextExtCurrent
) {
1343 // read current EMBR
1344 Status
= refit_call5_wrapper(BlockIO
->ReadBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, ExtCurrent
, 512, SectorBuffer
);
1345 if (EFI_ERROR(Status
))
1347 if (*((UINT16
*)(SectorBuffer
+ 510)) != 0xaa55)
1348 return EFI_NOT_FOUND
; // safety measure #3
1350 // scan EMBR, set appropriate partition active
1351 EMbrTable
= (MBR_PARTITION_INFO
*)(SectorBuffer
+ 446);
1353 for (i
= 0; i
< 4; i
++) {
1354 if (EMbrTable
[i
].Flags
!= 0x00 && EMbrTable
[i
].Flags
!= 0x80)
1355 return EFI_NOT_FOUND
; // safety measure #4
1356 if (EMbrTable
[i
].StartLBA
== 0 || EMbrTable
[i
].Size
== 0)
1358 if (IS_EXTENDED_PART_TYPE(EMbrTable
[i
].Type
)) {
1359 // link to next EMBR
1360 NextExtCurrent
= ExtBase
+ EMbrTable
[i
].StartLBA
;
1361 EMbrTable
[i
].Flags
= (PartitionIndex
>= LogicalPartitionIndex
) ? 0x80 : 0x00;
1364 // logical partition
1365 EMbrTable
[i
].Flags
= (PartitionIndex
== LogicalPartitionIndex
) ? 0x80 : 0x00;
1366 LogicalPartitionIndex
++;
1370 // write current EMBR
1371 Status
= refit_call5_wrapper(BlockIO
->WriteBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, ExtCurrent
, 512, SectorBuffer
);
1372 if (EFI_ERROR(Status
))
1375 if (PartitionIndex
< LogicalPartitionIndex
)
1376 break; // stop the loop, no need to touch further EMBRs
1382 } /* static EFI_STATUS ActivateMbrPartition() */
1384 // early 2006 Core Duo / Core Solo models
1385 static UINT8 LegacyLoaderDevicePath1Data
[] = {
1386 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1387 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1388 0xFF, 0xFF, 0xF9, 0xFF, 0x00, 0x00, 0x00, 0x00,
1389 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1390 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1391 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1393 // mid-2006 Mac Pro (and probably other Core 2 models)
1394 static UINT8 LegacyLoaderDevicePath2Data
[] = {
1395 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1396 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1397 0xFF, 0xFF, 0xF7, 0xFF, 0x00, 0x00, 0x00, 0x00,
1398 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1399 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1400 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1402 // mid-2007 MBP ("Santa Rosa" based models)
1403 static UINT8 LegacyLoaderDevicePath3Data
[] = {
1404 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1405 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1406 0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
1407 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1408 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1409 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1412 static UINT8 LegacyLoaderDevicePath4Data
[] = {
1413 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1414 0x00, 0x00, 0xC0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1415 0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
1416 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1417 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1418 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1420 // late-2008 MB/MBP (NVidia chipset)
1421 static UINT8 LegacyLoaderDevicePath5Data
[] = {
1422 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1423 0x00, 0x40, 0xCB, 0xFF, 0x00, 0x00, 0x00, 0x00,
1424 0xFF, 0xBF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
1425 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1426 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1427 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1430 static EFI_DEVICE_PATH
*LegacyLoaderList
[] = {
1431 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath1Data
,
1432 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath2Data
,
1433 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath3Data
,
1434 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath4Data
,
1435 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath5Data
,
1439 #define MAX_DISCOVERED_PATHS (16)
1441 static VOID
StartLegacy(IN LEGACY_ENTRY
*Entry
)
1444 EG_IMAGE
*BootLogoImage
;
1445 UINTN ErrorInStep
= 0;
1446 EFI_DEVICE_PATH
*DiscoveredPathList
[MAX_DISCOVERED_PATHS
];
1448 BeginExternalScreen(TRUE
, L
"Booting Legacy OS (Mac mode)");
1450 BootLogoImage
= LoadOSIcon(Entry
->Volume
->OSIconName
, L
"legacy", TRUE
);
1451 if (BootLogoImage
!= NULL
)
1452 BltImageAlpha(BootLogoImage
,
1453 (UGAWidth
- BootLogoImage
->Width
) >> 1,
1454 (UGAHeight
- BootLogoImage
->Height
) >> 1,
1455 &StdBackgroundPixel
);
1457 if (Entry
->Volume
->IsMbrPartition
) {
1458 ActivateMbrPartition(Entry
->Volume
->WholeDiskBlockIO
, Entry
->Volume
->MbrPartitionIndex
);
1461 ExtractLegacyLoaderPaths(DiscoveredPathList
, MAX_DISCOVERED_PATHS
, LegacyLoaderList
);
1463 Status
= StartEFIImageList(DiscoveredPathList
, Entry
->LoadOptions
, NULL
, L
"legacy loader", 0, &ErrorInStep
, TRUE
);
1464 if (Status
== EFI_NOT_FOUND
) {
1465 if (ErrorInStep
== 1) {
1466 Print(L
"\nPlease make sure that you have the latest firmware update installed.\n");
1467 } else if (ErrorInStep
== 3) {
1468 Print(L
"\nThe firmware refused to boot from the selected volume. Note that external\n"
1469 L
"hard drives are not well-supported by Apple's firmware for legacy OS booting.\n");
1472 FinishExternalScreen();
1473 } /* static VOID StartLegacy() */
1475 // Start a device on a non-Mac using the EFI_LEGACY_BIOS_PROTOCOL
1476 #ifdef __MAKEWITH_TIANO
1477 static VOID
StartLegacyUEFI(IN LEGACY_ENTRY
*Entry
)
1479 BeginExternalScreen(TRUE
, L
"Booting Legacy OS (UEFI mode)");
1481 BdsLibConnectDevicePath (Entry
->BdsOption
->DevicePath
);
1482 BdsLibDoLegacyBoot(Entry
->BdsOption
);
1484 // If we get here, it means that there was a failure....
1485 Print(L
"Failure booting legacy (BIOS) OS.");
1487 FinishExternalScreen();
1488 } // static VOID StartLegacyUEFI()
1489 #endif // __MAKEWITH_TIANO
1491 static LEGACY_ENTRY
* AddLegacyEntry(IN CHAR16
*LoaderTitle
, IN REFIT_VOLUME
*Volume
)
1493 LEGACY_ENTRY
*Entry
, *SubEntry
;
1494 REFIT_MENU_SCREEN
*SubScreen
;
1496 CHAR16 ShortcutLetter
= 0;
1498 if (LoaderTitle
== NULL
) {
1499 if (Volume
->OSName
!= NULL
) {
1500 LoaderTitle
= Volume
->OSName
;
1501 if (LoaderTitle
[0] == 'W' || LoaderTitle
[0] == 'L')
1502 ShortcutLetter
= LoaderTitle
[0];
1504 LoaderTitle
= L
"Legacy OS";
1506 if (Volume
->VolName
!= NULL
)
1507 VolDesc
= Volume
->VolName
;
1509 VolDesc
= (Volume
->DiskKind
== DISK_KIND_OPTICAL
) ? L
"CD" : L
"HD";
1511 // prepare the menu entry
1512 Entry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1513 Entry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1514 SPrint(Entry
->me
.Title
, 255, L
"Boot %s from %s", LoaderTitle
, VolDesc
);
1515 Entry
->me
.Tag
= TAG_LEGACY
;
1517 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1518 Entry
->me
.Image
= LoadOSIcon(Volume
->OSIconName
, L
"legacy", FALSE
);
1519 Entry
->me
.BadgeImage
= Volume
->VolBadgeImage
;
1520 Entry
->Volume
= Volume
;
1521 Entry
->LoadOptions
= (Volume
->DiskKind
== DISK_KIND_OPTICAL
) ? L
"CD" :
1522 ((Volume
->DiskKind
== DISK_KIND_EXTERNAL
) ? L
"USB" : L
"HD");
1523 Entry
->Enabled
= TRUE
;
1525 // create the submenu
1526 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
1527 SubScreen
->Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1528 SPrint(SubScreen
->Title
, 255, L
"Boot Options for %s on %s", LoaderTitle
, VolDesc
);
1529 SubScreen
->TitleImage
= Entry
->me
.Image
;
1530 SubScreen
->Hint1
= StrDuplicate(SUBSCREEN_HINT1
);
1531 if (GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_EDITOR
) {
1532 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2_NO_EDITOR
);
1534 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2
);
1538 SubEntry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1539 SubEntry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1540 SPrint(SubEntry
->me
.Title
, 255, L
"Boot %s", LoaderTitle
);
1541 SubEntry
->me
.Tag
= TAG_LEGACY
;
1542 SubEntry
->Volume
= Entry
->Volume
;
1543 SubEntry
->LoadOptions
= Entry
->LoadOptions
;
1544 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
1546 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
1547 Entry
->me
.SubScreen
= SubScreen
;
1548 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1550 } /* static LEGACY_ENTRY * AddLegacyEntry() */
1553 #ifdef __MAKEWITH_GNUEFI
1554 static VOID
ScanLegacyUEFI(IN UINTN DiskType
){}
1556 // default volume badge icon based on disk kind
1557 static EG_IMAGE
* GetDiskBadge(IN UINTN DiskType
) {
1558 EG_IMAGE
* Badge
= NULL
;
1562 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_INTERNAL
);
1565 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_EXTERNAL
);
1568 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_OPTICAL
);
1572 } // static EG_IMAGE * GetDiskBadge()
1575 Create a rEFInd boot option from a Legacy BIOS protocol option.
1577 static LEGACY_ENTRY
* AddLegacyEntryUEFI(BDS_COMMON_OPTION
*BdsOption
, IN UINT16 DiskType
)
1579 LEGACY_ENTRY
*Entry
, *SubEntry
;
1580 REFIT_MENU_SCREEN
*SubScreen
;
1581 CHAR16 ShortcutLetter
= 0;
1582 CHAR16
*LegacyDescription
= BdsOption
->Description
;
1584 // prepare the menu entry
1585 Entry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1586 Entry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1587 SPrint(Entry
->me
.Title
, 255, L
"Boot legacy target %s", LegacyDescription
);
1588 Entry
->me
.Tag
= TAG_LEGACY_UEFI
;
1590 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1591 Entry
->me
.Image
= LoadOSIcon(L
"legacy", L
"legacy", TRUE
);
1592 Entry
->LoadOptions
= (DiskType
== BBS_CDROM
) ? L
"CD" :
1593 ((DiskType
== BBS_USB
) ? L
"USB" : L
"HD");
1594 Entry
->me
.BadgeImage
= GetDiskBadge(DiskType
);
1595 Entry
->BdsOption
= BdsOption
;
1596 Entry
->Enabled
= TRUE
;
1598 // create the submenu
1599 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
1600 SubScreen
->Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1601 SPrint(SubScreen
->Title
, 255, L
"No boot options for legacy target");
1602 SubScreen
->TitleImage
= Entry
->me
.Image
;
1603 SubScreen
->Hint1
= StrDuplicate(SUBSCREEN_HINT1
);
1604 if (GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_EDITOR
) {
1605 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2_NO_EDITOR
);
1607 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2
);
1611 SubEntry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1612 SubEntry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1613 SPrint(SubEntry
->me
.Title
, 255, L
"Boot %s", LegacyDescription
);
1614 SubEntry
->me
.Tag
= TAG_LEGACY_UEFI
;
1615 Entry
->BdsOption
= BdsOption
;
1616 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
1618 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
1619 Entry
->me
.SubScreen
= SubScreen
;
1620 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1622 } /* static LEGACY_ENTRY * AddLegacyEntryUEFI() */
1625 Scan for legacy BIOS targets on machines that implement EFI_LEGACY_BIOS_PROTOCOL.
1626 In testing, protocol has not been implemented on Macs but has been
1627 implemented on several Dell PCs and an ASUS motherboard.
1628 Restricts output to disks of the specified DiskType.
1630 static VOID
ScanLegacyUEFI(IN UINTN DiskType
)
1633 EFI_LEGACY_BIOS_PROTOCOL
*LegacyBios
;
1634 UINT16
*BootOrder
= NULL
;
1636 CHAR16 BootOption
[10];
1637 UINTN BootOrderSize
= 0;
1639 BDS_COMMON_OPTION
*BdsOption
;
1640 LIST_ENTRY TempList
;
1641 BBS_BBS_DEVICE_PATH
* BbsDevicePath
= NULL
;
1643 InitializeListHead (&TempList
);
1644 ZeroMem (Buffer
, sizeof (Buffer
));
1646 // If LegacyBios protocol is not implemented on this platform, then
1647 //we do not support this type of legacy boot on this machine.
1648 Status
= gBS
->LocateProtocol(&gEfiLegacyBootProtocolGuid
, NULL
, (VOID
**) &LegacyBios
);
1649 if (EFI_ERROR (Status
))
1652 // Grab the boot order
1653 BootOrder
= BdsLibGetVariableAndSize(L
"BootOrder", &gEfiGlobalVariableGuid
, &BootOrderSize
);
1654 if (BootOrder
== NULL
) {
1659 while (Index
< BootOrderSize
/ sizeof (UINT16
))
1661 // Grab each boot option variable from the boot order, and convert
1662 // the variable into a BDS boot option
1663 UnicodeSPrint (BootOption
, sizeof (BootOption
), L
"Boot%04x", BootOrder
[Index
]);
1664 BdsOption
= BdsLibVariableToOption (&TempList
, BootOption
);
1666 if (BdsOption
!= NULL
) {
1667 BbsDevicePath
= (BBS_BBS_DEVICE_PATH
*)BdsOption
->DevicePath
;
1669 // Only add the entry if it is of a requested type (e.g. USB, HD)
1671 // Two checks necessary because some systems return EFI boot loaders
1672 // with a DeviceType value that would inappropriately include them
1673 // as legacy loaders....
1674 if ((BbsDevicePath
->DeviceType
== DiskType
) && (BdsOption
->DevicePath
->Type
== DEVICE_TYPE_BIOS
)) {
1675 AddLegacyEntryUEFI(BdsOption
, BbsDevicePath
->DeviceType
);
1680 } /* static VOID ScanLegacyUEFI() */
1681 #endif // __MAKEWITH_GNUEFI
1683 static VOID
ScanLegacyVolume(REFIT_VOLUME
*Volume
, UINTN VolumeIndex
) {
1685 BOOLEAN ShowVolume
, HideIfOthersFound
;
1688 HideIfOthersFound
= FALSE
;
1689 if (Volume
->IsAppleLegacy
) {
1691 HideIfOthersFound
= TRUE
;
1692 } else if (Volume
->HasBootCode
) {
1694 if (Volume
->BlockIO
== Volume
->WholeDiskBlockIO
&&
1695 Volume
->BlockIOOffset
== 0 &&
1696 Volume
->OSName
== NULL
)
1697 // this is a whole disk (MBR) entry; hide if we have entries for partitions
1698 HideIfOthersFound
= TRUE
;
1700 if (HideIfOthersFound
) {
1701 // check for other bootable entries on the same disk
1702 for (VolumeIndex2
= 0; VolumeIndex2
< VolumesCount
; VolumeIndex2
++) {
1703 if (VolumeIndex2
!= VolumeIndex
&& Volumes
[VolumeIndex2
]->HasBootCode
&&
1704 Volumes
[VolumeIndex2
]->WholeDiskBlockIO
== Volume
->WholeDiskBlockIO
)
1710 AddLegacyEntry(NULL
, Volume
);
1711 } // static VOID ScanLegacyVolume()
1713 // Scan attached optical discs for legacy (BIOS) boot code
1714 // and add anything found to the list....
1715 static VOID
ScanLegacyDisc(VOID
)
1718 REFIT_VOLUME
*Volume
;
1720 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
) {
1721 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1722 Volume
= Volumes
[VolumeIndex
];
1723 if (Volume
->DiskKind
== DISK_KIND_OPTICAL
)
1724 ScanLegacyVolume(Volume
, VolumeIndex
);
1726 } else if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
1727 ScanLegacyUEFI(BBS_CDROM
);
1729 } /* static VOID ScanLegacyDisc() */
1731 // Scan internal hard disks for legacy (BIOS) boot code
1732 // and add anything found to the list....
1733 static VOID
ScanLegacyInternal(VOID
)
1736 REFIT_VOLUME
*Volume
;
1738 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
) {
1739 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1740 Volume
= Volumes
[VolumeIndex
];
1741 if (Volume
->DiskKind
== DISK_KIND_INTERNAL
)
1742 ScanLegacyVolume(Volume
, VolumeIndex
);
1744 } else if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
1745 ScanLegacyUEFI(BBS_HARDDISK
);
1747 } /* static VOID ScanLegacyInternal() */
1749 // Scan external disks for legacy (BIOS) boot code
1750 // and add anything found to the list....
1751 static VOID
ScanLegacyExternal(VOID
)
1754 REFIT_VOLUME
*Volume
;
1756 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
) {
1757 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1758 Volume
= Volumes
[VolumeIndex
];
1759 if (Volume
->DiskKind
== DISK_KIND_EXTERNAL
)
1760 ScanLegacyVolume(Volume
, VolumeIndex
);
1762 } else if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
1763 ScanLegacyUEFI(BBS_USB
);
1765 } /* static VOID ScanLegacyExternal() */
1768 // pre-boot tool functions
1771 static VOID
StartTool(IN LOADER_ENTRY
*Entry
)
1773 BeginExternalScreen(Entry
->UseGraphicsMode
, Entry
->me
.Title
+ 6); // assumes "Start <title>" as assigned below
1774 StartEFIImage(Entry
->DevicePath
, Entry
->LoadOptions
, Basename(Entry
->LoaderPath
),
1775 Basename(Entry
->LoaderPath
), Entry
->OSType
, NULL
, TRUE
);
1776 FinishExternalScreen();
1777 } /* static VOID StartTool() */
1779 static LOADER_ENTRY
* AddToolEntry(EFI_HANDLE DeviceHandle
, IN CHAR16
*LoaderPath
, IN CHAR16
*LoaderTitle
, IN EG_IMAGE
*Image
,
1780 IN CHAR16 ShortcutLetter
, IN BOOLEAN UseGraphicsMode
)
1782 LOADER_ENTRY
*Entry
;
1783 CHAR16
*TitleStr
= NULL
;
1785 Entry
= AllocateZeroPool(sizeof(LOADER_ENTRY
));
1787 MergeStrings(&TitleStr
, L
"Start ", 0);
1788 MergeStrings(&TitleStr
, LoaderTitle
, 0);
1789 Entry
->me
.Title
= TitleStr
;
1790 Entry
->me
.Tag
= TAG_TOOL
;
1792 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1793 Entry
->me
.Image
= Image
;
1794 Entry
->LoaderPath
= (LoaderPath
) ? StrDuplicate(LoaderPath
) : NULL
;
1795 Entry
->DevicePath
= FileDevicePath(DeviceHandle
, Entry
->LoaderPath
);
1796 Entry
->UseGraphicsMode
= UseGraphicsMode
;
1798 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1800 } /* static LOADER_ENTRY * AddToolEntry() */
1803 // pre-boot driver functions
1806 static UINTN
ScanDriverDir(IN CHAR16
*Path
)
1809 REFIT_DIR_ITER DirIter
;
1811 EFI_FILE_INFO
*DirEntry
;
1812 CHAR16 FileName
[256];
1814 CleanUpPathNameSlashes(Path
);
1815 // look through contents of the directory
1816 DirIterOpen(SelfRootDir
, Path
, &DirIter
);
1817 while (DirIterNext(&DirIter
, 2, LOADER_MATCH_PATTERNS
, &DirEntry
)) {
1818 if (DirEntry
->FileName
[0] == '.')
1819 continue; // skip this
1821 SPrint(FileName
, 255, L
"%s\\%s", Path
, DirEntry
->FileName
);
1823 Status
= StartEFIImage(FileDevicePath(SelfLoadedImage
->DeviceHandle
, FileName
),
1824 L
"", DirEntry
->FileName
, DirEntry
->FileName
, 0, NULL
, FALSE
);
1826 Status
= DirIterClose(&DirIter
);
1827 if (Status
!= EFI_NOT_FOUND
) {
1828 SPrint(FileName
, 255, L
"while scanning the %s directory", Path
);
1829 CheckError(Status
, FileName
);
1834 #ifdef __MAKEWITH_GNUEFI
1835 static EFI_STATUS
ConnectAllDriversToAllControllers(VOID
)
1838 UINTN AllHandleCount
;
1839 EFI_HANDLE
*AllHandleBuffer
;
1842 EFI_HANDLE
*HandleBuffer
;
1848 Status
= LibLocateHandle(AllHandles
,
1853 if (EFI_ERROR(Status
))
1856 for (Index
= 0; Index
< AllHandleCount
; Index
++) {
1858 // Scan the handle database
1860 Status
= LibScanHandleDatabase(NULL
,
1862 AllHandleBuffer
[Index
],
1867 if (EFI_ERROR (Status
))
1871 if (HandleType
[Index
] & EFI_HANDLE_TYPE_DRIVER_BINDING_HANDLE
)
1873 if (HandleType
[Index
] & EFI_HANDLE_TYPE_IMAGE_HANDLE
)
1878 for (HandleIndex
= 0; HandleIndex
< HandleCount
; HandleIndex
++) {
1879 if (HandleType
[HandleIndex
] & EFI_HANDLE_TYPE_PARENT_HANDLE
)
1884 if (HandleType
[Index
] & EFI_HANDLE_TYPE_DEVICE_HANDLE
) {
1885 Status
= refit_call4_wrapper(BS
->ConnectController
,
1886 AllHandleBuffer
[Index
],
1894 MyFreePool (HandleBuffer
);
1895 MyFreePool (HandleType
);
1899 MyFreePool (AllHandleBuffer
);
1901 } /* EFI_STATUS ConnectAllDriversToAllControllers() */
1903 static EFI_STATUS
ConnectAllDriversToAllControllers(VOID
) {
1904 BdsLibConnectAllDriversToAllControllers();
1909 // Load all EFI drivers from rEFInd's "drivers" subdirectory and from the
1910 // directories specified by the user in the "scan_driver_dirs" configuration
1912 static VOID
LoadDrivers(VOID
)
1914 CHAR16
*Directory
, *SelfDirectory
;
1915 UINTN i
= 0, Length
, NumFound
= 0;
1917 // load drivers from the subdirectories of rEFInd's home directory specified
1918 // in the DRIVER_DIRS constant.
1919 while ((Directory
= FindCommaDelimited(DRIVER_DIRS
, i
++)) != NULL
) {
1920 SelfDirectory
= SelfDirPath
? StrDuplicate(SelfDirPath
) : NULL
;
1921 CleanUpPathNameSlashes(SelfDirectory
);
1922 MergeStrings(&SelfDirectory
, Directory
, L
'\\');
1923 NumFound
+= ScanDriverDir(SelfDirectory
);
1924 MyFreePool(Directory
);
1925 MyFreePool(SelfDirectory
);
1928 // Scan additional user-specified driver directories....
1930 while ((Directory
= FindCommaDelimited(GlobalConfig
.DriverDirs
, i
++)) != NULL
) {
1931 CleanUpPathNameSlashes(Directory
);
1932 Length
= StrLen(Directory
);
1934 NumFound
+= ScanDriverDir(Directory
);
1936 MyFreePool(Directory
);
1939 // connect all devices
1941 ConnectAllDriversToAllControllers();
1942 } /* static VOID LoadDrivers() */
1944 // Determine what (if any) type of legacy (BIOS) boot support is available
1945 static VOID
FindLegacyBootType(VOID
) {
1946 #ifdef __MAKEWITH_TIANO
1948 EFI_LEGACY_BIOS_PROTOCOL
*LegacyBios
;
1951 GlobalConfig
.LegacyType
= LEGACY_TYPE_NONE
;
1953 // UEFI-style legacy BIOS support is available only with the TianoCore EDK2
1954 // build environment, and then only with some EFI implementations....
1955 #ifdef __MAKEWITH_TIANO
1956 Status
= gBS
->LocateProtocol (&gEfiLegacyBootProtocolGuid
, NULL
, (VOID
**) &LegacyBios
);
1957 if (!EFI_ERROR (Status
))
1958 GlobalConfig
.LegacyType
= LEGACY_TYPE_UEFI
;
1961 // Macs have their own system. If the firmware vendor code contains the
1962 // string "Apple", assume it's available. Note that this overrides the
1963 // UEFI type, and might yield false positives if the vendor string
1964 // contains "Apple" as part of something bigger, so this isn't 100%
1966 if (StriSubCmp(L
"Apple", ST
->FirmwareVendor
))
1967 GlobalConfig
.LegacyType
= LEGACY_TYPE_MAC
;
1968 } // static VOID FindLegacyBootType
1970 // Warn the user if legacy OS scans are enabled but the firmware or this
1971 // application can't support them....
1972 static VOID
WarnIfLegacyProblems() {
1973 BOOLEAN found
= FALSE
;
1976 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_NONE
) {
1978 if (GlobalConfig
.ScanFor
[i
] == 'h' || GlobalConfig
.ScanFor
[i
] == 'b' || GlobalConfig
.ScanFor
[i
] == 'c')
1981 } while ((i
< NUM_SCAN_OPTIONS
) && (!found
));
1983 Print(L
"NOTE: refind.conf's 'scanfor' line specifies scanning for one or more legacy\n");
1984 Print(L
"(BIOS) boot options; however, this is not possible because ");
1985 #ifdef __MAKEWITH_TIANO
1986 Print(L
"your computer lacks\n");
1987 Print(L
"the necessary Compatibility Support Module (CSM) support.\n");
1989 Print(L
"this program was\n");
1990 Print(L
"compiled without the necessary support. Please visit\n");
1991 Print(L
"http://www.rodsbooks.com/refind/getting.html and download and install a rEFInd\n");
1992 Print(L
"binary built with the TianoCore EDK2 to enable legacy boot support.\n");
1996 } // if no legacy support
1997 } // static VOID WarnIfLegacyProblems()
1999 // Locates boot loaders. NOTE: This assumes that GlobalConfig.LegacyType is set correctly.
2000 static VOID
ScanForBootloaders(VOID
) {
2005 // scan for loaders and tools, add them to the menu
2006 for (i
= 0; i
< NUM_SCAN_OPTIONS
; i
++) {
2007 switch(GlobalConfig
.ScanFor
[i
]) {
2012 ScanLegacyInternal();
2015 ScanLegacyExternal();
2018 ScanUserConfigured(CONFIG_FILE_NAME
);
2032 // assign shortcut keys
2033 for (i
= 0; i
< MainMenu
.EntryCount
&& MainMenu
.Entries
[i
]->Row
== 0 && i
< 9; i
++)
2034 MainMenu
.Entries
[i
]->ShortcutDigit
= (CHAR16
)('1' + i
);
2036 // wait for user ACK when there were errors
2037 FinishTextScreen(FALSE
);
2038 } // static VOID ScanForBootloaders()
2040 // Add the second-row tags containing built-in and external tools (EFI shell,
2042 static VOID
ScanForTools(VOID
) {
2043 CHAR16
*FileName
= NULL
, *MokLocations
, *MokName
, *PathName
, Description
[256];
2044 REFIT_MENU_ENTRY
*TempMenuEntry
;
2045 UINTN i
, j
, k
, VolumeIndex
;
2049 MokLocations
= StrDuplicate(MOK_LOCATIONS
);
2050 if (MokLocations
!= NULL
)
2051 MergeStrings(&MokLocations
, SelfDirPath
, L
',');
2053 for (i
= 0; i
< NUM_TOOLS
; i
++) {
2054 switch(GlobalConfig
.ShowTools
[i
]) {
2055 // NOTE: Be sure that FileName is NULL at the end of each case.
2057 TempMenuEntry
= CopyMenuEntry(&MenuEntryShutdown
);
2058 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_SHUTDOWN
);
2059 AddMenuEntry(&MainMenu
, TempMenuEntry
);
2062 TempMenuEntry
= CopyMenuEntry(&MenuEntryReset
);
2063 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_RESET
);
2064 AddMenuEntry(&MainMenu
, TempMenuEntry
);
2067 TempMenuEntry
= CopyMenuEntry(&MenuEntryAbout
);
2068 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT
);
2069 AddMenuEntry(&MainMenu
, TempMenuEntry
);
2072 TempMenuEntry
= CopyMenuEntry(&MenuEntryExit
);
2073 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_EXIT
);
2074 AddMenuEntry(&MainMenu
, TempMenuEntry
);
2077 if (EfivarGetRaw(&GlobalGuid
, L
"OsIndicationsSupported", &b
, &j
) == EFI_SUCCESS
) {
2079 if (osind
& EFI_OS_INDICATIONS_BOOT_TO_FW_UI
) {
2080 TempMenuEntry
= CopyMenuEntry(&MenuEntryFirmware
);
2081 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_FIRMWARE
);
2082 AddMenuEntry(&MainMenu
, TempMenuEntry
);
2088 while ((FileName
= FindCommaDelimited(SHELL_NAMES
, j
++)) != NULL
) {
2089 if (FileExists(SelfRootDir
, FileName
)) {
2090 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, L
"EFI Shell", BuiltinIcon(BUILTIN_ICON_TOOL_SHELL
),
2093 MyFreePool(FileName
);
2098 while ((FileName
= FindCommaDelimited(GPTSYNC_NAMES
, j
++)) != NULL
) {
2099 if (FileExists(SelfRootDir
, FileName
)) {
2100 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, L
"Hybrid MBR tool", BuiltinIcon(BUILTIN_ICON_TOOL_PART
),
2103 MyFreePool(FileName
);
2107 case TAG_APPLE_RECOVERY
:
2108 FileName
= StrDuplicate(L
"\\com.apple.recovery.boot\\boot.efi");
2109 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
2110 if ((Volumes
[VolumeIndex
]->RootDir
!= NULL
) && (FileExists(Volumes
[VolumeIndex
]->RootDir
, FileName
))) {
2111 SPrint(Description
, 255, L
"Apple Recovery on %s", Volumes
[VolumeIndex
]->VolName
);
2112 AddToolEntry(Volumes
[VolumeIndex
]->DeviceHandle
, FileName
, Description
,
2113 BuiltinIcon(BUILTIN_ICON_TOOL_APPLE_RESCUE
), 'R', TRUE
);
2116 MyFreePool(FileName
);
2121 while ((FileName
= FindCommaDelimited(MokLocations
, j
++)) != NULL
) {
2123 while ((MokName
= FindCommaDelimited(MOK_NAMES
, k
++)) != NULL
) {
2124 PathName
= StrDuplicate(FileName
);
2125 MergeStrings(&PathName
, MokName
, (StriCmp(PathName
, L
"\\") == 0) ? 0 : L
'\\');
2126 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
2127 if ((Volumes
[VolumeIndex
]->RootDir
!= NULL
) && (FileExists(Volumes
[VolumeIndex
]->RootDir
, PathName
))) {
2128 SPrint(Description
, 255, L
"MOK utility at %s on %s", PathName
, Volumes
[VolumeIndex
]->VolName
);
2129 AddToolEntry(Volumes
[VolumeIndex
]->DeviceHandle
, PathName
, Description
,
2130 BuiltinIcon(BUILTIN_ICON_TOOL_MOK_TOOL
), 'S', FALSE
);
2133 MyFreePool(PathName
);
2134 MyFreePool(MokName
);
2135 } // while MOK_NAMES
2136 MyFreePool(FileName
);
2137 } // while MokLocations
2142 } // static VOID ScanForTools
2144 // Rescan for boot loaders
2145 VOID
RescanAll(VOID
) {
2152 egDisplayMessage(L
"Scanning for new boot loaders; please wait....", &BGColor
);
2153 FreeList((VOID
***) &(MainMenu
.Entries
), &MainMenu
.EntryCount
);
2154 MainMenu
.Entries
= NULL
;
2155 MainMenu
.EntryCount
= 0;
2156 ReadConfig(CONFIG_FILE_NAME
);
2157 ConnectAllDriversToAllControllers();
2159 ScanForBootloaders();
2162 } // VOID RescanAll()
2164 #ifdef __MAKEWITH_TIANO
2166 // Minimal initialization function
2167 static VOID
InitializeLib(IN EFI_HANDLE ImageHandle
, IN EFI_SYSTEM_TABLE
*SystemTable
) {
2169 // gImageHandle = ImageHandle;
2170 gBS
= SystemTable
->BootServices
;
2171 // gRS = SystemTable->RuntimeServices;
2172 gRT
= SystemTable
->RuntimeServices
; // Some BDS functions need gRT to be set
2173 EfiGetSystemConfigurationTable (&gEfiDxeServicesTableGuid
, (VOID
**) &gDS
);
2175 InitializeConsoleSim();
2180 // Set up our own Secure Boot extensions....
2181 // Returns TRUE on success, FALSE otherwise
2182 static BOOLEAN
SecureBootSetup(VOID
) {
2184 BOOLEAN Success
= FALSE
;
2186 if (secure_mode() && ShimLoaded()) {
2187 Status
= security_policy_install();
2188 if (Status
== EFI_SUCCESS
) {
2191 Print(L
"Failed to install MOK Secure Boot extensions");
2195 } // VOID SecureBootSetup()
2197 // Remove our own Secure Boot extensions....
2198 // Returns TRUE on success, FALSE otherwise
2199 static BOOLEAN
SecureBootUninstall(VOID
) {
2201 BOOLEAN Success
= TRUE
;
2203 if (secure_mode()) {
2204 Status
= security_policy_uninstall();
2205 if (Status
!= EFI_SUCCESS
) {
2207 BeginTextScreen(L
"Secure Boot Policy Failure");
2208 Print(L
"Failed to uninstall MOK Secure Boot extensions; forcing a reboot.");
2210 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);
2214 } // VOID SecureBootUninstall
2221 efi_main (EFI_HANDLE ImageHandle
, EFI_SYSTEM_TABLE
*SystemTable
)
2224 BOOLEAN MainLoopRunning
= TRUE
;
2225 BOOLEAN MokProtocol
;
2226 REFIT_MENU_ENTRY
*ChosenEntry
;
2228 CHAR16
*Selection
= NULL
;
2232 InitializeLib(ImageHandle
, SystemTable
);
2233 Status
= InitRefitLib(ImageHandle
);
2234 if (EFI_ERROR(Status
))
2237 // read configuration
2238 CopyMem(GlobalConfig
.ScanFor
, "ieom ", NUM_SCAN_OPTIONS
);
2239 FindLegacyBootType();
2240 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
)
2241 CopyMem(GlobalConfig
.ScanFor
, "ihebocm ", NUM_SCAN_OPTIONS
);
2243 ReadConfig(CONFIG_FILE_NAME
);
2246 WarnIfLegacyProblems();
2247 MainMenu
.TimeoutSeconds
= GlobalConfig
.Timeout
;
2249 // disable EFI watchdog timer
2250 refit_call4_wrapper(BS
->SetWatchdogTimer
, 0x0000, 0x0000, 0x0000, NULL
);
2252 // further bootstrap (now with config available)
2253 MokProtocol
= SecureBootSetup();
2255 ScanForBootloaders();
2259 if (GlobalConfig
.ScanDelay
> 0) {
2264 egDisplayMessage(L
"Pausing before disk scan; please wait....", &BGColor
);
2265 for (i
= 0; i
< GlobalConfig
.ScanDelay
; i
++)
2266 refit_call1_wrapper(BS
->Stall
, 1000000);
2270 if (GlobalConfig
.DefaultSelection
)
2271 Selection
= StrDuplicate(GlobalConfig
.DefaultSelection
);
2273 while (MainLoopRunning
) {
2274 MenuExit
= RunMainMenu(&MainMenu
, Selection
, &ChosenEntry
);
2276 // The Escape key triggers a re-scan operation....
2277 if (MenuExit
== MENU_EXIT_ESCAPE
) {
2282 switch (ChosenEntry
->Tag
) {
2284 case TAG_REBOOT
: // Reboot
2286 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);
2287 MainLoopRunning
= FALSE
; // just in case we get this far
2290 case TAG_SHUTDOWN
: // Shut Down
2292 refit_call4_wrapper(RT
->ResetSystem
, EfiResetShutdown
, EFI_SUCCESS
, 0, NULL
);
2293 MainLoopRunning
= FALSE
; // just in case we get this far
2296 case TAG_ABOUT
: // About rEFInd
2300 case TAG_LOADER
: // Boot OS via .EFI loader
2301 StartLoader((LOADER_ENTRY
*)ChosenEntry
);
2304 case TAG_LEGACY
: // Boot legacy OS
2305 StartLegacy((LEGACY_ENTRY
*)ChosenEntry
);
2308 #ifdef __MAKEWITH_TIANO
2309 case TAG_LEGACY_UEFI
: // Boot a legacy OS on a non-Mac
2310 StartLegacyUEFI((LEGACY_ENTRY
*)ChosenEntry
);
2314 case TAG_TOOL
: // Start a EFI tool
2315 StartTool((LOADER_ENTRY
*)ChosenEntry
);
2318 case TAG_EXIT
: // Terminate rEFInd
2319 if ((MokProtocol
) && !SecureBootUninstall()) {
2320 MainLoopRunning
= FALSE
; // just in case we get this far
2322 BeginTextScreen(L
" ");
2327 case TAG_FIRMWARE
: // Reboot into firmware's user interface
2328 RebootIntoFirmware();
2332 MyFreePool(Selection
);
2333 Selection
= (ChosenEntry
->Title
) ? StrDuplicate(ChosenEntry
->Title
) : NULL
;
2336 // If we end up here, things have gone wrong. Try to reboot, and if that
2337 // fails, go into an endless loop.
2338 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);