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-2013 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 Computer Setup Utility", 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.10.1");
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
) {
331 CopyMem(&vendor2
, vendor
, sizeof(EFI_GUID
));
332 l
= sizeof(CHAR16
*) * EFI_MAXIMUM_VARIABLE_SIZE
;
333 buf
= AllocatePool(l
);
335 return EFI_OUT_OF_RESOURCES
;
337 err
= refit_call5_wrapper(RT
->GetVariable
, name
, &vendor2
, NULL
, &l
, buf
);
338 if (EFI_ERROR(err
) == EFI_SUCCESS
) {
345 } // EFI_STATUS EfivarGetRaw()
347 // From gummiboot: Set an EFI variable
348 static EFI_STATUS
EfivarSetRaw(const EFI_GUID
*vendor
, CHAR16
*name
, CHAR8
*buf
, UINTN size
, BOOLEAN persistent
) {
352 CopyMem(&vendor2
, vendor
, sizeof(EFI_GUID
));
353 flags
= EFI_VARIABLE_BOOTSERVICE_ACCESS
|EFI_VARIABLE_RUNTIME_ACCESS
;
355 flags
|= EFI_VARIABLE_NON_VOLATILE
;
357 return refit_call5_wrapper(RT
->SetVariable
, name
, &vendor2
, flags
, size
, buf
);
358 } // EFI_STATUS EfivarSetRaw()
360 // From gummiboot: Reboot the computer into its built-in user interface
361 static EFI_STATUS
RebootIntoFirmware(VOID
) {
367 osind
= EFI_OS_INDICATIONS_BOOT_TO_FW_UI
;
369 err
= EfivarGetRaw(&GlobalGuid
, L
"OsIndications", &b
, &size
);
370 if (err
== EFI_SUCCESS
)
374 err
= EfivarSetRaw(&GlobalGuid
, L
"OsIndications", (CHAR8
*)&osind
, sizeof(UINT64
), TRUE
);
375 if (err
!= EFI_SUCCESS
)
378 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);
379 Print(L
"Error calling ResetSystem: %r", err
);
381 // uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
387 // EFI OS loader functions
390 static VOID
StartLoader(IN LOADER_ENTRY
*Entry
)
392 UINTN ErrorInStep
= 0;
394 BeginExternalScreen(Entry
->UseGraphicsMode
, L
"Booting OS");
395 StartEFIImage(Entry
->DevicePath
, Entry
->LoadOptions
, Basename(Entry
->LoaderPath
),
396 Basename(Entry
->LoaderPath
), Entry
->OSType
, &ErrorInStep
, !Entry
->UseGraphicsMode
);
397 FinishExternalScreen();
400 // Locate an initrd or initramfs file that matches the kernel specified by LoaderPath.
401 // The matching file has a name that begins with "init" and includes the same version
402 // number string as is found in LoaderPath -- but not a longer version number string.
403 // For instance, if LoaderPath is \EFI\kernels\bzImage-3.3.0.efi, and if \EFI\kernels
404 // has a file called initramfs-3.3.0.img, this function will return the string
405 // '\EFI\kernels\initramfs-3.3.0.img'. If the directory ALSO contains the file
406 // initramfs-3.3.0-rc7.img or initramfs-13.3.0.img, those files will NOT match;
407 // however, initmine-3.3.0.img might match. (FindInitrd() returns the first match it
408 // finds). Thus, care should be taken to avoid placing duplicate matching files in
409 // the kernel's directory.
410 // If no matching init file can be found, returns NULL.
411 static CHAR16
* FindInitrd(IN CHAR16
*LoaderPath
, IN REFIT_VOLUME
*Volume
) {
412 CHAR16
*InitrdName
= NULL
, *FileName
, *KernelVersion
, *InitrdVersion
, *Path
;
413 REFIT_DIR_ITER DirIter
;
414 EFI_FILE_INFO
*DirEntry
;
416 FileName
= Basename(LoaderPath
);
417 KernelVersion
= FindNumbers(FileName
);
418 Path
= FindPath(LoaderPath
);
420 // Add trailing backslash for root directory; necessary on some systems, but must
421 // NOT be added to all directories, since on other systems, a trailing backslash on
422 // anything but the root directory causes them to flake out!
423 if (StrLen(Path
) == 0) {
424 MergeStrings(&Path
, L
"\\", 0);
426 DirIterOpen(Volume
->RootDir
, Path
, &DirIter
);
427 // Now add a trailing backslash if it was NOT added earlier, for consistency in
428 // building the InitrdName later....
429 if ((StrLen(Path
) > 0) && (Path
[StrLen(Path
) - 1] != L
'\\'))
430 MergeStrings(&Path
, L
"\\", 0);
431 while ((DirIterNext(&DirIter
, 2, L
"init*", &DirEntry
)) && (InitrdName
== NULL
)) {
432 InitrdVersion
= FindNumbers(DirEntry
->FileName
);
433 if (KernelVersion
!= NULL
) {
434 if (StriCmp(InitrdVersion
, KernelVersion
) == 0) {
435 MergeStrings(&InitrdName
, Path
, 0);
436 MergeStrings(&InitrdName
, DirEntry
->FileName
, 0);
439 if (InitrdVersion
== NULL
) {
440 MergeStrings(&InitrdName
, Path
, 0);
441 MergeStrings(&InitrdName
, DirEntry
->FileName
, 0);
444 MyFreePool(InitrdVersion
);
446 DirIterClose(&DirIter
);
448 // Note: Don't FreePool(FileName), since Basename returns a pointer WITHIN the string it's passed.
449 MyFreePool(KernelVersion
);
452 } // static CHAR16 * FindInitrd()
454 LOADER_ENTRY
* AddPreparedLoaderEntry(LOADER_ENTRY
*Entry
) {
455 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
458 } // LOADER_ENTRY * AddPreparedLoaderEntry()
460 // Creates a copy of a menu screen.
461 // Returns a pointer to the copy of the menu screen.
462 static REFIT_MENU_SCREEN
* CopyMenuScreen(REFIT_MENU_SCREEN
*Entry
) {
463 REFIT_MENU_SCREEN
*NewEntry
;
466 NewEntry
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
467 if ((Entry
!= NULL
) && (NewEntry
!= NULL
)) {
468 CopyMem(NewEntry
, Entry
, sizeof(REFIT_MENU_SCREEN
));
469 NewEntry
->Title
= (Entry
->Title
) ? StrDuplicate(Entry
->Title
) : NULL
;
470 NewEntry
->TimeoutText
= (Entry
->TimeoutText
) ? StrDuplicate(Entry
->TimeoutText
) : NULL
;
471 if (Entry
->TitleImage
!= NULL
) {
472 NewEntry
->TitleImage
= AllocatePool(sizeof(EG_IMAGE
));
473 if (NewEntry
->TitleImage
!= NULL
)
474 CopyMem(NewEntry
->TitleImage
, Entry
->TitleImage
, sizeof(EG_IMAGE
));
476 NewEntry
->InfoLines
= (CHAR16
**) AllocateZeroPool(Entry
->InfoLineCount
* (sizeof(CHAR16
*)));
477 for (i
= 0; i
< Entry
->InfoLineCount
&& NewEntry
->InfoLines
; i
++) {
478 NewEntry
->InfoLines
[i
] = (Entry
->InfoLines
[i
]) ? StrDuplicate(Entry
->InfoLines
[i
]) : NULL
;
480 NewEntry
->Entries
= (REFIT_MENU_ENTRY
**) AllocateZeroPool(Entry
->EntryCount
* (sizeof (REFIT_MENU_ENTRY
*)));
481 for (i
= 0; i
< Entry
->EntryCount
&& NewEntry
->Entries
; i
++) {
482 AddMenuEntry(NewEntry
, Entry
->Entries
[i
]);
484 NewEntry
->Hint1
= (Entry
->Hint1
) ? StrDuplicate(Entry
->Hint1
) : NULL
;
485 NewEntry
->Hint2
= (Entry
->Hint2
) ? StrDuplicate(Entry
->Hint2
) : NULL
;
488 } // static REFIT_MENU_SCREEN* CopyMenuScreen()
490 // Creates a copy of a menu entry. Intended to enable moving a stack-based
491 // menu entry (such as the ones for the "reboot" and "exit" functions) to
492 // to the heap. This enables easier deletion of the whole set of menu
493 // entries when re-scanning.
494 // Returns a pointer to the copy of the menu entry.
495 static REFIT_MENU_ENTRY
* CopyMenuEntry(REFIT_MENU_ENTRY
*Entry
) {
496 REFIT_MENU_ENTRY
*NewEntry
;
498 NewEntry
= AllocateZeroPool(sizeof(REFIT_MENU_ENTRY
));
499 if ((Entry
!= NULL
) && (NewEntry
!= NULL
)) {
500 CopyMem(NewEntry
, Entry
, sizeof(REFIT_MENU_ENTRY
));
501 NewEntry
->Title
= (Entry
->Title
) ? StrDuplicate(Entry
->Title
) : NULL
;
502 if (Entry
->BadgeImage
!= NULL
) {
503 NewEntry
->BadgeImage
= AllocatePool(sizeof(EG_IMAGE
));
504 if (NewEntry
->BadgeImage
!= NULL
)
505 CopyMem(NewEntry
->BadgeImage
, Entry
->BadgeImage
, sizeof(EG_IMAGE
));
507 if (Entry
->Image
!= NULL
) {
508 NewEntry
->Image
= AllocatePool(sizeof(EG_IMAGE
));
509 if (NewEntry
->Image
!= NULL
)
510 CopyMem(NewEntry
->Image
, Entry
->Image
, sizeof(EG_IMAGE
));
512 if (Entry
->SubScreen
!= NULL
) {
513 NewEntry
->SubScreen
= CopyMenuScreen(Entry
->SubScreen
);
517 } // REFIT_MENU_ENTRY* CopyMenuEntry()
519 // Creates a new LOADER_ENTRY data structure and populates it with
520 // default values from the specified Entry, or NULL values if Entry
521 // is unspecified (NULL).
522 // Returns a pointer to the new data structure, or NULL if it
523 // couldn't be allocated
524 LOADER_ENTRY
*InitializeLoaderEntry(IN LOADER_ENTRY
*Entry
) {
525 LOADER_ENTRY
*NewEntry
= NULL
;
527 NewEntry
= AllocateZeroPool(sizeof(LOADER_ENTRY
));
528 if (NewEntry
!= NULL
) {
529 NewEntry
->me
.Title
= NULL
;
530 NewEntry
->me
.Tag
= TAG_LOADER
;
531 NewEntry
->Enabled
= TRUE
;
532 NewEntry
->UseGraphicsMode
= FALSE
;
533 NewEntry
->OSType
= 0;
535 NewEntry
->LoaderPath
= (Entry
->LoaderPath
) ? StrDuplicate(Entry
->LoaderPath
) : NULL
;
536 NewEntry
->VolName
= (Entry
->VolName
) ? StrDuplicate(Entry
->VolName
) : NULL
;
537 NewEntry
->DevicePath
= Entry
->DevicePath
;
538 NewEntry
->UseGraphicsMode
= Entry
->UseGraphicsMode
;
539 NewEntry
->LoadOptions
= (Entry
->LoadOptions
) ? StrDuplicate(Entry
->LoadOptions
) : NULL
;
540 NewEntry
->InitrdPath
= (Entry
->InitrdPath
) ? StrDuplicate(Entry
->InitrdPath
) : NULL
;
544 } // LOADER_ENTRY *InitializeLoaderEntry()
546 // Adds InitrdPath to Options, but only if Options doesn't already include an
547 // initrd= line. Done to enable overriding the default initrd selection in a
548 // refind_linux.conf file's options list.
549 // Returns a pointer to a new string. The calling function is responsible for
550 // freeing its memory.
551 static CHAR16
*AddInitrdToOptions(CHAR16
*Options
, CHAR16
*InitrdPath
) {
552 CHAR16
*NewOptions
= NULL
;
555 NewOptions
= StrDuplicate(Options
);
556 if ((InitrdPath
!= NULL
) && !StriSubCmp(L
"initrd=", Options
)) {
557 MergeStrings(&NewOptions
, L
"initrd=", L
' ');
558 MergeStrings(&NewOptions
, InitrdPath
, 0);
561 } // CHAR16 *AddInitrdToOptions()
563 // Prepare a REFIT_MENU_SCREEN data structure for a subscreen entry. This sets up
564 // the default entry that launches the boot loader using the same options as the
565 // main Entry does. Subsequent options can be added by the calling function.
566 // If a subscreen already exists in the Entry that's passed to this function,
567 // it's left unchanged and a pointer to it is returned.
568 // Returns a pointer to the new subscreen data structure, or NULL if there
569 // were problems allocating memory.
570 REFIT_MENU_SCREEN
*InitializeSubScreen(IN LOADER_ENTRY
*Entry
) {
571 CHAR16
*FileName
, *MainOptions
= NULL
;
572 REFIT_MENU_SCREEN
*SubScreen
= NULL
;
573 LOADER_ENTRY
*SubEntry
;
575 FileName
= Basename(Entry
->LoaderPath
);
576 if (Entry
->me
.SubScreen
== NULL
) { // No subscreen yet; initialize default entry....
577 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
578 if (SubScreen
!= NULL
) {
579 SubScreen
->Title
= AllocateZeroPool(sizeof(CHAR16
) * 256);
580 SPrint(SubScreen
->Title
, 255, L
"Boot Options for %s on %s",
581 (Entry
->Title
!= NULL
) ? Entry
->Title
: FileName
, Entry
->VolName
);
582 SubScreen
->TitleImage
= Entry
->me
.Image
;
584 SubEntry
= InitializeLoaderEntry(Entry
);
585 if (SubEntry
!= NULL
) {
586 SubEntry
->me
.Title
= StrDuplicate(L
"Boot using default options");
587 MainOptions
= SubEntry
->LoadOptions
;
588 SubEntry
->LoadOptions
= AddInitrdToOptions(MainOptions
, SubEntry
->InitrdPath
);
589 MyFreePool(MainOptions
);
590 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
591 } // if (SubEntry != NULL)
592 SubScreen
->Hint1
= StrDuplicate(SUBSCREEN_HINT1
);
593 if (GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_EDITOR
) {
594 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2_NO_EDITOR
);
596 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2
);
598 } // if (SubScreen != NULL)
599 } else { // existing subscreen; less initialization, and just add new entry later....
600 SubScreen
= Entry
->me
.SubScreen
;
603 } // REFIT_MENU_SCREEN *InitializeSubScreen()
605 VOID
GenerateSubScreen(LOADER_ENTRY
*Entry
, IN REFIT_VOLUME
*Volume
) {
606 REFIT_MENU_SCREEN
*SubScreen
;
607 LOADER_ENTRY
*SubEntry
;
609 CHAR16 DiagsFileName
[256];
614 // create the submenu
615 if (StrLen(Entry
->Title
) == 0) {
616 MyFreePool(Entry
->Title
);
619 SubScreen
= InitializeSubScreen(Entry
);
621 // loader-specific submenu entries
622 if (Entry
->OSType
== 'M') { // entries for Mac OS X
624 SubEntry
= InitializeLoaderEntry(Entry
);
625 if (SubEntry
!= NULL
) {
626 SubEntry
->me
.Title
= L
"Boot Mac OS X with a 64-bit kernel";
627 SubEntry
->LoadOptions
= L
"arch=x86_64";
628 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
629 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
632 SubEntry
= InitializeLoaderEntry(Entry
);
633 if (SubEntry
!= NULL
) {
634 SubEntry
->me
.Title
= L
"Boot Mac OS X with a 32-bit kernel";
635 SubEntry
->LoadOptions
= L
"arch=i386";
636 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
637 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
641 if (!(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_SINGLEUSER
)) {
642 SubEntry
= InitializeLoaderEntry(Entry
);
643 if (SubEntry
!= NULL
) {
644 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode";
645 SubEntry
->UseGraphicsMode
= FALSE
;
646 SubEntry
->LoadOptions
= L
"-v";
647 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
651 SubEntry
= InitializeLoaderEntry(Entry
);
652 if (SubEntry
!= NULL
) {
653 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode (64-bit)";
654 SubEntry
->UseGraphicsMode
= FALSE
;
655 SubEntry
->LoadOptions
= L
"-v arch=x86_64";
656 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
659 SubEntry
= InitializeLoaderEntry(Entry
);
660 if (SubEntry
!= NULL
) {
661 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode (32-bit)";
662 SubEntry
->UseGraphicsMode
= FALSE
;
663 SubEntry
->LoadOptions
= L
"-v arch=i386";
664 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
668 SubEntry
= InitializeLoaderEntry(Entry
);
669 if (SubEntry
!= NULL
) {
670 SubEntry
->me
.Title
= L
"Boot Mac OS X in single user mode";
671 SubEntry
->UseGraphicsMode
= FALSE
;
672 SubEntry
->LoadOptions
= L
"-v -s";
673 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
675 } // single-user mode allowed
677 if (!(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_SAFEMODE
)) {
678 SubEntry
= InitializeLoaderEntry(Entry
);
679 if (SubEntry
!= NULL
) {
680 SubEntry
->me
.Title
= L
"Boot Mac OS X in safe mode";
681 SubEntry
->UseGraphicsMode
= FALSE
;
682 SubEntry
->LoadOptions
= L
"-v -x";
683 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
685 } // safe mode allowed
687 // check for Apple hardware diagnostics
688 StrCpy(DiagsFileName
, L
"System\\Library\\CoreServices\\.diagnostics\\diags.efi");
689 if (FileExists(Volume
->RootDir
, DiagsFileName
) && !(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_HWTEST
)) {
690 SubEntry
= InitializeLoaderEntry(Entry
);
691 if (SubEntry
!= NULL
) {
692 SubEntry
->me
.Title
= L
"Run Apple Hardware Test";
693 MyFreePool(SubEntry
->LoaderPath
);
694 SubEntry
->LoaderPath
= StrDuplicate(DiagsFileName
);
695 SubEntry
->DevicePath
= FileDevicePath(Volume
->DeviceHandle
, SubEntry
->LoaderPath
);
696 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
697 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
699 } // if diagnostics entry found
701 } else if (Entry
->OSType
== 'L') { // entries for Linux kernels with EFI stub loaders
702 File
= ReadLinuxOptionsFile(Entry
->LoaderPath
, Volume
);
704 InitrdName
= FindInitrd(Entry
->LoaderPath
, Volume
);
705 TokenCount
= ReadTokenLine(File
, &TokenList
);
706 // first entry requires special processing, since it was initially set
707 // up with a default title but correct options by InitializeSubScreen(),
709 if ((SubScreen
->Entries
!= NULL
) && (SubScreen
->Entries
[0] != NULL
)) {
710 MyFreePool(SubScreen
->Entries
[0]->Title
);
711 SubScreen
->Entries
[0]->Title
= TokenList
[0] ? StrDuplicate(TokenList
[0]) : StrDuplicate(L
"Boot Linux");
713 FreeTokenLine(&TokenList
, &TokenCount
);
714 while ((TokenCount
= ReadTokenLine(File
, &TokenList
)) > 1) {
715 SubEntry
= InitializeLoaderEntry(Entry
);
716 SubEntry
->me
.Title
= TokenList
[0] ? StrDuplicate(TokenList
[0]) : StrDuplicate(L
"Boot Linux");
717 MyFreePool(SubEntry
->LoadOptions
);
718 SubEntry
->LoadOptions
= AddInitrdToOptions(TokenList
[1], InitrdName
);
719 FreeTokenLine(&TokenList
, &TokenCount
);
720 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_LINUX
;
721 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
723 MyFreePool(InitrdName
);
725 } // if Linux options file exists
727 } else if (Entry
->OSType
== 'E') { // entries for ELILO
728 SubEntry
= InitializeLoaderEntry(Entry
);
729 if (SubEntry
!= NULL
) {
730 SubEntry
->me
.Title
= L
"Run ELILO in interactive mode";
731 SubEntry
->LoadOptions
= L
"-p";
732 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
733 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
736 SubEntry
= InitializeLoaderEntry(Entry
);
737 if (SubEntry
!= NULL
) {
738 SubEntry
->me
.Title
= L
"Boot Linux for a 17\" iMac or a 15\" MacBook Pro (*)";
739 SubEntry
->UseGraphicsMode
= TRUE
;
740 SubEntry
->LoadOptions
= L
"-d 0 i17";
741 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
742 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
745 SubEntry
= InitializeLoaderEntry(Entry
);
746 if (SubEntry
!= NULL
) {
747 SubEntry
->me
.Title
= L
"Boot Linux for a 20\" iMac (*)";
748 SubEntry
->UseGraphicsMode
= TRUE
;
749 SubEntry
->LoadOptions
= L
"-d 0 i20";
750 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
751 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
754 SubEntry
= InitializeLoaderEntry(Entry
);
755 if (SubEntry
!= NULL
) {
756 SubEntry
->me
.Title
= L
"Boot Linux for a Mac Mini (*)";
757 SubEntry
->UseGraphicsMode
= TRUE
;
758 SubEntry
->LoadOptions
= L
"-d 0 mini";
759 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
760 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
763 AddMenuInfoLine(SubScreen
, L
"NOTE: This is an example. Entries");
764 AddMenuInfoLine(SubScreen
, L
"marked with (*) may not work.");
766 } else if (Entry
->OSType
== 'X') { // entries for xom.efi
767 // by default, skip the built-in selection and boot from hard disk only
768 Entry
->LoadOptions
= L
"-s -h";
770 SubEntry
= InitializeLoaderEntry(Entry
);
771 if (SubEntry
!= NULL
) {
772 SubEntry
->me
.Title
= L
"Boot Windows from Hard Disk";
773 SubEntry
->LoadOptions
= L
"-s -h";
774 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
775 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
778 SubEntry
= InitializeLoaderEntry(Entry
);
779 if (SubEntry
!= NULL
) {
780 SubEntry
->me
.Title
= L
"Boot Windows from CD-ROM";
781 SubEntry
->LoadOptions
= L
"-s -c";
782 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
783 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
786 SubEntry
= InitializeLoaderEntry(Entry
);
787 if (SubEntry
!= NULL
) {
788 SubEntry
->me
.Title
= L
"Run XOM in text mode";
789 SubEntry
->UseGraphicsMode
= FALSE
;
790 SubEntry
->LoadOptions
= L
"-v";
791 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
792 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
794 } // entries for xom.efi
795 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
796 Entry
->me
.SubScreen
= SubScreen
;
797 } // VOID GenerateSubScreen()
799 // Returns options for a Linux kernel. Reads them from an options file in the
800 // kernel's directory; and if present, adds an initrd= option for an initial
801 // RAM disk file with the same version number as the kernel file.
802 static CHAR16
* GetMainLinuxOptions(IN CHAR16
* LoaderPath
, IN REFIT_VOLUME
*Volume
) {
803 CHAR16
*Options
= NULL
, *InitrdName
, *FullOptions
= NULL
;
805 Options
= GetFirstOptionsFromFile(LoaderPath
, Volume
);
806 InitrdName
= FindInitrd(LoaderPath
, Volume
);
807 FullOptions
= AddInitrdToOptions(Options
, InitrdName
);
810 MyFreePool(InitrdName
);
811 return (FullOptions
);
812 } // static CHAR16 * GetMainLinuxOptions()
814 // Sets a few defaults for a loader entry -- mainly the icon, but also the OS type
815 // code and shortcut letter. For Linux EFI stub loaders, also sets kernel options
816 // that will (with luck) work fairly automatically.
817 VOID
SetLoaderDefaults(LOADER_ENTRY
*Entry
, CHAR16
*LoaderPath
, REFIT_VOLUME
*Volume
) {
818 CHAR16
*FileName
, *PathOnly
, *NoExtension
, *OSIconName
= NULL
, *Temp
, *SubString
;
819 CHAR16 ShortcutLetter
= 0;
822 FileName
= Basename(LoaderPath
);
823 PathOnly
= FindPath(LoaderPath
);
824 NoExtension
= StripEfiExtension(FileName
);
826 // locate a custom icon for the loader
827 // Anything found here takes precedence over the "hints" in the OSIconName variable
828 if (!Entry
->me
.Image
)
829 Entry
->me
.Image
= egLoadIconAnyType(Volume
->RootDir
, PathOnly
, NoExtension
, 128);
830 if (!Entry
->me
.Image
)
831 Entry
->me
.Image
= egCopyImage(Volume
->VolIconImage
);
833 // Begin creating icon "hints" by using last part of directory path leading
835 Temp
= FindLastDirName(LoaderPath
);
836 MergeStrings(&OSIconName
, Temp
, L
',');
839 if (OSIconName
!= NULL
) {
840 ShortcutLetter
= OSIconName
[0];
843 // Add every "word" in the volume label, delimited by spaces, dashes (-), or
844 // underscores (_), to the list of hints to be used in searching for OS
846 if ((Volume
->VolName
) && (StrLen(Volume
->VolName
) > 0)) {
847 Temp
= SubString
= StrDuplicate(Volume
->VolName
);
849 Length
= StrLen(Temp
);
850 for (i
= 0; i
< Length
; i
++) {
851 if ((Temp
[i
] == L
' ') || (Temp
[i
] == L
'_') || (Temp
[i
] == L
'-')) {
853 if (StrLen(SubString
) > 0)
854 MergeStrings(&OSIconName
, SubString
, L
',');
855 SubString
= Temp
+ i
+ 1;
858 MergeStrings(&OSIconName
, SubString
, L
',');
863 // detect specific loaders
864 if (StriSubCmp(L
"bzImage", LoaderPath
) || StriSubCmp(L
"vmlinuz", LoaderPath
)) {
865 MergeStrings(&OSIconName
, L
"linux", L
',');
867 if (ShortcutLetter
== 0)
868 ShortcutLetter
= 'L';
869 Entry
->LoadOptions
= GetMainLinuxOptions(LoaderPath
, Volume
);
870 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_LINUX
;
871 } else if (StriSubCmp(L
"refit", LoaderPath
)) {
872 MergeStrings(&OSIconName
, L
"refit", L
',');
874 ShortcutLetter
= 'R';
875 } else if (StriCmp(LoaderPath
, MACOSX_LOADER_PATH
) == 0) {
876 if (Volume
->VolIconImage
!= NULL
) { // custom icon file found
877 Entry
->me
.Image
= Volume
->VolIconImage
;
879 MergeStrings(&OSIconName
, L
"mac", L
',');
881 ShortcutLetter
= 'M';
882 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
883 } else if (StriCmp(FileName
, L
"diags.efi") == 0) {
884 MergeStrings(&OSIconName
, L
"hwtest", L
',');
885 } else if (StriCmp(FileName
, L
"e.efi") == 0 || StriCmp(FileName
, L
"elilo.efi") == 0 || StriSubCmp(L
"elilo", FileName
)) {
886 MergeStrings(&OSIconName
, L
"elilo,linux", L
',');
888 if (ShortcutLetter
== 0)
889 ShortcutLetter
= 'L';
890 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
891 } else if (StriSubCmp(L
"grub", FileName
)) {
893 ShortcutLetter
= 'G';
894 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_GRUB
;
895 } else if (StriCmp(FileName
, L
"cdboot.efi") == 0 ||
896 StriCmp(FileName
, L
"bootmgr.efi") == 0 ||
897 StriCmp(FileName
, L
"bootmgfw.efi") == 0) {
898 MergeStrings(&OSIconName
, L
"win", L
',');
900 ShortcutLetter
= 'W';
901 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
902 } else if (StriCmp(FileName
, L
"xom.efi") == 0) {
903 MergeStrings(&OSIconName
, L
"xom,win", L
',');
904 Entry
->UseGraphicsMode
= TRUE
;
906 ShortcutLetter
= 'W';
907 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
910 if ((ShortcutLetter
>= 'a') && (ShortcutLetter
<= 'z'))
911 ShortcutLetter
= ShortcutLetter
- 'a' + 'A'; // convert lowercase to uppercase
912 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
913 if (Entry
->me
.Image
== NULL
)
914 Entry
->me
.Image
= LoadOSIcon(OSIconName
, L
"unknown", FALSE
);
915 MyFreePool(PathOnly
);
916 } // VOID SetLoaderDefaults()
918 // Add a specified EFI boot loader to the list, using automatic settings
919 // for icons, options, etc.
920 LOADER_ENTRY
* AddLoaderEntry(IN CHAR16
*LoaderPath
, IN CHAR16
*LoaderTitle
, IN REFIT_VOLUME
*Volume
) {
923 CleanUpPathNameSlashes(LoaderPath
);
924 Entry
= InitializeLoaderEntry(NULL
);
926 Entry
->Title
= StrDuplicate((LoaderTitle
!= NULL
) ? LoaderTitle
: LoaderPath
);
927 Entry
->me
.Title
= AllocateZeroPool(sizeof(CHAR16
) * 256);
928 SPrint(Entry
->me
.Title
, 255, L
"Boot %s from %s", (LoaderTitle
!= NULL
) ? LoaderTitle
: LoaderPath
, Volume
->VolName
);
930 Entry
->me
.BadgeImage
= Volume
->VolBadgeImage
;
931 if ((LoaderPath
!= NULL
) && (LoaderPath
[0] != L
'\\')) {
932 Entry
->LoaderPath
= StrDuplicate(L
"\\");
934 Entry
->LoaderPath
= NULL
;
936 MergeStrings(&(Entry
->LoaderPath
), LoaderPath
, 0);
937 Entry
->VolName
= Volume
->VolName
;
938 Entry
->DevicePath
= FileDevicePath(Volume
->DeviceHandle
, Entry
->LoaderPath
);
939 SetLoaderDefaults(Entry
, LoaderPath
, Volume
);
940 GenerateSubScreen(Entry
, Volume
);
941 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
945 } // LOADER_ENTRY * AddLoaderEntry()
947 // Returns -1 if (Time1 < Time2), +1 if (Time1 > Time2), or 0 if
948 // (Time1 == Time2). Precision is only to the nearest second; since
949 // this is used for sorting boot loader entries, differences smaller
950 // than this are likely to be meaningless (and unlikely!).
951 INTN
TimeComp(IN EFI_TIME
*Time1
, IN EFI_TIME
*Time2
) {
952 INT64 Time1InSeconds
, Time2InSeconds
;
954 // Following values are overestimates; I'm assuming 31 days in every month.
955 // This is fine for the purpose of this function, which is limited
956 Time1InSeconds
= Time1
->Second
+ (Time1
->Minute
* 60) + (Time1
->Hour
* 3600) + (Time1
->Day
* 86400) +
957 (Time1
->Month
* 2678400) + ((Time1
->Year
- 1998) * 32140800);
958 Time2InSeconds
= Time2
->Second
+ (Time2
->Minute
* 60) + (Time2
->Hour
* 3600) + (Time2
->Day
* 86400) +
959 (Time2
->Month
* 2678400) + ((Time2
->Year
- 1998) * 32140800);
960 if (Time1InSeconds
< Time2InSeconds
)
962 else if (Time1InSeconds
> Time2InSeconds
)
968 // Adds a loader list element, keeping it sorted by date. Returns the new
969 // first element (the one with the most recent date).
970 static struct LOADER_LIST
* AddLoaderListEntry(struct LOADER_LIST
*LoaderList
, struct LOADER_LIST
*NewEntry
) {
971 struct LOADER_LIST
*LatestEntry
, *CurrentEntry
, *PrevEntry
= NULL
;
973 LatestEntry
= CurrentEntry
= LoaderList
;
974 if (LoaderList
== NULL
) {
975 LatestEntry
= NewEntry
;
977 while ((CurrentEntry
!= NULL
) && (TimeComp(&(NewEntry
->TimeStamp
), &(CurrentEntry
->TimeStamp
)) < 0)) {
978 PrevEntry
= CurrentEntry
;
979 CurrentEntry
= CurrentEntry
->NextEntry
;
981 NewEntry
->NextEntry
= CurrentEntry
;
982 if (PrevEntry
== NULL
) {
983 LatestEntry
= NewEntry
;
985 PrevEntry
->NextEntry
= NewEntry
;
988 return (LatestEntry
);
989 } // static VOID AddLoaderListEntry()
991 // Delete the LOADER_LIST linked list
992 static VOID
CleanUpLoaderList(struct LOADER_LIST
*LoaderList
) {
993 struct LOADER_LIST
*Temp
;
995 while (LoaderList
!= NULL
) {
997 LoaderList
= LoaderList
->NextEntry
;
998 MyFreePool(Temp
->FileName
);
1001 } // static VOID CleanUpLoaderList()
1003 // Returns FALSE if the specified file/volume matches the GlobalConfig.DontScanDirs
1004 // or GlobalConfig.DontScanVolumes specification, or if Path points to a volume
1005 // other than the one specified by Volume, or if the specified path is SelfDir.
1006 // Returns TRUE if none of these conditions is met -- that is, if the path is
1007 // eligible for scanning.
1008 static BOOLEAN
ShouldScan(REFIT_VOLUME
*Volume
, CHAR16
*Path
) {
1009 CHAR16
*VolName
= NULL
, *DontScanDir
;
1010 UINTN i
= 0, VolNum
;
1011 BOOLEAN ScanIt
= TRUE
;
1013 if (IsIn(Volume
->VolName
, GlobalConfig
.DontScanVolumes
))
1016 if ((StriCmp(Path
, SelfDirPath
) == 0) && (Volume
->DeviceHandle
== SelfVolume
->DeviceHandle
))
1019 while ((DontScanDir
= FindCommaDelimited(GlobalConfig
.DontScanDirs
, i
++)) && ScanIt
) {
1020 SplitVolumeAndFilename(&DontScanDir
, &VolName
);
1021 CleanUpPathNameSlashes(DontScanDir
);
1022 if (VolName
!= NULL
) {
1023 if ((StriCmp(VolName
, Volume
->VolName
) == 0) && (StriCmp(DontScanDir
, Path
) == 0))
1025 if ((StrLen(VolName
) > 2) && (VolName
[0] == L
'f') && (VolName
[1] == L
's') && (VolName
[2] >= L
'0') && (VolName
[2] <= L
'9')) {
1026 VolNum
= Atoi(VolName
+ 2);
1027 if ((VolNum
== Volume
->VolNumber
) && (StriCmp(DontScanDir
, Path
) == 0))
1031 if (StriCmp(DontScanDir
, Path
) == 0)
1034 MyFreePool(DontScanDir
);
1038 } // BOOLEAN ShouldScan()
1040 // Returns TRUE if the file is byte-for-byte identical with the fallback file
1041 // on the volume AND if the file is not itself the fallback file; returns
1042 // FALSE if the file is not identical to the fallback file OR if the file
1043 // IS the fallback file. Intended for use in excluding the fallback boot
1044 // loader when it's a duplicate of another boot loader.
1045 static BOOLEAN
DuplicatesFallback(IN REFIT_VOLUME
*Volume
, IN CHAR16
*FileName
) {
1046 CHAR8
*FileContents
, *FallbackContents
;
1047 EFI_FILE_HANDLE FileHandle
, FallbackHandle
;
1048 EFI_FILE_INFO
*FileInfo
, *FallbackInfo
;
1049 UINTN FileSize
= 0, FallbackSize
= 0;
1051 BOOLEAN AreIdentical
= FALSE
;
1053 if (!FileExists(Volume
->RootDir
, FileName
) || !FileExists(Volume
->RootDir
, FALLBACK_FULLNAME
))
1056 CleanUpPathNameSlashes(FileName
);
1058 if (StriCmp(FileName
, FALLBACK_FULLNAME
) == 0)
1059 return FALSE
; // identical filenames, so not a duplicate....
1061 Status
= refit_call5_wrapper(Volume
->RootDir
->Open
, Volume
->RootDir
, &FileHandle
, FileName
, EFI_FILE_MODE_READ
, 0);
1062 if (Status
== EFI_SUCCESS
) {
1063 FileInfo
= LibFileInfo(FileHandle
);
1064 FileSize
= FileInfo
->FileSize
;
1069 Status
= refit_call5_wrapper(Volume
->RootDir
->Open
, Volume
->RootDir
, &FallbackHandle
, FALLBACK_FULLNAME
, EFI_FILE_MODE_READ
, 0);
1070 if (Status
== EFI_SUCCESS
) {
1071 FallbackInfo
= LibFileInfo(FallbackHandle
);
1072 FallbackSize
= FallbackInfo
->FileSize
;
1074 refit_call1_wrapper(FileHandle
->Close
, FileHandle
);
1078 if (FallbackSize
!= FileSize
) { // not same size, so can't be identical
1079 AreIdentical
= FALSE
;
1080 } else { // could be identical; do full check....
1081 FileContents
= AllocatePool(FileSize
);
1082 FallbackContents
= AllocatePool(FallbackSize
);
1083 if (FileContents
&& FallbackContents
) {
1084 Status
= refit_call3_wrapper(FileHandle
->Read
, FileHandle
, &FileSize
, FileContents
);
1085 if (Status
== EFI_SUCCESS
) {
1086 Status
= refit_call3_wrapper(FallbackHandle
->Read
, FallbackHandle
, &FallbackSize
, FallbackContents
);
1088 if (Status
== EFI_SUCCESS
) {
1089 AreIdentical
= (CompareMem(FileContents
, FallbackContents
, FileSize
) == 0);
1092 MyFreePool(FileContents
);
1093 MyFreePool(FallbackContents
);
1096 // BUG ALERT: Some systems (e.g., DUET, some Macs with large displays) crash if the
1097 // following two calls are reversed. Go figure....
1098 refit_call1_wrapper(FileHandle
->Close
, FallbackHandle
);
1099 refit_call1_wrapper(FileHandle
->Close
, FileHandle
);
1100 return AreIdentical
;
1101 } // BOOLEAN DuplicatesFallback()
1103 // Scan an individual directory for EFI boot loader files and, if found,
1104 // add them to the list. Exception: Ignores FALLBACK_FULLNAME, which is picked
1105 // up in ScanEfiFiles(). Sorts the entries within the loader directory so that
1106 // the most recent one appears first in the list.
1107 // Returns TRUE if a duplicate for FALLBACK_FILENAME was found, FALSE if not.
1108 static BOOLEAN
ScanLoaderDir(IN REFIT_VOLUME
*Volume
, IN CHAR16
*Path
, IN CHAR16
*Pattern
)
1111 REFIT_DIR_ITER DirIter
;
1112 EFI_FILE_INFO
*DirEntry
;
1113 CHAR16 FileName
[256], *Extension
;
1114 struct LOADER_LIST
*LoaderList
= NULL
, *NewLoader
;
1115 BOOLEAN FoundFallbackDuplicate
= FALSE
;
1117 if ((!SelfDirPath
|| !Path
|| ((StriCmp(Path
, SelfDirPath
) == 0) && (Volume
->DeviceHandle
!= SelfVolume
->DeviceHandle
)) ||
1118 (StriCmp(Path
, SelfDirPath
) != 0)) &&
1119 (ShouldScan(Volume
, Path
))) {
1120 // look through contents of the directory
1121 DirIterOpen(Volume
->RootDir
, Path
, &DirIter
);
1122 while (DirIterNext(&DirIter
, 2, Pattern
, &DirEntry
)) {
1123 Extension
= FindExtension(DirEntry
->FileName
);
1124 if (DirEntry
->FileName
[0] == '.' ||
1125 StriCmp(Extension
, L
".icns") == 0 ||
1126 StriCmp(Extension
, L
".png") == 0 ||
1127 (StriCmp(DirEntry
->FileName
, FALLBACK_BASENAME
) == 0 && (StriCmp(Path
, L
"EFI\\BOOT") == 0)) ||
1128 StriSubCmp(L
"shell", DirEntry
->FileName
) ||
1129 IsIn(DirEntry
->FileName
, GlobalConfig
.DontScanFiles
))
1130 continue; // skip this
1133 SPrint(FileName
, 255, L
"\\%s\\%s", Path
, DirEntry
->FileName
);
1135 SPrint(FileName
, 255, L
"\\%s", DirEntry
->FileName
);
1136 CleanUpPathNameSlashes(FileName
);
1137 NewLoader
= AllocateZeroPool(sizeof(struct LOADER_LIST
));
1138 if (NewLoader
!= NULL
) {
1139 NewLoader
->FileName
= StrDuplicate(FileName
);
1140 NewLoader
->TimeStamp
= DirEntry
->ModificationTime
;
1141 LoaderList
= AddLoaderListEntry(LoaderList
, NewLoader
);
1142 if (DuplicatesFallback(Volume
, FileName
))
1143 FoundFallbackDuplicate
= TRUE
;
1145 MyFreePool(Extension
);
1148 NewLoader
= LoaderList
;
1149 while (NewLoader
!= NULL
) {
1150 AddLoaderEntry(NewLoader
->FileName
, NULL
, Volume
);
1151 NewLoader
= NewLoader
->NextEntry
;
1154 CleanUpLoaderList(LoaderList
);
1155 Status
= DirIterClose(&DirIter
);
1156 // NOTE: EFI_INVALID_PARAMETER really is an error that should be reported;
1157 // but I've gotten reports from users who are getting this error occasionally
1158 // and I can't find anything wrong or reproduce the problem, so I'm putting
1159 // it down to buggy EFI implementations and ignoring that particular error....
1160 if ((Status
!= EFI_NOT_FOUND
) && (Status
!= EFI_INVALID_PARAMETER
)) {
1162 SPrint(FileName
, 255, L
"while scanning the %s directory", Path
);
1164 StrCpy(FileName
, L
"while scanning the root directory");
1165 CheckError(Status
, FileName
);
1166 } // if (Status != EFI_NOT_FOUND)
1167 } // if not scanning a blacklisted directory
1169 return FoundFallbackDuplicate
;
1170 } /* static VOID ScanLoaderDir() */
1172 static VOID
ScanEfiFiles(REFIT_VOLUME
*Volume
) {
1174 REFIT_DIR_ITER EfiDirIter
;
1175 EFI_FILE_INFO
*EfiDirEntry
;
1176 CHAR16 FileName
[256], *Directory
, *MatchPatterns
, *VolName
= NULL
, *SelfPath
;
1178 BOOLEAN ScanFallbackLoader
= TRUE
;
1180 MatchPatterns
= StrDuplicate(LOADER_MATCH_PATTERNS
);
1181 if (GlobalConfig
.ScanAllLinux
)
1182 MergeStrings(&MatchPatterns
, LINUX_MATCH_PATTERNS
, L
',');
1184 if ((Volume
->RootDir
!= NULL
) && (Volume
->VolName
!= NULL
)) {
1185 // check for Mac OS X boot loader
1186 if (ShouldScan(Volume
, L
"System\\Library\\CoreServices")) {
1187 StrCpy(FileName
, MACOSX_LOADER_PATH
);
1188 if (FileExists(Volume
->RootDir
, FileName
) && !IsIn(L
"boot.efi", GlobalConfig
.DontScanFiles
)) {
1189 AddLoaderEntry(FileName
, L
"Mac OS X", Volume
);
1190 if (DuplicatesFallback(Volume
, FileName
))
1191 ScanFallbackLoader
= FALSE
;
1195 StrCpy(FileName
, L
"System\\Library\\CoreServices\\xom.efi");
1196 if (FileExists(Volume
->RootDir
, FileName
) && !IsIn(L
"boot.efi", GlobalConfig
.DontScanFiles
)) {
1197 AddLoaderEntry(FileName
, L
"Windows XP (XoM)", Volume
);
1198 if (DuplicatesFallback(Volume
, FileName
))
1199 ScanFallbackLoader
= FALSE
;
1201 } // if should scan Mac directory
1203 // check for Microsoft boot loader/menu
1204 StrCpy(FileName
, L
"EFI\\Microsoft\\Boot\\bootmgfw.efi");
1205 if (FileExists(Volume
->RootDir
, FileName
) && ShouldScan(Volume
, L
"EFI\\Microsoft\\Boot") &&
1206 !IsIn(L
"bootmgfw.efi", GlobalConfig
.DontScanFiles
)) {
1207 AddLoaderEntry(FileName
, L
"Microsoft EFI boot", Volume
);
1208 if (DuplicatesFallback(Volume
, FileName
))
1209 ScanFallbackLoader
= FALSE
;
1212 // scan the root directory for EFI executables
1213 if (ScanLoaderDir(Volume
, L
"\\", MatchPatterns
))
1214 ScanFallbackLoader
= FALSE
;
1216 // scan subdirectories of the EFI directory (as per the standard)
1217 DirIterOpen(Volume
->RootDir
, L
"EFI", &EfiDirIter
);
1218 while (DirIterNext(&EfiDirIter
, 1, NULL
, &EfiDirEntry
)) {
1219 if (StriCmp(EfiDirEntry
->FileName
, L
"tools") == 0 || EfiDirEntry
->FileName
[0] == '.')
1220 continue; // skip this, doesn't contain boot loaders or is scanned later
1221 SPrint(FileName
, 255, L
"EFI\\%s", EfiDirEntry
->FileName
);
1222 if (ScanLoaderDir(Volume
, FileName
, MatchPatterns
))
1223 ScanFallbackLoader
= FALSE
;
1225 Status
= DirIterClose(&EfiDirIter
);
1226 if (Status
!= EFI_NOT_FOUND
)
1227 CheckError(Status
, L
"while scanning the EFI directory");
1229 // Scan user-specified (or additional default) directories....
1231 while ((Directory
= FindCommaDelimited(GlobalConfig
.AlsoScan
, i
++)) != NULL
) {
1232 SplitVolumeAndFilename(&Directory
, &VolName
);
1233 CleanUpPathNameSlashes(Directory
);
1234 Length
= StrLen(Directory
);
1235 if ((Length
> 0) && ScanLoaderDir(Volume
, Directory
, MatchPatterns
))
1236 ScanFallbackLoader
= FALSE
;
1237 MyFreePool(Directory
);
1238 MyFreePool(VolName
);
1241 // Don't scan the fallback loader if it's on the same volume and a duplicate of rEFInd itself....
1242 SelfPath
= DevicePathToStr(SelfLoadedImage
->FilePath
);
1243 CleanUpPathNameSlashes(SelfPath
);
1244 if ((Volume
->DeviceHandle
== SelfLoadedImage
->DeviceHandle
) && DuplicatesFallback(Volume
, SelfPath
))
1245 ScanFallbackLoader
= FALSE
;
1247 // If not a duplicate & if it exists & if it's not us, create an entry
1248 // for the fallback boot loader
1249 if (ScanFallbackLoader
&& FileExists(Volume
->RootDir
, FALLBACK_FULLNAME
) && ShouldScan(Volume
, L
"EFI\\BOOT"))
1250 AddLoaderEntry(FALLBACK_FULLNAME
, L
"Fallback boot loader", Volume
);
1252 } // static VOID ScanEfiFiles()
1254 // Scan internal disks for valid EFI boot loaders....
1255 static VOID
ScanInternal(VOID
) {
1258 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1259 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_INTERNAL
) {
1260 ScanEfiFiles(Volumes
[VolumeIndex
]);
1263 } // static VOID ScanInternal()
1265 // Scan external disks for valid EFI boot loaders....
1266 static VOID
ScanExternal(VOID
) {
1269 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1270 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_EXTERNAL
) {
1271 ScanEfiFiles(Volumes
[VolumeIndex
]);
1274 } // static VOID ScanExternal()
1276 // Scan internal disks for valid EFI boot loaders....
1277 static VOID
ScanOptical(VOID
) {
1280 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1281 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_OPTICAL
) {
1282 ScanEfiFiles(Volumes
[VolumeIndex
]);
1285 } // static VOID ScanOptical()
1288 // legacy boot functions
1291 static EFI_STATUS
ActivateMbrPartition(IN EFI_BLOCK_IO
*BlockIO
, IN UINTN PartitionIndex
)
1294 UINT8 SectorBuffer
[512];
1295 MBR_PARTITION_INFO
*MbrTable
, *EMbrTable
;
1296 UINT32 ExtBase
, ExtCurrent
, NextExtCurrent
;
1297 UINTN LogicalPartitionIndex
= 4;
1299 BOOLEAN HaveBootCode
;
1302 Status
= refit_call5_wrapper(BlockIO
->ReadBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, 0, 512, SectorBuffer
);
1303 if (EFI_ERROR(Status
))
1305 if (*((UINT16
*)(SectorBuffer
+ 510)) != 0xaa55)
1306 return EFI_NOT_FOUND
; // safety measure #1
1308 // add boot code if necessary
1309 HaveBootCode
= FALSE
;
1310 for (i
= 0; i
< MBR_BOOTCODE_SIZE
; i
++) {
1311 if (SectorBuffer
[i
] != 0) {
1312 HaveBootCode
= TRUE
;
1316 if (!HaveBootCode
) {
1317 // no boot code found in the MBR, add the syslinux MBR code
1318 SetMem(SectorBuffer
, MBR_BOOTCODE_SIZE
, 0);
1319 CopyMem(SectorBuffer
, syslinux_mbr
, SYSLINUX_MBR_SIZE
);
1322 // set the partition active
1323 MbrTable
= (MBR_PARTITION_INFO
*)(SectorBuffer
+ 446);
1325 for (i
= 0; i
< 4; i
++) {
1326 if (MbrTable
[i
].Flags
!= 0x00 && MbrTable
[i
].Flags
!= 0x80)
1327 return EFI_NOT_FOUND
; // safety measure #2
1328 if (i
== PartitionIndex
)
1329 MbrTable
[i
].Flags
= 0x80;
1330 else if (PartitionIndex
>= 4 && IS_EXTENDED_PART_TYPE(MbrTable
[i
].Type
)) {
1331 MbrTable
[i
].Flags
= 0x80;
1332 ExtBase
= MbrTable
[i
].StartLBA
;
1334 MbrTable
[i
].Flags
= 0x00;
1338 Status
= refit_call5_wrapper(BlockIO
->WriteBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, 0, 512, SectorBuffer
);
1339 if (EFI_ERROR(Status
))
1342 if (PartitionIndex
>= 4) {
1343 // we have to activate a logical partition, so walk the EMBR chain
1345 // NOTE: ExtBase was set above while looking at the MBR table
1346 for (ExtCurrent
= ExtBase
; ExtCurrent
; ExtCurrent
= NextExtCurrent
) {
1347 // read current EMBR
1348 Status
= refit_call5_wrapper(BlockIO
->ReadBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, ExtCurrent
, 512, SectorBuffer
);
1349 if (EFI_ERROR(Status
))
1351 if (*((UINT16
*)(SectorBuffer
+ 510)) != 0xaa55)
1352 return EFI_NOT_FOUND
; // safety measure #3
1354 // scan EMBR, set appropriate partition active
1355 EMbrTable
= (MBR_PARTITION_INFO
*)(SectorBuffer
+ 446);
1357 for (i
= 0; i
< 4; i
++) {
1358 if (EMbrTable
[i
].Flags
!= 0x00 && EMbrTable
[i
].Flags
!= 0x80)
1359 return EFI_NOT_FOUND
; // safety measure #4
1360 if (EMbrTable
[i
].StartLBA
== 0 || EMbrTable
[i
].Size
== 0)
1362 if (IS_EXTENDED_PART_TYPE(EMbrTable
[i
].Type
)) {
1363 // link to next EMBR
1364 NextExtCurrent
= ExtBase
+ EMbrTable
[i
].StartLBA
;
1365 EMbrTable
[i
].Flags
= (PartitionIndex
>= LogicalPartitionIndex
) ? 0x80 : 0x00;
1368 // logical partition
1369 EMbrTable
[i
].Flags
= (PartitionIndex
== LogicalPartitionIndex
) ? 0x80 : 0x00;
1370 LogicalPartitionIndex
++;
1374 // write current EMBR
1375 Status
= refit_call5_wrapper(BlockIO
->WriteBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, ExtCurrent
, 512, SectorBuffer
);
1376 if (EFI_ERROR(Status
))
1379 if (PartitionIndex
< LogicalPartitionIndex
)
1380 break; // stop the loop, no need to touch further EMBRs
1386 } /* static EFI_STATUS ActivateMbrPartition() */
1388 // early 2006 Core Duo / Core Solo models
1389 static UINT8 LegacyLoaderDevicePath1Data
[] = {
1390 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1391 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1392 0xFF, 0xFF, 0xF9, 0xFF, 0x00, 0x00, 0x00, 0x00,
1393 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1394 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1395 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1397 // mid-2006 Mac Pro (and probably other Core 2 models)
1398 static UINT8 LegacyLoaderDevicePath2Data
[] = {
1399 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1400 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1401 0xFF, 0xFF, 0xF7, 0xFF, 0x00, 0x00, 0x00, 0x00,
1402 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1403 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1404 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1406 // mid-2007 MBP ("Santa Rosa" based models)
1407 static UINT8 LegacyLoaderDevicePath3Data
[] = {
1408 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1409 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1410 0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
1411 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1412 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1413 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1416 static UINT8 LegacyLoaderDevicePath4Data
[] = {
1417 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1418 0x00, 0x00, 0xC0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1419 0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
1420 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1421 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1422 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1424 // late-2008 MB/MBP (NVidia chipset)
1425 static UINT8 LegacyLoaderDevicePath5Data
[] = {
1426 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1427 0x00, 0x40, 0xCB, 0xFF, 0x00, 0x00, 0x00, 0x00,
1428 0xFF, 0xBF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
1429 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1430 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1431 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1434 static EFI_DEVICE_PATH
*LegacyLoaderList
[] = {
1435 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath1Data
,
1436 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath2Data
,
1437 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath3Data
,
1438 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath4Data
,
1439 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath5Data
,
1443 #define MAX_DISCOVERED_PATHS (16)
1445 static VOID
StartLegacy(IN LEGACY_ENTRY
*Entry
)
1448 EG_IMAGE
*BootLogoImage
;
1449 UINTN ErrorInStep
= 0;
1450 EFI_DEVICE_PATH
*DiscoveredPathList
[MAX_DISCOVERED_PATHS
];
1452 BeginExternalScreen(TRUE
, L
"Booting Legacy OS (Mac mode)");
1454 BootLogoImage
= LoadOSIcon(Entry
->Volume
->OSIconName
, L
"legacy", TRUE
);
1455 if (BootLogoImage
!= NULL
)
1456 BltImageAlpha(BootLogoImage
,
1457 (UGAWidth
- BootLogoImage
->Width
) >> 1,
1458 (UGAHeight
- BootLogoImage
->Height
) >> 1,
1459 &StdBackgroundPixel
);
1461 if (Entry
->Volume
->IsMbrPartition
) {
1462 ActivateMbrPartition(Entry
->Volume
->WholeDiskBlockIO
, Entry
->Volume
->MbrPartitionIndex
);
1465 ExtractLegacyLoaderPaths(DiscoveredPathList
, MAX_DISCOVERED_PATHS
, LegacyLoaderList
);
1467 Status
= StartEFIImageList(DiscoveredPathList
, Entry
->LoadOptions
, NULL
, L
"legacy loader", 0, &ErrorInStep
, TRUE
);
1468 if (Status
== EFI_NOT_FOUND
) {
1469 if (ErrorInStep
== 1) {
1470 Print(L
"\nPlease make sure that you have the latest firmware update installed.\n");
1471 } else if (ErrorInStep
== 3) {
1472 Print(L
"\nThe firmware refused to boot from the selected volume. Note that external\n"
1473 L
"hard drives are not well-supported by Apple's firmware for legacy OS booting.\n");
1476 FinishExternalScreen();
1477 } /* static VOID StartLegacy() */
1479 // Start a device on a non-Mac using the EFI_LEGACY_BIOS_PROTOCOL
1480 #ifdef __MAKEWITH_TIANO
1481 static VOID
StartLegacyUEFI(IN LEGACY_ENTRY
*Entry
)
1483 BeginExternalScreen(TRUE
, L
"Booting Legacy OS (UEFI mode)");
1485 BdsLibConnectDevicePath (Entry
->BdsOption
->DevicePath
);
1486 BdsLibDoLegacyBoot(Entry
->BdsOption
);
1488 // If we get here, it means that there was a failure....
1489 Print(L
"Failure booting legacy (BIOS) OS.");
1491 FinishExternalScreen();
1492 } // static VOID StartLegacyUEFI()
1493 #endif // __MAKEWITH_TIANO
1495 static LEGACY_ENTRY
* AddLegacyEntry(IN CHAR16
*LoaderTitle
, IN REFIT_VOLUME
*Volume
)
1497 LEGACY_ENTRY
*Entry
, *SubEntry
;
1498 REFIT_MENU_SCREEN
*SubScreen
;
1500 CHAR16 ShortcutLetter
= 0;
1502 if (LoaderTitle
== NULL
) {
1503 if (Volume
->OSName
!= NULL
) {
1504 LoaderTitle
= Volume
->OSName
;
1505 if (LoaderTitle
[0] == 'W' || LoaderTitle
[0] == 'L')
1506 ShortcutLetter
= LoaderTitle
[0];
1508 LoaderTitle
= L
"Legacy OS";
1510 if (Volume
->VolName
!= NULL
)
1511 VolDesc
= Volume
->VolName
;
1513 VolDesc
= (Volume
->DiskKind
== DISK_KIND_OPTICAL
) ? L
"CD" : L
"HD";
1515 // prepare the menu entry
1516 Entry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1517 Entry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1518 SPrint(Entry
->me
.Title
, 255, L
"Boot %s from %s", LoaderTitle
, VolDesc
);
1519 Entry
->me
.Tag
= TAG_LEGACY
;
1521 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1522 Entry
->me
.Image
= LoadOSIcon(Volume
->OSIconName
, L
"legacy", FALSE
);
1523 Entry
->me
.BadgeImage
= Volume
->VolBadgeImage
;
1524 Entry
->Volume
= Volume
;
1525 Entry
->LoadOptions
= (Volume
->DiskKind
== DISK_KIND_OPTICAL
) ? L
"CD" :
1526 ((Volume
->DiskKind
== DISK_KIND_EXTERNAL
) ? L
"USB" : L
"HD");
1527 Entry
->Enabled
= TRUE
;
1529 // create the submenu
1530 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
1531 SubScreen
->Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1532 SPrint(SubScreen
->Title
, 255, L
"Boot Options for %s on %s", LoaderTitle
, VolDesc
);
1533 SubScreen
->TitleImage
= Entry
->me
.Image
;
1534 SubScreen
->Hint1
= StrDuplicate(SUBSCREEN_HINT1
);
1535 if (GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_EDITOR
) {
1536 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2_NO_EDITOR
);
1538 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2
);
1542 SubEntry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1543 SubEntry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1544 SPrint(SubEntry
->me
.Title
, 255, L
"Boot %s", LoaderTitle
);
1545 SubEntry
->me
.Tag
= TAG_LEGACY
;
1546 SubEntry
->Volume
= Entry
->Volume
;
1547 SubEntry
->LoadOptions
= Entry
->LoadOptions
;
1548 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
1550 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
1551 Entry
->me
.SubScreen
= SubScreen
;
1552 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1554 } /* static LEGACY_ENTRY * AddLegacyEntry() */
1557 #ifdef __MAKEWITH_GNUEFI
1558 static VOID
ScanLegacyUEFI(IN UINTN DiskType
){}
1560 // default volume badge icon based on disk kind
1561 static EG_IMAGE
* GetDiskBadge(IN UINTN DiskType
) {
1562 EG_IMAGE
* Badge
= NULL
;
1566 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_INTERNAL
);
1569 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_EXTERNAL
);
1572 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_OPTICAL
);
1576 } // static EG_IMAGE * GetDiskBadge()
1579 Create a rEFInd boot option from a Legacy BIOS protocol option.
1581 static LEGACY_ENTRY
* AddLegacyEntryUEFI(BDS_COMMON_OPTION
*BdsOption
, IN UINT16 DiskType
)
1583 LEGACY_ENTRY
*Entry
, *SubEntry
;
1584 REFIT_MENU_SCREEN
*SubScreen
;
1585 CHAR16 ShortcutLetter
= 0;
1586 CHAR16
*LegacyDescription
= BdsOption
->Description
;
1588 // prepare the menu entry
1589 Entry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1590 Entry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1591 SPrint(Entry
->me
.Title
, 255, L
"Boot legacy target %s", LegacyDescription
);
1592 Entry
->me
.Tag
= TAG_LEGACY_UEFI
;
1594 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1595 Entry
->me
.Image
= LoadOSIcon(L
"legacy", L
"legacy", TRUE
);
1596 Entry
->LoadOptions
= (DiskType
== BBS_CDROM
) ? L
"CD" :
1597 ((DiskType
== BBS_USB
) ? L
"USB" : L
"HD");
1598 Entry
->me
.BadgeImage
= GetDiskBadge(DiskType
);
1599 Entry
->BdsOption
= BdsOption
;
1600 Entry
->Enabled
= TRUE
;
1602 // create the submenu
1603 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
1604 SubScreen
->Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1605 SPrint(SubScreen
->Title
, 255, L
"No boot options for legacy target");
1606 SubScreen
->TitleImage
= Entry
->me
.Image
;
1607 SubScreen
->Hint1
= StrDuplicate(SUBSCREEN_HINT1
);
1608 if (GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_EDITOR
) {
1609 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2_NO_EDITOR
);
1611 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2
);
1615 SubEntry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1616 SubEntry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1617 SPrint(SubEntry
->me
.Title
, 255, L
"Boot %s", LegacyDescription
);
1618 SubEntry
->me
.Tag
= TAG_LEGACY_UEFI
;
1619 Entry
->BdsOption
= BdsOption
;
1620 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
1622 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
1623 Entry
->me
.SubScreen
= SubScreen
;
1624 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1626 } /* static LEGACY_ENTRY * AddLegacyEntryUEFI() */
1629 Scan for legacy BIOS targets on machines that implement EFI_LEGACY_BIOS_PROTOCOL.
1630 In testing, protocol has not been implemented on Macs but has been
1631 implemented on several Dell PCs and an ASUS motherboard.
1632 Restricts output to disks of the specified DiskType.
1634 static VOID
ScanLegacyUEFI(IN UINTN DiskType
)
1637 EFI_LEGACY_BIOS_PROTOCOL
*LegacyBios
;
1638 UINT16
*BootOrder
= NULL
;
1640 CHAR16 BootOption
[10];
1641 UINTN BootOrderSize
= 0;
1643 BDS_COMMON_OPTION
*BdsOption
;
1644 LIST_ENTRY TempList
;
1645 BBS_BBS_DEVICE_PATH
* BbsDevicePath
= NULL
;
1647 InitializeListHead (&TempList
);
1648 ZeroMem (Buffer
, sizeof (Buffer
));
1650 // If LegacyBios protocol is not implemented on this platform, then
1651 //we do not support this type of legacy boot on this machine.
1652 Status
= gBS
->LocateProtocol(&gEfiLegacyBootProtocolGuid
, NULL
, (VOID
**) &LegacyBios
);
1653 if (EFI_ERROR (Status
))
1656 // Grab the boot order
1657 BootOrder
= BdsLibGetVariableAndSize(L
"BootOrder", &gEfiGlobalVariableGuid
, &BootOrderSize
);
1658 if (BootOrder
== NULL
) {
1663 while (Index
< BootOrderSize
/ sizeof (UINT16
))
1665 // Grab each boot option variable from the boot order, and convert
1666 // the variable into a BDS boot option
1667 UnicodeSPrint (BootOption
, sizeof (BootOption
), L
"Boot%04x", BootOrder
[Index
]);
1668 BdsOption
= BdsLibVariableToOption (&TempList
, BootOption
);
1670 if (BdsOption
!= NULL
) {
1671 BbsDevicePath
= (BBS_BBS_DEVICE_PATH
*)BdsOption
->DevicePath
;
1673 // Only add the entry if it is of a requested type (e.g. USB, HD)
1675 // Two checks necessary because some systems return EFI boot loaders
1676 // with a DeviceType value that would inappropriately include them
1677 // as legacy loaders....
1678 if ((BbsDevicePath
->DeviceType
== DiskType
) && (BdsOption
->DevicePath
->Type
== DEVICE_TYPE_BIOS
)) {
1679 AddLegacyEntryUEFI(BdsOption
, BbsDevicePath
->DeviceType
);
1684 } /* static VOID ScanLegacyUEFI() */
1685 #endif // __MAKEWITH_GNUEFI
1687 static VOID
ScanLegacyVolume(REFIT_VOLUME
*Volume
, UINTN VolumeIndex
) {
1689 BOOLEAN ShowVolume
, HideIfOthersFound
;
1692 HideIfOthersFound
= FALSE
;
1693 if (Volume
->IsAppleLegacy
) {
1695 HideIfOthersFound
= TRUE
;
1696 } else if (Volume
->HasBootCode
) {
1698 if (Volume
->BlockIO
== Volume
->WholeDiskBlockIO
&&
1699 Volume
->BlockIOOffset
== 0 &&
1700 Volume
->OSName
== NULL
)
1701 // this is a whole disk (MBR) entry; hide if we have entries for partitions
1702 HideIfOthersFound
= TRUE
;
1704 if (HideIfOthersFound
) {
1705 // check for other bootable entries on the same disk
1706 for (VolumeIndex2
= 0; VolumeIndex2
< VolumesCount
; VolumeIndex2
++) {
1707 if (VolumeIndex2
!= VolumeIndex
&& Volumes
[VolumeIndex2
]->HasBootCode
&&
1708 Volumes
[VolumeIndex2
]->WholeDiskBlockIO
== Volume
->WholeDiskBlockIO
)
1714 AddLegacyEntry(NULL
, Volume
);
1715 } // static VOID ScanLegacyVolume()
1717 // Scan attached optical discs for legacy (BIOS) boot code
1718 // and add anything found to the list....
1719 static VOID
ScanLegacyDisc(VOID
)
1722 REFIT_VOLUME
*Volume
;
1724 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
) {
1725 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1726 Volume
= Volumes
[VolumeIndex
];
1727 if (Volume
->DiskKind
== DISK_KIND_OPTICAL
)
1728 ScanLegacyVolume(Volume
, VolumeIndex
);
1730 } else if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
1731 ScanLegacyUEFI(BBS_CDROM
);
1733 } /* static VOID ScanLegacyDisc() */
1735 // Scan internal hard disks for legacy (BIOS) boot code
1736 // and add anything found to the list....
1737 static VOID
ScanLegacyInternal(VOID
)
1740 REFIT_VOLUME
*Volume
;
1742 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
) {
1743 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1744 Volume
= Volumes
[VolumeIndex
];
1745 if (Volume
->DiskKind
== DISK_KIND_INTERNAL
)
1746 ScanLegacyVolume(Volume
, VolumeIndex
);
1748 } else if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
1749 ScanLegacyUEFI(BBS_HARDDISK
);
1751 } /* static VOID ScanLegacyInternal() */
1753 // Scan external disks for legacy (BIOS) boot code
1754 // and add anything found to the list....
1755 static VOID
ScanLegacyExternal(VOID
)
1758 REFIT_VOLUME
*Volume
;
1760 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
) {
1761 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1762 Volume
= Volumes
[VolumeIndex
];
1763 if (Volume
->DiskKind
== DISK_KIND_EXTERNAL
)
1764 ScanLegacyVolume(Volume
, VolumeIndex
);
1766 } else if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
1767 ScanLegacyUEFI(BBS_USB
);
1769 } /* static VOID ScanLegacyExternal() */
1772 // pre-boot tool functions
1775 static VOID
StartTool(IN LOADER_ENTRY
*Entry
)
1777 BeginExternalScreen(Entry
->UseGraphicsMode
, Entry
->me
.Title
+ 6); // assumes "Start <title>" as assigned below
1778 StartEFIImage(Entry
->DevicePath
, Entry
->LoadOptions
, Basename(Entry
->LoaderPath
),
1779 Basename(Entry
->LoaderPath
), Entry
->OSType
, NULL
, TRUE
);
1780 FinishExternalScreen();
1781 } /* static VOID StartTool() */
1783 static LOADER_ENTRY
* AddToolEntry(EFI_HANDLE DeviceHandle
, IN CHAR16
*LoaderPath
, IN CHAR16
*LoaderTitle
, IN EG_IMAGE
*Image
,
1784 IN CHAR16 ShortcutLetter
, IN BOOLEAN UseGraphicsMode
)
1786 LOADER_ENTRY
*Entry
;
1787 CHAR16
*TitleStr
= NULL
;
1789 Entry
= AllocateZeroPool(sizeof(LOADER_ENTRY
));
1791 MergeStrings(&TitleStr
, L
"Start ", 0);
1792 MergeStrings(&TitleStr
, LoaderTitle
, 0);
1793 Entry
->me
.Title
= TitleStr
;
1794 Entry
->me
.Tag
= TAG_TOOL
;
1796 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1797 Entry
->me
.Image
= Image
;
1798 Entry
->LoaderPath
= (LoaderPath
) ? StrDuplicate(LoaderPath
) : NULL
;
1799 Entry
->DevicePath
= FileDevicePath(DeviceHandle
, Entry
->LoaderPath
);
1800 Entry
->UseGraphicsMode
= UseGraphicsMode
;
1802 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1804 } /* static LOADER_ENTRY * AddToolEntry() */
1807 // pre-boot driver functions
1810 static UINTN
ScanDriverDir(IN CHAR16
*Path
)
1813 REFIT_DIR_ITER DirIter
;
1815 EFI_FILE_INFO
*DirEntry
;
1816 CHAR16 FileName
[256];
1818 CleanUpPathNameSlashes(Path
);
1819 // look through contents of the directory
1820 DirIterOpen(SelfRootDir
, Path
, &DirIter
);
1821 while (DirIterNext(&DirIter
, 2, LOADER_MATCH_PATTERNS
, &DirEntry
)) {
1822 if (DirEntry
->FileName
[0] == '.')
1823 continue; // skip this
1825 SPrint(FileName
, 255, L
"%s\\%s", Path
, DirEntry
->FileName
);
1827 Status
= StartEFIImage(FileDevicePath(SelfLoadedImage
->DeviceHandle
, FileName
),
1828 L
"", DirEntry
->FileName
, DirEntry
->FileName
, 0, NULL
, FALSE
);
1830 Status
= DirIterClose(&DirIter
);
1831 if (Status
!= EFI_NOT_FOUND
) {
1832 SPrint(FileName
, 255, L
"while scanning the %s directory", Path
);
1833 CheckError(Status
, FileName
);
1838 #ifdef __MAKEWITH_GNUEFI
1839 static EFI_STATUS
ConnectAllDriversToAllControllers(VOID
)
1842 UINTN AllHandleCount
;
1843 EFI_HANDLE
*AllHandleBuffer
;
1846 EFI_HANDLE
*HandleBuffer
;
1852 Status
= LibLocateHandle(AllHandles
,
1857 if (EFI_ERROR(Status
))
1860 for (Index
= 0; Index
< AllHandleCount
; Index
++) {
1862 // Scan the handle database
1864 Status
= LibScanHandleDatabase(NULL
,
1866 AllHandleBuffer
[Index
],
1871 if (EFI_ERROR (Status
))
1875 if (HandleType
[Index
] & EFI_HANDLE_TYPE_DRIVER_BINDING_HANDLE
)
1877 if (HandleType
[Index
] & EFI_HANDLE_TYPE_IMAGE_HANDLE
)
1882 for (HandleIndex
= 0; HandleIndex
< HandleCount
; HandleIndex
++) {
1883 if (HandleType
[HandleIndex
] & EFI_HANDLE_TYPE_PARENT_HANDLE
)
1888 if (HandleType
[Index
] & EFI_HANDLE_TYPE_DEVICE_HANDLE
) {
1889 Status
= refit_call4_wrapper(BS
->ConnectController
,
1890 AllHandleBuffer
[Index
],
1898 MyFreePool (HandleBuffer
);
1899 MyFreePool (HandleType
);
1903 MyFreePool (AllHandleBuffer
);
1905 } /* EFI_STATUS ConnectAllDriversToAllControllers() */
1907 static EFI_STATUS
ConnectAllDriversToAllControllers(VOID
) {
1908 BdsLibConnectAllDriversToAllControllers();
1913 // Load all EFI drivers from rEFInd's "drivers" subdirectory and from the
1914 // directories specified by the user in the "scan_driver_dirs" configuration
1916 static VOID
LoadDrivers(VOID
)
1918 CHAR16
*Directory
, *SelfDirectory
;
1919 UINTN i
= 0, Length
, NumFound
= 0;
1921 // load drivers from the subdirectories of rEFInd's home directory specified
1922 // in the DRIVER_DIRS constant.
1923 while ((Directory
= FindCommaDelimited(DRIVER_DIRS
, i
++)) != NULL
) {
1924 SelfDirectory
= SelfDirPath
? StrDuplicate(SelfDirPath
) : NULL
;
1925 CleanUpPathNameSlashes(SelfDirectory
);
1926 MergeStrings(&SelfDirectory
, Directory
, L
'\\');
1927 NumFound
+= ScanDriverDir(SelfDirectory
);
1928 MyFreePool(Directory
);
1929 MyFreePool(SelfDirectory
);
1932 // Scan additional user-specified driver directories....
1934 while ((Directory
= FindCommaDelimited(GlobalConfig
.DriverDirs
, i
++)) != NULL
) {
1935 CleanUpPathNameSlashes(Directory
);
1936 Length
= StrLen(Directory
);
1938 NumFound
+= ScanDriverDir(Directory
);
1940 MyFreePool(Directory
);
1943 // connect all devices
1945 ConnectAllDriversToAllControllers();
1946 } /* static VOID LoadDrivers() */
1948 // Determine what (if any) type of legacy (BIOS) boot support is available
1949 static VOID
FindLegacyBootType(VOID
) {
1950 #ifdef __MAKEWITH_TIANO
1952 EFI_LEGACY_BIOS_PROTOCOL
*LegacyBios
;
1955 GlobalConfig
.LegacyType
= LEGACY_TYPE_NONE
;
1957 // UEFI-style legacy BIOS support is available only with the TianoCore EDK2
1958 // build environment, and then only with some EFI implementations....
1959 #ifdef __MAKEWITH_TIANO
1960 Status
= gBS
->LocateProtocol (&gEfiLegacyBootProtocolGuid
, NULL
, (VOID
**) &LegacyBios
);
1961 if (!EFI_ERROR (Status
))
1962 GlobalConfig
.LegacyType
= LEGACY_TYPE_UEFI
;
1965 // Macs have their own system. If the firmware vendor code contains the
1966 // string "Apple", assume it's available. Note that this overrides the
1967 // UEFI type, and might yield false positives if the vendor string
1968 // contains "Apple" as part of something bigger, so this isn't 100%
1970 if (StriSubCmp(L
"Apple", ST
->FirmwareVendor
))
1971 GlobalConfig
.LegacyType
= LEGACY_TYPE_MAC
;
1972 } // static VOID FindLegacyBootType
1974 // Warn the user if legacy OS scans are enabled but the firmware or this
1975 // application can't support them....
1976 static VOID
WarnIfLegacyProblems() {
1977 BOOLEAN found
= FALSE
;
1980 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_NONE
) {
1982 if (GlobalConfig
.ScanFor
[i
] == 'h' || GlobalConfig
.ScanFor
[i
] == 'b' || GlobalConfig
.ScanFor
[i
] == 'c')
1985 } while ((i
< NUM_SCAN_OPTIONS
) && (!found
));
1987 Print(L
"NOTE: refind.conf's 'scanfor' line specifies scanning for one or more legacy\n");
1988 Print(L
"(BIOS) boot options; however, this is not possible because ");
1989 #ifdef __MAKEWITH_TIANO
1990 Print(L
"your computer lacks\n");
1991 Print(L
"the necessary Compatibility Support Module (CSM) support.\n");
1993 Print(L
"this program was\n");
1994 Print(L
"compiled without the necessary support. Please visit\n");
1995 Print(L
"http://www.rodsbooks.com/refind/getting.html and download and install a rEFInd\n");
1996 Print(L
"binary built with the TianoCore EDK2 to enable legacy boot support.\n");
2000 } // if no legacy support
2001 } // static VOID WarnIfLegacyProblems()
2003 // Locates boot loaders. NOTE: This assumes that GlobalConfig.LegacyType is set correctly.
2004 static VOID
ScanForBootloaders(VOID
) {
2009 // scan for loaders and tools, add them to the menu
2010 for (i
= 0; i
< NUM_SCAN_OPTIONS
; i
++) {
2011 switch(GlobalConfig
.ScanFor
[i
]) {
2016 ScanLegacyInternal();
2019 ScanLegacyExternal();
2022 ScanUserConfigured(CONFIG_FILE_NAME
);
2036 // assign shortcut keys
2037 for (i
= 0; i
< MainMenu
.EntryCount
&& MainMenu
.Entries
[i
]->Row
== 0 && i
< 9; i
++)
2038 MainMenu
.Entries
[i
]->ShortcutDigit
= (CHAR16
)('1' + i
);
2040 // wait for user ACK when there were errors
2041 FinishTextScreen(FALSE
);
2042 } // static VOID ScanForBootloaders()
2044 // Add the second-row tags containing built-in and external tools (EFI shell,
2046 static VOID
ScanForTools(VOID
) {
2047 CHAR16
*FileName
= NULL
, *MokLocations
, *MokName
, *PathName
, Description
[256];
2048 REFIT_MENU_ENTRY
*TempMenuEntry
;
2049 UINTN i
, j
, k
, VolumeIndex
;
2053 MokLocations
= StrDuplicate(MOK_LOCATIONS
);
2054 if (MokLocations
!= NULL
)
2055 MergeStrings(&MokLocations
, SelfDirPath
, L
',');
2057 for (i
= 0; i
< NUM_TOOLS
; i
++) {
2058 switch(GlobalConfig
.ShowTools
[i
]) {
2059 // NOTE: Be sure that FileName is NULL at the end of each case.
2061 TempMenuEntry
= CopyMenuEntry(&MenuEntryShutdown
);
2062 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_SHUTDOWN
);
2063 AddMenuEntry(&MainMenu
, TempMenuEntry
);
2066 TempMenuEntry
= CopyMenuEntry(&MenuEntryReset
);
2067 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_RESET
);
2068 AddMenuEntry(&MainMenu
, TempMenuEntry
);
2071 TempMenuEntry
= CopyMenuEntry(&MenuEntryAbout
);
2072 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT
);
2073 AddMenuEntry(&MainMenu
, TempMenuEntry
);
2076 TempMenuEntry
= CopyMenuEntry(&MenuEntryExit
);
2077 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_EXIT
);
2078 AddMenuEntry(&MainMenu
, TempMenuEntry
);
2081 if (EfivarGetRaw(&GlobalGuid
, L
"OsIndicationsSupported", &b
, &j
) == EFI_SUCCESS
) {
2083 if (osind
& EFI_OS_INDICATIONS_BOOT_TO_FW_UI
) {
2084 TempMenuEntry
= CopyMenuEntry(&MenuEntryFirmware
);
2085 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_FIRMWARE
);
2086 AddMenuEntry(&MainMenu
, TempMenuEntry
);
2092 while ((FileName
= FindCommaDelimited(SHELL_NAMES
, j
++)) != NULL
) {
2093 if (FileExists(SelfRootDir
, FileName
)) {
2094 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, L
"EFI Shell", BuiltinIcon(BUILTIN_ICON_TOOL_SHELL
),
2097 MyFreePool(FileName
);
2102 while ((FileName
= FindCommaDelimited(GPTSYNC_NAMES
, j
++)) != NULL
) {
2103 if (FileExists(SelfRootDir
, FileName
)) {
2104 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, L
"Hybrid MBR tool", BuiltinIcon(BUILTIN_ICON_TOOL_PART
),
2107 MyFreePool(FileName
);
2111 case TAG_APPLE_RECOVERY
:
2112 FileName
= StrDuplicate(L
"\\com.apple.recovery.boot\\boot.efi");
2113 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
2114 if ((Volumes
[VolumeIndex
]->RootDir
!= NULL
) && (FileExists(Volumes
[VolumeIndex
]->RootDir
, FileName
))) {
2115 SPrint(Description
, 255, L
"Apple Recovery on %s", Volumes
[VolumeIndex
]->VolName
);
2116 AddToolEntry(Volumes
[VolumeIndex
]->DeviceHandle
, FileName
, Description
,
2117 BuiltinIcon(BUILTIN_ICON_TOOL_APPLE_RESCUE
), 'R', TRUE
);
2120 MyFreePool(FileName
);
2125 while ((FileName
= FindCommaDelimited(MokLocations
, j
++)) != NULL
) {
2127 while ((MokName
= FindCommaDelimited(MOK_NAMES
, k
++)) != NULL
) {
2128 PathName
= StrDuplicate(FileName
);
2129 MergeStrings(&PathName
, MokName
, (StriCmp(PathName
, L
"\\") == 0) ? 0 : L
'\\');
2130 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
2131 if ((Volumes
[VolumeIndex
]->RootDir
!= NULL
) && (FileExists(Volumes
[VolumeIndex
]->RootDir
, PathName
))) {
2132 SPrint(Description
, 255, L
"MOK utility at %s on %s", PathName
, Volumes
[VolumeIndex
]->VolName
);
2133 AddToolEntry(Volumes
[VolumeIndex
]->DeviceHandle
, PathName
, Description
,
2134 BuiltinIcon(BUILTIN_ICON_TOOL_MOK_TOOL
), 'S', FALSE
);
2137 MyFreePool(PathName
);
2138 MyFreePool(MokName
);
2139 } // while MOK_NAMES
2140 MyFreePool(FileName
);
2141 } // while MokLocations
2146 } // static VOID ScanForTools
2148 // Rescan for boot loaders
2149 VOID
RescanAll(VOID
) {
2156 egDisplayMessage(L
"Scanning for new boot loaders; please wait....", &BGColor
);
2157 FreeList((VOID
***) &(MainMenu
.Entries
), &MainMenu
.EntryCount
);
2158 MainMenu
.Entries
= NULL
;
2159 MainMenu
.EntryCount
= 0;
2160 ReadConfig(CONFIG_FILE_NAME
);
2161 ConnectAllDriversToAllControllers();
2163 ScanForBootloaders();
2166 } // VOID RescanAll()
2168 #ifdef __MAKEWITH_TIANO
2170 // Minimal initialization function
2171 static VOID
InitializeLib(IN EFI_HANDLE ImageHandle
, IN EFI_SYSTEM_TABLE
*SystemTable
) {
2173 // gImageHandle = ImageHandle;
2174 gBS
= SystemTable
->BootServices
;
2175 // gRS = SystemTable->RuntimeServices;
2176 gRT
= SystemTable
->RuntimeServices
; // Some BDS functions need gRT to be set
2177 EfiGetSystemConfigurationTable (&gEfiDxeServicesTableGuid
, (VOID
**) &gDS
);
2179 InitializeConsoleSim();
2184 // Set up our own Secure Boot extensions....
2185 // Returns TRUE on success, FALSE otherwise
2186 static BOOLEAN
SecureBootSetup(VOID
) {
2188 BOOLEAN Success
= FALSE
;
2190 if (secure_mode() && ShimLoaded()) {
2191 Status
= security_policy_install();
2192 if (Status
== EFI_SUCCESS
) {
2195 Print(L
"Failed to install MOK Secure Boot extensions");
2199 } // VOID SecureBootSetup()
2201 // Remove our own Secure Boot extensions....
2202 // Returns TRUE on success, FALSE otherwise
2203 static BOOLEAN
SecureBootUninstall(VOID
) {
2205 BOOLEAN Success
= TRUE
;
2207 if (secure_mode()) {
2208 Status
= security_policy_uninstall();
2209 if (Status
!= EFI_SUCCESS
) {
2211 BeginTextScreen(L
"Secure Boot Policy Failure");
2212 Print(L
"Failed to uninstall MOK Secure Boot extensions; forcing a reboot.");
2214 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);
2218 } // VOID SecureBootUninstall
2225 efi_main (EFI_HANDLE ImageHandle
, EFI_SYSTEM_TABLE
*SystemTable
)
2228 BOOLEAN MainLoopRunning
= TRUE
;
2229 BOOLEAN MokProtocol
;
2230 REFIT_MENU_ENTRY
*ChosenEntry
;
2232 CHAR16
*Selection
= NULL
;
2236 InitializeLib(ImageHandle
, SystemTable
);
2237 Status
= InitRefitLib(ImageHandle
);
2238 if (EFI_ERROR(Status
))
2241 // read configuration
2242 CopyMem(GlobalConfig
.ScanFor
, "ieom ", NUM_SCAN_OPTIONS
);
2243 FindLegacyBootType();
2244 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
)
2245 CopyMem(GlobalConfig
.ScanFor
, "ihebocm ", NUM_SCAN_OPTIONS
);
2247 ReadConfig(CONFIG_FILE_NAME
);
2250 WarnIfLegacyProblems();
2251 MainMenu
.TimeoutSeconds
= GlobalConfig
.Timeout
;
2253 // disable EFI watchdog timer
2254 refit_call4_wrapper(BS
->SetWatchdogTimer
, 0x0000, 0x0000, 0x0000, NULL
);
2256 // further bootstrap (now with config available)
2257 MokProtocol
= SecureBootSetup();
2259 ScanForBootloaders();
2263 if (GlobalConfig
.ScanDelay
> 0) {
2268 egDisplayMessage(L
"Pausing before disk scan; please wait....", &BGColor
);
2269 for (i
= 0; i
< GlobalConfig
.ScanDelay
; i
++)
2270 refit_call1_wrapper(BS
->Stall
, 1000000);
2274 if (GlobalConfig
.DefaultSelection
)
2275 Selection
= StrDuplicate(GlobalConfig
.DefaultSelection
);
2277 while (MainLoopRunning
) {
2278 MenuExit
= RunMainMenu(&MainMenu
, Selection
, &ChosenEntry
);
2280 // The Escape key triggers a re-scan operation....
2281 if (MenuExit
== MENU_EXIT_ESCAPE
) {
2286 switch (ChosenEntry
->Tag
) {
2288 case TAG_REBOOT
: // Reboot
2290 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);
2291 MainLoopRunning
= FALSE
; // just in case we get this far
2294 case TAG_SHUTDOWN
: // Shut Down
2296 refit_call4_wrapper(RT
->ResetSystem
, EfiResetShutdown
, EFI_SUCCESS
, 0, NULL
);
2297 MainLoopRunning
= FALSE
; // just in case we get this far
2300 case TAG_ABOUT
: // About rEFInd
2304 case TAG_LOADER
: // Boot OS via .EFI loader
2305 StartLoader((LOADER_ENTRY
*)ChosenEntry
);
2308 case TAG_LEGACY
: // Boot legacy OS
2309 StartLegacy((LEGACY_ENTRY
*)ChosenEntry
);
2312 #ifdef __MAKEWITH_TIANO
2313 case TAG_LEGACY_UEFI
: // Boot a legacy OS on a non-Mac
2314 StartLegacyUEFI((LEGACY_ENTRY
*)ChosenEntry
);
2318 case TAG_TOOL
: // Start a EFI tool
2319 StartTool((LOADER_ENTRY
*)ChosenEntry
);
2322 case TAG_EXIT
: // Terminate rEFInd
2323 if ((MokProtocol
) && !SecureBootUninstall()) {
2324 MainLoopRunning
= FALSE
; // just in case we get this far
2326 BeginTextScreen(L
" ");
2331 case TAG_FIRMWARE
: // Reboot into firmware's user interface
2332 RebootIntoFirmware();
2336 MyFreePool(Selection
);
2337 Selection
= (ChosenEntry
->Title
) ? StrDuplicate(ChosenEntry
->Title
) : NULL
;
2340 // If we end up here, things have gone wrong. Try to reboot, and if that
2341 // fails, go into an endless loop.
2342 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);