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,\\shell.efi,\\shellx64.efi"
70 #define GPTSYNC_NAMES L"\\EFI\\tools\\gptsync.efi,\\EFI\\tools\\gptsync_x64.efi"
71 #define DRIVER_DIRS L"drivers,drivers_x64"
72 #define FALLBACK_FULLNAME L"EFI\\BOOT\\bootx64.efi"
73 #define FALLBACK_BASENAME L"bootx64.efi"
75 #define SHELL_NAMES L"\\EFI\\tools\\shell.efi,\\EFI\\tools\\shellia32.efi,\\shell.efi,\\shellia32.efi"
76 #define GPTSYNC_NAMES L"\\EFI\\tools\\gptsync.efi,\\EFI\\tools\\gptsync_ia32.efi"
77 #define DRIVER_DIRS L"drivers,drivers_ia32"
78 #define FALLBACK_FULLNAME L"EFI\\BOOT\\bootia32.efi"
79 #define FALLBACK_BASENAME L"bootia32.efi"
81 #define SHELL_NAMES L"\\EFI\\tools\\shell.efi,\\shell.efi"
82 #define GPTSYNC_NAMES L"\\EFI\\tools\\gptsync.efi"
83 #define DRIVER_DIRS L"drivers"
84 #define FALLBACK_FULLNAME L"EFI\\BOOT\\boot.efi" /* Not really correct */
85 #define FALLBACK_BASENAME L"boot.efi" /* Not really correct */
88 // Filename patterns that identify EFI boot loaders. Note that a single case (either L"*.efi" or
89 // L"*.EFI") is fine for most systems; but Gigabyte's buggy Hybrid EFI does a case-sensitive
90 // comparison when it should do a case-insensitive comparison, so I'm doubling this up. It does
91 // no harm on other computers, AFAIK. In theory, every case variation should be done for
92 // completeness, but that's ridiculous....
93 #define LOADER_MATCH_PATTERNS L"*.efi,*.EFI"
95 // Patterns that identify Linux kernels. Added to the loader match pattern when the
96 // scan_all_linux_kernels option is set in the configuration file. Causes kernels WITHOUT
97 // a ".efi" extension to be found when scanning for boot loaders.
98 #define LINUX_MATCH_PATTERNS L"vmlinuz*,bzImage*"
100 // Default hint text for program-launch submenus
101 #define SUBSCREEN_HINT1 L"Use arrow keys to move cursor; Enter to boot;"
102 #define SUBSCREEN_HINT2 L"Insert or F2 to edit options; Esc to return to main menu"
103 #define SUBSCREEN_HINT2_NO_EDITOR L"Esc to return to main menu"
105 static REFIT_MENU_ENTRY MenuEntryAbout
= { L
"About rEFInd", TAG_ABOUT
, 1, 0, 'A', NULL
, NULL
, NULL
};
106 static REFIT_MENU_ENTRY MenuEntryReset
= { L
"Reboot Computer", TAG_REBOOT
, 1, 0, 'R', NULL
, NULL
, NULL
};
107 static REFIT_MENU_ENTRY MenuEntryShutdown
= { L
"Shut Down Computer", TAG_SHUTDOWN
, 1, 0, 'U', NULL
, NULL
, NULL
};
108 static REFIT_MENU_ENTRY MenuEntryReturn
= { L
"Return to Main Menu", TAG_RETURN
, 0, 0, 0, NULL
, NULL
, NULL
};
109 static REFIT_MENU_ENTRY MenuEntryExit
= { L
"Exit rEFInd", TAG_EXIT
, 1, 0, 0, NULL
, NULL
, NULL
};
111 static REFIT_MENU_SCREEN MainMenu
= { L
"Main Menu", NULL
, 0, NULL
, 0, NULL
, 0, L
"Automatic boot",
112 L
"Use arrow keys to move cursor; Enter to boot;",
113 L
"Insert or F2 for more options; Esc to refresh" };
114 static REFIT_MENU_SCREEN AboutMenu
= { L
"About", NULL
, 0, NULL
, 0, NULL
, 0, NULL
, L
"Press Enter to return to main menu", L
"" };
116 REFIT_CONFIG GlobalConfig
= { FALSE
, FALSE
, 0, 0, 0, DONT_CHANGE_TEXT_MODE
, 20, 0, 0, GRAPHICS_FOR_OSX
, LEGACY_TYPE_MAC
, 0, 0,
117 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
118 {TAG_SHELL
, TAG_APPLE_RECOVERY
, TAG_MOK_TOOL
, TAG_ABOUT
, TAG_SHUTDOWN
, TAG_REBOOT
, 0, 0, 0, 0, 0 }};
120 // Structure used to hold boot loader filenames and time stamps in
121 // a linked list; used to sort entries within a directory.
125 struct LOADER_LIST
*NextEntry
;
132 static VOID
AboutrEFInd(VOID
)
134 CHAR16
*TempStr
; // Note: Don't deallocate; moved to menu structure
136 if (AboutMenu
.EntryCount
== 0) {
137 AboutMenu
.TitleImage
= BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT
);
138 AddMenuInfoLine(&AboutMenu
, L
"rEFInd Version 0.6.9");
139 AddMenuInfoLine(&AboutMenu
, L
"");
140 AddMenuInfoLine(&AboutMenu
, L
"Copyright (c) 2006-2010 Christoph Pfisterer");
141 AddMenuInfoLine(&AboutMenu
, L
"Copyright (c) 2012-2013 Roderick W. Smith");
142 AddMenuInfoLine(&AboutMenu
, L
"Portions Copyright (c) Intel Corporation and others");
143 AddMenuInfoLine(&AboutMenu
, L
"Distributed under the terms of the GNU GPLv3 license");
144 AddMenuInfoLine(&AboutMenu
, L
"");
145 AddMenuInfoLine(&AboutMenu
, L
"Running on:");
146 TempStr
= AllocateZeroPool(256 * sizeof(CHAR16
));
147 SPrint(TempStr
, 255, L
" EFI Revision %d.%02d", ST
->Hdr
.Revision
>> 16, ST
->Hdr
.Revision
& ((1 << 16) - 1));
148 AddMenuInfoLine(&AboutMenu
, TempStr
);
150 AddMenuInfoLine(&AboutMenu
, L
" Platform: x86 (32 bit)");
151 #elif defined(EFIX64)
152 TempStr
= AllocateZeroPool(256 * sizeof(CHAR16
));
153 SPrint(TempStr
, 255, L
" Platform: x86_64 (64 bit); Secure Boot %s", secure_mode() ? L
"active" : L
"inactive");
154 AddMenuInfoLine(&AboutMenu
, TempStr
);
156 AddMenuInfoLine(&AboutMenu
, L
" Platform: unknown");
158 TempStr
= AllocateZeroPool(256 * sizeof(CHAR16
));
159 SPrint(TempStr
, 255, L
" Firmware: %s %d.%02d",
160 ST
->FirmwareVendor
, ST
->FirmwareRevision
>> 16, ST
->FirmwareRevision
& ((1 << 16) - 1));
161 AddMenuInfoLine(&AboutMenu
, TempStr
);
162 TempStr
= AllocateZeroPool(256 * sizeof(CHAR16
));
163 SPrint(TempStr
, 255, L
" Screen Output: %s", egScreenDescription());
164 AddMenuInfoLine(&AboutMenu
, TempStr
);
165 AddMenuInfoLine(&AboutMenu
, L
"");
166 #if defined(__MAKEWITH_GNUEFI)
167 AddMenuInfoLine(&AboutMenu
, L
"Built with GNU-EFI");
169 AddMenuInfoLine(&AboutMenu
, L
"Built with TianoCore EDK2");
171 AddMenuInfoLine(&AboutMenu
, L
"");
172 AddMenuInfoLine(&AboutMenu
, L
"For more information, see the rEFInd Web site:");
173 AddMenuInfoLine(&AboutMenu
, L
"http://www.rodsbooks.com/refind/");
174 AddMenuEntry(&AboutMenu
, &MenuEntryReturn
);
177 RunMenu(&AboutMenu
, NULL
);
178 } /* VOID AboutrEFInd() */
180 static VOID
WarnSecureBootError(CHAR16
*Name
, BOOLEAN Verbose
) {
182 Name
= L
"the loader";
184 refit_call2_wrapper(ST
->ConOut
->SetAttribute
, ST
->ConOut
, ATTR_ERROR
);
185 Print(L
"Secure Boot validation failure loading %s!\n", Name
);
186 refit_call2_wrapper(ST
->ConOut
->SetAttribute
, ST
->ConOut
, ATTR_BASIC
);
187 if (Verbose
&& secure_mode()) {
188 Print(L
"\nThis computer is configured with Secure Boot active, but\n%s has failed validation.\n", Name
);
189 Print(L
"\nYou can:\n * Launch another boot loader\n");
190 Print(L
" * Disable Secure Boot in your firmware\n");
191 Print(L
" * Sign %s with a machine owner key (MOK)\n", Name
);
192 Print(L
" * Use a MOK utility (often present on the second row) to add a MOK with which\n");
193 Print(L
" %s has already been signed.\n", Name
);
194 Print(L
" * Use a MOK utility to register %s (\"enroll its hash\") without\n", Name
);
195 Print(L
" signing it.\n");
196 Print(L
"\nSee http://www.rodsbooks.com/refind/secureboot.html for more information\n");
199 } // VOID WarnSecureBootError()
201 // Launch an EFI binary.
202 static EFI_STATUS
StartEFIImageList(IN EFI_DEVICE_PATH
**DevicePaths
,
203 IN CHAR16
*LoadOptions
, IN CHAR16
*LoadOptionsPrefix
,
204 IN CHAR16
*ImageTitle
, IN CHAR8 OSType
,
205 OUT UINTN
*ErrorInStep
,
208 EFI_STATUS Status
, ReturnStatus
;
209 EFI_HANDLE ChildImageHandle
;
210 EFI_LOADED_IMAGE
*ChildLoadedImage
= NULL
;
211 UINTN DevicePathIndex
;
212 CHAR16 ErrorInfo
[256];
213 CHAR16
*FullLoadOptions
= NULL
;
215 if (ErrorInStep
!= NULL
)
219 if (LoadOptions
!= NULL
) {
220 if (LoadOptionsPrefix
!= NULL
) {
221 MergeStrings(&FullLoadOptions
, LoadOptions
, L
' ');
223 MergeStrings(&FullLoadOptions
, L
" ", 0);
224 // NOTE: That last space is also added by the EFI shell and seems to be significant
225 // when passing options to Apple's boot.efi...
228 MergeStrings(&FullLoadOptions
, LoadOptions
, 0);
230 } else { // LoadOptions == NULL
231 // NOTE: We provide a non-null string when no options are specified for safety;
232 // some systems (at least DUET) can hang when launching some programs (such as
233 // an EFI shell) without this.
234 FullLoadOptions
= StrDuplicate(L
" ");
237 Print(L
"Starting %s\nUsing load options '%s'\n", ImageTitle
, FullLoadOptions
);
239 // load the image into memory (and execute it, in the case of a shim/MOK image).
240 ReturnStatus
= Status
= EFI_NOT_FOUND
; // in case the list is empty
241 for (DevicePathIndex
= 0; DevicePaths
[DevicePathIndex
] != NULL
; DevicePathIndex
++) {
242 // NOTE: Below commented-out line could be more efficient if file were read ahead of
243 // time and passed as a pre-loaded image to LoadImage(), but it doesn't work on my
244 // 32-bit Mac Mini or my 64-bit Intel box when launching a Linux kernel; the
245 // kernel returns a "Failed to handle fs_proto" error message.
246 // TODO: Track down the cause of this error and fix it, if possible.
247 // ReturnStatus = Status = refit_call6_wrapper(BS->LoadImage, FALSE, SelfImageHandle, DevicePaths[DevicePathIndex],
248 // ImageData, ImageSize, &ChildImageHandle);
249 ReturnStatus
= Status
= refit_call6_wrapper(BS
->LoadImage
, FALSE
, SelfImageHandle
, DevicePaths
[DevicePathIndex
],
250 NULL
, 0, &ChildImageHandle
);
251 if (ReturnStatus
!= EFI_NOT_FOUND
) {
255 if ((Status
== EFI_ACCESS_DENIED
) || (Status
== EFI_SECURITY_VIOLATION
)) {
256 WarnSecureBootError(ImageTitle
, Verbose
);
259 SPrint(ErrorInfo
, 255, L
"while loading %s", ImageTitle
);
260 if (CheckError(Status
, ErrorInfo
)) {
261 if (ErrorInStep
!= NULL
)
266 ReturnStatus
= Status
= refit_call3_wrapper(BS
->HandleProtocol
, ChildImageHandle
, &LoadedImageProtocol
,
267 (VOID
**) &ChildLoadedImage
);
268 if (CheckError(Status
, L
"while getting a LoadedImageProtocol handle")) {
269 if (ErrorInStep
!= NULL
)
273 ChildLoadedImage
->LoadOptions
= (VOID
*)FullLoadOptions
;
274 ChildLoadedImage
->LoadOptionsSize
= ((UINT32
)StrLen(FullLoadOptions
) + 1) * sizeof(CHAR16
);
275 // turn control over to the image
276 // TODO: (optionally) re-enable the EFI watchdog timer!
278 // close open file handles
280 ReturnStatus
= Status
= refit_call3_wrapper(BS
->StartImage
, ChildImageHandle
, NULL
, NULL
);
282 // control returns here when the child image calls Exit()
283 SPrint(ErrorInfo
, 255, L
"returned from %s", ImageTitle
);
284 if (CheckError(Status
, ErrorInfo
)) {
285 if (ErrorInStep
!= NULL
)
289 // re-open file handles
293 // unload the image, we don't care if it works or not...
294 Status
= refit_call1_wrapper(BS
->UnloadImage
, ChildImageHandle
);
297 MyFreePool(FullLoadOptions
);
299 } /* static EFI_STATUS StartEFIImageList() */
301 static EFI_STATUS
StartEFIImage(IN EFI_DEVICE_PATH
*DevicePath
,
302 IN CHAR16
*LoadOptions
, IN CHAR16
*LoadOptionsPrefix
,
303 IN CHAR16
*ImageTitle
, IN CHAR8 OSType
,
304 OUT UINTN
*ErrorInStep
,
307 EFI_DEVICE_PATH
*DevicePaths
[2];
309 DevicePaths
[0] = DevicePath
;
310 DevicePaths
[1] = NULL
;
311 return StartEFIImageList(DevicePaths
, LoadOptions
, LoadOptionsPrefix
, ImageTitle
, OSType
, ErrorInStep
, Verbose
);
312 } /* static EFI_STATUS StartEFIImage() */
315 // EFI OS loader functions
318 static VOID
StartLoader(IN LOADER_ENTRY
*Entry
)
320 UINTN ErrorInStep
= 0;
322 BeginExternalScreen(Entry
->UseGraphicsMode
, L
"Booting OS");
323 StartEFIImage(Entry
->DevicePath
, Entry
->LoadOptions
, Basename(Entry
->LoaderPath
),
324 Basename(Entry
->LoaderPath
), Entry
->OSType
, &ErrorInStep
, !Entry
->UseGraphicsMode
);
325 FinishExternalScreen();
328 // Locate an initrd or initramfs file that matches the kernel specified by LoaderPath.
329 // The matching file has a name that begins with "init" and includes the same version
330 // number string as is found in LoaderPath -- but not a longer version number string.
331 // For instance, if LoaderPath is \EFI\kernels\bzImage-3.3.0.efi, and if \EFI\kernels
332 // has a file called initramfs-3.3.0.img, this function will return the string
333 // '\EFI\kernels\initramfs-3.3.0.img'. If the directory ALSO contains the file
334 // initramfs-3.3.0-rc7.img or initramfs-13.3.0.img, those files will NOT match;
335 // however, initmine-3.3.0.img might match. (FindInitrd() returns the first match it
336 // finds). Thus, care should be taken to avoid placing duplicate matching files in
337 // the kernel's directory.
338 // If no matching init file can be found, returns NULL.
339 static CHAR16
* FindInitrd(IN CHAR16
*LoaderPath
, IN REFIT_VOLUME
*Volume
) {
340 CHAR16
*InitrdName
= NULL
, *FileName
, *KernelVersion
, *InitrdVersion
, *Path
;
341 REFIT_DIR_ITER DirIter
;
342 EFI_FILE_INFO
*DirEntry
;
344 FileName
= Basename(LoaderPath
);
345 KernelVersion
= FindNumbers(FileName
);
346 Path
= FindPath(LoaderPath
);
348 // Add trailing backslash for root directory; necessary on some systems, but must
349 // NOT be added to all directories, since on other systems, a trailing backslash on
350 // anything but the root directory causes them to flake out!
351 if (StrLen(Path
) == 0) {
352 MergeStrings(&Path
, L
"\\", 0);
354 DirIterOpen(Volume
->RootDir
, Path
, &DirIter
);
355 // Now add a trailing backslash if it was NOT added earlier, for consistency in
356 // building the InitrdName later....
357 if ((StrLen(Path
) > 0) && (Path
[StrLen(Path
) - 1] != L
'\\'))
358 MergeStrings(&Path
, L
"\\", 0);
359 while ((DirIterNext(&DirIter
, 2, L
"init*", &DirEntry
)) && (InitrdName
== NULL
)) {
360 InitrdVersion
= FindNumbers(DirEntry
->FileName
);
361 if (KernelVersion
!= NULL
) {
362 if (StriCmp(InitrdVersion
, KernelVersion
) == 0) {
363 MergeStrings(&InitrdName
, Path
, 0);
364 MergeStrings(&InitrdName
, DirEntry
->FileName
, 0);
367 if (InitrdVersion
== NULL
) {
368 MergeStrings(&InitrdName
, Path
, 0);
369 MergeStrings(&InitrdName
, DirEntry
->FileName
, 0);
372 MyFreePool(InitrdVersion
);
374 DirIterClose(&DirIter
);
376 // Note: Don't FreePool(FileName), since Basename returns a pointer WITHIN the string it's passed.
377 MyFreePool(KernelVersion
);
380 } // static CHAR16 * FindInitrd()
382 LOADER_ENTRY
* AddPreparedLoaderEntry(LOADER_ENTRY
*Entry
) {
383 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
386 } // LOADER_ENTRY * AddPreparedLoaderEntry()
388 // Creates a copy of a menu screen.
389 // Returns a pointer to the copy of the menu screen.
390 static REFIT_MENU_SCREEN
* CopyMenuScreen(REFIT_MENU_SCREEN
*Entry
) {
391 REFIT_MENU_SCREEN
*NewEntry
;
394 NewEntry
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
395 if ((Entry
!= NULL
) && (NewEntry
!= NULL
)) {
396 CopyMem(NewEntry
, Entry
, sizeof(REFIT_MENU_SCREEN
));
397 NewEntry
->Title
= (Entry
->Title
) ? StrDuplicate(Entry
->Title
) : NULL
;
398 NewEntry
->TimeoutText
= (Entry
->TimeoutText
) ? StrDuplicate(Entry
->TimeoutText
) : NULL
;
399 if (Entry
->TitleImage
!= NULL
) {
400 NewEntry
->TitleImage
= AllocatePool(sizeof(EG_IMAGE
));
401 if (NewEntry
->TitleImage
!= NULL
)
402 CopyMem(NewEntry
->TitleImage
, Entry
->TitleImage
, sizeof(EG_IMAGE
));
404 NewEntry
->InfoLines
= (CHAR16
**) AllocateZeroPool(Entry
->InfoLineCount
* (sizeof(CHAR16
*)));
405 for (i
= 0; i
< Entry
->InfoLineCount
&& NewEntry
->InfoLines
; i
++) {
406 NewEntry
->InfoLines
[i
] = (Entry
->InfoLines
[i
]) ? StrDuplicate(Entry
->InfoLines
[i
]) : NULL
;
408 NewEntry
->Entries
= (REFIT_MENU_ENTRY
**) AllocateZeroPool(Entry
->EntryCount
* (sizeof (REFIT_MENU_ENTRY
*)));
409 for (i
= 0; i
< Entry
->EntryCount
&& NewEntry
->Entries
; i
++) {
410 AddMenuEntry(NewEntry
, Entry
->Entries
[i
]);
412 NewEntry
->Hint1
= (Entry
->Hint1
) ? StrDuplicate(Entry
->Hint1
) : NULL
;
413 NewEntry
->Hint2
= (Entry
->Hint2
) ? StrDuplicate(Entry
->Hint2
) : NULL
;
416 } // static REFIT_MENU_SCREEN* CopyMenuScreen()
418 // Creates a copy of a menu entry. Intended to enable moving a stack-based
419 // menu entry (such as the ones for the "reboot" and "exit" functions) to
420 // to the heap. This enables easier deletion of the whole set of menu
421 // entries when re-scanning.
422 // Returns a pointer to the copy of the menu entry.
423 static REFIT_MENU_ENTRY
* CopyMenuEntry(REFIT_MENU_ENTRY
*Entry
) {
424 REFIT_MENU_ENTRY
*NewEntry
;
426 NewEntry
= AllocateZeroPool(sizeof(REFIT_MENU_ENTRY
));
427 if ((Entry
!= NULL
) && (NewEntry
!= NULL
)) {
428 CopyMem(NewEntry
, Entry
, sizeof(REFIT_MENU_ENTRY
));
429 NewEntry
->Title
= (Entry
->Title
) ? StrDuplicate(Entry
->Title
) : NULL
;
430 if (Entry
->BadgeImage
!= NULL
) {
431 NewEntry
->BadgeImage
= AllocatePool(sizeof(EG_IMAGE
));
432 if (NewEntry
->BadgeImage
!= NULL
)
433 CopyMem(NewEntry
->BadgeImage
, Entry
->BadgeImage
, sizeof(EG_IMAGE
));
435 if (Entry
->Image
!= NULL
) {
436 NewEntry
->Image
= AllocatePool(sizeof(EG_IMAGE
));
437 if (NewEntry
->Image
!= NULL
)
438 CopyMem(NewEntry
->Image
, Entry
->Image
, sizeof(EG_IMAGE
));
440 if (Entry
->SubScreen
!= NULL
) {
441 NewEntry
->SubScreen
= CopyMenuScreen(Entry
->SubScreen
);
445 } // REFIT_MENU_ENTRY* CopyMenuEntry()
447 // Creates a new LOADER_ENTRY data structure and populates it with
448 // default values from the specified Entry, or NULL values if Entry
449 // is unspecified (NULL).
450 // Returns a pointer to the new data structure, or NULL if it
451 // couldn't be allocated
452 LOADER_ENTRY
*InitializeLoaderEntry(IN LOADER_ENTRY
*Entry
) {
453 LOADER_ENTRY
*NewEntry
= NULL
;
455 NewEntry
= AllocateZeroPool(sizeof(LOADER_ENTRY
));
456 if (NewEntry
!= NULL
) {
457 NewEntry
->me
.Title
= NULL
;
458 NewEntry
->me
.Tag
= TAG_LOADER
;
459 NewEntry
->Enabled
= TRUE
;
460 NewEntry
->UseGraphicsMode
= FALSE
;
461 NewEntry
->OSType
= 0;
463 NewEntry
->LoaderPath
= (Entry
->LoaderPath
) ? StrDuplicate(Entry
->LoaderPath
) : NULL
;
464 NewEntry
->VolName
= (Entry
->VolName
) ? StrDuplicate(Entry
->VolName
) : NULL
;
465 NewEntry
->DevicePath
= Entry
->DevicePath
;
466 NewEntry
->UseGraphicsMode
= Entry
->UseGraphicsMode
;
467 NewEntry
->LoadOptions
= (Entry
->LoadOptions
) ? StrDuplicate(Entry
->LoadOptions
) : NULL
;
468 NewEntry
->InitrdPath
= (Entry
->InitrdPath
) ? StrDuplicate(Entry
->InitrdPath
) : NULL
;
472 } // LOADER_ENTRY *InitializeLoaderEntry()
474 // Adds InitrdPath to Options, but only if Options doesn't already include an
475 // initrd= line. Done to enable overriding the default initrd selection in a
476 // refind_linux.conf file's options list.
477 // Returns a pointer to a new string. The calling function is responsible for
478 // freeing its memory.
479 static CHAR16
*AddInitrdToOptions(CHAR16
*Options
, CHAR16
*InitrdPath
) {
480 CHAR16
*NewOptions
= NULL
;
483 NewOptions
= StrDuplicate(Options
);
484 if ((InitrdPath
!= NULL
) && !StriSubCmp(L
"initrd=", Options
)) {
485 MergeStrings(&NewOptions
, L
"initrd=", L
' ');
486 MergeStrings(&NewOptions
, InitrdPath
, 0);
489 } // CHAR16 *AddInitrdToOptions()
491 // Prepare a REFIT_MENU_SCREEN data structure for a subscreen entry. This sets up
492 // the default entry that launches the boot loader using the same options as the
493 // main Entry does. Subsequent options can be added by the calling function.
494 // If a subscreen already exists in the Entry that's passed to this function,
495 // it's left unchanged and a pointer to it is returned.
496 // Returns a pointer to the new subscreen data structure, or NULL if there
497 // were problems allocating memory.
498 REFIT_MENU_SCREEN
*InitializeSubScreen(IN LOADER_ENTRY
*Entry
) {
499 CHAR16
*FileName
, *MainOptions
= NULL
;
500 REFIT_MENU_SCREEN
*SubScreen
= NULL
;
501 LOADER_ENTRY
*SubEntry
;
503 FileName
= Basename(Entry
->LoaderPath
);
504 if (Entry
->me
.SubScreen
== NULL
) { // No subscreen yet; initialize default entry....
505 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
506 if (SubScreen
!= NULL
) {
507 SubScreen
->Title
= AllocateZeroPool(sizeof(CHAR16
) * 256);
508 SPrint(SubScreen
->Title
, 255, L
"Boot Options for %s on %s",
509 (Entry
->Title
!= NULL
) ? Entry
->Title
: FileName
, Entry
->VolName
);
510 SubScreen
->TitleImage
= Entry
->me
.Image
;
512 SubEntry
= InitializeLoaderEntry(Entry
);
513 if (SubEntry
!= NULL
) {
514 SubEntry
->me
.Title
= StrDuplicate(L
"Boot using default options");
515 MainOptions
= SubEntry
->LoadOptions
;
516 SubEntry
->LoadOptions
= AddInitrdToOptions(MainOptions
, SubEntry
->InitrdPath
);
517 MyFreePool(MainOptions
);
518 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
519 } // if (SubEntry != NULL)
520 SubScreen
->Hint1
= StrDuplicate(SUBSCREEN_HINT1
);
521 if (GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_EDITOR
) {
522 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2_NO_EDITOR
);
524 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2
);
526 } // if (SubScreen != NULL)
527 } else { // existing subscreen; less initialization, and just add new entry later....
528 SubScreen
= Entry
->me
.SubScreen
;
531 } // REFIT_MENU_SCREEN *InitializeSubScreen()
533 VOID
GenerateSubScreen(LOADER_ENTRY
*Entry
, IN REFIT_VOLUME
*Volume
) {
534 REFIT_MENU_SCREEN
*SubScreen
;
535 LOADER_ENTRY
*SubEntry
;
537 CHAR16 DiagsFileName
[256];
542 // create the submenu
543 if (StrLen(Entry
->Title
) == 0) {
544 MyFreePool(Entry
->Title
);
547 SubScreen
= InitializeSubScreen(Entry
);
549 // loader-specific submenu entries
550 if (Entry
->OSType
== 'M') { // entries for Mac OS X
552 SubEntry
= InitializeLoaderEntry(Entry
);
553 if (SubEntry
!= NULL
) {
554 SubEntry
->me
.Title
= L
"Boot Mac OS X with a 64-bit kernel";
555 SubEntry
->LoadOptions
= L
"arch=x86_64";
556 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
557 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
560 SubEntry
= InitializeLoaderEntry(Entry
);
561 if (SubEntry
!= NULL
) {
562 SubEntry
->me
.Title
= L
"Boot Mac OS X with a 32-bit kernel";
563 SubEntry
->LoadOptions
= L
"arch=i386";
564 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
565 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
569 if (!(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_SINGLEUSER
)) {
570 SubEntry
= InitializeLoaderEntry(Entry
);
571 if (SubEntry
!= NULL
) {
572 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode";
573 SubEntry
->UseGraphicsMode
= FALSE
;
574 SubEntry
->LoadOptions
= L
"-v";
575 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
579 SubEntry
= InitializeLoaderEntry(Entry
);
580 if (SubEntry
!= NULL
) {
581 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode (64-bit)";
582 SubEntry
->UseGraphicsMode
= FALSE
;
583 SubEntry
->LoadOptions
= L
"-v arch=x86_64";
584 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
587 SubEntry
= InitializeLoaderEntry(Entry
);
588 if (SubEntry
!= NULL
) {
589 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode (32-bit)";
590 SubEntry
->UseGraphicsMode
= FALSE
;
591 SubEntry
->LoadOptions
= L
"-v arch=i386";
592 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
596 SubEntry
= InitializeLoaderEntry(Entry
);
597 if (SubEntry
!= NULL
) {
598 SubEntry
->me
.Title
= L
"Boot Mac OS X in single user mode";
599 SubEntry
->UseGraphicsMode
= FALSE
;
600 SubEntry
->LoadOptions
= L
"-v -s";
601 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
603 } // single-user mode allowed
605 if (!(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_SAFEMODE
)) {
606 SubEntry
= InitializeLoaderEntry(Entry
);
607 if (SubEntry
!= NULL
) {
608 SubEntry
->me
.Title
= L
"Boot Mac OS X in safe mode";
609 SubEntry
->UseGraphicsMode
= FALSE
;
610 SubEntry
->LoadOptions
= L
"-v -x";
611 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
613 } // safe mode allowed
615 // check for Apple hardware diagnostics
616 StrCpy(DiagsFileName
, L
"System\\Library\\CoreServices\\.diagnostics\\diags.efi");
617 if (FileExists(Volume
->RootDir
, DiagsFileName
) && !(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_HWTEST
)) {
618 SubEntry
= InitializeLoaderEntry(Entry
);
619 if (SubEntry
!= NULL
) {
620 SubEntry
->me
.Title
= L
"Run Apple Hardware Test";
621 MyFreePool(SubEntry
->LoaderPath
);
622 SubEntry
->LoaderPath
= StrDuplicate(DiagsFileName
);
623 SubEntry
->DevicePath
= FileDevicePath(Volume
->DeviceHandle
, SubEntry
->LoaderPath
);
624 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
625 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
627 } // if diagnostics entry found
629 } else if (Entry
->OSType
== 'L') { // entries for Linux kernels with EFI stub loaders
630 File
= ReadLinuxOptionsFile(Entry
->LoaderPath
, Volume
);
632 InitrdName
= FindInitrd(Entry
->LoaderPath
, Volume
);
633 TokenCount
= ReadTokenLine(File
, &TokenList
);
634 // first entry requires special processing, since it was initially set
635 // up with a default title but correct options by InitializeSubScreen(),
637 if ((SubScreen
->Entries
!= NULL
) && (SubScreen
->Entries
[0] != NULL
)) {
638 MyFreePool(SubScreen
->Entries
[0]->Title
);
639 SubScreen
->Entries
[0]->Title
= TokenList
[0] ? StrDuplicate(TokenList
[0]) : StrDuplicate(L
"Boot Linux");
641 FreeTokenLine(&TokenList
, &TokenCount
);
642 while ((TokenCount
= ReadTokenLine(File
, &TokenList
)) > 1) {
643 SubEntry
= InitializeLoaderEntry(Entry
);
644 SubEntry
->me
.Title
= TokenList
[0] ? StrDuplicate(TokenList
[0]) : StrDuplicate(L
"Boot Linux");
645 MyFreePool(SubEntry
->LoadOptions
);
646 SubEntry
->LoadOptions
= AddInitrdToOptions(TokenList
[1], InitrdName
);
647 FreeTokenLine(&TokenList
, &TokenCount
);
648 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_LINUX
;
649 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
651 MyFreePool(InitrdName
);
653 } // if Linux options file exists
655 } else if (Entry
->OSType
== 'E') { // entries for ELILO
656 SubEntry
= InitializeLoaderEntry(Entry
);
657 if (SubEntry
!= NULL
) {
658 SubEntry
->me
.Title
= L
"Run ELILO in interactive mode";
659 SubEntry
->LoadOptions
= L
"-p";
660 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
661 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
664 SubEntry
= InitializeLoaderEntry(Entry
);
665 if (SubEntry
!= NULL
) {
666 SubEntry
->me
.Title
= L
"Boot Linux for a 17\" iMac or a 15\" MacBook Pro (*)";
667 SubEntry
->UseGraphicsMode
= TRUE
;
668 SubEntry
->LoadOptions
= L
"-d 0 i17";
669 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
670 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
673 SubEntry
= InitializeLoaderEntry(Entry
);
674 if (SubEntry
!= NULL
) {
675 SubEntry
->me
.Title
= L
"Boot Linux for a 20\" iMac (*)";
676 SubEntry
->UseGraphicsMode
= TRUE
;
677 SubEntry
->LoadOptions
= L
"-d 0 i20";
678 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
679 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
682 SubEntry
= InitializeLoaderEntry(Entry
);
683 if (SubEntry
!= NULL
) {
684 SubEntry
->me
.Title
= L
"Boot Linux for a Mac Mini (*)";
685 SubEntry
->UseGraphicsMode
= TRUE
;
686 SubEntry
->LoadOptions
= L
"-d 0 mini";
687 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
688 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
691 AddMenuInfoLine(SubScreen
, L
"NOTE: This is an example. Entries");
692 AddMenuInfoLine(SubScreen
, L
"marked with (*) may not work.");
694 } else if (Entry
->OSType
== 'X') { // entries for xom.efi
695 // by default, skip the built-in selection and boot from hard disk only
696 Entry
->LoadOptions
= L
"-s -h";
698 SubEntry
= InitializeLoaderEntry(Entry
);
699 if (SubEntry
!= NULL
) {
700 SubEntry
->me
.Title
= L
"Boot Windows from Hard Disk";
701 SubEntry
->LoadOptions
= L
"-s -h";
702 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
703 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
706 SubEntry
= InitializeLoaderEntry(Entry
);
707 if (SubEntry
!= NULL
) {
708 SubEntry
->me
.Title
= L
"Boot Windows from CD-ROM";
709 SubEntry
->LoadOptions
= L
"-s -c";
710 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
711 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
714 SubEntry
= InitializeLoaderEntry(Entry
);
715 if (SubEntry
!= NULL
) {
716 SubEntry
->me
.Title
= L
"Run XOM in text mode";
717 SubEntry
->UseGraphicsMode
= FALSE
;
718 SubEntry
->LoadOptions
= L
"-v";
719 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
720 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
722 } // entries for xom.efi
723 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
724 Entry
->me
.SubScreen
= SubScreen
;
725 } // VOID GenerateSubScreen()
727 // Returns options for a Linux kernel. Reads them from an options file in the
728 // kernel's directory; and if present, adds an initrd= option for an initial
729 // RAM disk file with the same version number as the kernel file.
730 static CHAR16
* GetMainLinuxOptions(IN CHAR16
* LoaderPath
, IN REFIT_VOLUME
*Volume
) {
731 CHAR16
*Options
= NULL
, *InitrdName
, *FullOptions
= NULL
;
733 Options
= GetFirstOptionsFromFile(LoaderPath
, Volume
);
734 InitrdName
= FindInitrd(LoaderPath
, Volume
);
735 FullOptions
= AddInitrdToOptions(Options
, InitrdName
);
738 MyFreePool(InitrdName
);
739 return (FullOptions
);
740 } // static CHAR16 * GetMainLinuxOptions()
742 // Sets a few defaults for a loader entry -- mainly the icon, but also the OS type
743 // code and shortcut letter. For Linux EFI stub loaders, also sets kernel options
744 // that will (with luck) work fairly automatically.
745 VOID
SetLoaderDefaults(LOADER_ENTRY
*Entry
, CHAR16
*LoaderPath
, REFIT_VOLUME
*Volume
) {
746 CHAR16
*FileName
, *PathOnly
, *NoExtension
, *OSIconName
= NULL
, *Temp
, *SubString
;
747 CHAR16 ShortcutLetter
= 0;
750 FileName
= Basename(LoaderPath
);
751 PathOnly
= FindPath(LoaderPath
);
752 NoExtension
= StripEfiExtension(FileName
);
754 // locate a custom icon for the loader
755 // Anything found here takes precedence over the "hints" in the OSIconName variable
756 if (!Entry
->me
.Image
)
757 Entry
->me
.Image
= egLoadIconAnyType(Volume
->RootDir
, PathOnly
, NoExtension
, 128);
758 if (!Entry
->me
.Image
)
759 Entry
->me
.Image
= egCopyImage(Volume
->VolIconImage
);
761 // Begin creating icon "hints" by using last part of directory path leading
763 Temp
= FindLastDirName(LoaderPath
);
764 MergeStrings(&OSIconName
, Temp
, L
',');
767 if (OSIconName
!= NULL
) {
768 ShortcutLetter
= OSIconName
[0];
771 // Add every "word" in the volume label, delimited by spaces, dashes (-), or
772 // underscores (_), to the list of hints to be used in searching for OS
774 if ((Volume
->VolName
) && (StrLen(Volume
->VolName
) > 0)) {
775 Temp
= SubString
= StrDuplicate(Volume
->VolName
);
777 Length
= StrLen(Temp
);
778 for (i
= 0; i
< Length
; i
++) {
779 if ((Temp
[i
] == L
' ') || (Temp
[i
] == L
'_') || (Temp
[i
] == L
'-')) {
781 if (StrLen(SubString
) > 0)
782 MergeStrings(&OSIconName
, SubString
, L
',');
783 SubString
= Temp
+ i
+ 1;
786 MergeStrings(&OSIconName
, SubString
, L
',');
791 // detect specific loaders
792 if (StriSubCmp(L
"bzImage", LoaderPath
) || StriSubCmp(L
"vmlinuz", LoaderPath
)) {
793 MergeStrings(&OSIconName
, L
"linux", L
',');
795 if (ShortcutLetter
== 0)
796 ShortcutLetter
= 'L';
797 Entry
->LoadOptions
= GetMainLinuxOptions(LoaderPath
, Volume
);
798 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_LINUX
;
799 } else if (StriSubCmp(L
"refit", LoaderPath
)) {
800 MergeStrings(&OSIconName
, L
"refit", L
',');
802 ShortcutLetter
= 'R';
803 } else if (StriCmp(LoaderPath
, MACOSX_LOADER_PATH
) == 0) {
804 if (Volume
->VolIconImage
!= NULL
) { // custom icon file found
805 Entry
->me
.Image
= Volume
->VolIconImage
;
807 MergeStrings(&OSIconName
, L
"mac", L
',');
809 ShortcutLetter
= 'M';
810 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
811 } else if (StriCmp(FileName
, L
"diags.efi") == 0) {
812 MergeStrings(&OSIconName
, L
"hwtest", L
',');
813 } else if (StriCmp(FileName
, L
"e.efi") == 0 || StriCmp(FileName
, L
"elilo.efi") == 0 || StriSubCmp(L
"elilo", FileName
)) {
814 MergeStrings(&OSIconName
, L
"elilo,linux", L
',');
816 if (ShortcutLetter
== 0)
817 ShortcutLetter
= 'L';
818 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
819 } else if (StriSubCmp(L
"grub", FileName
)) {
821 ShortcutLetter
= 'G';
822 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_GRUB
;
823 } else if (StriCmp(FileName
, L
"cdboot.efi") == 0 ||
824 StriCmp(FileName
, L
"bootmgr.efi") == 0 ||
825 StriCmp(FileName
, L
"bootmgfw.efi") == 0) {
826 MergeStrings(&OSIconName
, L
"win", L
',');
828 ShortcutLetter
= 'W';
829 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
830 } else if (StriCmp(FileName
, L
"xom.efi") == 0) {
831 MergeStrings(&OSIconName
, L
"xom,win", L
',');
832 Entry
->UseGraphicsMode
= TRUE
;
834 ShortcutLetter
= 'W';
835 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
838 if ((ShortcutLetter
>= 'a') && (ShortcutLetter
<= 'z'))
839 ShortcutLetter
= ShortcutLetter
- 'a' + 'A'; // convert lowercase to uppercase
840 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
841 if (Entry
->me
.Image
== NULL
)
842 Entry
->me
.Image
= LoadOSIcon(OSIconName
, L
"unknown", FALSE
);
843 MyFreePool(PathOnly
);
844 } // VOID SetLoaderDefaults()
846 // Add a specified EFI boot loader to the list, using automatic settings
847 // for icons, options, etc.
848 LOADER_ENTRY
* AddLoaderEntry(IN CHAR16
*LoaderPath
, IN CHAR16
*LoaderTitle
, IN REFIT_VOLUME
*Volume
) {
851 CleanUpPathNameSlashes(LoaderPath
);
852 Entry
= InitializeLoaderEntry(NULL
);
854 Entry
->Title
= StrDuplicate((LoaderTitle
!= NULL
) ? LoaderTitle
: LoaderPath
);
855 Entry
->me
.Title
= AllocateZeroPool(sizeof(CHAR16
) * 256);
856 SPrint(Entry
->me
.Title
, 255, L
"Boot %s from %s", (LoaderTitle
!= NULL
) ? LoaderTitle
: LoaderPath
, Volume
->VolName
);
858 Entry
->me
.BadgeImage
= Volume
->VolBadgeImage
;
859 if ((LoaderPath
!= NULL
) && (LoaderPath
[0] != L
'\\')) {
860 Entry
->LoaderPath
= StrDuplicate(L
"\\");
862 Entry
->LoaderPath
= NULL
;
864 MergeStrings(&(Entry
->LoaderPath
), LoaderPath
, 0);
865 Entry
->VolName
= Volume
->VolName
;
866 Entry
->DevicePath
= FileDevicePath(Volume
->DeviceHandle
, Entry
->LoaderPath
);
867 SetLoaderDefaults(Entry
, LoaderPath
, Volume
);
868 GenerateSubScreen(Entry
, Volume
);
869 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
873 } // LOADER_ENTRY * AddLoaderEntry()
875 // Returns -1 if (Time1 < Time2), +1 if (Time1 > Time2), or 0 if
876 // (Time1 == Time2). Precision is only to the nearest second; since
877 // this is used for sorting boot loader entries, differences smaller
878 // than this are likely to be meaningless (and unlikely!).
879 INTN
TimeComp(IN EFI_TIME
*Time1
, IN EFI_TIME
*Time2
) {
880 INT64 Time1InSeconds
, Time2InSeconds
;
882 // Following values are overestimates; I'm assuming 31 days in every month.
883 // This is fine for the purpose of this function, which is limited
884 Time1InSeconds
= Time1
->Second
+ (Time1
->Minute
* 60) + (Time1
->Hour
* 3600) + (Time1
->Day
* 86400) +
885 (Time1
->Month
* 2678400) + ((Time1
->Year
- 1998) * 32140800);
886 Time2InSeconds
= Time2
->Second
+ (Time2
->Minute
* 60) + (Time2
->Hour
* 3600) + (Time2
->Day
* 86400) +
887 (Time2
->Month
* 2678400) + ((Time2
->Year
- 1998) * 32140800);
888 if (Time1InSeconds
< Time2InSeconds
)
890 else if (Time1InSeconds
> Time2InSeconds
)
896 // Adds a loader list element, keeping it sorted by date. Returns the new
897 // first element (the one with the most recent date).
898 static struct LOADER_LIST
* AddLoaderListEntry(struct LOADER_LIST
*LoaderList
, struct LOADER_LIST
*NewEntry
) {
899 struct LOADER_LIST
*LatestEntry
, *CurrentEntry
, *PrevEntry
= NULL
;
901 LatestEntry
= CurrentEntry
= LoaderList
;
902 if (LoaderList
== NULL
) {
903 LatestEntry
= NewEntry
;
905 while ((CurrentEntry
!= NULL
) && (TimeComp(&(NewEntry
->TimeStamp
), &(CurrentEntry
->TimeStamp
)) < 0)) {
906 PrevEntry
= CurrentEntry
;
907 CurrentEntry
= CurrentEntry
->NextEntry
;
909 NewEntry
->NextEntry
= CurrentEntry
;
910 if (PrevEntry
== NULL
) {
911 LatestEntry
= NewEntry
;
913 PrevEntry
->NextEntry
= NewEntry
;
916 return (LatestEntry
);
917 } // static VOID AddLoaderListEntry()
919 // Delete the LOADER_LIST linked list
920 static VOID
CleanUpLoaderList(struct LOADER_LIST
*LoaderList
) {
921 struct LOADER_LIST
*Temp
;
923 while (LoaderList
!= NULL
) {
925 LoaderList
= LoaderList
->NextEntry
;
926 MyFreePool(Temp
->FileName
);
929 } // static VOID CleanUpLoaderList()
931 // Returns FALSE if the specified file/volume matches the GlobalConfig.DontScanDirs
932 // or GlobalConfig.DontScanVolumes specification, or if Path points to a volume
933 // other than the one specified by Volume, or if the specified path is SelfDir.
934 // Returns TRUE if none of these conditions is met -- that is, if the path is
935 // eligible for scanning.
936 static BOOLEAN
ShouldScan(REFIT_VOLUME
*Volume
, CHAR16
*Path
) {
937 CHAR16
*VolName
= NULL
, *DontScanDir
;
939 BOOLEAN ScanIt
= TRUE
;
941 if (IsIn(Volume
->VolName
, GlobalConfig
.DontScanVolumes
))
944 if ((StriCmp(Path
, SelfDirPath
) == 0) && (Volume
->DeviceHandle
== SelfVolume
->DeviceHandle
))
947 while ((DontScanDir
= FindCommaDelimited(GlobalConfig
.DontScanDirs
, i
++)) && ScanIt
) {
948 SplitVolumeAndFilename(&DontScanDir
, &VolName
);
949 CleanUpPathNameSlashes(DontScanDir
);
950 if (VolName
!= NULL
) {
951 if ((StriCmp(VolName
, Volume
->VolName
) == 0) && (StriCmp(DontScanDir
, Path
) == 0))
953 if ((StrLen(VolName
) > 2) && (VolName
[0] == L
'f') && (VolName
[1] == L
's') && (VolName
[2] >= L
'0') && (VolName
[2] <= L
'9')) {
954 VolNum
= Atoi(VolName
+ 2);
955 if ((VolNum
== Volume
->VolNumber
) && (StriCmp(DontScanDir
, Path
) == 0))
959 if (StriCmp(DontScanDir
, Path
) == 0)
962 MyFreePool(DontScanDir
);
966 } // BOOLEAN ShouldScan()
968 // Returns TRUE if the file is byte-for-byte identical with the fallback file
969 // on the volume AND if the file is not itself the fallback file; returns
970 // FALSE if the file is not identical to the fallback file OR if the file
971 // IS the fallback file. Intended for use in excluding the fallback boot
972 // loader when it's a duplicate of another boot loader.
973 static BOOLEAN
DuplicatesFallback(IN REFIT_VOLUME
*Volume
, IN CHAR16
*FileName
) {
974 CHAR8
*FileContents
, *FallbackContents
;
975 EFI_FILE_HANDLE FileHandle
, FallbackHandle
;
976 EFI_FILE_INFO
*FileInfo
, *FallbackInfo
;
977 UINTN FileSize
= 0, FallbackSize
= 0;
979 BOOLEAN AreIdentical
= FALSE
;
981 if (!FileExists(Volume
->RootDir
, FileName
) || !FileExists(Volume
->RootDir
, FALLBACK_FULLNAME
))
984 CleanUpPathNameSlashes(FileName
);
986 if (StriCmp(FileName
, FALLBACK_FULLNAME
) == 0)
987 return FALSE
; // identical filenames, so not a duplicate....
989 Status
= refit_call5_wrapper(Volume
->RootDir
->Open
, Volume
->RootDir
, &FileHandle
, FileName
, EFI_FILE_MODE_READ
, 0);
990 if (Status
== EFI_SUCCESS
) {
991 FileInfo
= LibFileInfo(FileHandle
);
992 FileSize
= FileInfo
->FileSize
;
997 Status
= refit_call5_wrapper(Volume
->RootDir
->Open
, Volume
->RootDir
, &FallbackHandle
, FALLBACK_FULLNAME
, EFI_FILE_MODE_READ
, 0);
998 if (Status
== EFI_SUCCESS
) {
999 FallbackInfo
= LibFileInfo(FallbackHandle
);
1000 FallbackSize
= FallbackInfo
->FileSize
;
1002 refit_call1_wrapper(FileHandle
->Close
, FileHandle
);
1006 if (FallbackSize
!= FileSize
) { // not same size, so can't be identical
1007 AreIdentical
= FALSE
;
1008 } else { // could be identical; do full check....
1009 FileContents
= AllocatePool(FileSize
);
1010 FallbackContents
= AllocatePool(FallbackSize
);
1011 if (FileContents
&& FallbackContents
) {
1012 Status
= refit_call3_wrapper(FileHandle
->Read
, FileHandle
, &FileSize
, FileContents
);
1013 if (Status
== EFI_SUCCESS
) {
1014 Status
= refit_call3_wrapper(FallbackHandle
->Read
, FallbackHandle
, &FallbackSize
, FallbackContents
);
1016 if (Status
== EFI_SUCCESS
) {
1017 AreIdentical
= (CompareMem(FileContents
, FallbackContents
, FileSize
) == 0);
1020 MyFreePool(FileContents
);
1021 MyFreePool(FallbackContents
);
1024 // BUG ALERT: Some systems (e.g., DUET, some Macs with large displays) crash if the
1025 // following two calls are reversed. Go figure....
1026 refit_call1_wrapper(FileHandle
->Close
, FallbackHandle
);
1027 refit_call1_wrapper(FileHandle
->Close
, FileHandle
);
1028 return AreIdentical
;
1029 } // BOOLEAN DuplicatesFallback()
1031 // Scan an individual directory for EFI boot loader files and, if found,
1032 // add them to the list. Exception: Ignores FALLBACK_FULLNAME, which is picked
1033 // up in ScanEfiFiles(). Sorts the entries within the loader directory so that
1034 // the most recent one appears first in the list.
1035 // Returns TRUE if a duplicate for FALLBACK_FILENAME was found, FALSE if not.
1036 static BOOLEAN
ScanLoaderDir(IN REFIT_VOLUME
*Volume
, IN CHAR16
*Path
, IN CHAR16
*Pattern
)
1039 REFIT_DIR_ITER DirIter
;
1040 EFI_FILE_INFO
*DirEntry
;
1041 CHAR16 FileName
[256], *Extension
;
1042 struct LOADER_LIST
*LoaderList
= NULL
, *NewLoader
;
1043 BOOLEAN FoundFallbackDuplicate
= FALSE
;
1045 if ((!SelfDirPath
|| !Path
|| ((StriCmp(Path
, SelfDirPath
) == 0) && (Volume
->DeviceHandle
!= SelfVolume
->DeviceHandle
)) ||
1046 (StriCmp(Path
, SelfDirPath
) != 0)) &&
1047 (ShouldScan(Volume
, Path
))) {
1048 // look through contents of the directory
1049 DirIterOpen(Volume
->RootDir
, Path
, &DirIter
);
1050 while (DirIterNext(&DirIter
, 2, Pattern
, &DirEntry
)) {
1051 Extension
= FindExtension(DirEntry
->FileName
);
1052 if (DirEntry
->FileName
[0] == '.' ||
1053 StriCmp(Extension
, L
".icns") == 0 ||
1054 StriCmp(Extension
, L
".png") == 0 ||
1055 (StriCmp(DirEntry
->FileName
, FALLBACK_BASENAME
) == 0 && (StriCmp(Path
, L
"EFI\\BOOT") == 0)) ||
1056 StriSubCmp(L
"shell", DirEntry
->FileName
) ||
1057 IsIn(DirEntry
->FileName
, GlobalConfig
.DontScanFiles
))
1058 continue; // skip this
1061 SPrint(FileName
, 255, L
"\\%s\\%s", Path
, DirEntry
->FileName
);
1063 SPrint(FileName
, 255, L
"\\%s", DirEntry
->FileName
);
1064 CleanUpPathNameSlashes(FileName
);
1065 NewLoader
= AllocateZeroPool(sizeof(struct LOADER_LIST
));
1066 if (NewLoader
!= NULL
) {
1067 NewLoader
->FileName
= StrDuplicate(FileName
);
1068 NewLoader
->TimeStamp
= DirEntry
->ModificationTime
;
1069 LoaderList
= AddLoaderListEntry(LoaderList
, NewLoader
);
1070 if (DuplicatesFallback(Volume
, FileName
))
1071 FoundFallbackDuplicate
= TRUE
;
1073 MyFreePool(Extension
);
1076 NewLoader
= LoaderList
;
1077 while (NewLoader
!= NULL
) {
1078 AddLoaderEntry(NewLoader
->FileName
, NULL
, Volume
);
1079 NewLoader
= NewLoader
->NextEntry
;
1082 CleanUpLoaderList(LoaderList
);
1083 Status
= DirIterClose(&DirIter
);
1084 // NOTE: EFI_INVALID_PARAMETER really is an error that should be reported;
1085 // but I've gotten reports from users who are getting this error occasionally
1086 // and I can't find anything wrong or reproduce the problem, so I'm putting
1087 // it down to buggy EFI implementations and ignoring that particular error....
1088 if ((Status
!= EFI_NOT_FOUND
) && (Status
!= EFI_INVALID_PARAMETER
)) {
1090 SPrint(FileName
, 255, L
"while scanning the %s directory", Path
);
1092 StrCpy(FileName
, L
"while scanning the root directory");
1093 CheckError(Status
, FileName
);
1094 } // if (Status != EFI_NOT_FOUND)
1095 } // if not scanning a blacklisted directory
1097 return FoundFallbackDuplicate
;
1098 } /* static VOID ScanLoaderDir() */
1100 static VOID
ScanEfiFiles(REFIT_VOLUME
*Volume
) {
1102 REFIT_DIR_ITER EfiDirIter
;
1103 EFI_FILE_INFO
*EfiDirEntry
;
1104 CHAR16 FileName
[256], *Directory
, *MatchPatterns
, *VolName
= NULL
, *SelfPath
;
1106 BOOLEAN ScanFallbackLoader
= TRUE
;
1108 MatchPatterns
= StrDuplicate(LOADER_MATCH_PATTERNS
);
1109 if (GlobalConfig
.ScanAllLinux
)
1110 MergeStrings(&MatchPatterns
, LINUX_MATCH_PATTERNS
, L
',');
1112 if ((Volume
->RootDir
!= NULL
) && (Volume
->VolName
!= NULL
)) {
1113 // check for Mac OS X boot loader
1114 if (ShouldScan(Volume
, L
"System\\Library\\CoreServices")) {
1115 StrCpy(FileName
, MACOSX_LOADER_PATH
);
1116 if (FileExists(Volume
->RootDir
, FileName
) && !IsIn(L
"boot.efi", GlobalConfig
.DontScanFiles
)) {
1117 AddLoaderEntry(FileName
, L
"Mac OS X", Volume
);
1118 if (DuplicatesFallback(Volume
, FileName
))
1119 ScanFallbackLoader
= FALSE
;
1123 StrCpy(FileName
, L
"System\\Library\\CoreServices\\xom.efi");
1124 if (FileExists(Volume
->RootDir
, FileName
) && !IsIn(L
"boot.efi", GlobalConfig
.DontScanFiles
)) {
1125 AddLoaderEntry(FileName
, L
"Windows XP (XoM)", Volume
);
1126 if (DuplicatesFallback(Volume
, FileName
))
1127 ScanFallbackLoader
= FALSE
;
1129 } // if should scan Mac directory
1131 // check for Microsoft boot loader/menu
1132 StrCpy(FileName
, L
"EFI\\Microsoft\\Boot\\Bootmgfw.efi");
1133 if (FileExists(Volume
->RootDir
, FileName
) && ShouldScan(Volume
, L
"EFI\\Microsoft\\Boot") &&
1134 !IsIn(L
"bootmgfw.efi", GlobalConfig
.DontScanFiles
)) {
1135 AddLoaderEntry(FileName
, L
"Microsoft EFI boot", Volume
);
1136 if (DuplicatesFallback(Volume
, FileName
))
1137 ScanFallbackLoader
= FALSE
;
1140 // scan the root directory for EFI executables
1141 if (ScanLoaderDir(Volume
, L
"\\", MatchPatterns
))
1142 ScanFallbackLoader
= FALSE
;
1144 // scan subdirectories of the EFI directory (as per the standard)
1145 DirIterOpen(Volume
->RootDir
, L
"EFI", &EfiDirIter
);
1146 while (DirIterNext(&EfiDirIter
, 1, NULL
, &EfiDirEntry
)) {
1147 if (StriCmp(EfiDirEntry
->FileName
, L
"tools") == 0 || EfiDirEntry
->FileName
[0] == '.')
1148 continue; // skip this, doesn't contain boot loaders or is scanned later
1149 SPrint(FileName
, 255, L
"EFI\\%s", EfiDirEntry
->FileName
);
1150 if (ScanLoaderDir(Volume
, FileName
, MatchPatterns
))
1151 ScanFallbackLoader
= FALSE
;
1153 Status
= DirIterClose(&EfiDirIter
);
1154 if (Status
!= EFI_NOT_FOUND
)
1155 CheckError(Status
, L
"while scanning the EFI directory");
1157 // Scan user-specified (or additional default) directories....
1159 while ((Directory
= FindCommaDelimited(GlobalConfig
.AlsoScan
, i
++)) != NULL
) {
1160 SplitVolumeAndFilename(&Directory
, &VolName
);
1161 CleanUpPathNameSlashes(Directory
);
1162 Length
= StrLen(Directory
);
1163 if ((Length
> 0) && ScanLoaderDir(Volume
, Directory
, MatchPatterns
))
1164 ScanFallbackLoader
= FALSE
;
1165 MyFreePool(Directory
);
1166 MyFreePool(VolName
);
1169 // Don't scan the fallback loader if it's on the same volume and a duplicate of rEFInd itself....
1170 SelfPath
= DevicePathToStr(SelfLoadedImage
->FilePath
);
1171 CleanUpPathNameSlashes(SelfPath
);
1172 if ((Volume
->DeviceHandle
== SelfLoadedImage
->DeviceHandle
) && DuplicatesFallback(Volume
, SelfPath
))
1173 ScanFallbackLoader
= FALSE
;
1175 // If not a duplicate & if it exists & if it's not us, create an entry
1176 // for the fallback boot loader
1177 if (ScanFallbackLoader
&& FileExists(Volume
->RootDir
, FALLBACK_FULLNAME
) && ShouldScan(Volume
, L
"EFI\\BOOT"))
1178 AddLoaderEntry(FALLBACK_FULLNAME
, L
"Fallback boot loader", Volume
);
1180 } // static VOID ScanEfiFiles()
1182 // Scan internal disks for valid EFI boot loaders....
1183 static VOID
ScanInternal(VOID
) {
1186 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1187 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_INTERNAL
) {
1188 ScanEfiFiles(Volumes
[VolumeIndex
]);
1191 } // static VOID ScanInternal()
1193 // Scan external disks for valid EFI boot loaders....
1194 static VOID
ScanExternal(VOID
) {
1197 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1198 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_EXTERNAL
) {
1199 ScanEfiFiles(Volumes
[VolumeIndex
]);
1202 } // static VOID ScanExternal()
1204 // Scan internal disks for valid EFI boot loaders....
1205 static VOID
ScanOptical(VOID
) {
1208 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1209 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_OPTICAL
) {
1210 ScanEfiFiles(Volumes
[VolumeIndex
]);
1213 } // static VOID ScanOptical()
1216 // legacy boot functions
1219 static EFI_STATUS
ActivateMbrPartition(IN EFI_BLOCK_IO
*BlockIO
, IN UINTN PartitionIndex
)
1222 UINT8 SectorBuffer
[512];
1223 MBR_PARTITION_INFO
*MbrTable
, *EMbrTable
;
1224 UINT32 ExtBase
, ExtCurrent
, NextExtCurrent
;
1225 UINTN LogicalPartitionIndex
= 4;
1227 BOOLEAN HaveBootCode
;
1230 Status
= refit_call5_wrapper(BlockIO
->ReadBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, 0, 512, SectorBuffer
);
1231 if (EFI_ERROR(Status
))
1233 if (*((UINT16
*)(SectorBuffer
+ 510)) != 0xaa55)
1234 return EFI_NOT_FOUND
; // safety measure #1
1236 // add boot code if necessary
1237 HaveBootCode
= FALSE
;
1238 for (i
= 0; i
< MBR_BOOTCODE_SIZE
; i
++) {
1239 if (SectorBuffer
[i
] != 0) {
1240 HaveBootCode
= TRUE
;
1244 if (!HaveBootCode
) {
1245 // no boot code found in the MBR, add the syslinux MBR code
1246 SetMem(SectorBuffer
, MBR_BOOTCODE_SIZE
, 0);
1247 CopyMem(SectorBuffer
, syslinux_mbr
, SYSLINUX_MBR_SIZE
);
1250 // set the partition active
1251 MbrTable
= (MBR_PARTITION_INFO
*)(SectorBuffer
+ 446);
1253 for (i
= 0; i
< 4; i
++) {
1254 if (MbrTable
[i
].Flags
!= 0x00 && MbrTable
[i
].Flags
!= 0x80)
1255 return EFI_NOT_FOUND
; // safety measure #2
1256 if (i
== PartitionIndex
)
1257 MbrTable
[i
].Flags
= 0x80;
1258 else if (PartitionIndex
>= 4 && IS_EXTENDED_PART_TYPE(MbrTable
[i
].Type
)) {
1259 MbrTable
[i
].Flags
= 0x80;
1260 ExtBase
= MbrTable
[i
].StartLBA
;
1262 MbrTable
[i
].Flags
= 0x00;
1266 Status
= refit_call5_wrapper(BlockIO
->WriteBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, 0, 512, SectorBuffer
);
1267 if (EFI_ERROR(Status
))
1270 if (PartitionIndex
>= 4) {
1271 // we have to activate a logical partition, so walk the EMBR chain
1273 // NOTE: ExtBase was set above while looking at the MBR table
1274 for (ExtCurrent
= ExtBase
; ExtCurrent
; ExtCurrent
= NextExtCurrent
) {
1275 // read current EMBR
1276 Status
= refit_call5_wrapper(BlockIO
->ReadBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, ExtCurrent
, 512, SectorBuffer
);
1277 if (EFI_ERROR(Status
))
1279 if (*((UINT16
*)(SectorBuffer
+ 510)) != 0xaa55)
1280 return EFI_NOT_FOUND
; // safety measure #3
1282 // scan EMBR, set appropriate partition active
1283 EMbrTable
= (MBR_PARTITION_INFO
*)(SectorBuffer
+ 446);
1285 for (i
= 0; i
< 4; i
++) {
1286 if (EMbrTable
[i
].Flags
!= 0x00 && EMbrTable
[i
].Flags
!= 0x80)
1287 return EFI_NOT_FOUND
; // safety measure #4
1288 if (EMbrTable
[i
].StartLBA
== 0 || EMbrTable
[i
].Size
== 0)
1290 if (IS_EXTENDED_PART_TYPE(EMbrTable
[i
].Type
)) {
1291 // link to next EMBR
1292 NextExtCurrent
= ExtBase
+ EMbrTable
[i
].StartLBA
;
1293 EMbrTable
[i
].Flags
= (PartitionIndex
>= LogicalPartitionIndex
) ? 0x80 : 0x00;
1296 // logical partition
1297 EMbrTable
[i
].Flags
= (PartitionIndex
== LogicalPartitionIndex
) ? 0x80 : 0x00;
1298 LogicalPartitionIndex
++;
1302 // write current EMBR
1303 Status
= refit_call5_wrapper(BlockIO
->WriteBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, ExtCurrent
, 512, SectorBuffer
);
1304 if (EFI_ERROR(Status
))
1307 if (PartitionIndex
< LogicalPartitionIndex
)
1308 break; // stop the loop, no need to touch further EMBRs
1314 } /* static EFI_STATUS ActivateMbrPartition() */
1316 // early 2006 Core Duo / Core Solo models
1317 static UINT8 LegacyLoaderDevicePath1Data
[] = {
1318 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1319 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1320 0xFF, 0xFF, 0xF9, 0xFF, 0x00, 0x00, 0x00, 0x00,
1321 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1322 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1323 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1325 // mid-2006 Mac Pro (and probably other Core 2 models)
1326 static UINT8 LegacyLoaderDevicePath2Data
[] = {
1327 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1328 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1329 0xFF, 0xFF, 0xF7, 0xFF, 0x00, 0x00, 0x00, 0x00,
1330 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1331 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1332 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1334 // mid-2007 MBP ("Santa Rosa" based models)
1335 static UINT8 LegacyLoaderDevicePath3Data
[] = {
1336 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1337 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1338 0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
1339 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1340 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1341 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1344 static UINT8 LegacyLoaderDevicePath4Data
[] = {
1345 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1346 0x00, 0x00, 0xC0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1347 0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
1348 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1349 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1350 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1352 // late-2008 MB/MBP (NVidia chipset)
1353 static UINT8 LegacyLoaderDevicePath5Data
[] = {
1354 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1355 0x00, 0x40, 0xCB, 0xFF, 0x00, 0x00, 0x00, 0x00,
1356 0xFF, 0xBF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
1357 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1358 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1359 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1362 static EFI_DEVICE_PATH
*LegacyLoaderList
[] = {
1363 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath1Data
,
1364 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath2Data
,
1365 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath3Data
,
1366 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath4Data
,
1367 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath5Data
,
1371 #define MAX_DISCOVERED_PATHS (16)
1373 static VOID
StartLegacy(IN LEGACY_ENTRY
*Entry
)
1376 EG_IMAGE
*BootLogoImage
;
1377 UINTN ErrorInStep
= 0;
1378 EFI_DEVICE_PATH
*DiscoveredPathList
[MAX_DISCOVERED_PATHS
];
1380 BeginExternalScreen(TRUE
, L
"Booting Legacy OS (Mac mode)");
1382 BootLogoImage
= LoadOSIcon(Entry
->Volume
->OSIconName
, L
"legacy", TRUE
);
1383 if (BootLogoImage
!= NULL
)
1384 BltImageAlpha(BootLogoImage
,
1385 (UGAWidth
- BootLogoImage
->Width
) >> 1,
1386 (UGAHeight
- BootLogoImage
->Height
) >> 1,
1387 &StdBackgroundPixel
);
1389 if (Entry
->Volume
->IsMbrPartition
) {
1390 ActivateMbrPartition(Entry
->Volume
->WholeDiskBlockIO
, Entry
->Volume
->MbrPartitionIndex
);
1393 ExtractLegacyLoaderPaths(DiscoveredPathList
, MAX_DISCOVERED_PATHS
, LegacyLoaderList
);
1395 Status
= StartEFIImageList(DiscoveredPathList
, Entry
->LoadOptions
, NULL
, L
"legacy loader", 0, &ErrorInStep
, TRUE
);
1396 if (Status
== EFI_NOT_FOUND
) {
1397 if (ErrorInStep
== 1) {
1398 Print(L
"\nPlease make sure that you have the latest firmware update installed.\n");
1399 } else if (ErrorInStep
== 3) {
1400 Print(L
"\nThe firmware refused to boot from the selected volume. Note that external\n"
1401 L
"hard drives are not well-supported by Apple's firmware for legacy OS booting.\n");
1404 FinishExternalScreen();
1405 } /* static VOID StartLegacy() */
1407 // Start a device on a non-Mac using the EFI_LEGACY_BIOS_PROTOCOL
1408 #ifdef __MAKEWITH_TIANO
1409 static VOID
StartLegacyUEFI(IN LEGACY_ENTRY
*Entry
)
1411 BeginExternalScreen(TRUE
, L
"Booting Legacy OS (UEFI mode)");
1413 BdsLibConnectDevicePath (Entry
->BdsOption
->DevicePath
);
1414 BdsLibDoLegacyBoot(Entry
->BdsOption
);
1416 // If we get here, it means that there was a failure....
1417 Print(L
"Failure booting legacy (BIOS) OS.");
1419 FinishExternalScreen();
1420 } // static VOID StartLegacyUEFI()
1421 #endif // __MAKEWITH_TIANO
1423 static LEGACY_ENTRY
* AddLegacyEntry(IN CHAR16
*LoaderTitle
, IN REFIT_VOLUME
*Volume
)
1425 LEGACY_ENTRY
*Entry
, *SubEntry
;
1426 REFIT_MENU_SCREEN
*SubScreen
;
1428 CHAR16 ShortcutLetter
= 0;
1430 if (LoaderTitle
== NULL
) {
1431 if (Volume
->OSName
!= NULL
) {
1432 LoaderTitle
= Volume
->OSName
;
1433 if (LoaderTitle
[0] == 'W' || LoaderTitle
[0] == 'L')
1434 ShortcutLetter
= LoaderTitle
[0];
1436 LoaderTitle
= L
"Legacy OS";
1438 if (Volume
->VolName
!= NULL
)
1439 VolDesc
= Volume
->VolName
;
1441 VolDesc
= (Volume
->DiskKind
== DISK_KIND_OPTICAL
) ? L
"CD" : L
"HD";
1443 // prepare the menu entry
1444 Entry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1445 Entry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1446 SPrint(Entry
->me
.Title
, 255, L
"Boot %s from %s", LoaderTitle
, VolDesc
);
1447 Entry
->me
.Tag
= TAG_LEGACY
;
1449 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1450 Entry
->me
.Image
= LoadOSIcon(Volume
->OSIconName
, L
"legacy", FALSE
);
1451 Entry
->me
.BadgeImage
= Volume
->VolBadgeImage
;
1452 Entry
->Volume
= Volume
;
1453 Entry
->LoadOptions
= (Volume
->DiskKind
== DISK_KIND_OPTICAL
) ? L
"CD" :
1454 ((Volume
->DiskKind
== DISK_KIND_EXTERNAL
) ? L
"USB" : L
"HD");
1455 Entry
->Enabled
= TRUE
;
1457 // create the submenu
1458 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
1459 SubScreen
->Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1460 SPrint(SubScreen
->Title
, 255, L
"Boot Options for %s on %s", LoaderTitle
, VolDesc
);
1461 SubScreen
->TitleImage
= Entry
->me
.Image
;
1462 SubScreen
->Hint1
= StrDuplicate(SUBSCREEN_HINT1
);
1463 if (GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_EDITOR
) {
1464 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2_NO_EDITOR
);
1466 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2
);
1470 SubEntry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1471 SubEntry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1472 SPrint(SubEntry
->me
.Title
, 255, L
"Boot %s", LoaderTitle
);
1473 SubEntry
->me
.Tag
= TAG_LEGACY
;
1474 SubEntry
->Volume
= Entry
->Volume
;
1475 SubEntry
->LoadOptions
= Entry
->LoadOptions
;
1476 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
1478 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
1479 Entry
->me
.SubScreen
= SubScreen
;
1480 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1482 } /* static LEGACY_ENTRY * AddLegacyEntry() */
1485 #ifdef __MAKEWITH_GNUEFI
1486 static VOID
ScanLegacyUEFI(IN UINTN DiskType
){}
1488 // default volume badge icon based on disk kind
1489 static EG_IMAGE
* GetDiskBadge(IN UINTN DiskType
) {
1490 EG_IMAGE
* Badge
= NULL
;
1494 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_INTERNAL
);
1497 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_EXTERNAL
);
1500 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_OPTICAL
);
1504 } // static EG_IMAGE * GetDiskBadge()
1507 Create a rEFInd boot option from a Legacy BIOS protocol option.
1509 static LEGACY_ENTRY
* AddLegacyEntryUEFI(BDS_COMMON_OPTION
*BdsOption
, IN UINT16 DiskType
)
1511 LEGACY_ENTRY
*Entry
, *SubEntry
;
1512 REFIT_MENU_SCREEN
*SubScreen
;
1513 CHAR16 ShortcutLetter
= 0;
1514 CHAR16
*LegacyDescription
= BdsOption
->Description
;
1516 // prepare the menu entry
1517 Entry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1518 Entry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1519 SPrint(Entry
->me
.Title
, 255, L
"Boot legacy target %s", LegacyDescription
);
1520 Entry
->me
.Tag
= TAG_LEGACY_UEFI
;
1522 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1523 Entry
->me
.Image
= LoadOSIcon(L
"legacy", L
"legacy", TRUE
);
1524 Entry
->LoadOptions
= (DiskType
== BBS_CDROM
) ? L
"CD" :
1525 ((DiskType
== BBS_USB
) ? L
"USB" : L
"HD");
1526 Entry
->me
.BadgeImage
= GetDiskBadge(DiskType
);
1527 Entry
->BdsOption
= BdsOption
;
1528 Entry
->Enabled
= TRUE
;
1530 // create the submenu
1531 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
1532 SubScreen
->Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1533 SPrint(SubScreen
->Title
, 255, L
"No boot options for legacy target");
1534 SubScreen
->TitleImage
= Entry
->me
.Image
;
1535 SubScreen
->Hint1
= StrDuplicate(SUBSCREEN_HINT1
);
1536 if (GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_EDITOR
) {
1537 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2_NO_EDITOR
);
1539 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2
);
1543 SubEntry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1544 SubEntry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1545 SPrint(SubEntry
->me
.Title
, 255, L
"Boot %s", LegacyDescription
);
1546 SubEntry
->me
.Tag
= TAG_LEGACY_UEFI
;
1547 Entry
->BdsOption
= BdsOption
;
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 * AddLegacyEntryUEFI() */
1557 Scan for legacy BIOS targets on machines that implement EFI_LEGACY_BIOS_PROTOCOL.
1558 In testing, protocol has not been implemented on Macs but has been
1559 implemented on several Dell PCs and an ASUS motherboard.
1560 Restricts output to disks of the specified DiskType.
1562 static VOID
ScanLegacyUEFI(IN UINTN DiskType
)
1565 EFI_LEGACY_BIOS_PROTOCOL
*LegacyBios
;
1566 UINT16
*BootOrder
= NULL
;
1568 CHAR16 BootOption
[10];
1569 UINTN BootOrderSize
= 0;
1571 BDS_COMMON_OPTION
*BdsOption
;
1572 LIST_ENTRY TempList
;
1573 BBS_BBS_DEVICE_PATH
* BbsDevicePath
= NULL
;
1575 InitializeListHead (&TempList
);
1576 ZeroMem (Buffer
, sizeof (Buffer
));
1578 // If LegacyBios protocol is not implemented on this platform, then
1579 //we do not support this type of legacy boot on this machine.
1580 Status
= gBS
->LocateProtocol(&gEfiLegacyBootProtocolGuid
, NULL
, (VOID
**) &LegacyBios
);
1581 if (EFI_ERROR (Status
))
1584 // Grab the boot order
1585 BootOrder
= BdsLibGetVariableAndSize(L
"BootOrder", &gEfiGlobalVariableGuid
, &BootOrderSize
);
1586 if (BootOrder
== NULL
) {
1591 while (Index
< BootOrderSize
/ sizeof (UINT16
))
1593 // Grab each boot option variable from the boot order, and convert
1594 // the variable into a BDS boot option
1595 UnicodeSPrint (BootOption
, sizeof (BootOption
), L
"Boot%04x", BootOrder
[Index
]);
1596 BdsOption
= BdsLibVariableToOption (&TempList
, BootOption
);
1598 if (BdsOption
!= NULL
) {
1599 BbsDevicePath
= (BBS_BBS_DEVICE_PATH
*)BdsOption
->DevicePath
;
1601 // Only add the entry if it is of a requested type (e.g. USB, HD)
1603 // Two checks necessary because some systems return EFI boot loaders
1604 // with a DeviceType value that would inappropriately include them
1605 // as legacy loaders....
1606 if ((BbsDevicePath
->DeviceType
== DiskType
) && (BdsOption
->DevicePath
->Type
== DEVICE_TYPE_BIOS
)) {
1607 AddLegacyEntryUEFI(BdsOption
, BbsDevicePath
->DeviceType
);
1612 } /* static VOID ScanLegacyUEFI() */
1613 #endif // __MAKEWITH_GNUEFI
1615 static VOID
ScanLegacyVolume(REFIT_VOLUME
*Volume
, UINTN VolumeIndex
) {
1617 BOOLEAN ShowVolume
, HideIfOthersFound
;
1620 HideIfOthersFound
= FALSE
;
1621 if (Volume
->IsAppleLegacy
) {
1623 HideIfOthersFound
= TRUE
;
1624 } else if (Volume
->HasBootCode
) {
1626 if (Volume
->BlockIO
== Volume
->WholeDiskBlockIO
&&
1627 Volume
->BlockIOOffset
== 0 &&
1628 Volume
->OSName
== NULL
)
1629 // this is a whole disk (MBR) entry; hide if we have entries for partitions
1630 HideIfOthersFound
= TRUE
;
1632 if (HideIfOthersFound
) {
1633 // check for other bootable entries on the same disk
1634 for (VolumeIndex2
= 0; VolumeIndex2
< VolumesCount
; VolumeIndex2
++) {
1635 if (VolumeIndex2
!= VolumeIndex
&& Volumes
[VolumeIndex2
]->HasBootCode
&&
1636 Volumes
[VolumeIndex2
]->WholeDiskBlockIO
== Volume
->WholeDiskBlockIO
)
1642 AddLegacyEntry(NULL
, Volume
);
1643 } // static VOID ScanLegacyVolume()
1645 // Scan attached optical discs for legacy (BIOS) boot code
1646 // and add anything found to the list....
1647 static VOID
ScanLegacyDisc(VOID
)
1650 REFIT_VOLUME
*Volume
;
1652 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
) {
1653 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1654 Volume
= Volumes
[VolumeIndex
];
1655 if (Volume
->DiskKind
== DISK_KIND_OPTICAL
)
1656 ScanLegacyVolume(Volume
, VolumeIndex
);
1658 } else if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
1659 ScanLegacyUEFI(BBS_CDROM
);
1661 } /* static VOID ScanLegacyDisc() */
1663 // Scan internal hard disks for legacy (BIOS) boot code
1664 // and add anything found to the list....
1665 static VOID
ScanLegacyInternal(VOID
)
1668 REFIT_VOLUME
*Volume
;
1670 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
) {
1671 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1672 Volume
= Volumes
[VolumeIndex
];
1673 if (Volume
->DiskKind
== DISK_KIND_INTERNAL
)
1674 ScanLegacyVolume(Volume
, VolumeIndex
);
1676 } else if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
1677 ScanLegacyUEFI(BBS_HARDDISK
);
1679 } /* static VOID ScanLegacyInternal() */
1681 // Scan external disks for legacy (BIOS) boot code
1682 // and add anything found to the list....
1683 static VOID
ScanLegacyExternal(VOID
)
1686 REFIT_VOLUME
*Volume
;
1688 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
) {
1689 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1690 Volume
= Volumes
[VolumeIndex
];
1691 if (Volume
->DiskKind
== DISK_KIND_EXTERNAL
)
1692 ScanLegacyVolume(Volume
, VolumeIndex
);
1694 } else if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
1695 ScanLegacyUEFI(BBS_USB
);
1697 } /* static VOID ScanLegacyExternal() */
1700 // pre-boot tool functions
1703 static VOID
StartTool(IN LOADER_ENTRY
*Entry
)
1705 BeginExternalScreen(Entry
->UseGraphicsMode
, Entry
->me
.Title
+ 6); // assumes "Start <title>" as assigned below
1706 StartEFIImage(Entry
->DevicePath
, Entry
->LoadOptions
, Basename(Entry
->LoaderPath
),
1707 Basename(Entry
->LoaderPath
), Entry
->OSType
, NULL
, TRUE
);
1708 FinishExternalScreen();
1709 } /* static VOID StartTool() */
1711 static LOADER_ENTRY
* AddToolEntry(EFI_HANDLE DeviceHandle
, IN CHAR16
*LoaderPath
, IN CHAR16
*LoaderTitle
, IN EG_IMAGE
*Image
,
1712 IN CHAR16 ShortcutLetter
, IN BOOLEAN UseGraphicsMode
)
1714 LOADER_ENTRY
*Entry
;
1715 CHAR16
*TitleStr
= NULL
;
1717 Entry
= AllocateZeroPool(sizeof(LOADER_ENTRY
));
1719 MergeStrings(&TitleStr
, L
"Start ", 0);
1720 MergeStrings(&TitleStr
, LoaderTitle
, 0);
1721 Entry
->me
.Title
= TitleStr
;
1722 Entry
->me
.Tag
= TAG_TOOL
;
1724 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1725 Entry
->me
.Image
= Image
;
1726 Entry
->LoaderPath
= (LoaderPath
) ? StrDuplicate(LoaderPath
) : NULL
;
1727 Entry
->DevicePath
= FileDevicePath(DeviceHandle
, Entry
->LoaderPath
);
1728 Entry
->UseGraphicsMode
= UseGraphicsMode
;
1730 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1732 } /* static LOADER_ENTRY * AddToolEntry() */
1735 // pre-boot driver functions
1738 static UINTN
ScanDriverDir(IN CHAR16
*Path
)
1741 REFIT_DIR_ITER DirIter
;
1743 EFI_FILE_INFO
*DirEntry
;
1744 CHAR16 FileName
[256];
1746 CleanUpPathNameSlashes(Path
);
1747 // look through contents of the directory
1748 DirIterOpen(SelfRootDir
, Path
, &DirIter
);
1749 while (DirIterNext(&DirIter
, 2, LOADER_MATCH_PATTERNS
, &DirEntry
)) {
1750 if (DirEntry
->FileName
[0] == '.')
1751 continue; // skip this
1753 SPrint(FileName
, 255, L
"%s\\%s", Path
, DirEntry
->FileName
);
1755 Status
= StartEFIImage(FileDevicePath(SelfLoadedImage
->DeviceHandle
, FileName
),
1756 L
"", DirEntry
->FileName
, DirEntry
->FileName
, 0, NULL
, FALSE
);
1758 Status
= DirIterClose(&DirIter
);
1759 if (Status
!= EFI_NOT_FOUND
) {
1760 SPrint(FileName
, 255, L
"while scanning the %s directory", Path
);
1761 CheckError(Status
, FileName
);
1766 #ifdef __MAKEWITH_GNUEFI
1767 static EFI_STATUS
ConnectAllDriversToAllControllers(VOID
)
1770 UINTN AllHandleCount
;
1771 EFI_HANDLE
*AllHandleBuffer
;
1774 EFI_HANDLE
*HandleBuffer
;
1780 Status
= LibLocateHandle(AllHandles
,
1785 if (EFI_ERROR(Status
))
1788 for (Index
= 0; Index
< AllHandleCount
; Index
++) {
1790 // Scan the handle database
1792 Status
= LibScanHandleDatabase(NULL
,
1794 AllHandleBuffer
[Index
],
1799 if (EFI_ERROR (Status
))
1803 if (HandleType
[Index
] & EFI_HANDLE_TYPE_DRIVER_BINDING_HANDLE
)
1805 if (HandleType
[Index
] & EFI_HANDLE_TYPE_IMAGE_HANDLE
)
1810 for (HandleIndex
= 0; HandleIndex
< HandleCount
; HandleIndex
++) {
1811 if (HandleType
[HandleIndex
] & EFI_HANDLE_TYPE_PARENT_HANDLE
)
1816 if (HandleType
[Index
] & EFI_HANDLE_TYPE_DEVICE_HANDLE
) {
1817 Status
= refit_call4_wrapper(BS
->ConnectController
,
1818 AllHandleBuffer
[Index
],
1826 MyFreePool (HandleBuffer
);
1827 MyFreePool (HandleType
);
1831 MyFreePool (AllHandleBuffer
);
1833 } /* EFI_STATUS ConnectAllDriversToAllControllers() */
1835 static EFI_STATUS
ConnectAllDriversToAllControllers(VOID
) {
1836 BdsLibConnectAllDriversToAllControllers();
1841 // Load all EFI drivers from rEFInd's "drivers" subdirectory and from the
1842 // directories specified by the user in the "scan_driver_dirs" configuration
1844 static VOID
LoadDrivers(VOID
)
1846 CHAR16
*Directory
, *SelfDirectory
;
1847 UINTN i
= 0, Length
, NumFound
= 0;
1849 // load drivers from the subdirectories of rEFInd's home directory specified
1850 // in the DRIVER_DIRS constant.
1851 while ((Directory
= FindCommaDelimited(DRIVER_DIRS
, i
++)) != NULL
) {
1852 SelfDirectory
= SelfDirPath
? StrDuplicate(SelfDirPath
) : NULL
;
1853 CleanUpPathNameSlashes(SelfDirectory
);
1854 MergeStrings(&SelfDirectory
, Directory
, L
'\\');
1855 NumFound
+= ScanDriverDir(SelfDirectory
);
1856 MyFreePool(Directory
);
1857 MyFreePool(SelfDirectory
);
1860 // Scan additional user-specified driver directories....
1862 while ((Directory
= FindCommaDelimited(GlobalConfig
.DriverDirs
, i
++)) != NULL
) {
1863 CleanUpPathNameSlashes(Directory
);
1864 Length
= StrLen(Directory
);
1866 NumFound
+= ScanDriverDir(Directory
);
1868 MyFreePool(Directory
);
1871 // connect all devices
1873 ConnectAllDriversToAllControllers();
1874 } /* static VOID LoadDrivers() */
1876 // Determine what (if any) type of legacy (BIOS) boot support is available
1877 static VOID
FindLegacyBootType(VOID
) {
1878 #ifdef __MAKEWITH_TIANO
1880 EFI_LEGACY_BIOS_PROTOCOL
*LegacyBios
;
1883 GlobalConfig
.LegacyType
= LEGACY_TYPE_NONE
;
1885 // UEFI-style legacy BIOS support is available only with the TianoCore EDK2
1886 // build environment, and then only with some EFI implementations....
1887 #ifdef __MAKEWITH_TIANO
1888 Status
= gBS
->LocateProtocol (&gEfiLegacyBootProtocolGuid
, NULL
, (VOID
**) &LegacyBios
);
1889 if (!EFI_ERROR (Status
))
1890 GlobalConfig
.LegacyType
= LEGACY_TYPE_UEFI
;
1893 // Macs have their own system. If the firmware vendor code contains the
1894 // string "Apple", assume it's available. Note that this overrides the
1895 // UEFI type, and might yield false positives if the vendor string
1896 // contains "Apple" as part of something bigger, so this isn't 100%
1898 if (StriSubCmp(L
"Apple", ST
->FirmwareVendor
))
1899 GlobalConfig
.LegacyType
= LEGACY_TYPE_MAC
;
1900 } // static VOID FindLegacyBootType
1902 // Warn the user if legacy OS scans are enabled but the firmware or this
1903 // application can't support them....
1904 static VOID
WarnIfLegacyProblems() {
1905 BOOLEAN found
= FALSE
;
1908 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_NONE
) {
1910 if (GlobalConfig
.ScanFor
[i
] == 'h' || GlobalConfig
.ScanFor
[i
] == 'b' || GlobalConfig
.ScanFor
[i
] == 'c')
1913 } while ((i
< NUM_SCAN_OPTIONS
) && (!found
));
1915 Print(L
"NOTE: refind.conf's 'scanfor' line specifies scanning for one or more legacy\n");
1916 Print(L
"(BIOS) boot options; however, this is not possible because ");
1917 #ifdef __MAKEWITH_TIANO
1918 Print(L
"your computer lacks\n");
1919 Print(L
"the necessary Compatibility Support Module (CSM) support.\n");
1921 Print(L
"this program was\n");
1922 Print(L
"compiled without the necessary support. Please visit\n");
1923 Print(L
"http://www.rodsbooks.com/refind/getting.html and download and install a rEFInd\n");
1924 Print(L
"binary built with the TianoCore EDK2 to enable legacy boot support.\n");
1928 } // if no legacy support
1929 } // static VOID WarnIfLegacyProblems()
1931 // Locates boot loaders. NOTE: This assumes that GlobalConfig.LegacyType is set correctly.
1932 static VOID
ScanForBootloaders(VOID
) {
1937 // scan for loaders and tools, add them to the menu
1938 for (i
= 0; i
< NUM_SCAN_OPTIONS
; i
++) {
1939 switch(GlobalConfig
.ScanFor
[i
]) {
1944 ScanLegacyInternal();
1947 ScanLegacyExternal();
1950 ScanUserConfigured(CONFIG_FILE_NAME
);
1964 // assign shortcut keys
1965 for (i
= 0; i
< MainMenu
.EntryCount
&& MainMenu
.Entries
[i
]->Row
== 0 && i
< 9; i
++)
1966 MainMenu
.Entries
[i
]->ShortcutDigit
= (CHAR16
)('1' + i
);
1968 // wait for user ACK when there were errors
1969 FinishTextScreen(FALSE
);
1970 } // static VOID ScanForBootloaders()
1972 // Add the second-row tags containing built-in and external tools (EFI shell,
1974 static VOID
ScanForTools(VOID
) {
1975 CHAR16
*FileName
= NULL
, *MokLocations
, *MokName
, *PathName
, Description
[256];
1976 REFIT_MENU_ENTRY
*TempMenuEntry
;
1977 UINTN i
, j
, k
, VolumeIndex
;
1979 MokLocations
= StrDuplicate(MOK_LOCATIONS
);
1980 if (MokLocations
!= NULL
)
1981 MergeStrings(&MokLocations
, SelfDirPath
, L
',');
1983 for (i
= 0; i
< NUM_TOOLS
; i
++) {
1984 switch(GlobalConfig
.ShowTools
[i
]) {
1985 // NOTE: Be sure that FileName is NULL at the end of each case.
1987 TempMenuEntry
= CopyMenuEntry(&MenuEntryShutdown
);
1988 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_SHUTDOWN
);
1989 AddMenuEntry(&MainMenu
, TempMenuEntry
);
1992 TempMenuEntry
= CopyMenuEntry(&MenuEntryReset
);
1993 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_RESET
);
1994 AddMenuEntry(&MainMenu
, TempMenuEntry
);
1997 TempMenuEntry
= CopyMenuEntry(&MenuEntryAbout
);
1998 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT
);
1999 AddMenuEntry(&MainMenu
, TempMenuEntry
);
2002 TempMenuEntry
= CopyMenuEntry(&MenuEntryExit
);
2003 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_EXIT
);
2004 AddMenuEntry(&MainMenu
, TempMenuEntry
);
2008 while ((FileName
= FindCommaDelimited(SHELL_NAMES
, j
++)) != NULL
) {
2009 if (FileExists(SelfRootDir
, FileName
)) {
2010 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, L
"EFI Shell", BuiltinIcon(BUILTIN_ICON_TOOL_SHELL
),
2013 MyFreePool(FileName
);
2018 while ((FileName
= FindCommaDelimited(GPTSYNC_NAMES
, j
++)) != NULL
) {
2019 if (FileExists(SelfRootDir
, FileName
)) {
2020 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, L
"Hybrid MBR tool", BuiltinIcon(BUILTIN_ICON_TOOL_PART
),
2023 MyFreePool(FileName
);
2027 case TAG_APPLE_RECOVERY
:
2028 FileName
= StrDuplicate(L
"\\com.apple.recovery.boot\\boot.efi");
2029 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
2030 if ((Volumes
[VolumeIndex
]->RootDir
!= NULL
) && (FileExists(Volumes
[VolumeIndex
]->RootDir
, FileName
))) {
2031 SPrint(Description
, 255, L
"Apple Recovery on %s", Volumes
[VolumeIndex
]->VolName
);
2032 AddToolEntry(Volumes
[VolumeIndex
]->DeviceHandle
, FileName
, Description
,
2033 BuiltinIcon(BUILTIN_ICON_TOOL_APPLE_RESCUE
), 'R', TRUE
);
2036 MyFreePool(FileName
);
2041 while ((FileName
= FindCommaDelimited(MokLocations
, j
++)) != NULL
) {
2043 while ((MokName
= FindCommaDelimited(MOK_NAMES
, k
++)) != NULL
) {
2044 PathName
= StrDuplicate(FileName
);
2045 MergeStrings(&PathName
, MokName
, (StriCmp(PathName
, L
"\\") == 0) ? 0 : L
'\\');
2046 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
2047 if ((Volumes
[VolumeIndex
]->RootDir
!= NULL
) && (FileExists(Volumes
[VolumeIndex
]->RootDir
, PathName
))) {
2048 SPrint(Description
, 255, L
"MOK utility at %s on %s", PathName
, Volumes
[VolumeIndex
]->VolName
);
2049 AddToolEntry(Volumes
[VolumeIndex
]->DeviceHandle
, PathName
, Description
,
2050 BuiltinIcon(BUILTIN_ICON_TOOL_MOK_TOOL
), 'S', FALSE
);
2053 MyFreePool(PathName
);
2054 MyFreePool(MokName
);
2055 } // while MOK_NAMES
2056 MyFreePool(FileName
);
2057 } // while MokLocations
2062 } // static VOID ScanForTools
2064 // Rescan for boot loaders
2065 VOID
RescanAll(VOID
) {
2072 egDisplayMessage(L
"Scanning for new boot loaders; please wait....", &BGColor
);
2073 FreeList((VOID
***) &(MainMenu
.Entries
), &MainMenu
.EntryCount
);
2074 MainMenu
.Entries
= NULL
;
2075 MainMenu
.EntryCount
= 0;
2076 ReadConfig(CONFIG_FILE_NAME
);
2077 ConnectAllDriversToAllControllers();
2079 ScanForBootloaders();
2082 } // VOID RescanAll()
2084 #ifdef __MAKEWITH_TIANO
2086 // Minimal initialization function
2087 static VOID
InitializeLib(IN EFI_HANDLE ImageHandle
, IN EFI_SYSTEM_TABLE
*SystemTable
) {
2089 // gImageHandle = ImageHandle;
2090 gBS
= SystemTable
->BootServices
;
2091 // gRS = SystemTable->RuntimeServices;
2092 gRT
= SystemTable
->RuntimeServices
; // Some BDS functions need gRT to be set
2093 EfiGetSystemConfigurationTable (&gEfiDxeServicesTableGuid
, (VOID
**) &gDS
);
2095 InitializeConsoleSim();
2100 // Set up our own Secure Boot extensions....
2101 // Returns TRUE on success, FALSE otherwise
2102 static BOOLEAN
SecureBootSetup(VOID
) {
2104 BOOLEAN Success
= FALSE
;
2106 if (secure_mode() && ShimLoaded()) {
2107 Status
= security_policy_install();
2108 if (Status
== EFI_SUCCESS
) {
2111 Print(L
"Failed to install MOK Secure Boot extensions");
2115 } // VOID SecureBootSetup()
2117 // Remove our own Secure Boot extensions....
2118 // Returns TRUE on success, FALSE otherwise
2119 static BOOLEAN
SecureBootUninstall(VOID
) {
2121 BOOLEAN Success
= TRUE
;
2123 if (secure_mode()) {
2124 Status
= security_policy_uninstall();
2125 if (Status
!= EFI_SUCCESS
) {
2127 BeginTextScreen(L
"Secure Boot Policy Failure");
2128 Print(L
"Failed to uninstall MOK Secure Boot extensions; forcing a reboot.");
2130 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);
2134 } // VOID SecureBootUninstall
2141 efi_main (EFI_HANDLE ImageHandle
, EFI_SYSTEM_TABLE
*SystemTable
)
2144 BOOLEAN MainLoopRunning
= TRUE
;
2145 BOOLEAN MokProtocol
;
2146 REFIT_MENU_ENTRY
*ChosenEntry
;
2148 CHAR16
*Selection
= NULL
;
2152 InitializeLib(ImageHandle
, SystemTable
);
2153 Status
= InitRefitLib(ImageHandle
);
2154 if (EFI_ERROR(Status
))
2157 // read configuration
2158 CopyMem(GlobalConfig
.ScanFor
, "ieom ", NUM_SCAN_OPTIONS
);
2159 FindLegacyBootType();
2160 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
)
2161 CopyMem(GlobalConfig
.ScanFor
, "ihebocm ", NUM_SCAN_OPTIONS
);
2163 ReadConfig(CONFIG_FILE_NAME
);
2166 WarnIfLegacyProblems();
2167 MainMenu
.TimeoutSeconds
= GlobalConfig
.Timeout
;
2169 // disable EFI watchdog timer
2170 refit_call4_wrapper(BS
->SetWatchdogTimer
, 0x0000, 0x0000, 0x0000, NULL
);
2172 // further bootstrap (now with config available)
2173 MokProtocol
= SecureBootSetup();
2175 ScanForBootloaders();
2179 if (GlobalConfig
.ScanDelay
> 0) {
2184 egDisplayMessage(L
"Pausing before disk scan; please wait....", &BGColor
);
2185 for (i
= 0; i
< GlobalConfig
.ScanDelay
; i
++)
2186 refit_call1_wrapper(BS
->Stall
, 1000000);
2190 if (GlobalConfig
.DefaultSelection
)
2191 Selection
= StrDuplicate(GlobalConfig
.DefaultSelection
);
2193 while (MainLoopRunning
) {
2194 MenuExit
= RunMainMenu(&MainMenu
, Selection
, &ChosenEntry
);
2196 // The Escape key triggers a re-scan operation....
2197 if (MenuExit
== MENU_EXIT_ESCAPE
) {
2202 switch (ChosenEntry
->Tag
) {
2204 case TAG_REBOOT
: // Reboot
2206 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);
2207 MainLoopRunning
= FALSE
; // just in case we get this far
2210 case TAG_SHUTDOWN
: // Shut Down
2212 refit_call4_wrapper(RT
->ResetSystem
, EfiResetShutdown
, EFI_SUCCESS
, 0, NULL
);
2213 MainLoopRunning
= FALSE
; // just in case we get this far
2216 case TAG_ABOUT
: // About rEFInd
2220 case TAG_LOADER
: // Boot OS via .EFI loader
2221 StartLoader((LOADER_ENTRY
*)ChosenEntry
);
2224 case TAG_LEGACY
: // Boot legacy OS
2225 StartLegacy((LEGACY_ENTRY
*)ChosenEntry
);
2228 #ifdef __MAKEWITH_TIANO
2229 case TAG_LEGACY_UEFI
: // Boot a legacy OS on a non-Mac
2230 StartLegacyUEFI((LEGACY_ENTRY
*)ChosenEntry
);
2234 case TAG_TOOL
: // Start a EFI tool
2235 StartTool((LOADER_ENTRY
*)ChosenEntry
);
2238 case TAG_EXIT
: // Terminate rEFInd
2239 if ((MokProtocol
) && !SecureBootUninstall()) {
2240 MainLoopRunning
= FALSE
; // just in case we get this far
2242 BeginTextScreen(L
" ");
2248 MyFreePool(Selection
);
2249 Selection
= (ChosenEntry
->Title
) ? StrDuplicate(ChosenEntry
->Title
) : NULL
;
2252 // If we end up here, things have gone wrong. Try to reboot, and if that
2253 // fails, go into an endless loop.
2254 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);