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.
51 #include "../include/Handle.h"
52 #include "../include/refit_call_wrapper.h"
53 #include "driver_support.h"
54 #include "../include/syslinux_mbr.h"
56 #ifdef __MAKEWITH_TIANO
57 #include "../EfiLib/BdsHelper.h"
58 #endif // __MAKEWITH_TIANO
63 #define MACOSX_LOADER_PATH L"System\\Library\\CoreServices\\boot.efi"
65 #define SHELL_NAMES L"\\EFI\\tools\\shell.efi,\\shellx64.efi"
66 #define DRIVER_DIRS L"drivers,drivers_x64"
68 #define SHELL_NAMES L"\\EFI\\tools\\shell.efi,\\shellia32.efi"
69 #define DRIVER_DIRS L"drivers,drivers_ia32"
71 #define SHELL_NAMES L"\\EFI\\tools\\shell.efi"
72 #define DRIVER_DIRS L"drivers"
75 // Filename patterns that identify EFI boot loaders. Note that a single case (either L"*.efi" or
76 // L"*.EFI") is fine for most systems; but Gigabyte's buggy Hybrid EFI does a case-sensitive
77 // comparison when it should do a case-insensitive comparison, so I'm doubling this up. It does
78 // no harm on other computers, AFAIK. In theory, every case variation should be done for
79 // completeness, but that's ridiculous....
80 #define LOADER_MATCH_PATTERNS L"*.efi,*.EFI"
82 // Patterns that identify Linux kernels. Added to the loader match pattern when the
83 // scan_all_linux_kernels option is set in the configuration file. Causes kernels WITHOUT
84 // a ".efi" extension to be found when scanning for boot loaders.
85 #define LINUX_MATCH_PATTERNS L"vmlinuz*,bzImage*"
87 static REFIT_MENU_ENTRY MenuEntryAbout
= { L
"About rEFInd", TAG_ABOUT
, 1, 0, 'A', NULL
, NULL
, NULL
};
88 static REFIT_MENU_ENTRY MenuEntryReset
= { L
"Reboot Computer", TAG_REBOOT
, 1, 0, 'R', NULL
, NULL
, NULL
};
89 static REFIT_MENU_ENTRY MenuEntryShutdown
= { L
"Shut Down Computer", TAG_SHUTDOWN
, 1, 0, 'U', NULL
, NULL
, NULL
};
90 static REFIT_MENU_ENTRY MenuEntryReturn
= { L
"Return to Main Menu", TAG_RETURN
, 0, 0, 0, NULL
, NULL
, NULL
};
91 static REFIT_MENU_ENTRY MenuEntryExit
= { L
"Exit rEFInd", TAG_EXIT
, 1, 0, 0, NULL
, NULL
, NULL
};
93 static REFIT_MENU_SCREEN MainMenu
= { L
"Main Menu", NULL
, 0, NULL
, 0, NULL
, 0, L
"Automatic boot" };
94 static REFIT_MENU_SCREEN AboutMenu
= { L
"About", NULL
, 0, NULL
, 0, NULL
, 0, NULL
};
96 REFIT_CONFIG GlobalConfig
= { FALSE
, FALSE
, 0, 0, 20, 0, 0, GRAPHICS_FOR_OSX
, LEGACY_TYPE_MAC
, 0,
97 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
98 {TAG_SHELL
, TAG_ABOUT
, TAG_SHUTDOWN
, TAG_REBOOT
, 0, 0, 0, 0, 0 }};
100 // Structure used to hold boot loader filenames and time stamps in
101 // a linked list; used to sort entries within a directory.
105 struct LOADER_LIST
*NextEntry
;
112 static VOID
AboutrEFInd(VOID
)
114 CHAR16
*TempStr
; // Note: Don't deallocate; moved to menu structure
116 if (AboutMenu
.EntryCount
== 0) {
117 AboutMenu
.TitleImage
= BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT
);
118 AddMenuInfoLine(&AboutMenu
, L
"rEFInd Version 0.4.6.2");
119 AddMenuInfoLine(&AboutMenu
, L
"");
120 AddMenuInfoLine(&AboutMenu
, L
"Copyright (c) 2006-2010 Christoph Pfisterer");
121 AddMenuInfoLine(&AboutMenu
, L
"Copyright (c) 2012 Roderick W. Smith");
122 AddMenuInfoLine(&AboutMenu
, L
"Portions Copyright (c) Intel Corporation and others");
123 AddMenuInfoLine(&AboutMenu
, L
"Distributed under the terms of the GNU GPLv3 license");
124 AddMenuInfoLine(&AboutMenu
, L
"");
125 AddMenuInfoLine(&AboutMenu
, L
"Running on:");
126 TempStr
= AllocateZeroPool(256 * sizeof(CHAR16
));
127 SPrint(TempStr
, 255, L
" EFI Revision %d.%02d", ST
->Hdr
.Revision
>> 16, ST
->Hdr
.Revision
& ((1 << 16) - 1));
128 AddMenuInfoLine(&AboutMenu
, TempStr
);
130 AddMenuInfoLine(&AboutMenu
, L
" Platform: x86 (32 bit)");
131 #elif defined(EFIX64)
132 AddMenuInfoLine(&AboutMenu
, L
" Platform: x86_64 (64 bit)");
134 AddMenuInfoLine(&AboutMenu
, L
" Platform: unknown");
136 TempStr
= AllocateZeroPool(256 * sizeof(CHAR16
));
137 SPrint(TempStr
, 255, L
" Firmware: %s %d.%02d",
138 ST
->FirmwareVendor
, ST
->FirmwareRevision
>> 16, ST
->FirmwareRevision
& ((1 << 16) - 1));
139 AddMenuInfoLine(&AboutMenu
, TempStr
);
140 TempStr
= AllocateZeroPool(256 * sizeof(CHAR16
));
141 SPrint(TempStr
, 255, L
" Screen Output: %s", egScreenDescription());
142 AddMenuInfoLine(&AboutMenu
, TempStr
);
143 AddMenuInfoLine(&AboutMenu
, L
"");
144 #if defined(__MAKEWITH_GNUEFI)
145 AddMenuInfoLine(&AboutMenu
, L
"Built with GNU-EFI");
147 AddMenuInfoLine(&AboutMenu
, L
"Built with TianoCore EDK2");
149 AddMenuInfoLine(&AboutMenu
, L
"");
150 AddMenuInfoLine(&AboutMenu
, L
"For more information, see the rEFInd Web site:");
151 AddMenuInfoLine(&AboutMenu
, L
"http://www.rodsbooks.com/refind/");
152 AddMenuEntry(&AboutMenu
, &MenuEntryReturn
);
155 RunMenu(&AboutMenu
, NULL
);
156 } /* VOID AboutrEFInd() */
158 static EFI_STATUS
StartEFIImageList(IN EFI_DEVICE_PATH
**DevicePaths
,
159 IN CHAR16
*LoadOptions
, IN CHAR16
*LoadOptionsPrefix
,
160 IN CHAR16
*ImageTitle
, IN CHAR8 OSType
,
161 OUT UINTN
*ErrorInStep
,
164 EFI_STATUS Status
, ReturnStatus
;
165 EFI_HANDLE ChildImageHandle
;
166 EFI_LOADED_IMAGE
*ChildLoadedImage
;
167 UINTN DevicePathIndex
;
168 CHAR16 ErrorInfo
[256];
169 CHAR16
*FullLoadOptions
= NULL
;
172 Print(L
"Starting %s\n", ImageTitle
);
173 if (ErrorInStep
!= NULL
)
176 // load the image into memory
177 ReturnStatus
= Status
= EFI_NOT_FOUND
; // in case the list is empty
178 for (DevicePathIndex
= 0; DevicePaths
[DevicePathIndex
] != NULL
; DevicePathIndex
++) {
179 ReturnStatus
= Status
= refit_call6_wrapper(BS
->LoadImage
, FALSE
, SelfImageHandle
, DevicePaths
[DevicePathIndex
], NULL
, 0, &ChildImageHandle
);
180 if (ReturnStatus
!= EFI_NOT_FOUND
) {
184 SPrint(ErrorInfo
, 255, L
"while loading %s", ImageTitle
);
185 if (CheckError(Status
, ErrorInfo
)) {
186 if (ErrorInStep
!= NULL
)
192 if (LoadOptions
!= NULL
) {
193 ReturnStatus
= Status
= refit_call3_wrapper(BS
->HandleProtocol
, ChildImageHandle
, &LoadedImageProtocol
, (VOID
**) &ChildLoadedImage
);
194 if (CheckError(Status
, L
"while getting a LoadedImageProtocol handle")) {
195 if (ErrorInStep
!= NULL
)
200 if (LoadOptionsPrefix
!= NULL
) {
201 MergeStrings(&FullLoadOptions
, LoadOptionsPrefix
, 0);
202 MergeStrings(&FullLoadOptions
, LoadOptions
, L
' ');
204 MergeStrings(&FullLoadOptions
, L
" ", 0);
205 // NOTE: That last space is also added by the EFI shell and seems to be significant
206 // when passing options to Apple's boot.efi...
209 MergeStrings(&FullLoadOptions
, LoadOptions
, 0);
211 // NOTE: We also include the terminating null in the length for safety.
212 ChildLoadedImage
->LoadOptions
= (VOID
*)FullLoadOptions
;
213 ChildLoadedImage
->LoadOptionsSize
= ((UINT32
)StrLen(FullLoadOptions
) + 1) * sizeof(CHAR16
);
215 Print(L
"Using load options '%s'\n", FullLoadOptions
);
218 // close open file handles
221 // turn control over to the image
222 // TODO: (optionally) re-enable the EFI watchdog timer!
223 ReturnStatus
= Status
= refit_call3_wrapper(BS
->StartImage
, ChildImageHandle
, NULL
, NULL
);
224 // control returns here when the child image calls Exit()
225 SPrint(ErrorInfo
, 255, L
"returned from %s", ImageTitle
);
226 if (CheckError(Status
, ErrorInfo
)) {
227 if (ErrorInStep
!= NULL
)
231 // re-open file handles
235 // unload the image, we don't care if it works or not...
236 Status
= refit_call1_wrapper(BS
->UnloadImage
, ChildImageHandle
);
238 MyFreePool(FullLoadOptions
);
240 } /* static EFI_STATUS StartEFIImageList() */
242 static EFI_STATUS
StartEFIImage(IN EFI_DEVICE_PATH
*DevicePath
,
243 IN CHAR16
*LoadOptions
, IN CHAR16
*LoadOptionsPrefix
,
244 IN CHAR16
*ImageTitle
, IN CHAR8 OSType
,
245 OUT UINTN
*ErrorInStep
,
248 EFI_DEVICE_PATH
*DevicePaths
[2];
250 DevicePaths
[0] = DevicePath
;
251 DevicePaths
[1] = NULL
;
252 return StartEFIImageList(DevicePaths
, LoadOptions
, LoadOptionsPrefix
, ImageTitle
, OSType
, ErrorInStep
, Verbose
);
253 } /* static EFI_STATUS StartEFIImage() */
256 // EFI OS loader functions
259 static VOID
StartLoader(IN LOADER_ENTRY
*Entry
)
261 UINTN ErrorInStep
= 0;
263 BeginExternalScreen(Entry
->UseGraphicsMode
, L
"Booting OS");
264 StartEFIImage(Entry
->DevicePath
, Entry
->LoadOptions
, Basename(Entry
->LoaderPath
),
265 Basename(Entry
->LoaderPath
), Entry
->OSType
, &ErrorInStep
, !Entry
->UseGraphicsMode
);
266 FinishExternalScreen();
269 // Locate an initrd or initramfs file that matches the kernel specified by LoaderPath.
270 // The matching file has a name that begins with "init" and includes the same version
271 // number string as is found in LoaderPath -- but not a longer version number string.
272 // For instance, if LoaderPath is \EFI\kernels\bzImage-3.3.0.efi, and if \EFI\kernels
273 // has a file called initramfs-3.3.0.img, this function will return the string
274 // '\EFI\kernels\initramfs-3.3.0.img'. If the directory ALSO contains the file
275 // initramfs-3.3.0-rc7.img or initramfs-13.3.0.img, those files will NOT match;
276 // however, initmine-3.3.0.img might match. (FindInitrd() returns the first match it
277 // finds). Thus, care should be taken to avoid placing duplicate matching files in
278 // the kernel's directory.
279 // If no matching init file can be found, returns NULL.
280 static CHAR16
* FindInitrd(IN CHAR16
*LoaderPath
, IN REFIT_VOLUME
*Volume
) {
281 CHAR16
*InitrdName
= NULL
, *FileName
, *KernelVersion
, *InitrdVersion
, *Path
;
282 REFIT_DIR_ITER DirIter
;
283 EFI_FILE_INFO
*DirEntry
;
285 FileName
= Basename(LoaderPath
);
286 KernelVersion
= FindNumbers(FileName
);
287 Path
= FindPath(LoaderPath
);
289 // Add trailing backslash for root directory; necessary on some systems, but must
290 // NOT be added to all directories, since on other systems, a trailing backslash on
291 // anything but the root directory causes them to flake out!
292 if (StrLen(Path
) == 0) {
293 MergeStrings(&Path
, L
"\\", 0);
295 DirIterOpen(Volume
->RootDir
, Path
, &DirIter
);
296 // Now add a trailing backslash if it was NOT added earlier, for consistency in
297 // building the InitrdName later....
298 if ((StrLen(Path
) > 0) && (Path
[StrLen(Path
) - 1] != L
'\\'))
299 MergeStrings(&Path
, L
"\\", 0);
300 while ((DirIterNext(&DirIter
, 2, L
"init*", &DirEntry
)) && (InitrdName
== NULL
)) {
301 InitrdVersion
= FindNumbers(DirEntry
->FileName
);
302 if (KernelVersion
!= NULL
) {
303 if (StriCmp(InitrdVersion
, KernelVersion
) == 0) {
304 MergeStrings(&InitrdName
, Path
, 0);
305 MergeStrings(&InitrdName
, DirEntry
->FileName
, 0);
308 if (InitrdVersion
== NULL
) {
309 MergeStrings(&InitrdName
, Path
, 0);
310 MergeStrings(&InitrdName
, DirEntry
->FileName
, 0);
313 MyFreePool(InitrdVersion
);
315 DirIterClose(&DirIter
);
317 // Note: Don't FreePool(FileName), since Basename returns a pointer WITHIN the string it's passed.
318 MyFreePool(KernelVersion
);
321 } // static CHAR16 * FindInitrd()
323 LOADER_ENTRY
* AddPreparedLoaderEntry(LOADER_ENTRY
*Entry
) {
324 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
327 } // LOADER_ENTRY * AddPreparedLoaderEntry()
329 // Creates a copy of a menu screen.
330 // Returns a pointer to the copy of the menu screen.
331 static REFIT_MENU_SCREEN
* CopyMenuScreen(REFIT_MENU_SCREEN
*Entry
) {
332 REFIT_MENU_SCREEN
*NewEntry
;
335 NewEntry
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
336 if ((Entry
!= NULL
) && (NewEntry
!= NULL
)) {
337 CopyMem(NewEntry
, Entry
, sizeof(REFIT_MENU_SCREEN
));
338 NewEntry
->Title
= (Entry
->Title
) ? StrDuplicate(Entry
->Title
) : NULL
;
339 NewEntry
->TimeoutText
= (Entry
->TimeoutText
) ? StrDuplicate(Entry
->TimeoutText
) : NULL
;
340 if (Entry
->TitleImage
!= NULL
) {
341 NewEntry
->TitleImage
= AllocatePool(sizeof(EG_IMAGE
));
342 if (NewEntry
->TitleImage
!= NULL
)
343 CopyMem(NewEntry
->TitleImage
, Entry
->TitleImage
, sizeof(EG_IMAGE
));
345 NewEntry
->InfoLines
= (CHAR16
**) AllocateZeroPool(Entry
->InfoLineCount
* (sizeof(CHAR16
*)));
346 for (i
= 0; i
< Entry
->InfoLineCount
&& NewEntry
->InfoLines
; i
++) {
347 NewEntry
->InfoLines
[i
] = (Entry
->InfoLines
[i
]) ? StrDuplicate(Entry
->InfoLines
[i
]) : NULL
;
349 NewEntry
->Entries
= (REFIT_MENU_ENTRY
**) AllocateZeroPool(Entry
->EntryCount
* (sizeof (REFIT_MENU_ENTRY
*)));
350 for (i
= 0; i
< Entry
->EntryCount
&& NewEntry
->Entries
; i
++) {
351 AddMenuEntry(NewEntry
, Entry
->Entries
[i
]);
355 } // static REFIT_MENU_SCREEN* CopyMenuScreen()
357 // Creates a copy of a menu entry. Intended to enable moving a stack-based
358 // menu entry (such as the ones for the "reboot" and "exit" functions) to
359 // to the heap. This enables easier deletion of the whole set of menu
360 // entries when re-scanning.
361 // Returns a pointer to the copy of the menu entry.
362 static REFIT_MENU_ENTRY
* CopyMenuEntry(REFIT_MENU_ENTRY
*Entry
) {
363 REFIT_MENU_ENTRY
*NewEntry
;
365 NewEntry
= AllocateZeroPool(sizeof(REFIT_MENU_ENTRY
));
366 if ((Entry
!= NULL
) && (NewEntry
!= NULL
)) {
367 CopyMem(NewEntry
, Entry
, sizeof(REFIT_MENU_ENTRY
));
368 NewEntry
->Title
= (Entry
->Title
) ? StrDuplicate(Entry
->Title
) : NULL
;
369 if (Entry
->BadgeImage
!= NULL
) {
370 NewEntry
->BadgeImage
= AllocatePool(sizeof(EG_IMAGE
));
371 if (NewEntry
->BadgeImage
!= NULL
)
372 CopyMem(NewEntry
->BadgeImage
, Entry
->BadgeImage
, sizeof(EG_IMAGE
));
374 if (Entry
->Image
!= NULL
) {
375 NewEntry
->Image
= AllocatePool(sizeof(EG_IMAGE
));
376 if (NewEntry
->Image
!= NULL
)
377 CopyMem(NewEntry
->Image
, Entry
->Image
, sizeof(EG_IMAGE
));
379 if (Entry
->SubScreen
!= NULL
) {
380 NewEntry
->SubScreen
= CopyMenuScreen(Entry
->SubScreen
);
384 } // REFIT_MENU_ENTRY* CopyMenuEntry()
386 // Creates a new LOADER_ENTRY data structure and populates it with
387 // default values from the specified Entry, or NULL values if Entry
388 // is unspecified (NULL).
389 // Returns a pointer to the new data structure, or NULL if it
390 // couldn't be allocated
391 LOADER_ENTRY
*InitializeLoaderEntry(IN LOADER_ENTRY
*Entry
) {
392 LOADER_ENTRY
*NewEntry
= NULL
;
394 NewEntry
= AllocateZeroPool(sizeof(LOADER_ENTRY
));
395 if (NewEntry
!= NULL
) {
396 NewEntry
->me
.Title
= NULL
;
397 NewEntry
->me
.Tag
= TAG_LOADER
;
398 NewEntry
->Enabled
= TRUE
;
399 NewEntry
->UseGraphicsMode
= FALSE
;
400 NewEntry
->OSType
= 0;
402 NewEntry
->LoaderPath
= (Entry
->LoaderPath
) ? StrDuplicate(Entry
->LoaderPath
) : NULL
;
403 NewEntry
->VolName
= (Entry
->VolName
) ? StrDuplicate(Entry
->VolName
) : NULL
;
404 NewEntry
->DevicePath
= Entry
->DevicePath
;
405 NewEntry
->UseGraphicsMode
= Entry
->UseGraphicsMode
;
406 NewEntry
->LoadOptions
= (Entry
->LoadOptions
) ? StrDuplicate(Entry
->LoadOptions
) : NULL
;
407 NewEntry
->InitrdPath
= (Entry
->InitrdPath
) ? StrDuplicate(Entry
->InitrdPath
) : NULL
;
411 } // LOADER_ENTRY *InitializeLoaderEntry()
413 // Prepare a REFIT_MENU_SCREEN data structure for a subscreen entry. This sets up
414 // the default entry that launches the boot loader using the same options as the
415 // main Entry does. Subsequent options can be added by the calling function.
416 // If a subscreen already exists in the Entry that's passed to this function,
417 // it's left unchanged and a pointer to it is returned.
418 // Returns a pointer to the new subscreen data structure, or NULL if there
419 // were problems allocating memory.
420 REFIT_MENU_SCREEN
*InitializeSubScreen(IN LOADER_ENTRY
*Entry
) {
421 CHAR16
*FileName
, *Temp
= NULL
;
422 REFIT_MENU_SCREEN
*SubScreen
= NULL
;
423 LOADER_ENTRY
*SubEntry
;
425 FileName
= Basename(Entry
->LoaderPath
);
426 if (Entry
->me
.SubScreen
== NULL
) { // No subscreen yet; initialize default entry....
427 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
428 if (SubScreen
!= NULL
) {
429 SubScreen
->Title
= AllocateZeroPool(sizeof(CHAR16
) * 256);
430 SPrint(SubScreen
->Title
, 255, L
"Boot Options for %s on %s",
431 (Entry
->Title
!= NULL
) ? Entry
->Title
: FileName
, Entry
->VolName
);
432 SubScreen
->TitleImage
= Entry
->me
.Image
;
434 SubEntry
= InitializeLoaderEntry(Entry
);
435 if (SubEntry
!= NULL
) {
436 SubEntry
->me
.Title
= L
"Boot using default options";
437 if ((SubEntry
->InitrdPath
!= NULL
) && (StrLen(SubEntry
->InitrdPath
) > 0) && (!StriSubCmp(L
"initrd", SubEntry
->LoadOptions
))) {
438 MergeStrings(&Temp
, L
"initrd=", 0);
439 MergeStrings(&Temp
, SubEntry
->InitrdPath
, 0);
440 MergeStrings(&SubEntry
->LoadOptions
, Temp
, L
' ');
443 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
444 } // if (SubEntry != NULL)
445 } // if (SubScreen != NULL)
446 } else { // existing subscreen; less initialization, and just add new entry later....
447 SubScreen
= Entry
->me
.SubScreen
;
450 } // REFIT_MENU_SCREEN *InitializeSubScreen()
452 VOID
GenerateSubScreen(LOADER_ENTRY
*Entry
, IN REFIT_VOLUME
*Volume
) {
453 REFIT_MENU_SCREEN
*SubScreen
;
454 LOADER_ENTRY
*SubEntry
;
455 CHAR16
*InitrdOption
= NULL
, *Temp
;
456 CHAR16 DiagsFileName
[256];
461 // create the submenu
462 if (StrLen(Entry
->Title
) == 0) {
463 MyFreePool(Entry
->Title
);
466 SubScreen
= InitializeSubScreen(Entry
);
468 // loader-specific submenu entries
469 if (Entry
->OSType
== 'M') { // entries for Mac OS X
471 SubEntry
= InitializeLoaderEntry(Entry
);
472 if (SubEntry
!= NULL
) {
473 SubEntry
->me
.Title
= L
"Boot Mac OS X with a 64-bit kernel";
474 SubEntry
->LoadOptions
= L
"arch=x86_64";
475 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
476 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
479 SubEntry
= InitializeLoaderEntry(Entry
);
480 if (SubEntry
!= NULL
) {
481 SubEntry
->me
.Title
= L
"Boot Mac OS X with a 32-bit kernel";
482 SubEntry
->LoadOptions
= L
"arch=i386";
483 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
484 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
488 if (!(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_SINGLEUSER
)) {
489 SubEntry
= InitializeLoaderEntry(Entry
);
490 if (SubEntry
!= NULL
) {
491 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode";
492 SubEntry
->UseGraphicsMode
= FALSE
;
493 SubEntry
->LoadOptions
= L
"-v";
494 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
498 SubEntry
= InitializeLoaderEntry(Entry
);
499 if (SubEntry
!= NULL
) {
500 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode (64-bit)";
501 SubEntry
->UseGraphicsMode
= FALSE
;
502 SubEntry
->LoadOptions
= L
"-v arch=x86_64";
503 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
506 SubEntry
= InitializeLoaderEntry(Entry
);
507 if (SubEntry
!= NULL
) {
508 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode (32-bit)";
509 SubEntry
->UseGraphicsMode
= FALSE
;
510 SubEntry
->LoadOptions
= L
"-v arch=i386";
511 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
515 SubEntry
= InitializeLoaderEntry(Entry
);
516 if (SubEntry
!= NULL
) {
517 SubEntry
->me
.Title
= L
"Boot Mac OS X in single user mode";
518 SubEntry
->UseGraphicsMode
= FALSE
;
519 SubEntry
->LoadOptions
= L
"-v -s";
520 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
524 // check for Apple hardware diagnostics
525 StrCpy(DiagsFileName
, L
"\\System\\Library\\CoreServices\\.diagnostics\\diags.efi");
526 if (FileExists(Volume
->RootDir
, DiagsFileName
) && !(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_HWTEST
)) {
527 SubEntry
= InitializeLoaderEntry(Entry
);
528 if (SubEntry
!= NULL
) {
529 SubEntry
->me
.Title
= L
"Run Apple Hardware Test";
530 MyFreePool(SubEntry
->LoaderPath
);
531 SubEntry
->LoaderPath
= StrDuplicate(DiagsFileName
);
532 SubEntry
->DevicePath
= FileDevicePath(Volume
->DeviceHandle
, SubEntry
->LoaderPath
);
533 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
534 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
536 } // if diagnostics entry found
538 } else if (Entry
->OSType
== 'L') { // entries for Linux kernels with EFI stub loaders
539 File
= ReadLinuxOptionsFile(Entry
->LoaderPath
, Volume
);
541 if ((Temp
= FindInitrd(Entry
->LoaderPath
, Volume
)) != NULL
) {
542 MergeStrings(&InitrdOption
, L
"initrd=", 0);
543 MergeStrings(&InitrdOption
, Temp
, 0);
545 TokenCount
= ReadTokenLine(File
, &TokenList
); // read and discard first entry, since it's
546 FreeTokenLine(&TokenList
, &TokenCount
); // set up by InitializeSubScreen(), earlier....
547 while ((TokenCount
= ReadTokenLine(File
, &TokenList
)) > 1) {
548 SubEntry
= InitializeLoaderEntry(Entry
);
549 SubEntry
->me
.Title
= StrDuplicate(TokenList
[0]);
550 MyFreePool(SubEntry
->LoadOptions
);
551 SubEntry
->LoadOptions
= StrDuplicate(TokenList
[1]);
552 MergeStrings(&SubEntry
->LoadOptions
, InitrdOption
, L
' ');
553 FreeTokenLine(&TokenList
, &TokenCount
);
554 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_LINUX
;
555 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
557 MyFreePool(InitrdOption
);
560 } // if Linux options file exists
562 } else if (Entry
->OSType
== 'E') { // entries for ELILO
563 SubEntry
= InitializeLoaderEntry(Entry
);
564 if (SubEntry
!= NULL
) {
565 SubEntry
->me
.Title
= L
"Run ELILO in interactive mode";
566 SubEntry
->LoadOptions
= L
"-p";
567 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
568 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
571 SubEntry
= InitializeLoaderEntry(Entry
);
572 if (SubEntry
!= NULL
) {
573 SubEntry
->me
.Title
= L
"Boot Linux for a 17\" iMac or a 15\" MacBook Pro (*)";
574 SubEntry
->UseGraphicsMode
= TRUE
;
575 SubEntry
->LoadOptions
= L
"-d 0 i17";
576 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
577 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
580 SubEntry
= InitializeLoaderEntry(Entry
);
581 if (SubEntry
!= NULL
) {
582 SubEntry
->me
.Title
= L
"Boot Linux for a 20\" iMac (*)";
583 SubEntry
->UseGraphicsMode
= TRUE
;
584 SubEntry
->LoadOptions
= L
"-d 0 i20";
585 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
586 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
589 SubEntry
= InitializeLoaderEntry(Entry
);
590 if (SubEntry
!= NULL
) {
591 SubEntry
->me
.Title
= L
"Boot Linux for a Mac Mini (*)";
592 SubEntry
->UseGraphicsMode
= TRUE
;
593 SubEntry
->LoadOptions
= L
"-d 0 mini";
594 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
595 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
598 AddMenuInfoLine(SubScreen
, L
"NOTE: This is an example. Entries");
599 AddMenuInfoLine(SubScreen
, L
"marked with (*) may not work.");
601 } else if (Entry
->OSType
== 'X') { // entries for xom.efi
602 // by default, skip the built-in selection and boot from hard disk only
603 Entry
->LoadOptions
= L
"-s -h";
605 SubEntry
= InitializeLoaderEntry(Entry
);
606 if (SubEntry
!= NULL
) {
607 SubEntry
->me
.Title
= L
"Boot Windows from Hard Disk";
608 SubEntry
->LoadOptions
= L
"-s -h";
609 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
610 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
613 SubEntry
= InitializeLoaderEntry(Entry
);
614 if (SubEntry
!= NULL
) {
615 SubEntry
->me
.Title
= L
"Boot Windows from CD-ROM";
616 SubEntry
->LoadOptions
= L
"-s -c";
617 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
618 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
621 SubEntry
= InitializeLoaderEntry(Entry
);
622 if (SubEntry
!= NULL
) {
623 SubEntry
->me
.Title
= L
"Run XOM in text mode";
624 SubEntry
->UseGraphicsMode
= FALSE
;
625 SubEntry
->LoadOptions
= L
"-v";
626 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
627 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
629 } // entries for xom.efi
630 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
631 Entry
->me
.SubScreen
= SubScreen
;
632 } // VOID GenerateSubScreen()
634 // Returns options for a Linux kernel. Reads them from an options file in the
635 // kernel's directory; and if present, adds an initrd= option for an initial
636 // RAM disk file with the same version number as the kernel file.
637 static CHAR16
* GetMainLinuxOptions(IN CHAR16
* LoaderPath
, IN REFIT_VOLUME
*Volume
) {
638 CHAR16
*Options
= NULL
, *InitrdName
, *InitrdOption
= NULL
;
640 Options
= GetFirstOptionsFromFile(LoaderPath
, Volume
);
641 InitrdName
= FindInitrd(LoaderPath
, Volume
);
642 if (InitrdName
!= NULL
) {
643 MergeStrings(&InitrdOption
, L
"initrd=", 0);
644 MergeStrings(&InitrdOption
, InitrdName
, 0);
646 MergeStrings(&Options
, InitrdOption
, ' ');
647 MyFreePool(InitrdOption
);
648 MyFreePool(InitrdName
);
650 } // static CHAR16 * GetMainLinuxOptions()
652 // Sets a few defaults for a loader entry -- mainly the icon, but also the OS type
653 // code and shortcut letter. For Linux EFI stub loaders, also sets kernel options
654 // that will (with luck) work fairly automatically.
655 VOID
SetLoaderDefaults(LOADER_ENTRY
*Entry
, CHAR16
*LoaderPath
, IN REFIT_VOLUME
*Volume
) {
656 CHAR16 IconFileName
[256];
657 CHAR16
*FileName
, *PathOnly
, *OSIconName
= NULL
, *Temp
;
658 CHAR16 ShortcutLetter
= 0;
660 FileName
= Basename(LoaderPath
);
661 PathOnly
= FindPath(LoaderPath
);
663 // locate a custom icon for the loader
664 StrCpy(IconFileName
, LoaderPath
);
665 ReplaceEfiExtension(IconFileName
, L
".icns");
666 if (FileExists(Volume
->RootDir
, IconFileName
)) {
667 Entry
->me
.Image
= LoadIcns(Volume
->RootDir
, IconFileName
, 128);
668 } else if ((StrLen(PathOnly
) == 0) && (Volume
->VolIconImage
!= NULL
)) {
669 Entry
->me
.Image
= Volume
->VolIconImage
;
670 } // icon matched to loader or volume
672 Temp
= FindLastDirName(LoaderPath
);
673 MergeStrings(&OSIconName
, Temp
, L
',');
675 if (OSIconName
!= NULL
) {
676 ShortcutLetter
= OSIconName
[0];
679 // detect specific loaders
680 if (StriSubCmp(L
"bzImage", LoaderPath
) || StriSubCmp(L
"vmlinuz", LoaderPath
)) {
681 MergeStrings(&OSIconName
, L
"linux", L
',');
683 if (ShortcutLetter
== 0)
684 ShortcutLetter
= 'L';
685 Entry
->LoadOptions
= GetMainLinuxOptions(LoaderPath
, Volume
);
686 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_LINUX
;
687 } else if (StriSubCmp(L
"refit", LoaderPath
)) {
688 MergeStrings(&OSIconName
, L
"refit", L
',');
690 ShortcutLetter
= 'R';
691 } else if (StriCmp(LoaderPath
, MACOSX_LOADER_PATH
) == 0) {
692 if (Volume
->VolIconImage
!= NULL
) { // custom icon file found
693 Entry
->me
.Image
= Volume
->VolIconImage
;
695 MergeStrings(&OSIconName
, L
"mac", L
',');
697 ShortcutLetter
= 'M';
698 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
699 } else if (StriCmp(FileName
, L
"diags.efi") == 0) {
700 MergeStrings(&OSIconName
, L
"hwtest", L
',');
701 } else if (StriCmp(FileName
, L
"e.efi") == 0 || StriCmp(FileName
, L
"elilo.efi") == 0) {
702 MergeStrings(&OSIconName
, L
"elilo,linux", L
',');
704 if (ShortcutLetter
== 0)
705 ShortcutLetter
= 'L';
706 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
707 } else if (StriSubCmp(L
"grub", FileName
)) {
709 ShortcutLetter
= 'G';
710 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_GRUB
;
711 } else if (StriCmp(FileName
, L
"cdboot.efi") == 0 ||
712 StriCmp(FileName
, L
"bootmgr.efi") == 0 ||
713 StriCmp(FileName
, L
"Bootmgfw.efi") == 0) {
714 MergeStrings(&OSIconName
, L
"win", L
',');
716 ShortcutLetter
= 'W';
717 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
718 } else if (StriCmp(FileName
, L
"xom.efi") == 0) {
719 MergeStrings(&OSIconName
, L
"xom,win", L
',');
720 Entry
->UseGraphicsMode
= TRUE
;
722 ShortcutLetter
= 'W';
723 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
726 if ((ShortcutLetter
>= 'a') && (ShortcutLetter
<= 'z'))
727 ShortcutLetter
= ShortcutLetter
- 'a' + 'A'; // convert lowercase to uppercase
728 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
729 if (Entry
->me
.Image
== NULL
)
730 Entry
->me
.Image
= LoadOSIcon(OSIconName
, L
"unknown", FALSE
);
731 MyFreePool(PathOnly
);
732 } // VOID SetLoaderDefaults()
734 // Add a specified EFI boot loader to the list, using automatic settings
735 // for icons, options, etc.
736 LOADER_ENTRY
* AddLoaderEntry(IN CHAR16
*LoaderPath
, IN CHAR16
*LoaderTitle
, IN REFIT_VOLUME
*Volume
) {
739 CleanUpPathNameSlashes(LoaderPath
);
740 Entry
= InitializeLoaderEntry(NULL
);
742 Entry
->Title
= StrDuplicate((LoaderTitle
!= NULL
) ? LoaderTitle
: LoaderPath
);
743 Entry
->me
.Title
= AllocateZeroPool(sizeof(CHAR16
) * 256);
744 SPrint(Entry
->me
.Title
, 255, L
"Boot %s from %s", (LoaderTitle
!= NULL
) ? LoaderTitle
: LoaderPath
, Volume
->VolName
);
746 Entry
->me
.BadgeImage
= Volume
->VolBadgeImage
;
747 if ((LoaderPath
!= NULL
) && (LoaderPath
[0] != L
'\\')) {
748 Entry
->LoaderPath
= StrDuplicate(L
"\\");
750 Entry
->LoaderPath
= NULL
;
752 MergeStrings(&(Entry
->LoaderPath
), LoaderPath
, 0);
753 Entry
->VolName
= Volume
->VolName
;
754 Entry
->DevicePath
= FileDevicePath(Volume
->DeviceHandle
, Entry
->LoaderPath
);
755 SetLoaderDefaults(Entry
, LoaderPath
, Volume
);
756 GenerateSubScreen(Entry
, Volume
);
757 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
761 } // LOADER_ENTRY * AddLoaderEntry()
763 // Returns -1 if (Time1 < Time2), +1 if (Time1 > Time2), or 0 if
764 // (Time1 == Time2). Precision is only to the nearest second; since
765 // this is used for sorting boot loader entries, differences smaller
766 // than this are likely to be meaningless (and unlikely!).
767 INTN
TimeComp(EFI_TIME
*Time1
, EFI_TIME
*Time2
) {
768 INT64 Time1InSeconds
, Time2InSeconds
;
770 // Following values are overestimates; I'm assuming 31 days in every month.
771 // This is fine for the purpose of this function, which has a limited
773 Time1InSeconds
= Time1
->Second
+ (Time1
->Minute
* 60) + (Time1
->Hour
* 3600) + (Time1
->Day
* 86400) +
774 (Time1
->Month
* 2678400) + ((Time1
->Year
- 1998) * 32140800);
775 Time2InSeconds
= Time2
->Second
+ (Time2
->Minute
* 60) + (Time2
->Hour
* 3600) + (Time2
->Day
* 86400) +
776 (Time2
->Month
* 2678400) + ((Time2
->Year
- 1998) * 32140800);
777 if (Time1InSeconds
< Time2InSeconds
)
779 else if (Time1InSeconds
> Time2InSeconds
)
785 // Adds a loader list element, keeping it sorted by date. Returns the new
786 // first element (the one with the most recent date).
787 static struct LOADER_LIST
* AddLoaderListEntry(struct LOADER_LIST
*LoaderList
, struct LOADER_LIST
*NewEntry
) {
788 struct LOADER_LIST
*LatestEntry
, *CurrentEntry
, *PrevEntry
= NULL
;
790 LatestEntry
= CurrentEntry
= LoaderList
;
791 if (LoaderList
== NULL
) {
792 LatestEntry
= NewEntry
;
794 while ((CurrentEntry
!= NULL
) && (TimeComp(&(NewEntry
->TimeStamp
), &(CurrentEntry
->TimeStamp
)) < 0)) {
795 PrevEntry
= CurrentEntry
;
796 CurrentEntry
= CurrentEntry
->NextEntry
;
798 NewEntry
->NextEntry
= CurrentEntry
;
799 if (PrevEntry
== NULL
) {
800 LatestEntry
= NewEntry
;
802 PrevEntry
->NextEntry
= NewEntry
;
805 return (LatestEntry
);
806 } // static VOID AddLoaderListEntry()
808 // Delete the LOADER_LIST linked list
809 static VOID
CleanUpLoaderList(struct LOADER_LIST
*LoaderList
) {
810 struct LOADER_LIST
*Temp
;
812 while (LoaderList
!= NULL
) {
814 LoaderList
= LoaderList
->NextEntry
;
815 MyFreePool(Temp
->FileName
);
818 } // static VOID CleanUpLoaderList()
820 // Scan an individual directory for EFI boot loader files and, if found,
821 // add them to the list. Sorts the entries within the loader directory
822 // so that the most recent one appears first in the list.
823 static VOID
ScanLoaderDir(IN REFIT_VOLUME
*Volume
, IN CHAR16
*Path
, IN CHAR16
*Pattern
)
826 REFIT_DIR_ITER DirIter
;
827 EFI_FILE_INFO
*DirEntry
;
828 CHAR16 FileName
[256], *Extension
;
829 struct LOADER_LIST
*LoaderList
= NULL
, *NewLoader
;
831 if ((!SelfDirPath
|| !Path
|| ((StriCmp(Path
, SelfDirPath
) == 0) && Volume
->DeviceHandle
!= SelfVolume
->DeviceHandle
) ||
832 (StriCmp(Path
, SelfDirPath
) != 0)) && (!IsIn(Path
, GlobalConfig
.DontScan
))) {
833 // look through contents of the directory
834 DirIterOpen(Volume
->RootDir
, Path
, &DirIter
);
835 while (DirIterNext(&DirIter
, 2, Pattern
, &DirEntry
)) {
836 Extension
= FindExtension(DirEntry
->FileName
);
837 if (DirEntry
->FileName
[0] == '.' ||
838 StriCmp(DirEntry
->FileName
, L
"TextMode.efi") == 0 ||
839 StriCmp(DirEntry
->FileName
, L
"ebounce.efi") == 0 ||
840 StriCmp(DirEntry
->FileName
, L
"GraphicsConsole.efi") == 0 ||
841 StriCmp(Extension
, L
".icns") == 0 ||
842 StriSubCmp(L
"shell", DirEntry
->FileName
))
843 continue; // skip this
846 SPrint(FileName
, 255, L
"\\%s\\%s", Path
, DirEntry
->FileName
);
848 SPrint(FileName
, 255, L
"\\%s", DirEntry
->FileName
);
849 CleanUpPathNameSlashes(FileName
);
850 NewLoader
= AllocateZeroPool(sizeof(struct LOADER_LIST
));
851 if (NewLoader
!= NULL
) {
852 NewLoader
->FileName
= StrDuplicate(FileName
);
853 NewLoader
->TimeStamp
= DirEntry
->ModificationTime
;
854 LoaderList
= AddLoaderListEntry(LoaderList
, NewLoader
);
856 MyFreePool(Extension
);
858 NewLoader
= LoaderList
;
859 while (NewLoader
!= NULL
) {
860 AddLoaderEntry(NewLoader
->FileName
, NULL
, Volume
);
861 NewLoader
= NewLoader
->NextEntry
;
863 CleanUpLoaderList(LoaderList
);
864 Status
= DirIterClose(&DirIter
);
865 if (Status
!= EFI_NOT_FOUND
) {
867 SPrint(FileName
, 255, L
"while scanning the %s directory", Path
);
869 StrCpy(FileName
, L
"while scanning the root directory");
870 CheckError(Status
, FileName
);
871 } // if (Status != EFI_NOT_FOUND)
872 } // if not scanning our own directory
873 } /* static VOID ScanLoaderDir() */
875 static VOID
ScanEfiFiles(REFIT_VOLUME
*Volume
) {
877 REFIT_DIR_ITER EfiDirIter
;
878 EFI_FILE_INFO
*EfiDirEntry
;
879 CHAR16 FileName
[256], *Directory
, *MatchPatterns
;
882 MatchPatterns
= StrDuplicate(LOADER_MATCH_PATTERNS
);
883 if (GlobalConfig
.ScanAllLinux
)
884 MergeStrings(&MatchPatterns
, LINUX_MATCH_PATTERNS
, L
',');
886 if ((Volume
->RootDir
!= NULL
) && (Volume
->VolName
!= NULL
)) {
887 // check for Mac OS X boot loader
888 if (!IsIn(L
"System\\Library\\CoreServices", GlobalConfig
.DontScan
)) {
889 StrCpy(FileName
, MACOSX_LOADER_PATH
);
890 if (FileExists(Volume
->RootDir
, FileName
)) {
891 AddLoaderEntry(FileName
, L
"Mac OS X", Volume
);
895 StrCpy(FileName
, L
"System\\Library\\CoreServices\\xom.efi");
896 if (FileExists(Volume
->RootDir
, FileName
)) {
897 AddLoaderEntry(FileName
, L
"Windows XP (XoM)", Volume
);
899 } // if Mac directory not in GlobalConfig.DontScan list
901 // check for Microsoft boot loader/menu
902 StrCpy(FileName
, L
"EFI\\Microsoft\\Boot\\Bootmgfw.efi");
903 if (FileExists(Volume
->RootDir
, FileName
) && !IsIn(L
"EFI\\Microsoft\\Boot", GlobalConfig
.DontScan
)) {
904 AddLoaderEntry(FileName
, L
"Microsoft EFI boot", Volume
);
907 // scan the root directory for EFI executables
908 ScanLoaderDir(Volume
, L
"\\", MatchPatterns
);
910 // scan subdirectories of the EFI directory (as per the standard)
911 DirIterOpen(Volume
->RootDir
, L
"EFI", &EfiDirIter
);
912 while (DirIterNext(&EfiDirIter
, 1, NULL
, &EfiDirEntry
)) {
913 if (StriCmp(EfiDirEntry
->FileName
, L
"tools") == 0 || EfiDirEntry
->FileName
[0] == '.')
914 continue; // skip this, doesn't contain boot loaders
915 SPrint(FileName
, 255, L
"EFI\\%s", EfiDirEntry
->FileName
);
916 ScanLoaderDir(Volume
, FileName
, MatchPatterns
);
918 Status
= DirIterClose(&EfiDirIter
);
919 if (Status
!= EFI_NOT_FOUND
)
920 CheckError(Status
, L
"while scanning the EFI directory");
922 // Scan user-specified (or additional default) directories....
924 while ((Directory
= FindCommaDelimited(GlobalConfig
.AlsoScan
, i
++)) != NULL
) {
925 CleanUpPathNameSlashes(Directory
);
926 Length
= StrLen(Directory
);
928 ScanLoaderDir(Volume
, Directory
, MatchPatterns
);
929 MyFreePool(Directory
);
932 } // static VOID ScanEfiFiles()
934 // Scan internal disks for valid EFI boot loaders....
935 static VOID
ScanInternal(VOID
) {
938 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
939 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_INTERNAL
) {
940 ScanEfiFiles(Volumes
[VolumeIndex
]);
943 } // static VOID ScanInternal()
945 // Scan external disks for valid EFI boot loaders....
946 static VOID
ScanExternal(VOID
) {
949 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
950 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_EXTERNAL
) {
951 ScanEfiFiles(Volumes
[VolumeIndex
]);
954 } // static VOID ScanExternal()
956 // Scan internal disks for valid EFI boot loaders....
957 static VOID
ScanOptical(VOID
) {
960 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
961 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_OPTICAL
) {
962 ScanEfiFiles(Volumes
[VolumeIndex
]);
965 } // static VOID ScanOptical()
968 // legacy boot functions
971 static EFI_STATUS
ActivateMbrPartition(IN EFI_BLOCK_IO
*BlockIO
, IN UINTN PartitionIndex
)
974 UINT8 SectorBuffer
[512];
975 MBR_PARTITION_INFO
*MbrTable
, *EMbrTable
;
976 UINT32 ExtBase
, ExtCurrent
, NextExtCurrent
;
977 UINTN LogicalPartitionIndex
= 4;
979 BOOLEAN HaveBootCode
;
982 Status
= refit_call5_wrapper(BlockIO
->ReadBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, 0, 512, SectorBuffer
);
983 if (EFI_ERROR(Status
))
985 if (*((UINT16
*)(SectorBuffer
+ 510)) != 0xaa55)
986 return EFI_NOT_FOUND
; // safety measure #1
988 // add boot code if necessary
989 HaveBootCode
= FALSE
;
990 for (i
= 0; i
< MBR_BOOTCODE_SIZE
; i
++) {
991 if (SectorBuffer
[i
] != 0) {
997 // no boot code found in the MBR, add the syslinux MBR code
998 SetMem(SectorBuffer
, MBR_BOOTCODE_SIZE
, 0);
999 CopyMem(SectorBuffer
, syslinux_mbr
, SYSLINUX_MBR_SIZE
);
1002 // set the partition active
1003 MbrTable
= (MBR_PARTITION_INFO
*)(SectorBuffer
+ 446);
1005 for (i
= 0; i
< 4; i
++) {
1006 if (MbrTable
[i
].Flags
!= 0x00 && MbrTable
[i
].Flags
!= 0x80)
1007 return EFI_NOT_FOUND
; // safety measure #2
1008 if (i
== PartitionIndex
)
1009 MbrTable
[i
].Flags
= 0x80;
1010 else if (PartitionIndex
>= 4 && IS_EXTENDED_PART_TYPE(MbrTable
[i
].Type
)) {
1011 MbrTable
[i
].Flags
= 0x80;
1012 ExtBase
= MbrTable
[i
].StartLBA
;
1014 MbrTable
[i
].Flags
= 0x00;
1018 Status
= refit_call5_wrapper(BlockIO
->WriteBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, 0, 512, SectorBuffer
);
1019 if (EFI_ERROR(Status
))
1022 if (PartitionIndex
>= 4) {
1023 // we have to activate a logical partition, so walk the EMBR chain
1025 // NOTE: ExtBase was set above while looking at the MBR table
1026 for (ExtCurrent
= ExtBase
; ExtCurrent
; ExtCurrent
= NextExtCurrent
) {
1027 // read current EMBR
1028 Status
= refit_call5_wrapper(BlockIO
->ReadBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, ExtCurrent
, 512, SectorBuffer
);
1029 if (EFI_ERROR(Status
))
1031 if (*((UINT16
*)(SectorBuffer
+ 510)) != 0xaa55)
1032 return EFI_NOT_FOUND
; // safety measure #3
1034 // scan EMBR, set appropriate partition active
1035 EMbrTable
= (MBR_PARTITION_INFO
*)(SectorBuffer
+ 446);
1037 for (i
= 0; i
< 4; i
++) {
1038 if (EMbrTable
[i
].Flags
!= 0x00 && EMbrTable
[i
].Flags
!= 0x80)
1039 return EFI_NOT_FOUND
; // safety measure #4
1040 if (EMbrTable
[i
].StartLBA
== 0 || EMbrTable
[i
].Size
== 0)
1042 if (IS_EXTENDED_PART_TYPE(EMbrTable
[i
].Type
)) {
1043 // link to next EMBR
1044 NextExtCurrent
= ExtBase
+ EMbrTable
[i
].StartLBA
;
1045 EMbrTable
[i
].Flags
= (PartitionIndex
>= LogicalPartitionIndex
) ? 0x80 : 0x00;
1048 // logical partition
1049 EMbrTable
[i
].Flags
= (PartitionIndex
== LogicalPartitionIndex
) ? 0x80 : 0x00;
1050 LogicalPartitionIndex
++;
1054 // write current EMBR
1055 Status
= refit_call5_wrapper(BlockIO
->WriteBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, ExtCurrent
, 512, SectorBuffer
);
1056 if (EFI_ERROR(Status
))
1059 if (PartitionIndex
< LogicalPartitionIndex
)
1060 break; // stop the loop, no need to touch further EMBRs
1066 } /* static EFI_STATUS ActivateMbrPartition() */
1068 // early 2006 Core Duo / Core Solo models
1069 static UINT8 LegacyLoaderDevicePath1Data
[] = {
1070 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1071 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1072 0xFF, 0xFF, 0xF9, 0xFF, 0x00, 0x00, 0x00, 0x00,
1073 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1074 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1075 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1077 // mid-2006 Mac Pro (and probably other Core 2 models)
1078 static UINT8 LegacyLoaderDevicePath2Data
[] = {
1079 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1080 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1081 0xFF, 0xFF, 0xF7, 0xFF, 0x00, 0x00, 0x00, 0x00,
1082 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1083 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1084 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1086 // mid-2007 MBP ("Santa Rosa" based models)
1087 static UINT8 LegacyLoaderDevicePath3Data
[] = {
1088 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1089 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1090 0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
1091 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1092 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1093 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1096 static UINT8 LegacyLoaderDevicePath4Data
[] = {
1097 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1098 0x00, 0x00, 0xC0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1099 0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
1100 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1101 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1102 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1104 // late-2008 MB/MBP (NVidia chipset)
1105 static UINT8 LegacyLoaderDevicePath5Data
[] = {
1106 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1107 0x00, 0x40, 0xCB, 0xFF, 0x00, 0x00, 0x00, 0x00,
1108 0xFF, 0xBF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
1109 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1110 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1111 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1114 static EFI_DEVICE_PATH
*LegacyLoaderList
[] = {
1115 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath1Data
,
1116 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath2Data
,
1117 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath3Data
,
1118 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath4Data
,
1119 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath5Data
,
1123 #define MAX_DISCOVERED_PATHS (16)
1125 static VOID
StartLegacy(IN LEGACY_ENTRY
*Entry
)
1128 EG_IMAGE
*BootLogoImage
;
1129 UINTN ErrorInStep
= 0;
1130 EFI_DEVICE_PATH
*DiscoveredPathList
[MAX_DISCOVERED_PATHS
];
1132 BeginExternalScreen(TRUE
, L
"Booting Legacy OS (Mac mode)");
1134 BootLogoImage
= LoadOSIcon(Entry
->Volume
->OSIconName
, L
"legacy", TRUE
);
1135 if (BootLogoImage
!= NULL
)
1136 BltImageAlpha(BootLogoImage
,
1137 (UGAWidth
- BootLogoImage
->Width
) >> 1,
1138 (UGAHeight
- BootLogoImage
->Height
) >> 1,
1139 &StdBackgroundPixel
);
1141 if (Entry
->Volume
->IsMbrPartition
) {
1142 ActivateMbrPartition(Entry
->Volume
->WholeDiskBlockIO
, Entry
->Volume
->MbrPartitionIndex
);
1145 ExtractLegacyLoaderPaths(DiscoveredPathList
, MAX_DISCOVERED_PATHS
, LegacyLoaderList
);
1147 Status
= StartEFIImageList(DiscoveredPathList
, Entry
->LoadOptions
, NULL
, L
"legacy loader", 0, &ErrorInStep
, TRUE
);
1148 if (Status
== EFI_NOT_FOUND
) {
1149 if (ErrorInStep
== 1) {
1150 Print(L
"\nPlease make sure that you have the latest firmware update installed.\n");
1151 } else if (ErrorInStep
== 3) {
1152 Print(L
"\nThe firmware refused to boot from the selected volume. Note that external\n"
1153 L
"hard drives are not well-supported by Apple's firmware for legacy OS booting.\n");
1156 FinishExternalScreen();
1157 } /* static VOID StartLegacy() */
1159 // Start a device on a non-Mac using the EFI_LEGACY_BIOS_PROTOCOL
1160 #ifdef __MAKEWITH_TIANO
1161 static VOID
StartLegacyUEFI(IN LEGACY_ENTRY
*Entry
)
1163 BeginExternalScreen(TRUE
, L
"Booting Legacy OS (UEFI mode)");
1165 BdsLibConnectDevicePath (Entry
->BdsOption
->DevicePath
);
1166 BdsLibDoLegacyBoot(Entry
->BdsOption
);
1168 // If we get here, it means that there was a failure....
1169 Print(L
"Failure booting legacy (BIOS) OS.");
1171 FinishExternalScreen();
1172 } // static VOID StartLegacyUEFI()
1173 #endif // __MAKEWITH_TIANO
1175 static LEGACY_ENTRY
* AddLegacyEntry(IN CHAR16
*LoaderTitle
, IN REFIT_VOLUME
*Volume
)
1177 LEGACY_ENTRY
*Entry
, *SubEntry
;
1178 REFIT_MENU_SCREEN
*SubScreen
;
1180 CHAR16 ShortcutLetter
= 0;
1182 if (LoaderTitle
== NULL
) {
1183 if (Volume
->OSName
!= NULL
) {
1184 LoaderTitle
= Volume
->OSName
;
1185 if (LoaderTitle
[0] == 'W' || LoaderTitle
[0] == 'L')
1186 ShortcutLetter
= LoaderTitle
[0];
1188 LoaderTitle
= L
"Legacy OS";
1190 if (Volume
->VolName
!= NULL
)
1191 VolDesc
= Volume
->VolName
;
1193 VolDesc
= (Volume
->DiskKind
== DISK_KIND_OPTICAL
) ? L
"CD" : L
"HD";
1195 // prepare the menu entry
1196 Entry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1197 Entry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1198 SPrint(Entry
->me
.Title
, 255, L
"Boot %s from %s", LoaderTitle
, VolDesc
);
1199 Entry
->me
.Tag
= TAG_LEGACY
;
1201 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1202 Entry
->me
.Image
= LoadOSIcon(Volume
->OSIconName
, L
"legacy", FALSE
);
1203 Entry
->me
.BadgeImage
= Volume
->VolBadgeImage
;
1204 Entry
->Volume
= Volume
;
1205 Entry
->LoadOptions
= (Volume
->DiskKind
== DISK_KIND_OPTICAL
) ? L
"CD" :
1206 ((Volume
->DiskKind
== DISK_KIND_EXTERNAL
) ? L
"USB" : L
"HD");
1207 Entry
->Enabled
= TRUE
;
1209 // create the submenu
1210 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
1211 SubScreen
->Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1212 SPrint(SubScreen
->Title
, 255, L
"Boot Options for %s on %s", LoaderTitle
, VolDesc
);
1213 SubScreen
->TitleImage
= Entry
->me
.Image
;
1216 SubEntry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1217 SubEntry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1218 SPrint(SubEntry
->me
.Title
, 255, L
"Boot %s", LoaderTitle
);
1219 SubEntry
->me
.Tag
= TAG_LEGACY
;
1220 SubEntry
->Volume
= Entry
->Volume
;
1221 SubEntry
->LoadOptions
= Entry
->LoadOptions
;
1222 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
1224 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
1225 Entry
->me
.SubScreen
= SubScreen
;
1226 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1228 } /* static LEGACY_ENTRY * AddLegacyEntry() */
1231 #ifdef __MAKEWITH_TIANO
1232 // default volume badge icon based on disk kind
1233 static EG_IMAGE
* GetDiskBadge(IN UINTN DiskType
) {
1234 EG_IMAGE
* Badge
= NULL
;
1238 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_INTERNAL
);
1241 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_EXTERNAL
);
1244 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_OPTICAL
);
1248 } // static EG_IMAGE * GetDiskBadge()
1251 Create a rEFInd boot option from a Legacy BIOS protocol option.
1253 static LEGACY_ENTRY
* AddLegacyEntryUEFI(BDS_COMMON_OPTION
*BdsOption
, IN UINT16 DiskType
)
1255 LEGACY_ENTRY
*Entry
, *SubEntry
;
1256 REFIT_MENU_SCREEN
*SubScreen
;
1257 CHAR16 ShortcutLetter
= 0;
1258 CHAR16
*LegacyDescription
= BdsOption
->Description
;
1260 // ScanVolume(Volume);
1262 // prepare the menu entry
1263 Entry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1264 Entry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1265 SPrint(Entry
->me
.Title
, 255, L
"Boot legacy target %s", LegacyDescription
);
1266 Entry
->me
.Tag
= TAG_LEGACY_UEFI
;
1268 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1269 Entry
->me
.Image
= LoadOSIcon(L
"legacy", L
"legacy", TRUE
);
1270 Entry
->LoadOptions
= (DiskType
== BBS_CDROM
) ? L
"CD" :
1271 ((DiskType
== BBS_USB
) ? L
"USB" : L
"HD");
1272 Entry
->me
.BadgeImage
= GetDiskBadge(DiskType
);
1273 // Entry->me.BadgeImage = Volume->VolBadgeImage;
1274 Entry
->BdsOption
= BdsOption
;
1275 Entry
->Enabled
= TRUE
;
1277 // create the submenu
1278 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
1279 SubScreen
->Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1280 SPrint(SubScreen
->Title
, 255, L
"No boot options for legacy target");
1281 SubScreen
->TitleImage
= Entry
->me
.Image
;
1284 SubEntry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1285 SubEntry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1286 SPrint(SubEntry
->me
.Title
, 255, L
"Boot %s", LegacyDescription
);
1287 SubEntry
->me
.Tag
= TAG_LEGACY_UEFI
;
1288 Entry
->BdsOption
= BdsOption
;
1289 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
1291 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
1292 Entry
->me
.SubScreen
= SubScreen
;
1293 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1295 } /* static LEGACY_ENTRY * AddLegacyEntryUEFI() */
1298 Scan for legacy BIOS targets on machines that implement EFI_LEGACY_BIOS_PROTOCOL.
1299 In testing, protocol has not been implemented on Macs but has been
1300 implemented on several Dell PCs and an ASUS motherboard.
1301 Restricts output to disks of the specified DiskType.
1303 static VOID
ScanLegacyUEFI(IN UINTN DiskType
)
1306 EFI_LEGACY_BIOS_PROTOCOL
*LegacyBios
;
1307 UINT16
*BootOrder
= NULL
;
1309 CHAR16 BootOption
[10];
1310 UINTN BootOrderSize
= 0;
1312 BDS_COMMON_OPTION
*BdsOption
;
1313 LIST_ENTRY TempList
;
1314 BBS_BBS_DEVICE_PATH
* BbsDevicePath
= NULL
;
1315 // REFIT_VOLUME Volume;
1317 InitializeListHead (&TempList
);
1318 ZeroMem (Buffer
, sizeof (Buffer
));
1320 // If LegacyBios protocol is not implemented on this platform, then
1321 //we do not support this type of legacy boot on this machine.
1322 Status
= gBS
->LocateProtocol(&gEfiLegacyBootProtocolGuid
, NULL
, (VOID
**) &LegacyBios
);
1323 if (EFI_ERROR (Status
))
1326 // Grab the boot order
1327 BootOrder
= BdsLibGetVariableAndSize(L
"BootOrder", &gEfiGlobalVariableGuid
, &BootOrderSize
);
1328 if (BootOrder
== NULL
) {
1333 while (Index
< BootOrderSize
/ sizeof (UINT16
))
1335 // Grab each boot option variable from the boot order, and convert
1336 // the variable into a BDS boot option
1337 UnicodeSPrint (BootOption
, sizeof (BootOption
), L
"Boot%04x", BootOrder
[Index
]);
1338 BdsOption
= BdsLibVariableToOption (&TempList
, BootOption
);
1340 if (BdsOption
!= NULL
) {
1341 BbsDevicePath
= (BBS_BBS_DEVICE_PATH
*)BdsOption
->DevicePath
;
1343 // Only add the entry if it is of a requested type (e.g. USB, HD)
1345 // Two checks necessary because some systems return EFI boot loaders
1346 // with a DeviceType value that would inappropriately include them
1347 // as legacy loaders....
1348 if ((BbsDevicePath
->DeviceType
== DiskType
) && (BdsOption
->DevicePath
->Type
== DEVICE_TYPE_BIOS
)) {
1349 AddLegacyEntryUEFI(BdsOption
, BbsDevicePath
->DeviceType
);
1354 } /* static VOID ScanLegacyUEFI() */
1356 static VOID
ScanLegacyUEFI(IN UINTN DiskType
){}
1357 #endif // __MAKEWITH_TIANO
1359 static VOID
ScanLegacyVolume(REFIT_VOLUME
*Volume
, UINTN VolumeIndex
) {
1361 BOOLEAN ShowVolume
, HideIfOthersFound
;
1364 HideIfOthersFound
= FALSE
;
1365 if (Volume
->IsAppleLegacy
) {
1367 HideIfOthersFound
= TRUE
;
1368 } else if (Volume
->HasBootCode
) {
1370 if (Volume
->BlockIO
== Volume
->WholeDiskBlockIO
&&
1371 Volume
->BlockIOOffset
== 0 &&
1372 Volume
->OSName
== NULL
)
1373 // this is a whole disk (MBR) entry; hide if we have entries for partitions
1374 HideIfOthersFound
= TRUE
;
1376 if (HideIfOthersFound
) {
1377 // check for other bootable entries on the same disk
1378 for (VolumeIndex2
= 0; VolumeIndex2
< VolumesCount
; VolumeIndex2
++) {
1379 if (VolumeIndex2
!= VolumeIndex
&& Volumes
[VolumeIndex2
]->HasBootCode
&&
1380 Volumes
[VolumeIndex2
]->WholeDiskBlockIO
== Volume
->WholeDiskBlockIO
)
1386 AddLegacyEntry(NULL
, Volume
);
1387 } // static VOID ScanLegacyVolume()
1389 // Scan attached optical discs for legacy (BIOS) boot code
1390 // and add anything found to the list....
1391 static VOID
ScanLegacyDisc(VOID
)
1394 REFIT_VOLUME
*Volume
;
1396 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
) {
1397 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1398 Volume
= Volumes
[VolumeIndex
];
1399 if (Volume
->DiskKind
== DISK_KIND_OPTICAL
)
1400 ScanLegacyVolume(Volume
, VolumeIndex
);
1402 } else if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
1403 ScanLegacyUEFI(BBS_CDROM
);
1405 } /* static VOID ScanLegacyDisc() */
1407 // Scan internal hard disks for legacy (BIOS) boot code
1408 // and add anything found to the list....
1409 static VOID
ScanLegacyInternal(VOID
)
1412 REFIT_VOLUME
*Volume
;
1414 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
) {
1415 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1416 Volume
= Volumes
[VolumeIndex
];
1417 if (Volume
->DiskKind
== DISK_KIND_INTERNAL
)
1418 ScanLegacyVolume(Volume
, VolumeIndex
);
1420 } else if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
1421 ScanLegacyUEFI(BBS_HARDDISK
);
1423 } /* static VOID ScanLegacyInternal() */
1425 // Scan external disks for legacy (BIOS) boot code
1426 // and add anything found to the list....
1427 static VOID
ScanLegacyExternal(VOID
)
1430 REFIT_VOLUME
*Volume
;
1432 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
) {
1433 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1434 Volume
= Volumes
[VolumeIndex
];
1435 if (Volume
->DiskKind
== DISK_KIND_EXTERNAL
)
1436 ScanLegacyVolume(Volume
, VolumeIndex
);
1438 } else if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
1439 ScanLegacyUEFI(BBS_USB
);
1441 } /* static VOID ScanLegacyExternal() */
1444 // pre-boot tool functions
1447 static VOID
StartTool(IN LOADER_ENTRY
*Entry
)
1449 BeginExternalScreen(Entry
->UseGraphicsMode
, Entry
->me
.Title
+ 6); // assumes "Start <title>" as assigned below
1450 StartEFIImage(Entry
->DevicePath
, Entry
->LoadOptions
, Basename(Entry
->LoaderPath
),
1451 Basename(Entry
->LoaderPath
), Entry
->OSType
, NULL
, TRUE
);
1452 FinishExternalScreen();
1453 } /* static VOID StartTool() */
1455 static LOADER_ENTRY
* AddToolEntry(IN CHAR16
*LoaderPath
, IN CHAR16
*LoaderTitle
, IN EG_IMAGE
*Image
,
1456 IN CHAR16 ShortcutLetter
, IN BOOLEAN UseGraphicsMode
)
1458 LOADER_ENTRY
*Entry
;
1459 CHAR16
*TitleStr
= NULL
;
1461 Entry
= AllocateZeroPool(sizeof(LOADER_ENTRY
));
1463 MergeStrings(&TitleStr
, L
"Start ", 0);
1464 MergeStrings(&TitleStr
, LoaderTitle
, 0);
1465 Entry
->me
.Title
= TitleStr
;
1466 Entry
->me
.Tag
= TAG_TOOL
;
1468 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1469 Entry
->me
.Image
= Image
;
1470 Entry
->LoaderPath
= (LoaderPath
) ? StrDuplicate(LoaderPath
) : NULL
;
1471 Entry
->DevicePath
= FileDevicePath(SelfLoadedImage
->DeviceHandle
, Entry
->LoaderPath
);
1472 Entry
->UseGraphicsMode
= UseGraphicsMode
;
1474 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1476 } /* static LOADER_ENTRY * AddToolEntry() */
1479 // pre-boot driver functions
1482 static UINTN
ScanDriverDir(IN CHAR16
*Path
)
1485 REFIT_DIR_ITER DirIter
;
1487 EFI_FILE_INFO
*DirEntry
;
1488 CHAR16 FileName
[256];
1490 CleanUpPathNameSlashes(Path
);
1491 // look through contents of the directory
1492 DirIterOpen(SelfRootDir
, Path
, &DirIter
);
1493 while (DirIterNext(&DirIter
, 2, LOADER_MATCH_PATTERNS
, &DirEntry
)) {
1494 if (DirEntry
->FileName
[0] == '.')
1495 continue; // skip this
1497 SPrint(FileName
, 255, L
"%s\\%s", Path
, DirEntry
->FileName
);
1499 Status
= StartEFIImage(FileDevicePath(SelfLoadedImage
->DeviceHandle
, FileName
),
1500 L
"", DirEntry
->FileName
, DirEntry
->FileName
, 0, NULL
, FALSE
);
1502 Status
= DirIterClose(&DirIter
);
1503 if (Status
!= EFI_NOT_FOUND
) {
1504 SPrint(FileName
, 255, L
"while scanning the %s directory", Path
);
1505 CheckError(Status
, FileName
);
1510 #ifdef __MAKEWITH_GNUEFI
1511 static EFI_STATUS
ConnectAllDriversToAllControllers(VOID
)
1514 UINTN AllHandleCount
;
1515 EFI_HANDLE
*AllHandleBuffer
;
1518 EFI_HANDLE
*HandleBuffer
;
1524 Status
= LibLocateHandle(AllHandles
,
1529 if (EFI_ERROR(Status
))
1532 for (Index
= 0; Index
< AllHandleCount
; Index
++) {
1534 // Scan the handle database
1536 Status
= LibScanHandleDatabase(NULL
,
1538 AllHandleBuffer
[Index
],
1543 if (EFI_ERROR (Status
))
1547 if (HandleType
[Index
] & EFI_HANDLE_TYPE_DRIVER_BINDING_HANDLE
)
1549 if (HandleType
[Index
] & EFI_HANDLE_TYPE_IMAGE_HANDLE
)
1554 for (HandleIndex
= 0; HandleIndex
< HandleCount
; HandleIndex
++) {
1555 if (HandleType
[HandleIndex
] & EFI_HANDLE_TYPE_PARENT_HANDLE
)
1560 if (HandleType
[Index
] & EFI_HANDLE_TYPE_DEVICE_HANDLE
) {
1561 Status
= refit_call4_wrapper(BS
->ConnectController
,
1562 AllHandleBuffer
[Index
],
1570 MyFreePool (HandleBuffer
);
1571 MyFreePool (HandleType
);
1575 MyFreePool (AllHandleBuffer
);
1577 } /* EFI_STATUS ConnectAllDriversToAllControllers() */
1579 static EFI_STATUS
ConnectAllDriversToAllControllers(VOID
) {
1580 BdsLibConnectAllDriversToAllControllers();
1585 // Load all EFI drivers from rEFInd's "drivers" subdirectory and from the
1586 // directories specified by the user in the "scan_driver_dirs" configuration
1588 static VOID
LoadDrivers(VOID
)
1590 CHAR16
*Directory
, *SelfDirectory
;
1591 UINTN i
= 0, Length
, NumFound
= 0;
1593 // load drivers from the subdirectories of rEFInd's home directory specified
1594 // in the DRIVER_DIRS constant.
1595 while ((Directory
= FindCommaDelimited(DRIVER_DIRS
, i
++)) != NULL
) {
1596 SelfDirectory
= SelfDirPath
? StrDuplicate(SelfDirPath
) : NULL
;
1597 CleanUpPathNameSlashes(SelfDirectory
);
1598 MergeStrings(&SelfDirectory
, Directory
, L
'\\');
1599 NumFound
+= ScanDriverDir(SelfDirectory
);
1600 MyFreePool(Directory
);
1601 MyFreePool(SelfDirectory
);
1604 // Scan additional user-specified driver directories....
1606 while ((Directory
= FindCommaDelimited(GlobalConfig
.DriverDirs
, i
++)) != NULL
) {
1607 CleanUpPathNameSlashes(Directory
);
1608 Length
= StrLen(Directory
);
1610 NumFound
+= ScanDriverDir(Directory
);
1611 MyFreePool(Directory
);
1614 // connect all devices
1616 ConnectAllDriversToAllControllers();
1617 } /* static VOID LoadDrivers() */
1619 // Determine what (if any) type of legacy (BIOS) boot support is available
1620 static VOID
FindLegacyBootType(VOID
) {
1621 #ifdef __MAKEWITH_TIANO
1623 EFI_LEGACY_BIOS_PROTOCOL
*LegacyBios
;
1626 GlobalConfig
.LegacyType
= LEGACY_TYPE_NONE
;
1628 // UEFI-style legacy BIOS support is available only with the TianoCore EDK2
1629 // build environment, and then only with some implementations....
1630 #ifdef __MAKEWITH_TIANO
1631 Status
= gBS
->LocateProtocol (&gEfiLegacyBootProtocolGuid
, NULL
, (VOID
**) &LegacyBios
);
1632 if (!EFI_ERROR (Status
))
1633 GlobalConfig
.LegacyType
= LEGACY_TYPE_UEFI
;
1636 // Macs have their own system. If the firmware vendor code contains the
1637 // string "Apple", assume it's available. Note that this overrides the
1638 // UEFI type, and might yield false positives if the vendor string
1639 // contains "Apple" as part of something bigger, so this isn't 100%
1641 if (StriSubCmp(L
"Apple", ST
->FirmwareVendor
))
1642 GlobalConfig
.LegacyType
= LEGACY_TYPE_MAC
;
1643 } // static VOID FindLegacyBootType
1645 // Warn the user if legacy OS scans are enabled but the firmware or this
1646 // application can't support them....
1647 static VOID
WarnIfLegacyProblems() {
1648 BOOLEAN found
= FALSE
;
1651 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_NONE
) {
1653 if (GlobalConfig
.ScanFor
[i
] == 'h' || GlobalConfig
.ScanFor
[i
] == 'b' || GlobalConfig
.ScanFor
[i
] == 'c')
1656 } while ((i
< NUM_SCAN_OPTIONS
) && (!found
));
1658 Print(L
"NOTE: refind.conf's 'scanfor' line specifies scanning for one or more legacy\n");
1659 Print(L
"(BIOS) boot options; however, this is not possible because ");
1660 #ifdef __MAKEWITH_TIANO
1661 Print(L
"your computer lacks\n");
1662 Print(L
"the necessary Compatibility Support Module (CSM) support.\n");
1664 Print(L
"this program was\n");
1665 Print(L
"compiled without the necessary support. Please visit\n");
1666 Print(L
"http://www.rodsbooks.com/refind/getting.html and download and install a rEFInd\n");
1667 Print(L
"binary built with the TianoCore EDK2 to enable legacy boot support.\n");
1671 } // if no legacy support
1672 } // static VOID WarnIfLegacyProblems()
1674 // Locates boot loaders. NOTE: This assumes that GlobalConfig.LegacyType is set correctly.
1675 static VOID
ScanForBootloaders(VOID
) {
1680 // scan for loaders and tools, add them to the menu
1681 for (i
= 0; i
< NUM_SCAN_OPTIONS
; i
++) {
1682 switch(GlobalConfig
.ScanFor
[i
]) {
1687 ScanLegacyInternal();
1690 ScanLegacyExternal();
1693 ScanUserConfigured();
1707 // assign shortcut keys
1708 for (i
= 0; i
< MainMenu
.EntryCount
&& MainMenu
.Entries
[i
]->Row
== 0 && i
< 9; i
++)
1709 MainMenu
.Entries
[i
]->ShortcutDigit
= (CHAR16
)('1' + i
);
1711 // wait for user ACK when there were errors
1712 FinishTextScreen(FALSE
);
1713 } // static VOID ScanForBootloaders()
1715 // Add the second-row tags containing built-in and external tools (EFI shell,
1717 static VOID
ScanForTools(VOID
) {
1718 CHAR16
*FileName
= NULL
;
1719 REFIT_MENU_ENTRY
*TempMenuEntry
;
1722 for (i
= 0; i
< NUM_TOOLS
; i
++) {
1723 switch(GlobalConfig
.ShowTools
[i
]) {
1725 TempMenuEntry
= CopyMenuEntry(&MenuEntryShutdown
);
1726 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_SHUTDOWN
);
1727 AddMenuEntry(&MainMenu
, TempMenuEntry
);
1730 TempMenuEntry
= CopyMenuEntry(&MenuEntryReset
);
1731 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_RESET
);
1732 AddMenuEntry(&MainMenu
, TempMenuEntry
);
1735 TempMenuEntry
= CopyMenuEntry(&MenuEntryAbout
);
1736 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT
);
1737 AddMenuEntry(&MainMenu
, TempMenuEntry
);
1740 TempMenuEntry
= CopyMenuEntry(&MenuEntryExit
);
1741 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_EXIT
);
1742 AddMenuEntry(&MainMenu
, TempMenuEntry
);
1746 while ((FileName
= FindCommaDelimited(SHELL_NAMES
, j
++)) != NULL
) {
1747 if (FileExists(SelfRootDir
, FileName
)) {
1748 AddToolEntry(FileName
, L
"EFI Shell", BuiltinIcon(BUILTIN_ICON_TOOL_SHELL
), 'S', FALSE
);
1753 MergeStrings(&FileName
, L
"\\efi\\tools\\gptsync.efi", 0);
1754 if (FileExists(SelfRootDir
, FileName
)) {
1755 AddToolEntry(FileName
, L
"Make Hybrid MBR", BuiltinIcon(BUILTIN_ICON_TOOL_PART
), 'P', FALSE
);
1759 MyFreePool(FileName
);
1762 } // static VOID ScanForTools
1764 // Rescan for boot loaders
1765 VOID
RescanAll(VOID
) {
1772 egDisplayMessage(L
"Scanning for new boot loaders; please wait....", &BGColor
);
1773 FreeList((VOID
***) &(MainMenu
.Entries
), &MainMenu
.EntryCount
);
1774 MainMenu
.Entries
= NULL
;
1775 MainMenu
.EntryCount
= 0;
1777 ConnectAllDriversToAllControllers();
1778 ScanForBootloaders();
1781 } // VOID RescanAll()
1783 #ifndef __MAKEWITH_GNUEFI
1785 // Minimal initialization function
1786 static VOID
InitializeLib(IN EFI_HANDLE ImageHandle
, IN EFI_SYSTEM_TABLE
*SystemTable
) {
1788 // gImageHandle = ImageHandle;
1789 gBS
= SystemTable
->BootServices
;
1790 // gRS = SystemTable->RuntimeServices;
1791 gRT
= SystemTable
->RuntimeServices
; // Some BDS functions need gRT to be set
1792 EfiGetSystemConfigurationTable (&gEfiDxeServicesTableGuid
, (VOID
**) &gDS
);
1794 InitializeConsoleSim();
1804 efi_main (IN EFI_HANDLE ImageHandle
, IN EFI_SYSTEM_TABLE
*SystemTable
)
1807 BOOLEAN MainLoopRunning
= TRUE
;
1808 REFIT_MENU_ENTRY
*ChosenEntry
;
1814 InitializeLib(ImageHandle
, SystemTable
);
1816 Status
= InitRefitLib(ImageHandle
);
1817 if (EFI_ERROR(Status
))
1820 // read configuration
1821 CopyMem(GlobalConfig
.ScanFor
, "ieom ", NUM_SCAN_OPTIONS
);
1822 FindLegacyBootType();
1823 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
)
1824 CopyMem(GlobalConfig
.ScanFor
, "ihebocm ", NUM_SCAN_OPTIONS
);
1826 WarnIfLegacyProblems();
1827 MainMenu
.TimeoutSeconds
= GlobalConfig
.Timeout
;
1829 // disable EFI watchdog timer
1830 refit_call4_wrapper(BS
->SetWatchdogTimer
, 0x0000, 0x0000, 0x0000, NULL
);
1832 // further bootstrap (now with config available)
1834 if (GlobalConfig
.ScanDelay
> 0) {
1839 egDisplayMessage(L
"Pausing before disk scan; please wait....", &BGColor
);
1840 for (i
= 0; i
< GlobalConfig
.ScanDelay
; i
++)
1841 refit_call1_wrapper(BS
->Stall
, 1000000);
1844 ScanForBootloaders();
1847 Selection
= StrDuplicate(GlobalConfig
.DefaultSelection
);
1848 while (MainLoopRunning
) {
1849 MenuExit
= RunMainMenu(&MainMenu
, Selection
, &ChosenEntry
);
1851 // The Escape key triggers a re-scan operation....
1852 if (MenuExit
== MENU_EXIT_ESCAPE
) {
1857 switch (ChosenEntry
->Tag
) {
1859 case TAG_REBOOT
: // Reboot
1861 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);
1862 MainLoopRunning
= FALSE
; // just in case we get this far
1865 case TAG_SHUTDOWN
: // Shut Down
1867 refit_call4_wrapper(RT
->ResetSystem
, EfiResetShutdown
, EFI_SUCCESS
, 0, NULL
);
1868 MainLoopRunning
= FALSE
; // just in case we get this far
1871 case TAG_ABOUT
: // About rEFInd
1875 case TAG_LOADER
: // Boot OS via .EFI loader
1876 StartLoader((LOADER_ENTRY
*)ChosenEntry
);
1879 case TAG_LEGACY
: // Boot legacy OS
1880 StartLegacy((LEGACY_ENTRY
*)ChosenEntry
);
1883 #ifdef __MAKEWITH_TIANO
1884 case TAG_LEGACY_UEFI
: // Boot a legacy OS on a non-Mac
1885 StartLegacyUEFI((LEGACY_ENTRY
*)ChosenEntry
);
1887 #endif // __MAKEWITH_TIANO
1889 case TAG_TOOL
: // Start a EFI tool
1890 StartTool((LOADER_ENTRY
*)ChosenEntry
);
1893 case TAG_EXIT
: // Terminate rEFInd
1894 BeginTextScreen(L
" ");
1899 MyFreePool(Selection
);
1900 Selection
= (ChosenEntry
->Title
) ? StrDuplicate(ChosenEntry
->Title
) : NULL
;
1903 // If we end up here, things have gone wrong. Try to reboot, and if that
1904 // fails, go into an endless loop.
1905 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);