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
67 #define MACOSX_LOADER_PATH L"System\\Library\\CoreServices\\boot.efi"
69 #define SHELL_NAMES L"\\EFI\\tools\\shell.efi,\\EFI\\tools\\shellx64.efi,\\shellx64.efi"
70 #define DRIVER_DIRS L"drivers,drivers_x64"
71 #define FALLBACK_FULLNAME L"EFI\\BOOT\\bootx64.efi"
72 #define FALLBACK_BASENAME L"bootx64.efi"
74 #define SHELL_NAMES L"\\EFI\\tools\\shell.efi,\\EFI\\tools\\shellia32.efi,\\shellia32.efi"
75 #define DRIVER_DIRS L"drivers,drivers_ia32"
76 #define FALLBACK_FULLNAME L"EFI\\BOOT\\bootia32.efi"
77 #define FALLBACK_BASENAME L"bootia32.efi"
79 #define SHELL_NAMES L"\\EFI\\tools\\shell.efi"
80 #define DRIVER_DIRS L"drivers"
81 #define FALLBACK_FULLNAME L"EFI\\BOOT\\boot.efi" /* Not really correct */
82 #define FALLBACK_BASENAME L"boot.efi" /* Not really correct */
85 // Filename patterns that identify EFI boot loaders. Note that a single case (either L"*.efi" or
86 // L"*.EFI") is fine for most systems; but Gigabyte's buggy Hybrid EFI does a case-sensitive
87 // comparison when it should do a case-insensitive comparison, so I'm doubling this up. It does
88 // no harm on other computers, AFAIK. In theory, every case variation should be done for
89 // completeness, but that's ridiculous....
90 #define LOADER_MATCH_PATTERNS L"*.efi,*.EFI"
92 // Patterns that identify Linux kernels. Added to the loader match pattern when the
93 // scan_all_linux_kernels option is set in the configuration file. Causes kernels WITHOUT
94 // a ".efi" extension to be found when scanning for boot loaders.
95 #define LINUX_MATCH_PATTERNS L"vmlinuz*,bzImage*"
97 // Default hint text for program-launch submenus
98 #define SUBSCREEN_HINT1 L"Use arrow keys to move cursor; Enter to boot;"
99 #define SUBSCREEN_HINT2 L"Insert or F2 to edit options; Esc to return to main menu"
100 #define SUBSCREEN_HINT2_NO_EDITOR L"Esc to return to main menu"
102 static REFIT_MENU_ENTRY MenuEntryAbout
= { L
"About rEFInd", TAG_ABOUT
, 1, 0, 'A', NULL
, NULL
, NULL
};
103 static REFIT_MENU_ENTRY MenuEntryReset
= { L
"Reboot Computer", TAG_REBOOT
, 1, 0, 'R', NULL
, NULL
, NULL
};
104 static REFIT_MENU_ENTRY MenuEntryShutdown
= { L
"Shut Down Computer", TAG_SHUTDOWN
, 1, 0, 'U', NULL
, NULL
, NULL
};
105 static REFIT_MENU_ENTRY MenuEntryReturn
= { L
"Return to Main Menu", TAG_RETURN
, 0, 0, 0, NULL
, NULL
, NULL
};
106 static REFIT_MENU_ENTRY MenuEntryExit
= { L
"Exit rEFInd", TAG_EXIT
, 1, 0, 0, NULL
, NULL
, NULL
};
108 static REFIT_MENU_SCREEN MainMenu
= { L
"Main Menu", NULL
, 0, NULL
, 0, NULL
, 0, L
"Automatic boot",
109 L
"Use arrow keys to move cursor; Enter to boot;",
110 L
"Insert or F2 for more options; Esc to refresh" };
111 static REFIT_MENU_SCREEN AboutMenu
= { L
"About", NULL
, 0, NULL
, 0, NULL
, 0, NULL
, L
"Press Enter to return to main menu", L
"" };
113 REFIT_CONFIG GlobalConfig
= { FALSE
, FALSE
, 0, 0, 0, DONT_CHANGE_TEXT_MODE
, 20, 0, 0, GRAPHICS_FOR_OSX
, LEGACY_TYPE_MAC
, 0,
114 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
115 {TAG_SHELL
, TAG_APPLE_RECOVERY
, TAG_MOK_TOOL
, TAG_ABOUT
, TAG_SHUTDOWN
, TAG_REBOOT
, 0, 0, 0, 0, 0 }};
117 // Structure used to hold boot loader filenames and time stamps in
118 // a linked list; used to sort entries within a directory.
122 struct LOADER_LIST
*NextEntry
;
129 static VOID
AboutrEFInd(VOID
)
131 CHAR16
*TempStr
; // Note: Don't deallocate; moved to menu structure
133 if (AboutMenu
.EntryCount
== 0) {
134 AboutMenu
.TitleImage
= BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT
);
135 AddMenuInfoLine(&AboutMenu
, L
"rEFInd Version 0.6.7");
136 AddMenuInfoLine(&AboutMenu
, L
"");
137 AddMenuInfoLine(&AboutMenu
, L
"Copyright (c) 2006-2010 Christoph Pfisterer");
138 AddMenuInfoLine(&AboutMenu
, L
"Copyright (c) 2012 Roderick W. Smith");
139 AddMenuInfoLine(&AboutMenu
, L
"Portions Copyright (c) Intel Corporation and others");
140 AddMenuInfoLine(&AboutMenu
, L
"Distributed under the terms of the GNU GPLv3 license");
141 AddMenuInfoLine(&AboutMenu
, L
"");
142 AddMenuInfoLine(&AboutMenu
, L
"Running on:");
143 TempStr
= AllocateZeroPool(256 * sizeof(CHAR16
));
144 SPrint(TempStr
, 255, L
" EFI Revision %d.%02d", ST
->Hdr
.Revision
>> 16, ST
->Hdr
.Revision
& ((1 << 16) - 1));
145 AddMenuInfoLine(&AboutMenu
, TempStr
);
147 AddMenuInfoLine(&AboutMenu
, L
" Platform: x86 (32 bit)");
148 #elif defined(EFIX64)
149 TempStr
= AllocateZeroPool(256 * sizeof(CHAR16
));
150 SPrint(TempStr
, 255, L
" Platform: x86_64 (64 bit); Secure Boot %s", secure_mode() ? L
"active" : L
"inactive");
151 AddMenuInfoLine(&AboutMenu
, TempStr
);
153 AddMenuInfoLine(&AboutMenu
, L
" Platform: unknown");
155 TempStr
= AllocateZeroPool(256 * sizeof(CHAR16
));
156 SPrint(TempStr
, 255, L
" Firmware: %s %d.%02d",
157 ST
->FirmwareVendor
, ST
->FirmwareRevision
>> 16, ST
->FirmwareRevision
& ((1 << 16) - 1));
158 AddMenuInfoLine(&AboutMenu
, TempStr
);
159 TempStr
= AllocateZeroPool(256 * sizeof(CHAR16
));
160 SPrint(TempStr
, 255, L
" Screen Output: %s", egScreenDescription());
161 AddMenuInfoLine(&AboutMenu
, TempStr
);
162 AddMenuInfoLine(&AboutMenu
, L
"");
163 #if defined(__MAKEWITH_GNUEFI)
164 AddMenuInfoLine(&AboutMenu
, L
"Built with GNU-EFI");
166 AddMenuInfoLine(&AboutMenu
, L
"Built with TianoCore EDK2");
168 AddMenuInfoLine(&AboutMenu
, L
"");
169 AddMenuInfoLine(&AboutMenu
, L
"For more information, see the rEFInd Web site:");
170 AddMenuInfoLine(&AboutMenu
, L
"http://www.rodsbooks.com/refind/");
171 AddMenuEntry(&AboutMenu
, &MenuEntryReturn
);
174 RunMenu(&AboutMenu
, NULL
);
175 } /* VOID AboutrEFInd() */
177 static VOID
WarnSecureBootError(CHAR16
*Name
, BOOLEAN Verbose
) {
179 Name
= L
"the loader";
181 refit_call2_wrapper(ST
->ConOut
->SetAttribute
, ST
->ConOut
, ATTR_ERROR
);
182 Print(L
"Secure Boot validation failure loading %s!\n", Name
);
183 refit_call2_wrapper(ST
->ConOut
->SetAttribute
, ST
->ConOut
, ATTR_BASIC
);
184 if (Verbose
&& secure_mode()) {
185 Print(L
"\nThis computer is configured with Secure Boot active, but\n%s has failed validation.\n", Name
);
186 Print(L
"\nYou can:\n * Launch another boot loader\n");
187 Print(L
" * Disable Secure Boot in your firmware\n");
188 Print(L
" * Sign %s with a machine owner key (MOK)\n", Name
);
189 Print(L
" * Use a MOK utility (often present on the second row) to add a MOK with which\n");
190 Print(L
" %s has already been signed.\n", Name
);
191 Print(L
" * Use a MOK utility to register %s (\"enroll its hash\") without\n", Name
);
192 Print(L
" signing it.\n");
193 Print(L
"\nSee http://www.rodsbooks.com/refind/secureboot.html for more information\n");
196 } // VOID WarnSecureBootError()
198 // Launch an EFI binary.
199 static EFI_STATUS
StartEFIImageList(IN EFI_DEVICE_PATH
**DevicePaths
,
200 IN CHAR16
*LoadOptions
, IN CHAR16
*LoadOptionsPrefix
,
201 IN CHAR16
*ImageTitle
, IN CHAR8 OSType
,
202 OUT UINTN
*ErrorInStep
,
205 EFI_STATUS Status
, ReturnStatus
;
206 EFI_HANDLE ChildImageHandle
;
207 EFI_LOADED_IMAGE
*ChildLoadedImage
= NULL
;
208 UINTN DevicePathIndex
;
209 CHAR16 ErrorInfo
[256];
210 CHAR16
*FullLoadOptions
= NULL
;
212 if (ErrorInStep
!= NULL
)
216 if (LoadOptions
!= NULL
) {
217 if (LoadOptionsPrefix
!= NULL
) {
218 MergeStrings(&FullLoadOptions
, LoadOptions
, L
' ');
220 MergeStrings(&FullLoadOptions
, L
" ", 0);
221 // NOTE: That last space is also added by the EFI shell and seems to be significant
222 // when passing options to Apple's boot.efi...
225 MergeStrings(&FullLoadOptions
, LoadOptions
, 0);
227 } else { // LoadOptions == NULL
228 // NOTE: We provide a non-null string when no options are specified for safety;
229 // some systems (at least DUET) can hang when launching some programs (such as
230 // an EFI shell) without this.
231 FullLoadOptions
= StrDuplicate(L
" ");
234 Print(L
"Starting %s\nUsing load options '%s'\n", ImageTitle
, FullLoadOptions
);
236 // load the image into memory (and execute it, in the case of a shim/MOK image).
237 ReturnStatus
= Status
= EFI_NOT_FOUND
; // in case the list is empty
238 for (DevicePathIndex
= 0; DevicePaths
[DevicePathIndex
] != NULL
; DevicePathIndex
++) {
239 // NOTE: Below commented-out line could be more efficient if file were read ahead of
240 // time and passed as a pre-loaded image to LoadImage(), but it doesn't work on my
241 // 32-bit Mac Mini or my 64-bit Intel box when launching a Linux kernel; the
242 // kernel returns a "Failed to handle fs_proto" error message.
243 // TODO: Track down the cause of this error and fix it, if possible.
244 // ReturnStatus = Status = refit_call6_wrapper(BS->LoadImage, FALSE, SelfImageHandle, DevicePaths[DevicePathIndex],
245 // ImageData, ImageSize, &ChildImageHandle);
246 ReturnStatus
= Status
= refit_call6_wrapper(BS
->LoadImage
, FALSE
, SelfImageHandle
, DevicePaths
[DevicePathIndex
],
247 NULL
, 0, &ChildImageHandle
);
248 if (ReturnStatus
!= EFI_NOT_FOUND
) {
252 if ((Status
== EFI_ACCESS_DENIED
) || (Status
== EFI_SECURITY_VIOLATION
)) {
253 WarnSecureBootError(ImageTitle
, Verbose
);
256 SPrint(ErrorInfo
, 255, L
"while loading %s", ImageTitle
);
257 if (CheckError(Status
, ErrorInfo
)) {
258 if (ErrorInStep
!= NULL
)
263 ReturnStatus
= Status
= refit_call3_wrapper(BS
->HandleProtocol
, ChildImageHandle
, &LoadedImageProtocol
,
264 (VOID
**) &ChildLoadedImage
);
265 if (CheckError(Status
, L
"while getting a LoadedImageProtocol handle")) {
266 if (ErrorInStep
!= NULL
)
270 ChildLoadedImage
->LoadOptions
= (VOID
*)FullLoadOptions
;
271 ChildLoadedImage
->LoadOptionsSize
= ((UINT32
)StrLen(FullLoadOptions
) + 1) * sizeof(CHAR16
);
272 // turn control over to the image
273 // TODO: (optionally) re-enable the EFI watchdog timer!
275 // close open file handles
277 ReturnStatus
= Status
= refit_call3_wrapper(BS
->StartImage
, ChildImageHandle
, NULL
, NULL
);
279 // control returns here when the child image calls Exit()
280 SPrint(ErrorInfo
, 255, L
"returned from %s", ImageTitle
);
281 if (CheckError(Status
, ErrorInfo
)) {
282 if (ErrorInStep
!= NULL
)
286 // re-open file handles
290 // unload the image, we don't care if it works or not...
291 Status
= refit_call1_wrapper(BS
->UnloadImage
, ChildImageHandle
);
294 MyFreePool(FullLoadOptions
);
296 } /* static EFI_STATUS StartEFIImageList() */
298 static EFI_STATUS
StartEFIImage(IN EFI_DEVICE_PATH
*DevicePath
,
299 IN CHAR16
*LoadOptions
, IN CHAR16
*LoadOptionsPrefix
,
300 IN CHAR16
*ImageTitle
, IN CHAR8 OSType
,
301 OUT UINTN
*ErrorInStep
,
304 EFI_DEVICE_PATH
*DevicePaths
[2];
306 DevicePaths
[0] = DevicePath
;
307 DevicePaths
[1] = NULL
;
308 return StartEFIImageList(DevicePaths
, LoadOptions
, LoadOptionsPrefix
, ImageTitle
, OSType
, ErrorInStep
, Verbose
);
309 } /* static EFI_STATUS StartEFIImage() */
312 // EFI OS loader functions
315 static VOID
StartLoader(IN LOADER_ENTRY
*Entry
)
317 UINTN ErrorInStep
= 0;
319 BeginExternalScreen(Entry
->UseGraphicsMode
, L
"Booting OS");
320 StartEFIImage(Entry
->DevicePath
, Entry
->LoadOptions
, Basename(Entry
->LoaderPath
),
321 Basename(Entry
->LoaderPath
), Entry
->OSType
, &ErrorInStep
, !Entry
->UseGraphicsMode
);
322 FinishExternalScreen();
325 // Locate an initrd or initramfs file that matches the kernel specified by LoaderPath.
326 // The matching file has a name that begins with "init" and includes the same version
327 // number string as is found in LoaderPath -- but not a longer version number string.
328 // For instance, if LoaderPath is \EFI\kernels\bzImage-3.3.0.efi, and if \EFI\kernels
329 // has a file called initramfs-3.3.0.img, this function will return the string
330 // '\EFI\kernels\initramfs-3.3.0.img'. If the directory ALSO contains the file
331 // initramfs-3.3.0-rc7.img or initramfs-13.3.0.img, those files will NOT match;
332 // however, initmine-3.3.0.img might match. (FindInitrd() returns the first match it
333 // finds). Thus, care should be taken to avoid placing duplicate matching files in
334 // the kernel's directory.
335 // If no matching init file can be found, returns NULL.
336 static CHAR16
* FindInitrd(IN CHAR16
*LoaderPath
, IN REFIT_VOLUME
*Volume
) {
337 CHAR16
*InitrdName
= NULL
, *FileName
, *KernelVersion
, *InitrdVersion
, *Path
;
338 REFIT_DIR_ITER DirIter
;
339 EFI_FILE_INFO
*DirEntry
;
341 FileName
= Basename(LoaderPath
);
342 KernelVersion
= FindNumbers(FileName
);
343 Path
= FindPath(LoaderPath
);
345 // Add trailing backslash for root directory; necessary on some systems, but must
346 // NOT be added to all directories, since on other systems, a trailing backslash on
347 // anything but the root directory causes them to flake out!
348 if (StrLen(Path
) == 0) {
349 MergeStrings(&Path
, L
"\\", 0);
351 DirIterOpen(Volume
->RootDir
, Path
, &DirIter
);
352 // Now add a trailing backslash if it was NOT added earlier, for consistency in
353 // building the InitrdName later....
354 if ((StrLen(Path
) > 0) && (Path
[StrLen(Path
) - 1] != L
'\\'))
355 MergeStrings(&Path
, L
"\\", 0);
356 while ((DirIterNext(&DirIter
, 2, L
"init*", &DirEntry
)) && (InitrdName
== NULL
)) {
357 InitrdVersion
= FindNumbers(DirEntry
->FileName
);
358 if (KernelVersion
!= NULL
) {
359 if (StriCmp(InitrdVersion
, KernelVersion
) == 0) {
360 MergeStrings(&InitrdName
, Path
, 0);
361 MergeStrings(&InitrdName
, DirEntry
->FileName
, 0);
364 if (InitrdVersion
== NULL
) {
365 MergeStrings(&InitrdName
, Path
, 0);
366 MergeStrings(&InitrdName
, DirEntry
->FileName
, 0);
369 MyFreePool(InitrdVersion
);
371 DirIterClose(&DirIter
);
373 // Note: Don't FreePool(FileName), since Basename returns a pointer WITHIN the string it's passed.
374 MyFreePool(KernelVersion
);
377 } // static CHAR16 * FindInitrd()
379 LOADER_ENTRY
* AddPreparedLoaderEntry(LOADER_ENTRY
*Entry
) {
380 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
383 } // LOADER_ENTRY * AddPreparedLoaderEntry()
385 // Creates a copy of a menu screen.
386 // Returns a pointer to the copy of the menu screen.
387 static REFIT_MENU_SCREEN
* CopyMenuScreen(REFIT_MENU_SCREEN
*Entry
) {
388 REFIT_MENU_SCREEN
*NewEntry
;
391 NewEntry
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
392 if ((Entry
!= NULL
) && (NewEntry
!= NULL
)) {
393 CopyMem(NewEntry
, Entry
, sizeof(REFIT_MENU_SCREEN
));
394 NewEntry
->Title
= (Entry
->Title
) ? StrDuplicate(Entry
->Title
) : NULL
;
395 NewEntry
->TimeoutText
= (Entry
->TimeoutText
) ? StrDuplicate(Entry
->TimeoutText
) : NULL
;
396 if (Entry
->TitleImage
!= NULL
) {
397 NewEntry
->TitleImage
= AllocatePool(sizeof(EG_IMAGE
));
398 if (NewEntry
->TitleImage
!= NULL
)
399 CopyMem(NewEntry
->TitleImage
, Entry
->TitleImage
, sizeof(EG_IMAGE
));
401 NewEntry
->InfoLines
= (CHAR16
**) AllocateZeroPool(Entry
->InfoLineCount
* (sizeof(CHAR16
*)));
402 for (i
= 0; i
< Entry
->InfoLineCount
&& NewEntry
->InfoLines
; i
++) {
403 NewEntry
->InfoLines
[i
] = (Entry
->InfoLines
[i
]) ? StrDuplicate(Entry
->InfoLines
[i
]) : NULL
;
405 NewEntry
->Entries
= (REFIT_MENU_ENTRY
**) AllocateZeroPool(Entry
->EntryCount
* (sizeof (REFIT_MENU_ENTRY
*)));
406 for (i
= 0; i
< Entry
->EntryCount
&& NewEntry
->Entries
; i
++) {
407 AddMenuEntry(NewEntry
, Entry
->Entries
[i
]);
409 NewEntry
->Hint1
= (Entry
->Hint1
) ? StrDuplicate(Entry
->Hint1
) : NULL
;
410 NewEntry
->Hint2
= (Entry
->Hint2
) ? StrDuplicate(Entry
->Hint2
) : NULL
;
413 } // static REFIT_MENU_SCREEN* CopyMenuScreen()
415 // Creates a copy of a menu entry. Intended to enable moving a stack-based
416 // menu entry (such as the ones for the "reboot" and "exit" functions) to
417 // to the heap. This enables easier deletion of the whole set of menu
418 // entries when re-scanning.
419 // Returns a pointer to the copy of the menu entry.
420 static REFIT_MENU_ENTRY
* CopyMenuEntry(REFIT_MENU_ENTRY
*Entry
) {
421 REFIT_MENU_ENTRY
*NewEntry
;
423 NewEntry
= AllocateZeroPool(sizeof(REFIT_MENU_ENTRY
));
424 if ((Entry
!= NULL
) && (NewEntry
!= NULL
)) {
425 CopyMem(NewEntry
, Entry
, sizeof(REFIT_MENU_ENTRY
));
426 NewEntry
->Title
= (Entry
->Title
) ? StrDuplicate(Entry
->Title
) : NULL
;
427 if (Entry
->BadgeImage
!= NULL
) {
428 NewEntry
->BadgeImage
= AllocatePool(sizeof(EG_IMAGE
));
429 if (NewEntry
->BadgeImage
!= NULL
)
430 CopyMem(NewEntry
->BadgeImage
, Entry
->BadgeImage
, sizeof(EG_IMAGE
));
432 if (Entry
->Image
!= NULL
) {
433 NewEntry
->Image
= AllocatePool(sizeof(EG_IMAGE
));
434 if (NewEntry
->Image
!= NULL
)
435 CopyMem(NewEntry
->Image
, Entry
->Image
, sizeof(EG_IMAGE
));
437 if (Entry
->SubScreen
!= NULL
) {
438 NewEntry
->SubScreen
= CopyMenuScreen(Entry
->SubScreen
);
442 } // REFIT_MENU_ENTRY* CopyMenuEntry()
444 // Creates a new LOADER_ENTRY data structure and populates it with
445 // default values from the specified Entry, or NULL values if Entry
446 // is unspecified (NULL).
447 // Returns a pointer to the new data structure, or NULL if it
448 // couldn't be allocated
449 LOADER_ENTRY
*InitializeLoaderEntry(IN LOADER_ENTRY
*Entry
) {
450 LOADER_ENTRY
*NewEntry
= NULL
;
452 NewEntry
= AllocateZeroPool(sizeof(LOADER_ENTRY
));
453 if (NewEntry
!= NULL
) {
454 NewEntry
->me
.Title
= NULL
;
455 NewEntry
->me
.Tag
= TAG_LOADER
;
456 NewEntry
->Enabled
= TRUE
;
457 NewEntry
->UseGraphicsMode
= FALSE
;
458 NewEntry
->OSType
= 0;
460 NewEntry
->LoaderPath
= (Entry
->LoaderPath
) ? StrDuplicate(Entry
->LoaderPath
) : NULL
;
461 NewEntry
->VolName
= (Entry
->VolName
) ? StrDuplicate(Entry
->VolName
) : NULL
;
462 NewEntry
->DevicePath
= Entry
->DevicePath
;
463 NewEntry
->UseGraphicsMode
= Entry
->UseGraphicsMode
;
464 NewEntry
->LoadOptions
= (Entry
->LoadOptions
) ? StrDuplicate(Entry
->LoadOptions
) : NULL
;
465 NewEntry
->InitrdPath
= (Entry
->InitrdPath
) ? StrDuplicate(Entry
->InitrdPath
) : NULL
;
469 } // LOADER_ENTRY *InitializeLoaderEntry()
471 // Adds InitrdPath to Options, but only if Options doesn't already include an
472 // initrd= line. Done to enable overriding the default initrd selection in a
473 // refind_linux.conf file's options list.
474 // Returns a pointer to a new string. The calling function is responsible for
475 // freeing its memory.
476 static CHAR16
*AddInitrdToOptions(CHAR16
*Options
, CHAR16
*InitrdPath
) {
477 CHAR16
*NewOptions
= NULL
;
480 NewOptions
= StrDuplicate(Options
);
481 if ((InitrdPath
!= NULL
) && !StriSubCmp(L
"initrd=", Options
)) {
482 MergeStrings(&NewOptions
, L
"initrd=", L
' ');
483 MergeStrings(&NewOptions
, InitrdPath
, 0);
486 } // CHAR16 *AddInitrdToOptions()
488 // Prepare a REFIT_MENU_SCREEN data structure for a subscreen entry. This sets up
489 // the default entry that launches the boot loader using the same options as the
490 // main Entry does. Subsequent options can be added by the calling function.
491 // If a subscreen already exists in the Entry that's passed to this function,
492 // it's left unchanged and a pointer to it is returned.
493 // Returns a pointer to the new subscreen data structure, or NULL if there
494 // were problems allocating memory.
495 REFIT_MENU_SCREEN
*InitializeSubScreen(IN LOADER_ENTRY
*Entry
) {
496 CHAR16
*FileName
, *MainOptions
= NULL
;
497 REFIT_MENU_SCREEN
*SubScreen
= NULL
;
498 LOADER_ENTRY
*SubEntry
;
500 FileName
= Basename(Entry
->LoaderPath
);
501 if (Entry
->me
.SubScreen
== NULL
) { // No subscreen yet; initialize default entry....
502 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
503 if (SubScreen
!= NULL
) {
504 SubScreen
->Title
= AllocateZeroPool(sizeof(CHAR16
) * 256);
505 SPrint(SubScreen
->Title
, 255, L
"Boot Options for %s on %s",
506 (Entry
->Title
!= NULL
) ? Entry
->Title
: FileName
, Entry
->VolName
);
507 SubScreen
->TitleImage
= Entry
->me
.Image
;
509 SubEntry
= InitializeLoaderEntry(Entry
);
510 if (SubEntry
!= NULL
) {
511 SubEntry
->me
.Title
= StrDuplicate(L
"Boot using default options");
512 MainOptions
= SubEntry
->LoadOptions
;
513 SubEntry
->LoadOptions
= AddInitrdToOptions(MainOptions
, SubEntry
->InitrdPath
);
514 MyFreePool(MainOptions
);
515 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
516 } // if (SubEntry != NULL)
517 SubScreen
->Hint1
= StrDuplicate(SUBSCREEN_HINT1
);
518 if (GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_EDITOR
) {
519 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2_NO_EDITOR
);
521 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2
);
523 } // if (SubScreen != NULL)
524 } else { // existing subscreen; less initialization, and just add new entry later....
525 SubScreen
= Entry
->me
.SubScreen
;
528 } // REFIT_MENU_SCREEN *InitializeSubScreen()
530 VOID
GenerateSubScreen(LOADER_ENTRY
*Entry
, IN REFIT_VOLUME
*Volume
) {
531 REFIT_MENU_SCREEN
*SubScreen
;
532 LOADER_ENTRY
*SubEntry
;
534 CHAR16 DiagsFileName
[256];
539 // create the submenu
540 if (StrLen(Entry
->Title
) == 0) {
541 MyFreePool(Entry
->Title
);
544 SubScreen
= InitializeSubScreen(Entry
);
546 // loader-specific submenu entries
547 if (Entry
->OSType
== 'M') { // entries for Mac OS X
549 SubEntry
= InitializeLoaderEntry(Entry
);
550 if (SubEntry
!= NULL
) {
551 SubEntry
->me
.Title
= L
"Boot Mac OS X with a 64-bit kernel";
552 SubEntry
->LoadOptions
= L
"arch=x86_64";
553 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
554 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
557 SubEntry
= InitializeLoaderEntry(Entry
);
558 if (SubEntry
!= NULL
) {
559 SubEntry
->me
.Title
= L
"Boot Mac OS X with a 32-bit kernel";
560 SubEntry
->LoadOptions
= L
"arch=i386";
561 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
562 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
566 if (!(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_SINGLEUSER
)) {
567 SubEntry
= InitializeLoaderEntry(Entry
);
568 if (SubEntry
!= NULL
) {
569 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode";
570 SubEntry
->UseGraphicsMode
= FALSE
;
571 SubEntry
->LoadOptions
= L
"-v";
572 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
576 SubEntry
= InitializeLoaderEntry(Entry
);
577 if (SubEntry
!= NULL
) {
578 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode (64-bit)";
579 SubEntry
->UseGraphicsMode
= FALSE
;
580 SubEntry
->LoadOptions
= L
"-v arch=x86_64";
581 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
584 SubEntry
= InitializeLoaderEntry(Entry
);
585 if (SubEntry
!= NULL
) {
586 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode (32-bit)";
587 SubEntry
->UseGraphicsMode
= FALSE
;
588 SubEntry
->LoadOptions
= L
"-v arch=i386";
589 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
593 SubEntry
= InitializeLoaderEntry(Entry
);
594 if (SubEntry
!= NULL
) {
595 SubEntry
->me
.Title
= L
"Boot Mac OS X in single user mode";
596 SubEntry
->UseGraphicsMode
= FALSE
;
597 SubEntry
->LoadOptions
= L
"-v -s";
598 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
600 } // single-user mode allowed
602 if (!(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_SAFEMODE
)) {
603 SubEntry
= InitializeLoaderEntry(Entry
);
604 if (SubEntry
!= NULL
) {
605 SubEntry
->me
.Title
= L
"Boot Mac OS X in safe mode";
606 SubEntry
->UseGraphicsMode
= FALSE
;
607 SubEntry
->LoadOptions
= L
"-v -x";
608 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
610 } // safe mode allowed
612 // check for Apple hardware diagnostics
613 StrCpy(DiagsFileName
, L
"System\\Library\\CoreServices\\.diagnostics\\diags.efi");
614 if (FileExists(Volume
->RootDir
, DiagsFileName
) && !(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_HWTEST
)) {
615 SubEntry
= InitializeLoaderEntry(Entry
);
616 if (SubEntry
!= NULL
) {
617 SubEntry
->me
.Title
= L
"Run Apple Hardware Test";
618 MyFreePool(SubEntry
->LoaderPath
);
619 SubEntry
->LoaderPath
= StrDuplicate(DiagsFileName
);
620 SubEntry
->DevicePath
= FileDevicePath(Volume
->DeviceHandle
, SubEntry
->LoaderPath
);
621 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
622 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
624 } // if diagnostics entry found
626 } else if (Entry
->OSType
== 'L') { // entries for Linux kernels with EFI stub loaders
627 File
= ReadLinuxOptionsFile(Entry
->LoaderPath
, Volume
);
629 InitrdName
= FindInitrd(Entry
->LoaderPath
, Volume
);
630 TokenCount
= ReadTokenLine(File
, &TokenList
);
631 // first entry requires special processing, since it was initially set
632 // up with a default title but correct options by InitializeSubScreen(),
634 if ((SubScreen
->Entries
!= NULL
) && (SubScreen
->Entries
[0] != NULL
)) {
635 MyFreePool(SubScreen
->Entries
[0]->Title
);
636 SubScreen
->Entries
[0]->Title
= TokenList
[0] ? StrDuplicate(TokenList
[0]) : StrDuplicate(L
"Boot Linux");
638 FreeTokenLine(&TokenList
, &TokenCount
);
639 while ((TokenCount
= ReadTokenLine(File
, &TokenList
)) > 1) {
640 SubEntry
= InitializeLoaderEntry(Entry
);
641 SubEntry
->me
.Title
= TokenList
[0] ? StrDuplicate(TokenList
[0]) : StrDuplicate(L
"Boot Linux");
642 MyFreePool(SubEntry
->LoadOptions
);
643 SubEntry
->LoadOptions
= AddInitrdToOptions(TokenList
[1], InitrdName
);
644 FreeTokenLine(&TokenList
, &TokenCount
);
645 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_LINUX
;
646 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
648 MyFreePool(InitrdName
);
650 } // if Linux options file exists
652 } else if (Entry
->OSType
== 'E') { // entries for ELILO
653 SubEntry
= InitializeLoaderEntry(Entry
);
654 if (SubEntry
!= NULL
) {
655 SubEntry
->me
.Title
= L
"Run ELILO in interactive mode";
656 SubEntry
->LoadOptions
= L
"-p";
657 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
658 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
661 SubEntry
= InitializeLoaderEntry(Entry
);
662 if (SubEntry
!= NULL
) {
663 SubEntry
->me
.Title
= L
"Boot Linux for a 17\" iMac or a 15\" MacBook Pro (*)";
664 SubEntry
->UseGraphicsMode
= TRUE
;
665 SubEntry
->LoadOptions
= L
"-d 0 i17";
666 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
667 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
670 SubEntry
= InitializeLoaderEntry(Entry
);
671 if (SubEntry
!= NULL
) {
672 SubEntry
->me
.Title
= L
"Boot Linux for a 20\" iMac (*)";
673 SubEntry
->UseGraphicsMode
= TRUE
;
674 SubEntry
->LoadOptions
= L
"-d 0 i20";
675 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
676 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
679 SubEntry
= InitializeLoaderEntry(Entry
);
680 if (SubEntry
!= NULL
) {
681 SubEntry
->me
.Title
= L
"Boot Linux for a Mac Mini (*)";
682 SubEntry
->UseGraphicsMode
= TRUE
;
683 SubEntry
->LoadOptions
= L
"-d 0 mini";
684 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
685 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
688 AddMenuInfoLine(SubScreen
, L
"NOTE: This is an example. Entries");
689 AddMenuInfoLine(SubScreen
, L
"marked with (*) may not work.");
691 } else if (Entry
->OSType
== 'X') { // entries for xom.efi
692 // by default, skip the built-in selection and boot from hard disk only
693 Entry
->LoadOptions
= L
"-s -h";
695 SubEntry
= InitializeLoaderEntry(Entry
);
696 if (SubEntry
!= NULL
) {
697 SubEntry
->me
.Title
= L
"Boot Windows from Hard Disk";
698 SubEntry
->LoadOptions
= L
"-s -h";
699 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
700 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
703 SubEntry
= InitializeLoaderEntry(Entry
);
704 if (SubEntry
!= NULL
) {
705 SubEntry
->me
.Title
= L
"Boot Windows from CD-ROM";
706 SubEntry
->LoadOptions
= L
"-s -c";
707 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
708 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
711 SubEntry
= InitializeLoaderEntry(Entry
);
712 if (SubEntry
!= NULL
) {
713 SubEntry
->me
.Title
= L
"Run XOM in text mode";
714 SubEntry
->UseGraphicsMode
= FALSE
;
715 SubEntry
->LoadOptions
= L
"-v";
716 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
717 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
719 } // entries for xom.efi
720 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
721 Entry
->me
.SubScreen
= SubScreen
;
722 } // VOID GenerateSubScreen()
724 // Returns options for a Linux kernel. Reads them from an options file in the
725 // kernel's directory; and if present, adds an initrd= option for an initial
726 // RAM disk file with the same version number as the kernel file.
727 static CHAR16
* GetMainLinuxOptions(IN CHAR16
* LoaderPath
, IN REFIT_VOLUME
*Volume
) {
728 CHAR16
*Options
= NULL
, *InitrdName
, *FullOptions
= NULL
;
730 Options
= GetFirstOptionsFromFile(LoaderPath
, Volume
);
731 InitrdName
= FindInitrd(LoaderPath
, Volume
);
732 FullOptions
= AddInitrdToOptions(Options
, InitrdName
);
735 MyFreePool(InitrdName
);
736 return (FullOptions
);
737 } // static CHAR16 * GetMainLinuxOptions()
739 // Sets a few defaults for a loader entry -- mainly the icon, but also the OS type
740 // code and shortcut letter. For Linux EFI stub loaders, also sets kernel options
741 // that will (with luck) work fairly automatically.
742 VOID
SetLoaderDefaults(LOADER_ENTRY
*Entry
, CHAR16
*LoaderPath
, REFIT_VOLUME
*Volume
) {
743 CHAR16
*FileName
, *PathOnly
, *NoExtension
, *OSIconName
= NULL
, *Temp
, *SubString
;
744 CHAR16 ShortcutLetter
= 0;
747 FileName
= Basename(LoaderPath
);
748 PathOnly
= FindPath(LoaderPath
);
749 NoExtension
= StripEfiExtension(FileName
);
751 // locate a custom icon for the loader
752 // Anything found here takes precedence over the "hints" in the OSIconName variable
753 if (!Entry
->me
.Image
)
754 Entry
->me
.Image
= egFindIcon(NoExtension
, 128);
755 if (!Entry
->me
.Image
)
756 Entry
->me
.Image
= Volume
->VolIconImage
;
758 // Begin creating icon "hints" by using last part of directory path leading
760 Temp
= FindLastDirName(LoaderPath
);
761 MergeStrings(&OSIconName
, Temp
, L
',');
764 if (OSIconName
!= NULL
) {
765 ShortcutLetter
= OSIconName
[0];
768 // Add every "word" in the volume label, delimited by spaces, dashes (-), or
769 // underscores (_), to the list of hints to be used in searching for OS
771 if ((Volume
->VolName
) && (StrLen(Volume
->VolName
) > 0)) {
772 Temp
= SubString
= StrDuplicate(Volume
->VolName
);
774 Length
= StrLen(Temp
);
775 for (i
= 0; i
< Length
; i
++) {
776 if ((Temp
[i
] == L
' ') || (Temp
[i
] == L
'_') || (Temp
[i
] == L
'-')) {
778 if (StrLen(SubString
) > 0)
779 MergeStrings(&OSIconName
, SubString
, L
',');
780 SubString
= Temp
+ i
+ 1;
783 MergeStrings(&OSIconName
, SubString
, L
',');
788 // detect specific loaders
789 if (StriSubCmp(L
"bzImage", LoaderPath
) || StriSubCmp(L
"vmlinuz", LoaderPath
)) {
790 MergeStrings(&OSIconName
, L
"linux", L
',');
792 if (ShortcutLetter
== 0)
793 ShortcutLetter
= 'L';
794 Entry
->LoadOptions
= GetMainLinuxOptions(LoaderPath
, Volume
);
795 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_LINUX
;
796 } else if (StriSubCmp(L
"refit", LoaderPath
)) {
797 MergeStrings(&OSIconName
, L
"refit", L
',');
799 ShortcutLetter
= 'R';
800 } else if (StriCmp(LoaderPath
, MACOSX_LOADER_PATH
) == 0) {
801 if (Volume
->VolIconImage
!= NULL
) { // custom icon file found
802 Entry
->me
.Image
= Volume
->VolIconImage
;
804 MergeStrings(&OSIconName
, L
"mac", L
',');
806 ShortcutLetter
= 'M';
807 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
808 } else if (StriCmp(FileName
, L
"diags.efi") == 0) {
809 MergeStrings(&OSIconName
, L
"hwtest", L
',');
810 } else if (StriCmp(FileName
, L
"e.efi") == 0 || StriCmp(FileName
, L
"elilo.efi") == 0 || StriSubCmp(L
"elilo", FileName
)) {
811 MergeStrings(&OSIconName
, L
"elilo,linux", L
',');
813 if (ShortcutLetter
== 0)
814 ShortcutLetter
= 'L';
815 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
816 } else if (StriSubCmp(L
"grub", FileName
)) {
818 ShortcutLetter
= 'G';
819 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_GRUB
;
820 } else if (StriCmp(FileName
, L
"cdboot.efi") == 0 ||
821 StriCmp(FileName
, L
"bootmgr.efi") == 0 ||
822 StriCmp(FileName
, L
"bootmgfw.efi") == 0) {
823 MergeStrings(&OSIconName
, L
"win", L
',');
825 ShortcutLetter
= 'W';
826 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
827 } else if (StriCmp(FileName
, L
"xom.efi") == 0) {
828 MergeStrings(&OSIconName
, L
"xom,win", L
',');
829 Entry
->UseGraphicsMode
= TRUE
;
831 ShortcutLetter
= 'W';
832 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
835 if ((ShortcutLetter
>= 'a') && (ShortcutLetter
<= 'z'))
836 ShortcutLetter
= ShortcutLetter
- 'a' + 'A'; // convert lowercase to uppercase
837 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
838 if (Entry
->me
.Image
== NULL
)
839 Entry
->me
.Image
= LoadOSIcon(OSIconName
, L
"unknown", FALSE
);
840 MyFreePool(PathOnly
);
841 } // VOID SetLoaderDefaults()
843 // Add a specified EFI boot loader to the list, using automatic settings
844 // for icons, options, etc.
845 LOADER_ENTRY
* AddLoaderEntry(IN CHAR16
*LoaderPath
, IN CHAR16
*LoaderTitle
, IN REFIT_VOLUME
*Volume
) {
848 CleanUpPathNameSlashes(LoaderPath
);
849 Entry
= InitializeLoaderEntry(NULL
);
851 Entry
->Title
= StrDuplicate((LoaderTitle
!= NULL
) ? LoaderTitle
: LoaderPath
);
852 Entry
->me
.Title
= AllocateZeroPool(sizeof(CHAR16
) * 256);
853 SPrint(Entry
->me
.Title
, 255, L
"Boot %s from %s", (LoaderTitle
!= NULL
) ? LoaderTitle
: LoaderPath
, Volume
->VolName
);
855 Entry
->me
.BadgeImage
= Volume
->VolBadgeImage
;
856 if ((LoaderPath
!= NULL
) && (LoaderPath
[0] != L
'\\')) {
857 Entry
->LoaderPath
= StrDuplicate(L
"\\");
859 Entry
->LoaderPath
= NULL
;
861 MergeStrings(&(Entry
->LoaderPath
), LoaderPath
, 0);
862 Entry
->VolName
= Volume
->VolName
;
863 Entry
->DevicePath
= FileDevicePath(Volume
->DeviceHandle
, Entry
->LoaderPath
);
864 SetLoaderDefaults(Entry
, LoaderPath
, Volume
);
865 GenerateSubScreen(Entry
, Volume
);
866 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
870 } // LOADER_ENTRY * AddLoaderEntry()
872 // Returns -1 if (Time1 < Time2), +1 if (Time1 > Time2), or 0 if
873 // (Time1 == Time2). Precision is only to the nearest second; since
874 // this is used for sorting boot loader entries, differences smaller
875 // than this are likely to be meaningless (and unlikely!).
876 INTN
TimeComp(IN EFI_TIME
*Time1
, IN EFI_TIME
*Time2
) {
877 INT64 Time1InSeconds
, Time2InSeconds
;
879 // Following values are overestimates; I'm assuming 31 days in every month.
880 // This is fine for the purpose of this function, which is limited
881 Time1InSeconds
= Time1
->Second
+ (Time1
->Minute
* 60) + (Time1
->Hour
* 3600) + (Time1
->Day
* 86400) +
882 (Time1
->Month
* 2678400) + ((Time1
->Year
- 1998) * 32140800);
883 Time2InSeconds
= Time2
->Second
+ (Time2
->Minute
* 60) + (Time2
->Hour
* 3600) + (Time2
->Day
* 86400) +
884 (Time2
->Month
* 2678400) + ((Time2
->Year
- 1998) * 32140800);
885 if (Time1InSeconds
< Time2InSeconds
)
887 else if (Time1InSeconds
> Time2InSeconds
)
893 // Adds a loader list element, keeping it sorted by date. Returns the new
894 // first element (the one with the most recent date).
895 static struct LOADER_LIST
* AddLoaderListEntry(struct LOADER_LIST
*LoaderList
, struct LOADER_LIST
*NewEntry
) {
896 struct LOADER_LIST
*LatestEntry
, *CurrentEntry
, *PrevEntry
= NULL
;
898 LatestEntry
= CurrentEntry
= LoaderList
;
899 if (LoaderList
== NULL
) {
900 LatestEntry
= NewEntry
;
902 while ((CurrentEntry
!= NULL
) && (TimeComp(&(NewEntry
->TimeStamp
), &(CurrentEntry
->TimeStamp
)) < 0)) {
903 PrevEntry
= CurrentEntry
;
904 CurrentEntry
= CurrentEntry
->NextEntry
;
906 NewEntry
->NextEntry
= CurrentEntry
;
907 if (PrevEntry
== NULL
) {
908 LatestEntry
= NewEntry
;
910 PrevEntry
->NextEntry
= NewEntry
;
913 return (LatestEntry
);
914 } // static VOID AddLoaderListEntry()
916 // Delete the LOADER_LIST linked list
917 static VOID
CleanUpLoaderList(struct LOADER_LIST
*LoaderList
) {
918 struct LOADER_LIST
*Temp
;
920 while (LoaderList
!= NULL
) {
922 LoaderList
= LoaderList
->NextEntry
;
923 MyFreePool(Temp
->FileName
);
926 } // static VOID CleanUpLoaderList()
928 // Returns FALSE if the specified file/volume matches the GlobalConfig.DontScanDirs
929 // or GlobalConfig.DontScanVolumes specification, or if Path points to a volume
930 // other than the one specified by Volume, or if the specified path is SelfDir.
931 // Returns TRUE if none of these conditions is met -- that is, if the path is
932 // eligible for scanning.
933 static BOOLEAN
ShouldScan(REFIT_VOLUME
*Volume
, CHAR16
*Path
) {
934 CHAR16
*VolName
= NULL
, *DontScanDir
;
936 BOOLEAN ScanIt
= TRUE
;
938 if (IsIn(Volume
->VolName
, GlobalConfig
.DontScanVolumes
))
941 if ((StriCmp(Path
, SelfDirPath
) == 0) && (Volume
->DeviceHandle
== SelfVolume
->DeviceHandle
))
944 while ((DontScanDir
= FindCommaDelimited(GlobalConfig
.DontScanDirs
, i
++)) && ScanIt
) {
945 SplitVolumeAndFilename(&DontScanDir
, &VolName
);
946 CleanUpPathNameSlashes(DontScanDir
);
947 if (VolName
!= NULL
) {
948 if ((StriCmp(VolName
, Volume
->VolName
) == 0) && (StriCmp(DontScanDir
, Path
) == 0))
950 if ((StrLen(VolName
) > 2) && (VolName
[0] == L
'f') && (VolName
[1] == L
's') && (VolName
[2] >= L
'0') && (VolName
[2] <= L
'9')) {
951 VolNum
= Atoi(VolName
+ 2);
952 if ((VolNum
== Volume
->VolNumber
) && (StriCmp(DontScanDir
, Path
) == 0))
956 if (StriCmp(DontScanDir
, Path
) == 0)
959 MyFreePool(DontScanDir
);
963 } // BOOLEAN ShouldScan()
965 // Returns TRUE if the file is byte-for-byte identical with the fallback file
966 // on the volume AND if the file is not itself the fallback file; returns
967 // FALSE if the file is not identical to the fallback file OR if the file
968 // IS the fallback file. Intended for use in excluding the fallback boot
969 // loader when it's a duplicate of another boot loader.
970 static BOOLEAN
DuplicatesFallback(IN REFIT_VOLUME
*Volume
, IN CHAR16
*FileName
) {
971 CHAR8
*FileContents
, *FallbackContents
;
972 EFI_FILE_HANDLE FileHandle
, FallbackHandle
;
973 EFI_FILE_INFO
*FileInfo
, *FallbackInfo
;
974 UINTN FileSize
= 0, FallbackSize
= 0;
976 BOOLEAN AreIdentical
= FALSE
;
978 CleanUpPathNameSlashes(FileName
);
980 if (StriCmp(FileName
, FALLBACK_FULLNAME
) == 0)
981 return FALSE
; // identical filenames, so not a duplicate....
983 Status
= refit_call5_wrapper(Volume
->RootDir
->Open
, Volume
->RootDir
, &FileHandle
, FileName
, EFI_FILE_MODE_READ
, 0);
984 if (Status
== EFI_SUCCESS
) {
985 FileInfo
= LibFileInfo(FileHandle
);
986 FileSize
= FileInfo
->FileSize
;
991 Status
= refit_call5_wrapper(Volume
->RootDir
->Open
, Volume
->RootDir
, &FallbackHandle
, FALLBACK_FULLNAME
, EFI_FILE_MODE_READ
, 0);
992 if (Status
== EFI_SUCCESS
) {
993 FallbackInfo
= LibFileInfo(FallbackHandle
);
994 FallbackSize
= FallbackInfo
->FileSize
;
996 refit_call1_wrapper(FileHandle
->Close
, FileHandle
);
1000 if (FallbackSize
!= FileSize
) { // not same size, so can't be identical
1001 AreIdentical
= FALSE
;
1002 } else { // could be identical; do full check....
1003 FileContents
= AllocatePool(FileSize
);
1004 FallbackContents
= AllocatePool(FallbackSize
);
1005 if (FileContents
&& FallbackContents
) {
1006 Status
= refit_call3_wrapper(FileHandle
->Read
, FileHandle
, &FileSize
, FileContents
);
1007 if (Status
== EFI_SUCCESS
)
1008 Status
= refit_call3_wrapper(FallbackHandle
->Read
, FallbackHandle
, &FallbackSize
, FallbackContents
);
1009 if (Status
== EFI_SUCCESS
) {
1010 AreIdentical
= (CompareMem(FileContents
, FallbackContents
, FileSize
) == 0);
1013 MyFreePool(FileContents
);
1014 MyFreePool(FallbackContents
);
1017 refit_call1_wrapper(FileHandle
->Close
, FileHandle
);
1018 refit_call1_wrapper(FileHandle
->Close
, FallbackHandle
);
1019 return AreIdentical
;
1021 } // BOOLEAN DuplicatesFallback()
1023 // Scan an individual directory for EFI boot loader files and, if found,
1024 // add them to the list. Exception: Ignores FALLBACK_FULLNAME, which is picked
1025 // up in ScanEfiFiles(). Sorts the entries within the loader directory so that
1026 // the most recent one appears first in the list.
1027 // Returns TRUE if a duplicate for FALLBACK_FILENAME was found, FALSE if not.
1028 static BOOLEAN
ScanLoaderDir(IN REFIT_VOLUME
*Volume
, IN CHAR16
*Path
, IN CHAR16
*Pattern
)
1031 REFIT_DIR_ITER DirIter
;
1032 EFI_FILE_INFO
*DirEntry
;
1033 CHAR16 FileName
[256], *Extension
;
1034 struct LOADER_LIST
*LoaderList
= NULL
, *NewLoader
;
1035 BOOLEAN FoundFallbackDuplicate
= FALSE
;
1037 if ((!SelfDirPath
|| !Path
|| ((StriCmp(Path
, SelfDirPath
) == 0) && (Volume
->DeviceHandle
!= SelfVolume
->DeviceHandle
)) ||
1038 (StriCmp(Path
, SelfDirPath
) != 0)) &&
1039 (ShouldScan(Volume
, Path
))) {
1040 // look through contents of the directory
1041 DirIterOpen(Volume
->RootDir
, Path
, &DirIter
);
1042 while (DirIterNext(&DirIter
, 2, Pattern
, &DirEntry
)) {
1043 Extension
= FindExtension(DirEntry
->FileName
);
1044 if (DirEntry
->FileName
[0] == '.' ||
1045 StriCmp(Extension
, L
".icns") == 0 ||
1046 StriCmp(Extension
, L
".png") == 0 ||
1047 (StriCmp(DirEntry
->FileName
, FALLBACK_BASENAME
) == 0 && (StriCmp(Path
, L
"EFI\\BOOT") == 0)) ||
1048 StriSubCmp(L
"shell", DirEntry
->FileName
) ||
1049 IsIn(DirEntry
->FileName
, GlobalConfig
.DontScanFiles
))
1050 continue; // skip this
1053 SPrint(FileName
, 255, L
"\\%s\\%s", Path
, DirEntry
->FileName
);
1055 SPrint(FileName
, 255, L
"\\%s", DirEntry
->FileName
);
1056 CleanUpPathNameSlashes(FileName
);
1057 NewLoader
= AllocateZeroPool(sizeof(struct LOADER_LIST
));
1058 if (NewLoader
!= NULL
) {
1059 NewLoader
->FileName
= StrDuplicate(FileName
);
1060 NewLoader
->TimeStamp
= DirEntry
->ModificationTime
;
1061 LoaderList
= AddLoaderListEntry(LoaderList
, NewLoader
);
1062 if (DuplicatesFallback(Volume
, FileName
))
1063 FoundFallbackDuplicate
= TRUE
;
1065 MyFreePool(Extension
);
1067 NewLoader
= LoaderList
;
1068 while (NewLoader
!= NULL
) {
1069 AddLoaderEntry(NewLoader
->FileName
, NULL
, Volume
);
1070 NewLoader
= NewLoader
->NextEntry
;
1072 CleanUpLoaderList(LoaderList
);
1073 Status
= DirIterClose(&DirIter
);
1074 if (Status
!= EFI_NOT_FOUND
) {
1076 SPrint(FileName
, 255, L
"while scanning the %s directory", Path
);
1078 StrCpy(FileName
, L
"while scanning the root directory");
1079 CheckError(Status
, FileName
);
1080 } // if (Status != EFI_NOT_FOUND)
1081 } // if not scanning our own directory
1083 return FoundFallbackDuplicate
;
1084 } /* static VOID ScanLoaderDir() */
1086 static VOID
ScanEfiFiles(REFIT_VOLUME
*Volume
) {
1088 REFIT_DIR_ITER EfiDirIter
;
1089 EFI_FILE_INFO
*EfiDirEntry
;
1090 CHAR16 FileName
[256], *Directory
, *MatchPatterns
, *VolName
= NULL
;
1092 BOOLEAN ScanFallbackLoader
= TRUE
;
1094 // Print(L"Entering ScanEfiFiles(), GlobalConfig.ScanAllLinux = %s\n", GlobalConfig.ScanAllLinux ? L"TRUE" : L"FALSE");
1095 MatchPatterns
= StrDuplicate(LOADER_MATCH_PATTERNS
);
1096 if (GlobalConfig
.ScanAllLinux
)
1097 MergeStrings(&MatchPatterns
, LINUX_MATCH_PATTERNS
, L
',');
1098 // Print(L"MatchPatterns = '%s'\n", MatchPatterns);
1100 if ((Volume
->RootDir
!= NULL
) && (Volume
->VolName
!= NULL
)) {
1101 // check for Mac OS X boot loader
1102 if (ShouldScan(Volume
, L
"System\\Library\\CoreServices")) {
1103 StrCpy(FileName
, MACOSX_LOADER_PATH
);
1104 if (FileExists(Volume
->RootDir
, FileName
) && !IsIn(L
"boot.efi", GlobalConfig
.DontScanFiles
)) {
1105 AddLoaderEntry(FileName
, L
"Mac OS X", Volume
);
1106 if (DuplicatesFallback(Volume
, FileName
))
1107 ScanFallbackLoader
= FALSE
;
1111 StrCpy(FileName
, L
"System\\Library\\CoreServices\\xom.efi");
1112 if (FileExists(Volume
->RootDir
, FileName
) && !IsIn(L
"boot.efi", GlobalConfig
.DontScanFiles
)) {
1113 AddLoaderEntry(FileName
, L
"Windows XP (XoM)", Volume
);
1114 if (DuplicatesFallback(Volume
, FileName
))
1115 ScanFallbackLoader
= FALSE
;
1117 } // if should scan Mac directory
1119 // check for Microsoft boot loader/menu
1120 StrCpy(FileName
, L
"EFI\\Microsoft\\Boot\\Bootmgfw.efi");
1121 if (FileExists(Volume
->RootDir
, FileName
) && ShouldScan(Volume
, L
"EFI\\Microsoft\\Boot") &&
1122 !IsIn(L
"bootmgfw.efi", GlobalConfig
.DontScanFiles
)) {
1123 AddLoaderEntry(FileName
, L
"Microsoft EFI boot", Volume
);
1124 if (DuplicatesFallback(Volume
, FileName
))
1125 ScanFallbackLoader
= FALSE
;
1128 // scan the root directory for EFI executables
1129 if (ScanLoaderDir(Volume
, L
"\\", MatchPatterns
))
1130 ScanFallbackLoader
= FALSE
;
1132 // scan subdirectories of the EFI directory (as per the standard)
1133 DirIterOpen(Volume
->RootDir
, L
"EFI", &EfiDirIter
);
1134 while (DirIterNext(&EfiDirIter
, 1, NULL
, &EfiDirEntry
)) {
1135 if (StriCmp(EfiDirEntry
->FileName
, L
"tools") == 0 || EfiDirEntry
->FileName
[0] == '.')
1136 continue; // skip this, doesn't contain boot loaders or is scanned later
1137 SPrint(FileName
, 255, L
"EFI\\%s", EfiDirEntry
->FileName
);
1138 if (ScanLoaderDir(Volume
, FileName
, MatchPatterns
))
1139 ScanFallbackLoader
= FALSE
;
1141 Status
= DirIterClose(&EfiDirIter
);
1142 if (Status
!= EFI_NOT_FOUND
)
1143 CheckError(Status
, L
"while scanning the EFI directory");
1145 // Scan user-specified (or additional default) directories....
1147 while ((Directory
= FindCommaDelimited(GlobalConfig
.AlsoScan
, i
++)) != NULL
) {
1148 SplitVolumeAndFilename(&Directory
, &VolName
);
1149 CleanUpPathNameSlashes(Directory
);
1150 Length
= StrLen(Directory
);
1151 if ((Length
> 0) && ScanLoaderDir(Volume
, Directory
, MatchPatterns
))
1152 ScanFallbackLoader
= FALSE
;
1153 MyFreePool(Directory
);
1154 MyFreePool(VolName
);
1157 // If not a duplicate & if it exists & if it's not us, create an entry
1158 // for the fallback boot loader
1159 if (ScanFallbackLoader
&& FileExists(Volume
->RootDir
, FALLBACK_FULLNAME
) && ShouldScan(Volume
, L
"EFI\\BOOT")) {
1160 AddLoaderEntry(FALLBACK_FULLNAME
, L
"Fallback boot loader", Volume
);
1163 } // static VOID ScanEfiFiles()
1165 // Scan internal disks for valid EFI boot loaders....
1166 static VOID
ScanInternal(VOID
) {
1169 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1170 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_INTERNAL
) {
1171 ScanEfiFiles(Volumes
[VolumeIndex
]);
1174 } // static VOID ScanInternal()
1176 // Scan external disks for valid EFI boot loaders....
1177 static VOID
ScanExternal(VOID
) {
1180 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1181 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_EXTERNAL
) {
1182 ScanEfiFiles(Volumes
[VolumeIndex
]);
1185 } // static VOID ScanExternal()
1187 // Scan internal disks for valid EFI boot loaders....
1188 static VOID
ScanOptical(VOID
) {
1191 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1192 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_OPTICAL
) {
1193 ScanEfiFiles(Volumes
[VolumeIndex
]);
1196 } // static VOID ScanOptical()
1199 // legacy boot functions
1202 static EFI_STATUS
ActivateMbrPartition(IN EFI_BLOCK_IO
*BlockIO
, IN UINTN PartitionIndex
)
1205 UINT8 SectorBuffer
[512];
1206 MBR_PARTITION_INFO
*MbrTable
, *EMbrTable
;
1207 UINT32 ExtBase
, ExtCurrent
, NextExtCurrent
;
1208 UINTN LogicalPartitionIndex
= 4;
1210 BOOLEAN HaveBootCode
;
1213 Status
= refit_call5_wrapper(BlockIO
->ReadBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, 0, 512, SectorBuffer
);
1214 if (EFI_ERROR(Status
))
1216 if (*((UINT16
*)(SectorBuffer
+ 510)) != 0xaa55)
1217 return EFI_NOT_FOUND
; // safety measure #1
1219 // add boot code if necessary
1220 HaveBootCode
= FALSE
;
1221 for (i
= 0; i
< MBR_BOOTCODE_SIZE
; i
++) {
1222 if (SectorBuffer
[i
] != 0) {
1223 HaveBootCode
= TRUE
;
1227 if (!HaveBootCode
) {
1228 // no boot code found in the MBR, add the syslinux MBR code
1229 SetMem(SectorBuffer
, MBR_BOOTCODE_SIZE
, 0);
1230 CopyMem(SectorBuffer
, syslinux_mbr
, SYSLINUX_MBR_SIZE
);
1233 // set the partition active
1234 MbrTable
= (MBR_PARTITION_INFO
*)(SectorBuffer
+ 446);
1236 for (i
= 0; i
< 4; i
++) {
1237 if (MbrTable
[i
].Flags
!= 0x00 && MbrTable
[i
].Flags
!= 0x80)
1238 return EFI_NOT_FOUND
; // safety measure #2
1239 if (i
== PartitionIndex
)
1240 MbrTable
[i
].Flags
= 0x80;
1241 else if (PartitionIndex
>= 4 && IS_EXTENDED_PART_TYPE(MbrTable
[i
].Type
)) {
1242 MbrTable
[i
].Flags
= 0x80;
1243 ExtBase
= MbrTable
[i
].StartLBA
;
1245 MbrTable
[i
].Flags
= 0x00;
1249 Status
= refit_call5_wrapper(BlockIO
->WriteBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, 0, 512, SectorBuffer
);
1250 if (EFI_ERROR(Status
))
1253 if (PartitionIndex
>= 4) {
1254 // we have to activate a logical partition, so walk the EMBR chain
1256 // NOTE: ExtBase was set above while looking at the MBR table
1257 for (ExtCurrent
= ExtBase
; ExtCurrent
; ExtCurrent
= NextExtCurrent
) {
1258 // read current EMBR
1259 Status
= refit_call5_wrapper(BlockIO
->ReadBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, ExtCurrent
, 512, SectorBuffer
);
1260 if (EFI_ERROR(Status
))
1262 if (*((UINT16
*)(SectorBuffer
+ 510)) != 0xaa55)
1263 return EFI_NOT_FOUND
; // safety measure #3
1265 // scan EMBR, set appropriate partition active
1266 EMbrTable
= (MBR_PARTITION_INFO
*)(SectorBuffer
+ 446);
1268 for (i
= 0; i
< 4; i
++) {
1269 if (EMbrTable
[i
].Flags
!= 0x00 && EMbrTable
[i
].Flags
!= 0x80)
1270 return EFI_NOT_FOUND
; // safety measure #4
1271 if (EMbrTable
[i
].StartLBA
== 0 || EMbrTable
[i
].Size
== 0)
1273 if (IS_EXTENDED_PART_TYPE(EMbrTable
[i
].Type
)) {
1274 // link to next EMBR
1275 NextExtCurrent
= ExtBase
+ EMbrTable
[i
].StartLBA
;
1276 EMbrTable
[i
].Flags
= (PartitionIndex
>= LogicalPartitionIndex
) ? 0x80 : 0x00;
1279 // logical partition
1280 EMbrTable
[i
].Flags
= (PartitionIndex
== LogicalPartitionIndex
) ? 0x80 : 0x00;
1281 LogicalPartitionIndex
++;
1285 // write current EMBR
1286 Status
= refit_call5_wrapper(BlockIO
->WriteBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, ExtCurrent
, 512, SectorBuffer
);
1287 if (EFI_ERROR(Status
))
1290 if (PartitionIndex
< LogicalPartitionIndex
)
1291 break; // stop the loop, no need to touch further EMBRs
1297 } /* static EFI_STATUS ActivateMbrPartition() */
1299 // early 2006 Core Duo / Core Solo models
1300 static UINT8 LegacyLoaderDevicePath1Data
[] = {
1301 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1302 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1303 0xFF, 0xFF, 0xF9, 0xFF, 0x00, 0x00, 0x00, 0x00,
1304 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1305 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1306 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1308 // mid-2006 Mac Pro (and probably other Core 2 models)
1309 static UINT8 LegacyLoaderDevicePath2Data
[] = {
1310 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1311 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1312 0xFF, 0xFF, 0xF7, 0xFF, 0x00, 0x00, 0x00, 0x00,
1313 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1314 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1315 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1317 // mid-2007 MBP ("Santa Rosa" based models)
1318 static UINT8 LegacyLoaderDevicePath3Data
[] = {
1319 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1320 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1321 0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
1322 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1323 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1324 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1327 static UINT8 LegacyLoaderDevicePath4Data
[] = {
1328 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1329 0x00, 0x00, 0xC0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1330 0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
1331 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1332 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1333 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1335 // late-2008 MB/MBP (NVidia chipset)
1336 static UINT8 LegacyLoaderDevicePath5Data
[] = {
1337 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1338 0x00, 0x40, 0xCB, 0xFF, 0x00, 0x00, 0x00, 0x00,
1339 0xFF, 0xBF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
1340 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1341 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1342 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1345 static EFI_DEVICE_PATH
*LegacyLoaderList
[] = {
1346 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath1Data
,
1347 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath2Data
,
1348 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath3Data
,
1349 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath4Data
,
1350 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath5Data
,
1354 #define MAX_DISCOVERED_PATHS (16)
1356 static VOID
StartLegacy(IN LEGACY_ENTRY
*Entry
)
1359 EG_IMAGE
*BootLogoImage
;
1360 UINTN ErrorInStep
= 0;
1361 EFI_DEVICE_PATH
*DiscoveredPathList
[MAX_DISCOVERED_PATHS
];
1363 BeginExternalScreen(TRUE
, L
"Booting Legacy OS (Mac mode)");
1365 BootLogoImage
= LoadOSIcon(Entry
->Volume
->OSIconName
, L
"legacy", TRUE
);
1366 if (BootLogoImage
!= NULL
)
1367 BltImageAlpha(BootLogoImage
,
1368 (UGAWidth
- BootLogoImage
->Width
) >> 1,
1369 (UGAHeight
- BootLogoImage
->Height
) >> 1,
1370 &StdBackgroundPixel
);
1372 if (Entry
->Volume
->IsMbrPartition
) {
1373 ActivateMbrPartition(Entry
->Volume
->WholeDiskBlockIO
, Entry
->Volume
->MbrPartitionIndex
);
1376 ExtractLegacyLoaderPaths(DiscoveredPathList
, MAX_DISCOVERED_PATHS
, LegacyLoaderList
);
1378 Status
= StartEFIImageList(DiscoveredPathList
, Entry
->LoadOptions
, NULL
, L
"legacy loader", 0, &ErrorInStep
, TRUE
);
1379 if (Status
== EFI_NOT_FOUND
) {
1380 if (ErrorInStep
== 1) {
1381 Print(L
"\nPlease make sure that you have the latest firmware update installed.\n");
1382 } else if (ErrorInStep
== 3) {
1383 Print(L
"\nThe firmware refused to boot from the selected volume. Note that external\n"
1384 L
"hard drives are not well-supported by Apple's firmware for legacy OS booting.\n");
1387 FinishExternalScreen();
1388 } /* static VOID StartLegacy() */
1390 // Start a device on a non-Mac using the EFI_LEGACY_BIOS_PROTOCOL
1391 #ifdef __MAKEWITH_TIANO
1392 static VOID
StartLegacyUEFI(IN LEGACY_ENTRY
*Entry
)
1394 BeginExternalScreen(TRUE
, L
"Booting Legacy OS (UEFI mode)");
1396 BdsLibConnectDevicePath (Entry
->BdsOption
->DevicePath
);
1397 BdsLibDoLegacyBoot(Entry
->BdsOption
);
1399 // If we get here, it means that there was a failure....
1400 Print(L
"Failure booting legacy (BIOS) OS.");
1402 FinishExternalScreen();
1403 } // static VOID StartLegacyUEFI()
1404 #endif // __MAKEWITH_TIANO
1406 static LEGACY_ENTRY
* AddLegacyEntry(IN CHAR16
*LoaderTitle
, IN REFIT_VOLUME
*Volume
)
1408 LEGACY_ENTRY
*Entry
, *SubEntry
;
1409 REFIT_MENU_SCREEN
*SubScreen
;
1411 CHAR16 ShortcutLetter
= 0;
1413 if (LoaderTitle
== NULL
) {
1414 if (Volume
->OSName
!= NULL
) {
1415 LoaderTitle
= Volume
->OSName
;
1416 if (LoaderTitle
[0] == 'W' || LoaderTitle
[0] == 'L')
1417 ShortcutLetter
= LoaderTitle
[0];
1419 LoaderTitle
= L
"Legacy OS";
1421 if (Volume
->VolName
!= NULL
)
1422 VolDesc
= Volume
->VolName
;
1424 VolDesc
= (Volume
->DiskKind
== DISK_KIND_OPTICAL
) ? L
"CD" : L
"HD";
1426 // prepare the menu entry
1427 Entry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1428 Entry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1429 SPrint(Entry
->me
.Title
, 255, L
"Boot %s from %s", LoaderTitle
, VolDesc
);
1430 Entry
->me
.Tag
= TAG_LEGACY
;
1432 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1433 Entry
->me
.Image
= LoadOSIcon(Volume
->OSIconName
, L
"legacy", FALSE
);
1434 Entry
->me
.BadgeImage
= Volume
->VolBadgeImage
;
1435 Entry
->Volume
= Volume
;
1436 Entry
->LoadOptions
= (Volume
->DiskKind
== DISK_KIND_OPTICAL
) ? L
"CD" :
1437 ((Volume
->DiskKind
== DISK_KIND_EXTERNAL
) ? L
"USB" : L
"HD");
1438 Entry
->Enabled
= TRUE
;
1440 // create the submenu
1441 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
1442 SubScreen
->Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1443 SPrint(SubScreen
->Title
, 255, L
"Boot Options for %s on %s", LoaderTitle
, VolDesc
);
1444 SubScreen
->TitleImage
= Entry
->me
.Image
;
1445 SubScreen
->Hint1
= StrDuplicate(SUBSCREEN_HINT1
);
1446 if (GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_EDITOR
) {
1447 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2_NO_EDITOR
);
1449 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2
);
1453 SubEntry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1454 SubEntry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1455 SPrint(SubEntry
->me
.Title
, 255, L
"Boot %s", LoaderTitle
);
1456 SubEntry
->me
.Tag
= TAG_LEGACY
;
1457 SubEntry
->Volume
= Entry
->Volume
;
1458 SubEntry
->LoadOptions
= Entry
->LoadOptions
;
1459 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
1461 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
1462 Entry
->me
.SubScreen
= SubScreen
;
1463 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1465 } /* static LEGACY_ENTRY * AddLegacyEntry() */
1468 #ifdef __MAKEWITH_GNUEFI
1469 static VOID
ScanLegacyUEFI(IN UINTN DiskType
){}
1471 // default volume badge icon based on disk kind
1472 static EG_IMAGE
* GetDiskBadge(IN UINTN DiskType
) {
1473 EG_IMAGE
* Badge
= NULL
;
1477 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_INTERNAL
);
1480 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_EXTERNAL
);
1483 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_OPTICAL
);
1487 } // static EG_IMAGE * GetDiskBadge()
1490 Create a rEFInd boot option from a Legacy BIOS protocol option.
1492 static LEGACY_ENTRY
* AddLegacyEntryUEFI(BDS_COMMON_OPTION
*BdsOption
, IN UINT16 DiskType
)
1494 LEGACY_ENTRY
*Entry
, *SubEntry
;
1495 REFIT_MENU_SCREEN
*SubScreen
;
1496 CHAR16 ShortcutLetter
= 0;
1497 CHAR16
*LegacyDescription
= BdsOption
->Description
;
1499 // prepare the menu entry
1500 Entry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1501 Entry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1502 SPrint(Entry
->me
.Title
, 255, L
"Boot legacy target %s", LegacyDescription
);
1503 Entry
->me
.Tag
= TAG_LEGACY_UEFI
;
1505 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1506 Entry
->me
.Image
= LoadOSIcon(L
"legacy", L
"legacy", TRUE
);
1507 Entry
->LoadOptions
= (DiskType
== BBS_CDROM
) ? L
"CD" :
1508 ((DiskType
== BBS_USB
) ? L
"USB" : L
"HD");
1509 Entry
->me
.BadgeImage
= GetDiskBadge(DiskType
);
1510 Entry
->BdsOption
= BdsOption
;
1511 Entry
->Enabled
= TRUE
;
1513 // create the submenu
1514 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
1515 SubScreen
->Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1516 SPrint(SubScreen
->Title
, 255, L
"No boot options for legacy target");
1517 SubScreen
->TitleImage
= Entry
->me
.Image
;
1518 SubScreen
->Hint1
= StrDuplicate(SUBSCREEN_HINT1
);
1519 if (GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_EDITOR
) {
1520 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2_NO_EDITOR
);
1522 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2
);
1526 SubEntry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1527 SubEntry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1528 SPrint(SubEntry
->me
.Title
, 255, L
"Boot %s", LegacyDescription
);
1529 SubEntry
->me
.Tag
= TAG_LEGACY_UEFI
;
1530 Entry
->BdsOption
= BdsOption
;
1531 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
1533 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
1534 Entry
->me
.SubScreen
= SubScreen
;
1535 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1537 } /* static LEGACY_ENTRY * AddLegacyEntryUEFI() */
1540 Scan for legacy BIOS targets on machines that implement EFI_LEGACY_BIOS_PROTOCOL.
1541 In testing, protocol has not been implemented on Macs but has been
1542 implemented on several Dell PCs and an ASUS motherboard.
1543 Restricts output to disks of the specified DiskType.
1545 static VOID
ScanLegacyUEFI(IN UINTN DiskType
)
1548 EFI_LEGACY_BIOS_PROTOCOL
*LegacyBios
;
1549 UINT16
*BootOrder
= NULL
;
1551 CHAR16 BootOption
[10];
1552 UINTN BootOrderSize
= 0;
1554 BDS_COMMON_OPTION
*BdsOption
;
1555 LIST_ENTRY TempList
;
1556 BBS_BBS_DEVICE_PATH
* BbsDevicePath
= NULL
;
1558 InitializeListHead (&TempList
);
1559 ZeroMem (Buffer
, sizeof (Buffer
));
1561 // If LegacyBios protocol is not implemented on this platform, then
1562 //we do not support this type of legacy boot on this machine.
1563 Status
= gBS
->LocateProtocol(&gEfiLegacyBootProtocolGuid
, NULL
, (VOID
**) &LegacyBios
);
1564 if (EFI_ERROR (Status
))
1567 // Grab the boot order
1568 BootOrder
= BdsLibGetVariableAndSize(L
"BootOrder", &gEfiGlobalVariableGuid
, &BootOrderSize
);
1569 if (BootOrder
== NULL
) {
1574 while (Index
< BootOrderSize
/ sizeof (UINT16
))
1576 // Grab each boot option variable from the boot order, and convert
1577 // the variable into a BDS boot option
1578 UnicodeSPrint (BootOption
, sizeof (BootOption
), L
"Boot%04x", BootOrder
[Index
]);
1579 BdsOption
= BdsLibVariableToOption (&TempList
, BootOption
);
1581 if (BdsOption
!= NULL
) {
1582 BbsDevicePath
= (BBS_BBS_DEVICE_PATH
*)BdsOption
->DevicePath
;
1584 // Only add the entry if it is of a requested type (e.g. USB, HD)
1586 // Two checks necessary because some systems return EFI boot loaders
1587 // with a DeviceType value that would inappropriately include them
1588 // as legacy loaders....
1589 if ((BbsDevicePath
->DeviceType
== DiskType
) && (BdsOption
->DevicePath
->Type
== DEVICE_TYPE_BIOS
)) {
1590 AddLegacyEntryUEFI(BdsOption
, BbsDevicePath
->DeviceType
);
1595 } /* static VOID ScanLegacyUEFI() */
1596 #endif // __MAKEWITH_GNUEFI
1598 static VOID
ScanLegacyVolume(REFIT_VOLUME
*Volume
, UINTN VolumeIndex
) {
1600 BOOLEAN ShowVolume
, HideIfOthersFound
;
1603 HideIfOthersFound
= FALSE
;
1604 if (Volume
->IsAppleLegacy
) {
1606 HideIfOthersFound
= TRUE
;
1607 } else if (Volume
->HasBootCode
) {
1609 if (Volume
->BlockIO
== Volume
->WholeDiskBlockIO
&&
1610 Volume
->BlockIOOffset
== 0 &&
1611 Volume
->OSName
== NULL
)
1612 // this is a whole disk (MBR) entry; hide if we have entries for partitions
1613 HideIfOthersFound
= TRUE
;
1615 if (HideIfOthersFound
) {
1616 // check for other bootable entries on the same disk
1617 for (VolumeIndex2
= 0; VolumeIndex2
< VolumesCount
; VolumeIndex2
++) {
1618 if (VolumeIndex2
!= VolumeIndex
&& Volumes
[VolumeIndex2
]->HasBootCode
&&
1619 Volumes
[VolumeIndex2
]->WholeDiskBlockIO
== Volume
->WholeDiskBlockIO
)
1625 AddLegacyEntry(NULL
, Volume
);
1626 } // static VOID ScanLegacyVolume()
1628 // Scan attached optical discs for legacy (BIOS) boot code
1629 // and add anything found to the list....
1630 static VOID
ScanLegacyDisc(VOID
)
1633 REFIT_VOLUME
*Volume
;
1635 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
) {
1636 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1637 Volume
= Volumes
[VolumeIndex
];
1638 if (Volume
->DiskKind
== DISK_KIND_OPTICAL
)
1639 ScanLegacyVolume(Volume
, VolumeIndex
);
1641 } else if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
1642 ScanLegacyUEFI(BBS_CDROM
);
1644 } /* static VOID ScanLegacyDisc() */
1646 // Scan internal hard disks for legacy (BIOS) boot code
1647 // and add anything found to the list....
1648 static VOID
ScanLegacyInternal(VOID
)
1651 REFIT_VOLUME
*Volume
;
1653 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
) {
1654 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1655 Volume
= Volumes
[VolumeIndex
];
1656 if (Volume
->DiskKind
== DISK_KIND_INTERNAL
)
1657 ScanLegacyVolume(Volume
, VolumeIndex
);
1659 } else if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
1660 ScanLegacyUEFI(BBS_HARDDISK
);
1662 } /* static VOID ScanLegacyInternal() */
1664 // Scan external disks for legacy (BIOS) boot code
1665 // and add anything found to the list....
1666 static VOID
ScanLegacyExternal(VOID
)
1669 REFIT_VOLUME
*Volume
;
1671 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
) {
1672 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1673 Volume
= Volumes
[VolumeIndex
];
1674 if (Volume
->DiskKind
== DISK_KIND_EXTERNAL
)
1675 ScanLegacyVolume(Volume
, VolumeIndex
);
1677 } else if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
1678 ScanLegacyUEFI(BBS_USB
);
1680 } /* static VOID ScanLegacyExternal() */
1683 // pre-boot tool functions
1686 static VOID
StartTool(IN LOADER_ENTRY
*Entry
)
1688 BeginExternalScreen(Entry
->UseGraphicsMode
, Entry
->me
.Title
+ 6); // assumes "Start <title>" as assigned below
1689 StartEFIImage(Entry
->DevicePath
, Entry
->LoadOptions
, Basename(Entry
->LoaderPath
),
1690 Basename(Entry
->LoaderPath
), Entry
->OSType
, NULL
, TRUE
);
1691 FinishExternalScreen();
1692 } /* static VOID StartTool() */
1694 static LOADER_ENTRY
* AddToolEntry(EFI_HANDLE DeviceHandle
, IN CHAR16
*LoaderPath
, IN CHAR16
*LoaderTitle
, IN EG_IMAGE
*Image
,
1695 IN CHAR16 ShortcutLetter
, IN BOOLEAN UseGraphicsMode
)
1697 LOADER_ENTRY
*Entry
;
1698 CHAR16
*TitleStr
= NULL
;
1700 Entry
= AllocateZeroPool(sizeof(LOADER_ENTRY
));
1702 MergeStrings(&TitleStr
, L
"Start ", 0);
1703 MergeStrings(&TitleStr
, LoaderTitle
, 0);
1704 Entry
->me
.Title
= TitleStr
;
1705 Entry
->me
.Tag
= TAG_TOOL
;
1707 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1708 Entry
->me
.Image
= Image
;
1709 Entry
->LoaderPath
= (LoaderPath
) ? StrDuplicate(LoaderPath
) : NULL
;
1710 Entry
->DevicePath
= FileDevicePath(DeviceHandle
, Entry
->LoaderPath
);
1711 Entry
->UseGraphicsMode
= UseGraphicsMode
;
1713 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1715 } /* static LOADER_ENTRY * AddToolEntry() */
1718 // pre-boot driver functions
1721 static UINTN
ScanDriverDir(IN CHAR16
*Path
)
1724 REFIT_DIR_ITER DirIter
;
1726 EFI_FILE_INFO
*DirEntry
;
1727 CHAR16 FileName
[256];
1729 CleanUpPathNameSlashes(Path
);
1730 // look through contents of the directory
1731 DirIterOpen(SelfRootDir
, Path
, &DirIter
);
1732 while (DirIterNext(&DirIter
, 2, LOADER_MATCH_PATTERNS
, &DirEntry
)) {
1733 if (DirEntry
->FileName
[0] == '.')
1734 continue; // skip this
1736 SPrint(FileName
, 255, L
"%s\\%s", Path
, DirEntry
->FileName
);
1738 Status
= StartEFIImage(FileDevicePath(SelfLoadedImage
->DeviceHandle
, FileName
),
1739 L
"", DirEntry
->FileName
, DirEntry
->FileName
, 0, NULL
, FALSE
);
1741 Status
= DirIterClose(&DirIter
);
1742 if (Status
!= EFI_NOT_FOUND
) {
1743 SPrint(FileName
, 255, L
"while scanning the %s directory", Path
);
1744 CheckError(Status
, FileName
);
1749 #ifdef __MAKEWITH_GNUEFI
1750 static EFI_STATUS
ConnectAllDriversToAllControllers(VOID
)
1753 UINTN AllHandleCount
;
1754 EFI_HANDLE
*AllHandleBuffer
;
1757 EFI_HANDLE
*HandleBuffer
;
1763 Status
= LibLocateHandle(AllHandles
,
1768 if (EFI_ERROR(Status
))
1771 for (Index
= 0; Index
< AllHandleCount
; Index
++) {
1773 // Scan the handle database
1775 Status
= LibScanHandleDatabase(NULL
,
1777 AllHandleBuffer
[Index
],
1782 if (EFI_ERROR (Status
))
1786 if (HandleType
[Index
] & EFI_HANDLE_TYPE_DRIVER_BINDING_HANDLE
)
1788 if (HandleType
[Index
] & EFI_HANDLE_TYPE_IMAGE_HANDLE
)
1793 for (HandleIndex
= 0; HandleIndex
< HandleCount
; HandleIndex
++) {
1794 if (HandleType
[HandleIndex
] & EFI_HANDLE_TYPE_PARENT_HANDLE
)
1799 if (HandleType
[Index
] & EFI_HANDLE_TYPE_DEVICE_HANDLE
) {
1800 Status
= refit_call4_wrapper(BS
->ConnectController
,
1801 AllHandleBuffer
[Index
],
1809 MyFreePool (HandleBuffer
);
1810 MyFreePool (HandleType
);
1814 MyFreePool (AllHandleBuffer
);
1816 } /* EFI_STATUS ConnectAllDriversToAllControllers() */
1818 static EFI_STATUS
ConnectAllDriversToAllControllers(VOID
) {
1819 BdsLibConnectAllDriversToAllControllers();
1824 // Load all EFI drivers from rEFInd's "drivers" subdirectory and from the
1825 // directories specified by the user in the "scan_driver_dirs" configuration
1827 static VOID
LoadDrivers(VOID
)
1829 CHAR16
*Directory
, *SelfDirectory
;
1830 UINTN i
= 0, Length
, NumFound
= 0;
1832 // load drivers from the subdirectories of rEFInd's home directory specified
1833 // in the DRIVER_DIRS constant.
1834 while ((Directory
= FindCommaDelimited(DRIVER_DIRS
, i
++)) != NULL
) {
1835 SelfDirectory
= SelfDirPath
? StrDuplicate(SelfDirPath
) : NULL
;
1836 CleanUpPathNameSlashes(SelfDirectory
);
1837 MergeStrings(&SelfDirectory
, Directory
, L
'\\');
1838 NumFound
+= ScanDriverDir(SelfDirectory
);
1839 MyFreePool(Directory
);
1840 MyFreePool(SelfDirectory
);
1843 // Scan additional user-specified driver directories....
1845 while ((Directory
= FindCommaDelimited(GlobalConfig
.DriverDirs
, i
++)) != NULL
) {
1846 CleanUpPathNameSlashes(Directory
);
1847 Length
= StrLen(Directory
);
1849 NumFound
+= ScanDriverDir(Directory
);
1851 MyFreePool(Directory
);
1854 // connect all devices
1856 ConnectAllDriversToAllControllers();
1857 } /* static VOID LoadDrivers() */
1859 // Determine what (if any) type of legacy (BIOS) boot support is available
1860 static VOID
FindLegacyBootType(VOID
) {
1861 #ifdef __MAKEWITH_TIANO
1863 EFI_LEGACY_BIOS_PROTOCOL
*LegacyBios
;
1866 GlobalConfig
.LegacyType
= LEGACY_TYPE_NONE
;
1868 // UEFI-style legacy BIOS support is available only with the TianoCore EDK2
1869 // build environment, and then only with some EFI implementations....
1870 #ifdef __MAKEWITH_TIANO
1871 Status
= gBS
->LocateProtocol (&gEfiLegacyBootProtocolGuid
, NULL
, (VOID
**) &LegacyBios
);
1872 if (!EFI_ERROR (Status
))
1873 GlobalConfig
.LegacyType
= LEGACY_TYPE_UEFI
;
1876 // Macs have their own system. If the firmware vendor code contains the
1877 // string "Apple", assume it's available. Note that this overrides the
1878 // UEFI type, and might yield false positives if the vendor string
1879 // contains "Apple" as part of something bigger, so this isn't 100%
1881 if (StriSubCmp(L
"Apple", ST
->FirmwareVendor
))
1882 GlobalConfig
.LegacyType
= LEGACY_TYPE_MAC
;
1883 } // static VOID FindLegacyBootType
1885 // Warn the user if legacy OS scans are enabled but the firmware or this
1886 // application can't support them....
1887 static VOID
WarnIfLegacyProblems() {
1888 BOOLEAN found
= FALSE
;
1891 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_NONE
) {
1893 if (GlobalConfig
.ScanFor
[i
] == 'h' || GlobalConfig
.ScanFor
[i
] == 'b' || GlobalConfig
.ScanFor
[i
] == 'c')
1896 } while ((i
< NUM_SCAN_OPTIONS
) && (!found
));
1898 Print(L
"NOTE: refind.conf's 'scanfor' line specifies scanning for one or more legacy\n");
1899 Print(L
"(BIOS) boot options; however, this is not possible because ");
1900 #ifdef __MAKEWITH_TIANO
1901 Print(L
"your computer lacks\n");
1902 Print(L
"the necessary Compatibility Support Module (CSM) support.\n");
1904 Print(L
"this program was\n");
1905 Print(L
"compiled without the necessary support. Please visit\n");
1906 Print(L
"http://www.rodsbooks.com/refind/getting.html and download and install a rEFInd\n");
1907 Print(L
"binary built with the TianoCore EDK2 to enable legacy boot support.\n");
1911 } // if no legacy support
1912 } // static VOID WarnIfLegacyProblems()
1914 // Locates boot loaders. NOTE: This assumes that GlobalConfig.LegacyType is set correctly.
1915 static VOID
ScanForBootloaders(VOID
) {
1920 // scan for loaders and tools, add them to the menu
1921 for (i
= 0; i
< NUM_SCAN_OPTIONS
; i
++) {
1922 switch(GlobalConfig
.ScanFor
[i
]) {
1927 ScanLegacyInternal();
1930 ScanLegacyExternal();
1933 ScanUserConfigured(CONFIG_FILE_NAME
);
1947 // assign shortcut keys
1948 for (i
= 0; i
< MainMenu
.EntryCount
&& MainMenu
.Entries
[i
]->Row
== 0 && i
< 9; i
++)
1949 MainMenu
.Entries
[i
]->ShortcutDigit
= (CHAR16
)('1' + i
);
1951 // wait for user ACK when there were errors
1952 FinishTextScreen(FALSE
);
1953 } // static VOID ScanForBootloaders()
1955 // Add the second-row tags containing built-in and external tools (EFI shell,
1957 static VOID
ScanForTools(VOID
) {
1958 CHAR16
*FileName
= NULL
, *MokLocations
, *MokName
, *PathName
, Description
[256];
1959 REFIT_MENU_ENTRY
*TempMenuEntry
;
1960 UINTN i
, j
, k
, VolumeIndex
;
1962 MokLocations
= StrDuplicate(MOK_LOCATIONS
);
1963 if (MokLocations
!= NULL
)
1964 MergeStrings(&MokLocations
, SelfDirPath
, L
',');
1966 for (i
= 0; i
< NUM_TOOLS
; i
++) {
1967 switch(GlobalConfig
.ShowTools
[i
]) {
1968 // NOTE: Be sure that FileName is NULL at the end of each case.
1970 TempMenuEntry
= CopyMenuEntry(&MenuEntryShutdown
);
1971 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_SHUTDOWN
);
1972 AddMenuEntry(&MainMenu
, TempMenuEntry
);
1975 TempMenuEntry
= CopyMenuEntry(&MenuEntryReset
);
1976 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_RESET
);
1977 AddMenuEntry(&MainMenu
, TempMenuEntry
);
1980 TempMenuEntry
= CopyMenuEntry(&MenuEntryAbout
);
1981 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT
);
1982 AddMenuEntry(&MainMenu
, TempMenuEntry
);
1985 TempMenuEntry
= CopyMenuEntry(&MenuEntryExit
);
1986 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_EXIT
);
1987 AddMenuEntry(&MainMenu
, TempMenuEntry
);
1991 while ((FileName
= FindCommaDelimited(SHELL_NAMES
, j
++)) != NULL
) {
1992 if (FileExists(SelfRootDir
, FileName
)) {
1993 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, L
"EFI Shell", BuiltinIcon(BUILTIN_ICON_TOOL_SHELL
),
1996 MyFreePool(FileName
);
2000 FileName
= StrDuplicate(L
"\\efi\\tools\\gptsync.efi");
2001 if (FileExists(SelfRootDir
, FileName
)) {
2002 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, L
"Make Hybrid MBR", BuiltinIcon(BUILTIN_ICON_TOOL_PART
), 'P', FALSE
);
2004 MyFreePool(FileName
);
2007 case TAG_APPLE_RECOVERY
:
2008 FileName
= StrDuplicate(L
"\\com.apple.recovery.boot\\boot.efi");
2009 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
2010 if ((Volumes
[VolumeIndex
]->RootDir
!= NULL
) && (FileExists(Volumes
[VolumeIndex
]->RootDir
, FileName
))) {
2011 SPrint(Description
, 255, L
"Apple Recovery on %s", Volumes
[VolumeIndex
]->VolName
);
2012 AddToolEntry(Volumes
[VolumeIndex
]->DeviceHandle
, FileName
, Description
,
2013 BuiltinIcon(BUILTIN_ICON_TOOL_APPLE_RESCUE
), 'R', TRUE
);
2016 MyFreePool(FileName
);
2021 while ((FileName
= FindCommaDelimited(MokLocations
, j
++)) != NULL
) {
2023 while ((MokName
= FindCommaDelimited(MOK_NAMES
, k
++)) != NULL
) {
2024 PathName
= StrDuplicate(FileName
);
2025 MergeStrings(&PathName
, MokName
, (StriCmp(PathName
, L
"\\") == 0) ? 0 : L
'\\');
2026 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
2027 if ((Volumes
[VolumeIndex
]->RootDir
!= NULL
) && (FileExists(Volumes
[VolumeIndex
]->RootDir
, PathName
))) {
2028 SPrint(Description
, 255, L
"MOK utility at %s on %s", PathName
, Volumes
[VolumeIndex
]->VolName
);
2029 AddToolEntry(Volumes
[VolumeIndex
]->DeviceHandle
, PathName
, Description
,
2030 BuiltinIcon(BUILTIN_ICON_TOOL_MOK_TOOL
), 'S', FALSE
);
2033 MyFreePool(PathName
);
2034 MyFreePool(MokName
);
2035 } // while MOK_NAMES
2036 MyFreePool(FileName
);
2037 } // while MokLocations
2042 } // static VOID ScanForTools
2044 // Rescan for boot loaders
2045 VOID
RescanAll(VOID
) {
2052 egDisplayMessage(L
"Scanning for new boot loaders; please wait....", &BGColor
);
2053 FreeList((VOID
***) &(MainMenu
.Entries
), &MainMenu
.EntryCount
);
2054 MainMenu
.Entries
= NULL
;
2055 MainMenu
.EntryCount
= 0;
2056 ReadConfig(CONFIG_FILE_NAME
);
2057 ConnectAllDriversToAllControllers();
2059 ScanForBootloaders();
2062 } // VOID RescanAll()
2064 #ifdef __MAKEWITH_TIANO
2066 // Minimal initialization function
2067 static VOID
InitializeLib(IN EFI_HANDLE ImageHandle
, IN EFI_SYSTEM_TABLE
*SystemTable
) {
2069 // gImageHandle = ImageHandle;
2070 gBS
= SystemTable
->BootServices
;
2071 // gRS = SystemTable->RuntimeServices;
2072 gRT
= SystemTable
->RuntimeServices
; // Some BDS functions need gRT to be set
2073 EfiGetSystemConfigurationTable (&gEfiDxeServicesTableGuid
, (VOID
**) &gDS
);
2075 InitializeConsoleSim();
2080 // Set up our own Secure Boot extensions....
2081 // Returns TRUE on success, FALSE otherwise
2082 static BOOLEAN
SecureBootSetup(VOID
) {
2084 BOOLEAN Success
= FALSE
;
2086 if (secure_mode() && ShimLoaded()) {
2087 Status
= security_policy_install();
2088 if (Status
== EFI_SUCCESS
) {
2091 Print(L
"Failed to install MOK Secure Boot extensions");
2095 } // VOID SecureBootSetup()
2097 // Remove our own Secure Boot extensions....
2098 // Returns TRUE on success, FALSE otherwise
2099 static BOOLEAN
SecureBootUninstall(VOID
) {
2101 BOOLEAN Success
= TRUE
;
2103 if (secure_mode()) {
2104 Status
= security_policy_uninstall();
2105 if (Status
!= EFI_SUCCESS
) {
2107 BeginTextScreen(L
"Secure Boot Policy Failure");
2108 Print(L
"Failed to uninstall MOK Secure Boot extensions; forcing a reboot.");
2110 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);
2114 } // VOID SecureBootUninstall
2121 efi_main (EFI_HANDLE ImageHandle
, EFI_SYSTEM_TABLE
*SystemTable
)
2124 BOOLEAN MainLoopRunning
= TRUE
;
2125 BOOLEAN MokProtocol
;
2126 REFIT_MENU_ENTRY
*ChosenEntry
;
2128 CHAR16
*Selection
= NULL
;
2132 InitializeLib(ImageHandle
, SystemTable
);
2133 Status
= InitRefitLib(ImageHandle
);
2134 if (EFI_ERROR(Status
))
2137 // read configuration
2138 CopyMem(GlobalConfig
.ScanFor
, "ieom ", NUM_SCAN_OPTIONS
);
2139 FindLegacyBootType();
2140 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
)
2141 CopyMem(GlobalConfig
.ScanFor
, "ihebocm ", NUM_SCAN_OPTIONS
);
2143 ReadConfig(CONFIG_FILE_NAME
);
2146 WarnIfLegacyProblems();
2147 MainMenu
.TimeoutSeconds
= GlobalConfig
.Timeout
;
2149 // disable EFI watchdog timer
2150 refit_call4_wrapper(BS
->SetWatchdogTimer
, 0x0000, 0x0000, 0x0000, NULL
);
2152 // further bootstrap (now with config available)
2153 MokProtocol
= SecureBootSetup();
2155 ScanForBootloaders();
2159 if (GlobalConfig
.ScanDelay
> 0) {
2164 egDisplayMessage(L
"Pausing before disk scan; please wait....", &BGColor
);
2165 for (i
= 0; i
< GlobalConfig
.ScanDelay
; i
++)
2166 refit_call1_wrapper(BS
->Stall
, 1000000);
2170 if (GlobalConfig
.DefaultSelection
)
2171 Selection
= StrDuplicate(GlobalConfig
.DefaultSelection
);
2173 while (MainLoopRunning
) {
2174 MenuExit
= RunMainMenu(&MainMenu
, Selection
, &ChosenEntry
);
2176 // The Escape key triggers a re-scan operation....
2177 if (MenuExit
== MENU_EXIT_ESCAPE
) {
2182 switch (ChosenEntry
->Tag
) {
2184 case TAG_REBOOT
: // Reboot
2186 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);
2187 MainLoopRunning
= FALSE
; // just in case we get this far
2190 case TAG_SHUTDOWN
: // Shut Down
2192 refit_call4_wrapper(RT
->ResetSystem
, EfiResetShutdown
, EFI_SUCCESS
, 0, NULL
);
2193 MainLoopRunning
= FALSE
; // just in case we get this far
2196 case TAG_ABOUT
: // About rEFInd
2200 case TAG_LOADER
: // Boot OS via .EFI loader
2201 StartLoader((LOADER_ENTRY
*)ChosenEntry
);
2204 case TAG_LEGACY
: // Boot legacy OS
2205 StartLegacy((LEGACY_ENTRY
*)ChosenEntry
);
2208 #ifdef __MAKEWITH_TIANO
2209 case TAG_LEGACY_UEFI
: // Boot a legacy OS on a non-Mac
2210 StartLegacyUEFI((LEGACY_ENTRY
*)ChosenEntry
);
2214 case TAG_TOOL
: // Start a EFI tool
2215 StartTool((LOADER_ENTRY
*)ChosenEntry
);
2218 case TAG_EXIT
: // Terminate rEFInd
2219 if ((MokProtocol
) && !SecureBootUninstall()) {
2220 MainLoopRunning
= FALSE
; // just in case we get this far
2222 BeginTextScreen(L
" ");
2228 MyFreePool(Selection
);
2229 Selection
= (ChosenEntry
->Title
) ? StrDuplicate(ChosenEntry
->Title
) : NULL
;
2232 // If we end up here, things have gone wrong. Try to reboot, and if that
2233 // fails, go into an endless loop.
2234 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);