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");
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
,
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
' ');
203 MergeStrings(&FullLoadOptions
, L
" ", 0);
204 // NOTE: That last space is also added by the EFI shell and seems to be significant
205 // when passing options to Apple's boot.efi...
207 MergeStrings(&FullLoadOptions
, LoadOptions
, 0);
209 // NOTE: We also include the terminating null in the length for safety.
210 ChildLoadedImage
->LoadOptions
= (VOID
*)FullLoadOptions
;
211 ChildLoadedImage
->LoadOptionsSize
= ((UINT32
)StrLen(FullLoadOptions
) + 1) * sizeof(CHAR16
);
213 Print(L
"Using load options '%s'\n", FullLoadOptions
);
216 // close open file handles
219 // turn control over to the image
220 // TODO: (optionally) re-enable the EFI watchdog timer!
221 ReturnStatus
= Status
= refit_call3_wrapper(BS
->StartImage
, ChildImageHandle
, NULL
, NULL
);
222 // control returns here when the child image calls Exit()
223 SPrint(ErrorInfo
, 255, L
"returned from %s", ImageTitle
);
224 if (CheckError(Status
, ErrorInfo
)) {
225 if (ErrorInStep
!= NULL
)
229 // re-open file handles
233 // unload the image, we don't care if it works or not...
234 Status
= refit_call1_wrapper(BS
->UnloadImage
, ChildImageHandle
);
236 MyFreePool(FullLoadOptions
);
238 } /* static EFI_STATUS StartEFIImageList() */
240 static EFI_STATUS
StartEFIImage(IN EFI_DEVICE_PATH
*DevicePath
,
241 IN CHAR16
*LoadOptions
, IN CHAR16
*LoadOptionsPrefix
,
242 IN CHAR16
*ImageTitle
,
243 OUT UINTN
*ErrorInStep
,
246 EFI_DEVICE_PATH
*DevicePaths
[2];
248 DevicePaths
[0] = DevicePath
;
249 DevicePaths
[1] = NULL
;
250 return StartEFIImageList(DevicePaths
, LoadOptions
, LoadOptionsPrefix
, ImageTitle
, ErrorInStep
, Verbose
);
251 } /* static EFI_STATUS StartEFIImage() */
254 // EFI OS loader functions
257 static VOID
StartLoader(IN LOADER_ENTRY
*Entry
)
259 UINTN ErrorInStep
= 0;
261 BeginExternalScreen(Entry
->UseGraphicsMode
, L
"Booting OS");
262 StartEFIImage(Entry
->DevicePath
, Entry
->LoadOptions
,
263 Basename(Entry
->LoaderPath
), Basename(Entry
->LoaderPath
), &ErrorInStep
, !Entry
->UseGraphicsMode
);
264 FinishExternalScreen();
267 // Locate an initrd or initramfs file that matches the kernel specified by LoaderPath.
268 // The matching file has a name that begins with "init" and includes the same version
269 // number string as is found in LoaderPath -- but not a longer version number string.
270 // For instance, if LoaderPath is \EFI\kernels\bzImage-3.3.0.efi, and if \EFI\kernels
271 // has a file called initramfs-3.3.0.img, this function will return the string
272 // '\EFI\kernels\initramfs-3.3.0.img'. If the directory ALSO contains the file
273 // initramfs-3.3.0-rc7.img or initramfs-13.3.0.img, those files will NOT match;
274 // however, initmine-3.3.0.img might match. (FindInitrd() returns the first match it
275 // finds). Thus, care should be taken to avoid placing duplicate matching files in
276 // the kernel's directory.
277 // If no matching init file can be found, returns NULL.
278 static CHAR16
* FindInitrd(IN CHAR16
*LoaderPath
, IN REFIT_VOLUME
*Volume
) {
279 CHAR16
*InitrdName
= NULL
, *FileName
, *KernelVersion
, *InitrdVersion
, *Path
;
280 REFIT_DIR_ITER DirIter
;
281 EFI_FILE_INFO
*DirEntry
;
283 FileName
= Basename(LoaderPath
);
284 KernelVersion
= FindNumbers(FileName
);
285 Path
= FindPath(LoaderPath
);
287 // Add trailing backslash for root directory; necessary on some systems, but must
288 // NOT be added to all directories, since on other systems, a trailing backslash on
289 // anything but the root directory causes them to flake out!
290 if (StrLen(Path
) == 0) {
291 MergeStrings(&Path
, L
"\\", 0);
293 DirIterOpen(Volume
->RootDir
, Path
, &DirIter
);
294 // Now add a trailing backslash if it was NOT added earlier, for consistency in
295 // building the InitrdName later....
296 if ((StrLen(Path
) > 0) && (Path
[StrLen(Path
) - 1] != L
'\\'))
297 MergeStrings(&Path
, L
"\\", 0);
298 while ((DirIterNext(&DirIter
, 2, L
"init*", &DirEntry
)) && (InitrdName
== NULL
)) {
299 InitrdVersion
= FindNumbers(DirEntry
->FileName
);
300 if (KernelVersion
!= NULL
) {
301 if (StriCmp(InitrdVersion
, KernelVersion
) == 0) {
302 MergeStrings(&InitrdName
, Path
, 0);
303 MergeStrings(&InitrdName
, DirEntry
->FileName
, 0);
306 if (InitrdVersion
== NULL
) {
307 MergeStrings(&InitrdName
, Path
, 0);
308 MergeStrings(&InitrdName
, DirEntry
->FileName
, 0);
311 MyFreePool(InitrdVersion
);
313 DirIterClose(&DirIter
);
315 // Note: Don't FreePool(FileName), since Basename returns a pointer WITHIN the string it's passed.
316 MyFreePool(KernelVersion
);
319 } // static CHAR16 * FindInitrd()
321 LOADER_ENTRY
* AddPreparedLoaderEntry(LOADER_ENTRY
*Entry
) {
322 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
325 } // LOADER_ENTRY * AddPreparedLoaderEntry()
327 // Creates a copy of a menu screen.
328 // Returns a pointer to the copy of the menu screen.
329 static REFIT_MENU_SCREEN
* CopyMenuScreen(REFIT_MENU_SCREEN
*Entry
) {
330 REFIT_MENU_SCREEN
*NewEntry
;
333 NewEntry
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
334 if ((Entry
!= NULL
) && (NewEntry
!= NULL
)) {
335 CopyMem(NewEntry
, Entry
, sizeof(REFIT_MENU_SCREEN
));
336 NewEntry
->Title
= (Entry
->Title
) ? StrDuplicate(Entry
->Title
) : NULL
;
337 NewEntry
->TimeoutText
= (Entry
->TimeoutText
) ? StrDuplicate(Entry
->TimeoutText
) : NULL
;
338 if (Entry
->TitleImage
!= NULL
) {
339 NewEntry
->TitleImage
= AllocatePool(sizeof(EG_IMAGE
));
340 if (NewEntry
->TitleImage
!= NULL
)
341 CopyMem(NewEntry
->TitleImage
, Entry
->TitleImage
, sizeof(EG_IMAGE
));
343 NewEntry
->InfoLines
= (CHAR16
**) AllocateZeroPool(Entry
->InfoLineCount
* (sizeof(CHAR16
*)));
344 for (i
= 0; i
< Entry
->InfoLineCount
&& NewEntry
->InfoLines
; i
++) {
345 NewEntry
->InfoLines
[i
] = (Entry
->InfoLines
[i
]) ? StrDuplicate(Entry
->InfoLines
[i
]) : NULL
;
347 NewEntry
->Entries
= (REFIT_MENU_ENTRY
**) AllocateZeroPool(Entry
->EntryCount
* (sizeof (REFIT_MENU_ENTRY
*)));
348 for (i
= 0; i
< Entry
->EntryCount
&& NewEntry
->Entries
; i
++) {
349 AddMenuEntry(NewEntry
, Entry
->Entries
[i
]);
353 } // static REFIT_MENU_SCREEN* CopyMenuScreen()
355 // Creates a copy of a menu entry. Intended to enable moving a stack-based
356 // menu entry (such as the ones for the "reboot" and "exit" functions) to
357 // to the heap. This enables easier deletion of the whole set of menu
358 // entries when re-scanning.
359 // Returns a pointer to the copy of the menu entry.
360 static REFIT_MENU_ENTRY
* CopyMenuEntry(REFIT_MENU_ENTRY
*Entry
) {
361 REFIT_MENU_ENTRY
*NewEntry
;
363 NewEntry
= AllocateZeroPool(sizeof(REFIT_MENU_ENTRY
));
364 if ((Entry
!= NULL
) && (NewEntry
!= NULL
)) {
365 CopyMem(NewEntry
, Entry
, sizeof(REFIT_MENU_ENTRY
));
366 NewEntry
->Title
= (Entry
->Title
) ? StrDuplicate(Entry
->Title
) : NULL
;
367 if (Entry
->BadgeImage
!= NULL
) {
368 NewEntry
->BadgeImage
= AllocatePool(sizeof(EG_IMAGE
));
369 if (NewEntry
->BadgeImage
!= NULL
)
370 CopyMem(NewEntry
->BadgeImage
, Entry
->BadgeImage
, sizeof(EG_IMAGE
));
372 if (Entry
->Image
!= NULL
) {
373 NewEntry
->Image
= AllocatePool(sizeof(EG_IMAGE
));
374 if (NewEntry
->Image
!= NULL
)
375 CopyMem(NewEntry
->Image
, Entry
->Image
, sizeof(EG_IMAGE
));
377 if (Entry
->SubScreen
!= NULL
) {
378 NewEntry
->SubScreen
= CopyMenuScreen(Entry
->SubScreen
);
382 } // REFIT_MENU_ENTRY* CopyMenuEntry()
384 // Creates a new LOADER_ENTRY data structure and populates it with
385 // default values from the specified Entry, or NULL values if Entry
386 // is unspecified (NULL).
387 // Returns a pointer to the new data structure, or NULL if it
388 // couldn't be allocated
389 LOADER_ENTRY
*InitializeLoaderEntry(IN LOADER_ENTRY
*Entry
) {
390 LOADER_ENTRY
*NewEntry
= NULL
;
392 NewEntry
= AllocateZeroPool(sizeof(LOADER_ENTRY
));
393 if (NewEntry
!= NULL
) {
394 NewEntry
->me
.Title
= NULL
;
395 NewEntry
->me
.Tag
= TAG_LOADER
;
396 NewEntry
->Enabled
= TRUE
;
397 NewEntry
->UseGraphicsMode
= FALSE
;
398 NewEntry
->OSType
= 0;
400 NewEntry
->LoaderPath
= (Entry
->LoaderPath
) ? StrDuplicate(Entry
->LoaderPath
) : NULL
;
401 NewEntry
->VolName
= (Entry
->VolName
) ? StrDuplicate(Entry
->VolName
) : NULL
;
402 NewEntry
->DevicePath
= Entry
->DevicePath
;
403 NewEntry
->UseGraphicsMode
= Entry
->UseGraphicsMode
;
404 NewEntry
->LoadOptions
= (Entry
->LoadOptions
) ? StrDuplicate(Entry
->LoadOptions
) : NULL
;
405 NewEntry
->InitrdPath
= (Entry
->InitrdPath
) ? StrDuplicate(Entry
->InitrdPath
) : NULL
;
409 } // LOADER_ENTRY *InitializeLoaderEntry()
411 // Prepare a REFIT_MENU_SCREEN data structure for a subscreen entry. This sets up
412 // the default entry that launches the boot loader using the same options as the
413 // main Entry does. Subsequent options can be added by the calling function.
414 // If a subscreen already exists in the Entry that's passed to this function,
415 // it's left unchanged and a pointer to it is returned.
416 // Returns a pointer to the new subscreen data structure, or NULL if there
417 // were problems allocating memory.
418 REFIT_MENU_SCREEN
*InitializeSubScreen(IN LOADER_ENTRY
*Entry
) {
419 CHAR16
*FileName
, *Temp
= NULL
;
420 REFIT_MENU_SCREEN
*SubScreen
= NULL
;
421 LOADER_ENTRY
*SubEntry
;
423 FileName
= Basename(Entry
->LoaderPath
);
424 if (Entry
->me
.SubScreen
== NULL
) { // No subscreen yet; initialize default entry....
425 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
426 if (SubScreen
!= NULL
) {
427 SubScreen
->Title
= AllocateZeroPool(sizeof(CHAR16
) * 256);
428 SPrint(SubScreen
->Title
, 255, L
"Boot Options for %s on %s",
429 (Entry
->Title
!= NULL
) ? Entry
->Title
: FileName
, Entry
->VolName
);
430 SubScreen
->TitleImage
= Entry
->me
.Image
;
432 SubEntry
= InitializeLoaderEntry(Entry
);
433 if (SubEntry
!= NULL
) {
434 SubEntry
->me
.Title
= L
"Boot using default options";
435 if ((SubEntry
->InitrdPath
!= NULL
) && (StrLen(SubEntry
->InitrdPath
) > 0) && (!StriSubCmp(L
"initrd", SubEntry
->LoadOptions
))) {
436 MergeStrings(&Temp
, L
"initrd=", 0);
437 MergeStrings(&Temp
, SubEntry
->InitrdPath
, 0);
438 MergeStrings(&SubEntry
->LoadOptions
, Temp
, L
' ');
441 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
442 } // if (SubEntry != NULL)
443 } // if (SubScreen != NULL)
444 } else { // existing subscreen; less initialization, and just add new entry later....
445 SubScreen
= Entry
->me
.SubScreen
;
448 } // REFIT_MENU_SCREEN *InitializeSubScreen()
450 VOID
GenerateSubScreen(LOADER_ENTRY
*Entry
, IN REFIT_VOLUME
*Volume
) {
451 REFIT_MENU_SCREEN
*SubScreen
;
452 LOADER_ENTRY
*SubEntry
;
453 CHAR16
*InitrdOption
= NULL
, *Temp
;
454 CHAR16 DiagsFileName
[256];
459 // create the submenu
460 if (StrLen(Entry
->Title
) == 0) {
461 MyFreePool(Entry
->Title
);
464 SubScreen
= InitializeSubScreen(Entry
);
466 // loader-specific submenu entries
467 if (Entry
->OSType
== 'M') { // entries for Mac OS X
469 SubEntry
= InitializeLoaderEntry(Entry
);
470 if (SubEntry
!= NULL
) {
471 SubEntry
->me
.Title
= L
"Boot Mac OS X with a 64-bit kernel";
472 SubEntry
->LoadOptions
= L
"arch=x86_64";
473 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
474 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
477 SubEntry
= InitializeLoaderEntry(Entry
);
478 if (SubEntry
!= NULL
) {
479 SubEntry
->me
.Title
= L
"Boot Mac OS X with a 32-bit kernel";
480 SubEntry
->LoadOptions
= L
"arch=i386";
481 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
482 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
486 if (!(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_SINGLEUSER
)) {
487 SubEntry
= InitializeLoaderEntry(Entry
);
488 if (SubEntry
!= NULL
) {
489 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode";
490 SubEntry
->UseGraphicsMode
= FALSE
;
491 SubEntry
->LoadOptions
= L
"-v";
492 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
496 SubEntry
= InitializeLoaderEntry(Entry
);
497 if (SubEntry
!= NULL
) {
498 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode (64-bit)";
499 SubEntry
->UseGraphicsMode
= FALSE
;
500 SubEntry
->LoadOptions
= L
"-v arch=x86_64";
501 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
504 SubEntry
= InitializeLoaderEntry(Entry
);
505 if (SubEntry
!= NULL
) {
506 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode (32-bit)";
507 SubEntry
->UseGraphicsMode
= FALSE
;
508 SubEntry
->LoadOptions
= L
"-v arch=i386";
509 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
513 SubEntry
= InitializeLoaderEntry(Entry
);
514 if (SubEntry
!= NULL
) {
515 SubEntry
->me
.Title
= L
"Boot Mac OS X in single user mode";
516 SubEntry
->UseGraphicsMode
= FALSE
;
517 SubEntry
->LoadOptions
= L
"-v -s";
518 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
522 // check for Apple hardware diagnostics
523 StrCpy(DiagsFileName
, L
"\\System\\Library\\CoreServices\\.diagnostics\\diags.efi");
524 if (FileExists(Volume
->RootDir
, DiagsFileName
) && !(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_HWTEST
)) {
525 SubEntry
= InitializeLoaderEntry(Entry
);
526 if (SubEntry
!= NULL
) {
527 SubEntry
->me
.Title
= L
"Run Apple Hardware Test";
528 MyFreePool(SubEntry
->LoaderPath
);
529 SubEntry
->LoaderPath
= StrDuplicate(DiagsFileName
);
530 SubEntry
->DevicePath
= FileDevicePath(Volume
->DeviceHandle
, SubEntry
->LoaderPath
);
531 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
532 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
534 } // if diagnostics entry found
536 } else if (Entry
->OSType
== 'L') { // entries for Linux kernels with EFI stub loaders
537 File
= ReadLinuxOptionsFile(Entry
->LoaderPath
, Volume
);
539 if ((Temp
= FindInitrd(Entry
->LoaderPath
, Volume
)) != NULL
) {
540 MergeStrings(&InitrdOption
, L
"initrd=", 0);
541 MergeStrings(&InitrdOption
, Temp
, 0);
543 TokenCount
= ReadTokenLine(File
, &TokenList
); // read and discard first entry, since it's
544 FreeTokenLine(&TokenList
, &TokenCount
); // set up by InitializeSubScreen(), earlier....
545 while ((TokenCount
= ReadTokenLine(File
, &TokenList
)) > 1) {
546 SubEntry
= InitializeLoaderEntry(Entry
);
547 SubEntry
->me
.Title
= StrDuplicate(TokenList
[0]);
548 MyFreePool(SubEntry
->LoadOptions
);
549 SubEntry
->LoadOptions
= StrDuplicate(TokenList
[1]);
550 MergeStrings(&SubEntry
->LoadOptions
, InitrdOption
, L
' ');
551 FreeTokenLine(&TokenList
, &TokenCount
);
552 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_LINUX
;
553 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
555 MyFreePool(InitrdOption
);
558 } // if Linux options file exists
560 } else if (Entry
->OSType
== 'E') { // entries for ELILO
561 SubEntry
= InitializeLoaderEntry(Entry
);
562 if (SubEntry
!= NULL
) {
563 SubEntry
->me
.Title
= L
"Run ELILO in interactive mode";
564 SubEntry
->LoadOptions
= L
"-p";
565 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
566 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
569 SubEntry
= InitializeLoaderEntry(Entry
);
570 if (SubEntry
!= NULL
) {
571 SubEntry
->me
.Title
= L
"Boot Linux for a 17\" iMac or a 15\" MacBook Pro (*)";
572 SubEntry
->UseGraphicsMode
= TRUE
;
573 SubEntry
->LoadOptions
= L
"-d 0 i17";
574 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
575 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
578 SubEntry
= InitializeLoaderEntry(Entry
);
579 if (SubEntry
!= NULL
) {
580 SubEntry
->me
.Title
= L
"Boot Linux for a 20\" iMac (*)";
581 SubEntry
->UseGraphicsMode
= TRUE
;
582 SubEntry
->LoadOptions
= L
"-d 0 i20";
583 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
584 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
587 SubEntry
= InitializeLoaderEntry(Entry
);
588 if (SubEntry
!= NULL
) {
589 SubEntry
->me
.Title
= L
"Boot Linux for a Mac Mini (*)";
590 SubEntry
->UseGraphicsMode
= TRUE
;
591 SubEntry
->LoadOptions
= L
"-d 0 mini";
592 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
593 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
596 AddMenuInfoLine(SubScreen
, L
"NOTE: This is an example. Entries");
597 AddMenuInfoLine(SubScreen
, L
"marked with (*) may not work.");
599 } else if (Entry
->OSType
== 'X') { // entries for xom.efi
600 // by default, skip the built-in selection and boot from hard disk only
601 Entry
->LoadOptions
= L
"-s -h";
603 SubEntry
= InitializeLoaderEntry(Entry
);
604 if (SubEntry
!= NULL
) {
605 SubEntry
->me
.Title
= L
"Boot Windows from Hard Disk";
606 SubEntry
->LoadOptions
= L
"-s -h";
607 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
608 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
611 SubEntry
= InitializeLoaderEntry(Entry
);
612 if (SubEntry
!= NULL
) {
613 SubEntry
->me
.Title
= L
"Boot Windows from CD-ROM";
614 SubEntry
->LoadOptions
= L
"-s -c";
615 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
616 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
619 SubEntry
= InitializeLoaderEntry(Entry
);
620 if (SubEntry
!= NULL
) {
621 SubEntry
->me
.Title
= L
"Run XOM in text mode";
622 SubEntry
->UseGraphicsMode
= FALSE
;
623 SubEntry
->LoadOptions
= L
"-v";
624 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
625 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
627 } // entries for xom.efi
628 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
629 Entry
->me
.SubScreen
= SubScreen
;
630 } // VOID GenerateSubScreen()
632 // Returns options for a Linux kernel. Reads them from an options file in the
633 // kernel's directory; and if present, adds an initrd= option for an initial
634 // RAM disk file with the same version number as the kernel file.
635 static CHAR16
* GetMainLinuxOptions(IN CHAR16
* LoaderPath
, IN REFIT_VOLUME
*Volume
) {
636 CHAR16
*Options
= NULL
, *InitrdName
, *InitrdOption
= NULL
;
638 Options
= GetFirstOptionsFromFile(LoaderPath
, Volume
);
639 InitrdName
= FindInitrd(LoaderPath
, Volume
);
640 if (InitrdName
!= NULL
) {
641 MergeStrings(&InitrdOption
, L
"initrd=", 0);
642 MergeStrings(&InitrdOption
, InitrdName
, 0);
644 MergeStrings(&Options
, InitrdOption
, ' ');
645 MyFreePool(InitrdOption
);
646 MyFreePool(InitrdName
);
648 } // static CHAR16 * GetMainLinuxOptions()
650 // Sets a few defaults for a loader entry -- mainly the icon, but also the OS type
651 // code and shortcut letter. For Linux EFI stub loaders, also sets kernel options
652 // that will (with luck) work fairly automatically.
653 VOID
SetLoaderDefaults(LOADER_ENTRY
*Entry
, CHAR16
*LoaderPath
, IN REFIT_VOLUME
*Volume
) {
654 CHAR16 IconFileName
[256];
655 CHAR16
*FileName
, *PathOnly
, *OSIconName
= NULL
, *Temp
;
656 CHAR16 ShortcutLetter
= 0;
658 FileName
= Basename(LoaderPath
);
659 PathOnly
= FindPath(LoaderPath
);
661 // locate a custom icon for the loader
662 StrCpy(IconFileName
, LoaderPath
);
663 ReplaceEfiExtension(IconFileName
, L
".icns");
664 if (FileExists(Volume
->RootDir
, IconFileName
)) {
665 Entry
->me
.Image
= LoadIcns(Volume
->RootDir
, IconFileName
, 128);
666 } else if ((StrLen(PathOnly
) == 0) && (Volume
->VolIconImage
!= NULL
)) {
667 Entry
->me
.Image
= Volume
->VolIconImage
;
668 } // icon matched to loader or volume
670 Temp
= FindLastDirName(LoaderPath
);
671 MergeStrings(&OSIconName
, Temp
, L
',');
673 if (OSIconName
!= NULL
) {
674 ShortcutLetter
= OSIconName
[0];
677 // detect specific loaders
678 if (StriSubCmp(L
"bzImage", LoaderPath
) || StriSubCmp(L
"vmlinuz", LoaderPath
)) {
679 MergeStrings(&OSIconName
, L
"linux", L
',');
681 if (ShortcutLetter
== 0)
682 ShortcutLetter
= 'L';
683 Entry
->LoadOptions
= GetMainLinuxOptions(LoaderPath
, Volume
);
684 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_LINUX
;
685 } else if (StriSubCmp(L
"refit", LoaderPath
)) {
686 MergeStrings(&OSIconName
, L
"refit", L
',');
688 ShortcutLetter
= 'R';
689 } else if (StriCmp(LoaderPath
, MACOSX_LOADER_PATH
) == 0) {
690 if (Volume
->VolIconImage
!= NULL
) { // custom icon file found
691 Entry
->me
.Image
= Volume
->VolIconImage
;
693 MergeStrings(&OSIconName
, L
"mac", L
',');
695 ShortcutLetter
= 'M';
696 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
697 } else if (StriCmp(FileName
, L
"diags.efi") == 0) {
698 MergeStrings(&OSIconName
, L
"hwtest", L
',');
699 } else if (StriCmp(FileName
, L
"e.efi") == 0 || StriCmp(FileName
, L
"elilo.efi") == 0) {
700 MergeStrings(&OSIconName
, L
"elilo,linux", L
',');
702 if (ShortcutLetter
== 0)
703 ShortcutLetter
= 'L';
704 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
705 } else if (StriSubCmp(L
"grub", FileName
)) {
707 ShortcutLetter
= 'G';
708 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_GRUB
;
709 } else if (StriCmp(FileName
, L
"cdboot.efi") == 0 ||
710 StriCmp(FileName
, L
"bootmgr.efi") == 0 ||
711 StriCmp(FileName
, L
"Bootmgfw.efi") == 0) {
712 MergeStrings(&OSIconName
, L
"win", L
',');
714 ShortcutLetter
= 'W';
715 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
716 } else if (StriCmp(FileName
, L
"xom.efi") == 0) {
717 MergeStrings(&OSIconName
, L
"xom,win", L
',');
718 Entry
->UseGraphicsMode
= TRUE
;
720 ShortcutLetter
= 'W';
721 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
724 if ((ShortcutLetter
>= 'a') && (ShortcutLetter
<= 'z'))
725 ShortcutLetter
= ShortcutLetter
- 'a' + 'A'; // convert lowercase to uppercase
726 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
727 if (Entry
->me
.Image
== NULL
)
728 Entry
->me
.Image
= LoadOSIcon(OSIconName
, L
"unknown", FALSE
);
729 MyFreePool(PathOnly
);
730 } // VOID SetLoaderDefaults()
732 // Add a specified EFI boot loader to the list, using automatic settings
733 // for icons, options, etc.
734 LOADER_ENTRY
* AddLoaderEntry(IN CHAR16
*LoaderPath
, IN CHAR16
*LoaderTitle
, IN REFIT_VOLUME
*Volume
) {
737 CleanUpPathNameSlashes(LoaderPath
);
738 Entry
= InitializeLoaderEntry(NULL
);
740 Entry
->Title
= StrDuplicate((LoaderTitle
!= NULL
) ? LoaderTitle
: LoaderPath
);
741 Entry
->me
.Title
= AllocateZeroPool(sizeof(CHAR16
) * 256);
742 SPrint(Entry
->me
.Title
, 255, L
"Boot %s from %s", (LoaderTitle
!= NULL
) ? LoaderTitle
: LoaderPath
, Volume
->VolName
);
744 Entry
->me
.BadgeImage
= Volume
->VolBadgeImage
;
745 if ((LoaderPath
!= NULL
) && (LoaderPath
[0] != L
'\\')) {
746 Entry
->LoaderPath
= StrDuplicate(L
"\\");
748 Entry
->LoaderPath
= NULL
;
750 MergeStrings(&(Entry
->LoaderPath
), LoaderPath
, 0);
751 Entry
->VolName
= Volume
->VolName
;
752 Entry
->DevicePath
= FileDevicePath(Volume
->DeviceHandle
, Entry
->LoaderPath
);
753 SetLoaderDefaults(Entry
, LoaderPath
, Volume
);
754 GenerateSubScreen(Entry
, Volume
);
755 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
759 } // LOADER_ENTRY * AddLoaderEntry()
761 // Returns -1 if (Time1 < Time2), +1 if (Time1 > Time2), or 0 if
762 // (Time1 == Time2). Precision is only to the nearest second; since
763 // this is used for sorting boot loader entries, differences smaller
764 // than this are likely to be meaningless (and unlikely!).
765 INTN
TimeComp(EFI_TIME
*Time1
, EFI_TIME
*Time2
) {
766 INT64 Time1InSeconds
, Time2InSeconds
;
768 // Following values are overestimates; I'm assuming 31 days in every month.
769 // This is fine for the purpose of this function, which has a limited
771 Time1InSeconds
= Time1
->Second
+ (Time1
->Minute
* 60) + (Time1
->Hour
* 3600) + (Time1
->Day
* 86400) +
772 (Time1
->Month
* 2678400) + ((Time1
->Year
- 1998) * 32140800);
773 Time2InSeconds
= Time2
->Second
+ (Time2
->Minute
* 60) + (Time2
->Hour
* 3600) + (Time2
->Day
* 86400) +
774 (Time2
->Month
* 2678400) + ((Time2
->Year
- 1998) * 32140800);
775 if (Time1InSeconds
< Time2InSeconds
)
777 else if (Time1InSeconds
> Time2InSeconds
)
783 // Adds a loader list element, keeping it sorted by date. Returns the new
784 // first element (the one with the most recent date).
785 static struct LOADER_LIST
* AddLoaderListEntry(struct LOADER_LIST
*LoaderList
, struct LOADER_LIST
*NewEntry
) {
786 struct LOADER_LIST
*LatestEntry
, *CurrentEntry
, *PrevEntry
= NULL
;
788 LatestEntry
= CurrentEntry
= LoaderList
;
789 if (LoaderList
== NULL
) {
790 LatestEntry
= NewEntry
;
792 while ((CurrentEntry
!= NULL
) && (TimeComp(&(NewEntry
->TimeStamp
), &(CurrentEntry
->TimeStamp
)) < 0)) {
793 PrevEntry
= CurrentEntry
;
794 CurrentEntry
= CurrentEntry
->NextEntry
;
796 NewEntry
->NextEntry
= CurrentEntry
;
797 if (PrevEntry
== NULL
) {
798 LatestEntry
= NewEntry
;
800 PrevEntry
->NextEntry
= NewEntry
;
803 return (LatestEntry
);
804 } // static VOID AddLoaderListEntry()
806 // Delete the LOADER_LIST linked list
807 static VOID
CleanUpLoaderList(struct LOADER_LIST
*LoaderList
) {
808 struct LOADER_LIST
*Temp
;
810 while (LoaderList
!= NULL
) {
812 LoaderList
= LoaderList
->NextEntry
;
813 MyFreePool(Temp
->FileName
);
816 } // static VOID CleanUpLoaderList()
818 // Scan an individual directory for EFI boot loader files and, if found,
819 // add them to the list. Sorts the entries within the loader directory
820 // so that the most recent one appears first in the list.
821 static VOID
ScanLoaderDir(IN REFIT_VOLUME
*Volume
, IN CHAR16
*Path
, IN CHAR16
*Pattern
)
824 REFIT_DIR_ITER DirIter
;
825 EFI_FILE_INFO
*DirEntry
;
826 CHAR16 FileName
[256], *Extension
;
827 struct LOADER_LIST
*LoaderList
= NULL
, *NewLoader
;
829 if ((!SelfDirPath
|| !Path
|| ((StriCmp(Path
, SelfDirPath
) == 0) && Volume
->DeviceHandle
!= SelfVolume
->DeviceHandle
) ||
830 (StriCmp(Path
, SelfDirPath
) != 0)) && (!IsIn(Path
, GlobalConfig
.DontScan
))) {
831 // look through contents of the directory
832 DirIterOpen(Volume
->RootDir
, Path
, &DirIter
);
833 while (DirIterNext(&DirIter
, 2, Pattern
, &DirEntry
)) {
834 Extension
= FindExtension(DirEntry
->FileName
);
835 if (DirEntry
->FileName
[0] == '.' ||
836 StriCmp(DirEntry
->FileName
, L
"TextMode.efi") == 0 ||
837 StriCmp(DirEntry
->FileName
, L
"ebounce.efi") == 0 ||
838 StriCmp(DirEntry
->FileName
, L
"GraphicsConsole.efi") == 0 ||
839 StriCmp(Extension
, L
".icns") == 0 ||
840 StriSubCmp(L
"shell", DirEntry
->FileName
))
841 continue; // skip this
844 SPrint(FileName
, 255, L
"\\%s\\%s", Path
, DirEntry
->FileName
);
846 SPrint(FileName
, 255, L
"\\%s", DirEntry
->FileName
);
847 CleanUpPathNameSlashes(FileName
);
848 NewLoader
= AllocateZeroPool(sizeof(struct LOADER_LIST
));
849 if (NewLoader
!= NULL
) {
850 NewLoader
->FileName
= StrDuplicate(FileName
);
851 NewLoader
->TimeStamp
= DirEntry
->ModificationTime
;
852 LoaderList
= AddLoaderListEntry(LoaderList
, NewLoader
);
854 MyFreePool(Extension
);
856 NewLoader
= LoaderList
;
857 while (NewLoader
!= NULL
) {
858 AddLoaderEntry(NewLoader
->FileName
, NULL
, Volume
);
859 NewLoader
= NewLoader
->NextEntry
;
861 CleanUpLoaderList(LoaderList
);
862 Status
= DirIterClose(&DirIter
);
863 if (Status
!= EFI_NOT_FOUND
) {
865 SPrint(FileName
, 255, L
"while scanning the %s directory", Path
);
867 StrCpy(FileName
, L
"while scanning the root directory");
868 CheckError(Status
, FileName
);
869 } // if (Status != EFI_NOT_FOUND)
870 } // if not scanning our own directory
871 } /* static VOID ScanLoaderDir() */
873 static VOID
ScanEfiFiles(REFIT_VOLUME
*Volume
) {
875 REFIT_DIR_ITER EfiDirIter
;
876 EFI_FILE_INFO
*EfiDirEntry
;
877 CHAR16 FileName
[256], *Directory
, *MatchPatterns
;
880 MatchPatterns
= StrDuplicate(LOADER_MATCH_PATTERNS
);
881 if (GlobalConfig
.ScanAllLinux
)
882 MergeStrings(&MatchPatterns
, LINUX_MATCH_PATTERNS
, L
',');
884 if ((Volume
->RootDir
!= NULL
) && (Volume
->VolName
!= NULL
)) {
885 // check for Mac OS X boot loader
886 if (!IsIn(L
"System\\Library\\CoreServices", GlobalConfig
.DontScan
)) {
887 StrCpy(FileName
, MACOSX_LOADER_PATH
);
888 if (FileExists(Volume
->RootDir
, FileName
)) {
889 AddLoaderEntry(FileName
, L
"Mac OS X", Volume
);
893 StrCpy(FileName
, L
"System\\Library\\CoreServices\\xom.efi");
894 if (FileExists(Volume
->RootDir
, FileName
)) {
895 AddLoaderEntry(FileName
, L
"Windows XP (XoM)", Volume
);
897 } // if Mac directory not in GlobalConfig.DontScan list
899 // check for Microsoft boot loader/menu
900 StrCpy(FileName
, L
"EFI\\Microsoft\\Boot\\Bootmgfw.efi");
901 if (FileExists(Volume
->RootDir
, FileName
) && !IsIn(L
"EFI\\Microsoft\\Boot", GlobalConfig
.DontScan
)) {
902 AddLoaderEntry(FileName
, L
"Microsoft EFI boot", Volume
);
905 // scan the root directory for EFI executables
906 ScanLoaderDir(Volume
, L
"\\", MatchPatterns
);
908 // scan subdirectories of the EFI directory (as per the standard)
909 DirIterOpen(Volume
->RootDir
, L
"EFI", &EfiDirIter
);
910 while (DirIterNext(&EfiDirIter
, 1, NULL
, &EfiDirEntry
)) {
911 if (StriCmp(EfiDirEntry
->FileName
, L
"tools") == 0 || EfiDirEntry
->FileName
[0] == '.')
912 continue; // skip this, doesn't contain boot loaders
913 SPrint(FileName
, 255, L
"EFI\\%s", EfiDirEntry
->FileName
);
914 ScanLoaderDir(Volume
, FileName
, MatchPatterns
);
916 Status
= DirIterClose(&EfiDirIter
);
917 if (Status
!= EFI_NOT_FOUND
)
918 CheckError(Status
, L
"while scanning the EFI directory");
920 // Scan user-specified (or additional default) directories....
922 while ((Directory
= FindCommaDelimited(GlobalConfig
.AlsoScan
, i
++)) != NULL
) {
923 CleanUpPathNameSlashes(Directory
);
924 Length
= StrLen(Directory
);
926 ScanLoaderDir(Volume
, Directory
, MatchPatterns
);
927 MyFreePool(Directory
);
930 } // static VOID ScanEfiFiles()
932 // Scan internal disks for valid EFI boot loaders....
933 static VOID
ScanInternal(VOID
) {
936 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
937 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_INTERNAL
) {
938 ScanEfiFiles(Volumes
[VolumeIndex
]);
941 } // static VOID ScanInternal()
943 // Scan external disks for valid EFI boot loaders....
944 static VOID
ScanExternal(VOID
) {
947 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
948 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_EXTERNAL
) {
949 ScanEfiFiles(Volumes
[VolumeIndex
]);
952 } // static VOID ScanExternal()
954 // Scan internal disks for valid EFI boot loaders....
955 static VOID
ScanOptical(VOID
) {
958 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
959 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_OPTICAL
) {
960 ScanEfiFiles(Volumes
[VolumeIndex
]);
963 } // static VOID ScanOptical()
966 // legacy boot functions
969 static EFI_STATUS
ActivateMbrPartition(IN EFI_BLOCK_IO
*BlockIO
, IN UINTN PartitionIndex
)
972 UINT8 SectorBuffer
[512];
973 MBR_PARTITION_INFO
*MbrTable
, *EMbrTable
;
974 UINT32 ExtBase
, ExtCurrent
, NextExtCurrent
;
975 UINTN LogicalPartitionIndex
= 4;
977 BOOLEAN HaveBootCode
;
980 Status
= refit_call5_wrapper(BlockIO
->ReadBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, 0, 512, SectorBuffer
);
981 if (EFI_ERROR(Status
))
983 if (*((UINT16
*)(SectorBuffer
+ 510)) != 0xaa55)
984 return EFI_NOT_FOUND
; // safety measure #1
986 // add boot code if necessary
987 HaveBootCode
= FALSE
;
988 for (i
= 0; i
< MBR_BOOTCODE_SIZE
; i
++) {
989 if (SectorBuffer
[i
] != 0) {
995 // no boot code found in the MBR, add the syslinux MBR code
996 SetMem(SectorBuffer
, MBR_BOOTCODE_SIZE
, 0);
997 CopyMem(SectorBuffer
, syslinux_mbr
, SYSLINUX_MBR_SIZE
);
1000 // set the partition active
1001 MbrTable
= (MBR_PARTITION_INFO
*)(SectorBuffer
+ 446);
1003 for (i
= 0; i
< 4; i
++) {
1004 if (MbrTable
[i
].Flags
!= 0x00 && MbrTable
[i
].Flags
!= 0x80)
1005 return EFI_NOT_FOUND
; // safety measure #2
1006 if (i
== PartitionIndex
)
1007 MbrTable
[i
].Flags
= 0x80;
1008 else if (PartitionIndex
>= 4 && IS_EXTENDED_PART_TYPE(MbrTable
[i
].Type
)) {
1009 MbrTable
[i
].Flags
= 0x80;
1010 ExtBase
= MbrTable
[i
].StartLBA
;
1012 MbrTable
[i
].Flags
= 0x00;
1016 Status
= refit_call5_wrapper(BlockIO
->WriteBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, 0, 512, SectorBuffer
);
1017 if (EFI_ERROR(Status
))
1020 if (PartitionIndex
>= 4) {
1021 // we have to activate a logical partition, so walk the EMBR chain
1023 // NOTE: ExtBase was set above while looking at the MBR table
1024 for (ExtCurrent
= ExtBase
; ExtCurrent
; ExtCurrent
= NextExtCurrent
) {
1025 // read current EMBR
1026 Status
= refit_call5_wrapper(BlockIO
->ReadBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, ExtCurrent
, 512, SectorBuffer
);
1027 if (EFI_ERROR(Status
))
1029 if (*((UINT16
*)(SectorBuffer
+ 510)) != 0xaa55)
1030 return EFI_NOT_FOUND
; // safety measure #3
1032 // scan EMBR, set appropriate partition active
1033 EMbrTable
= (MBR_PARTITION_INFO
*)(SectorBuffer
+ 446);
1035 for (i
= 0; i
< 4; i
++) {
1036 if (EMbrTable
[i
].Flags
!= 0x00 && EMbrTable
[i
].Flags
!= 0x80)
1037 return EFI_NOT_FOUND
; // safety measure #4
1038 if (EMbrTable
[i
].StartLBA
== 0 || EMbrTable
[i
].Size
== 0)
1040 if (IS_EXTENDED_PART_TYPE(EMbrTable
[i
].Type
)) {
1041 // link to next EMBR
1042 NextExtCurrent
= ExtBase
+ EMbrTable
[i
].StartLBA
;
1043 EMbrTable
[i
].Flags
= (PartitionIndex
>= LogicalPartitionIndex
) ? 0x80 : 0x00;
1046 // logical partition
1047 EMbrTable
[i
].Flags
= (PartitionIndex
== LogicalPartitionIndex
) ? 0x80 : 0x00;
1048 LogicalPartitionIndex
++;
1052 // write current EMBR
1053 Status
= refit_call5_wrapper(BlockIO
->WriteBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, ExtCurrent
, 512, SectorBuffer
);
1054 if (EFI_ERROR(Status
))
1057 if (PartitionIndex
< LogicalPartitionIndex
)
1058 break; // stop the loop, no need to touch further EMBRs
1064 } /* static EFI_STATUS ActivateMbrPartition() */
1066 // early 2006 Core Duo / Core Solo models
1067 static UINT8 LegacyLoaderDevicePath1Data
[] = {
1068 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1069 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1070 0xFF, 0xFF, 0xF9, 0xFF, 0x00, 0x00, 0x00, 0x00,
1071 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1072 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1073 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1075 // mid-2006 Mac Pro (and probably other Core 2 models)
1076 static UINT8 LegacyLoaderDevicePath2Data
[] = {
1077 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1078 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1079 0xFF, 0xFF, 0xF7, 0xFF, 0x00, 0x00, 0x00, 0x00,
1080 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1081 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1082 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1084 // mid-2007 MBP ("Santa Rosa" based models)
1085 static UINT8 LegacyLoaderDevicePath3Data
[] = {
1086 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1087 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1088 0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
1089 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1090 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1091 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1094 static UINT8 LegacyLoaderDevicePath4Data
[] = {
1095 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1096 0x00, 0x00, 0xC0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1097 0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
1098 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1099 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1100 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1102 // late-2008 MB/MBP (NVidia chipset)
1103 static UINT8 LegacyLoaderDevicePath5Data
[] = {
1104 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1105 0x00, 0x40, 0xCB, 0xFF, 0x00, 0x00, 0x00, 0x00,
1106 0xFF, 0xBF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
1107 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1108 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1109 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1112 static EFI_DEVICE_PATH
*LegacyLoaderList
[] = {
1113 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath1Data
,
1114 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath2Data
,
1115 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath3Data
,
1116 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath4Data
,
1117 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath5Data
,
1121 #define MAX_DISCOVERED_PATHS (16)
1123 static VOID
StartLegacy(IN LEGACY_ENTRY
*Entry
)
1126 EG_IMAGE
*BootLogoImage
;
1127 UINTN ErrorInStep
= 0;
1128 EFI_DEVICE_PATH
*DiscoveredPathList
[MAX_DISCOVERED_PATHS
];
1130 BeginExternalScreen(TRUE
, L
"Booting Legacy OS (Mac mode)");
1132 BootLogoImage
= LoadOSIcon(Entry
->Volume
->OSIconName
, L
"legacy", TRUE
);
1133 if (BootLogoImage
!= NULL
)
1134 BltImageAlpha(BootLogoImage
,
1135 (UGAWidth
- BootLogoImage
->Width
) >> 1,
1136 (UGAHeight
- BootLogoImage
->Height
) >> 1,
1137 &StdBackgroundPixel
);
1139 if (Entry
->Volume
->IsMbrPartition
) {
1140 ActivateMbrPartition(Entry
->Volume
->WholeDiskBlockIO
, Entry
->Volume
->MbrPartitionIndex
);
1143 ExtractLegacyLoaderPaths(DiscoveredPathList
, MAX_DISCOVERED_PATHS
, LegacyLoaderList
);
1145 Status
= StartEFIImageList(DiscoveredPathList
, Entry
->LoadOptions
, NULL
, L
"legacy loader", &ErrorInStep
, TRUE
);
1146 if (Status
== EFI_NOT_FOUND
) {
1147 if (ErrorInStep
== 1) {
1148 Print(L
"\nPlease make sure that you have the latest firmware update installed.\n");
1149 } else if (ErrorInStep
== 3) {
1150 Print(L
"\nThe firmware refused to boot from the selected volume. Note that external\n"
1151 L
"hard drives are not well-supported by Apple's firmware for legacy OS booting.\n");
1154 FinishExternalScreen();
1155 } /* static VOID StartLegacy() */
1157 // Start a device on a non-Mac using the EFI_LEGACY_BIOS_PROTOCOL
1158 #ifdef __MAKEWITH_TIANO
1159 static VOID
StartLegacyUEFI(IN LEGACY_ENTRY
*Entry
)
1161 BeginExternalScreen(TRUE
, L
"Booting Legacy OS (UEFI mode)");
1163 BdsLibConnectDevicePath (Entry
->BdsOption
->DevicePath
);
1164 BdsLibDoLegacyBoot(Entry
->BdsOption
);
1166 // If we get here, it means that there was a failure....
1167 Print(L
"Failure booting legacy (BIOS) OS.");
1169 FinishExternalScreen();
1170 } // static VOID StartLegacyUEFI()
1171 #endif // __MAKEWITH_TIANO
1173 static LEGACY_ENTRY
* AddLegacyEntry(IN CHAR16
*LoaderTitle
, IN REFIT_VOLUME
*Volume
)
1175 LEGACY_ENTRY
*Entry
, *SubEntry
;
1176 REFIT_MENU_SCREEN
*SubScreen
;
1178 CHAR16 ShortcutLetter
= 0;
1180 if (LoaderTitle
== NULL
) {
1181 if (Volume
->OSName
!= NULL
) {
1182 LoaderTitle
= Volume
->OSName
;
1183 if (LoaderTitle
[0] == 'W' || LoaderTitle
[0] == 'L')
1184 ShortcutLetter
= LoaderTitle
[0];
1186 LoaderTitle
= L
"Legacy OS";
1188 if (Volume
->VolName
!= NULL
)
1189 VolDesc
= Volume
->VolName
;
1191 VolDesc
= (Volume
->DiskKind
== DISK_KIND_OPTICAL
) ? L
"CD" : L
"HD";
1193 // prepare the menu entry
1194 Entry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1195 Entry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1196 SPrint(Entry
->me
.Title
, 255, L
"Boot %s from %s", LoaderTitle
, VolDesc
);
1197 Entry
->me
.Tag
= TAG_LEGACY
;
1199 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1200 Entry
->me
.Image
= LoadOSIcon(Volume
->OSIconName
, L
"legacy", FALSE
);
1201 Entry
->me
.BadgeImage
= Volume
->VolBadgeImage
;
1202 Entry
->Volume
= Volume
;
1203 Entry
->LoadOptions
= (Volume
->DiskKind
== DISK_KIND_OPTICAL
) ? L
"CD" :
1204 ((Volume
->DiskKind
== DISK_KIND_EXTERNAL
) ? L
"USB" : L
"HD");
1205 Entry
->Enabled
= TRUE
;
1207 // create the submenu
1208 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
1209 SubScreen
->Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1210 SPrint(SubScreen
->Title
, 255, L
"Boot Options for %s on %s", LoaderTitle
, VolDesc
);
1211 SubScreen
->TitleImage
= Entry
->me
.Image
;
1214 SubEntry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1215 SubEntry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1216 SPrint(SubEntry
->me
.Title
, 255, L
"Boot %s", LoaderTitle
);
1217 SubEntry
->me
.Tag
= TAG_LEGACY
;
1218 SubEntry
->Volume
= Entry
->Volume
;
1219 SubEntry
->LoadOptions
= Entry
->LoadOptions
;
1220 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
1222 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
1223 Entry
->me
.SubScreen
= SubScreen
;
1224 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1226 } /* static LEGACY_ENTRY * AddLegacyEntry() */
1229 #ifdef __MAKEWITH_TIANO
1230 // default volume badge icon based on disk kind
1231 static EG_IMAGE
* GetDiskBadge(IN UINTN DiskType
) {
1232 EG_IMAGE
* Badge
= NULL
;
1236 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_INTERNAL
);
1239 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_EXTERNAL
);
1242 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_OPTICAL
);
1246 } // static EG_IMAGE * GetDiskBadge()
1249 Create a rEFInd boot option from a Legacy BIOS protocol option.
1251 static LEGACY_ENTRY
* AddLegacyEntryUEFI(BDS_COMMON_OPTION
*BdsOption
, IN UINT16 DiskType
)
1253 LEGACY_ENTRY
*Entry
, *SubEntry
;
1254 REFIT_MENU_SCREEN
*SubScreen
;
1255 CHAR16 ShortcutLetter
= 0;
1256 CHAR16
*LegacyDescription
= BdsOption
->Description
;
1258 // ScanVolume(Volume);
1260 // prepare the menu entry
1261 Entry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1262 Entry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1263 SPrint(Entry
->me
.Title
, 255, L
"Boot legacy target %s", LegacyDescription
);
1264 Entry
->me
.Tag
= TAG_LEGACY_UEFI
;
1266 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1267 Entry
->me
.Image
= LoadOSIcon(L
"legacy", L
"legacy", TRUE
);
1268 Entry
->LoadOptions
= (DiskType
== BBS_CDROM
) ? L
"CD" :
1269 ((DiskType
== BBS_USB
) ? L
"USB" : L
"HD");
1270 Entry
->me
.BadgeImage
= GetDiskBadge(DiskType
);
1271 // Entry->me.BadgeImage = Volume->VolBadgeImage;
1272 Entry
->BdsOption
= BdsOption
;
1273 Entry
->Enabled
= TRUE
;
1275 // create the submenu
1276 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
1277 SubScreen
->Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1278 SPrint(SubScreen
->Title
, 255, L
"No boot options for legacy target");
1279 SubScreen
->TitleImage
= Entry
->me
.Image
;
1282 SubEntry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1283 SubEntry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1284 SPrint(SubEntry
->me
.Title
, 255, L
"Boot %s", LegacyDescription
);
1285 SubEntry
->me
.Tag
= TAG_LEGACY_UEFI
;
1286 Entry
->BdsOption
= BdsOption
;
1287 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
1289 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
1290 Entry
->me
.SubScreen
= SubScreen
;
1291 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1293 } /* static LEGACY_ENTRY * AddLegacyEntryUEFI() */
1296 Scan for legacy BIOS targets on machines that implement EFI_LEGACY_BIOS_PROTOCOL.
1297 In testing, protocol has not been implemented on Macs but has been
1298 implemented on several Dell PCs and an ASUS motherboard.
1299 Restricts output to disks of the specified DiskType.
1301 static VOID
ScanLegacyUEFI(IN UINTN DiskType
)
1304 EFI_LEGACY_BIOS_PROTOCOL
*LegacyBios
;
1305 UINT16
*BootOrder
= NULL
;
1307 CHAR16 BootOption
[10];
1308 UINTN BootOrderSize
= 0;
1310 BDS_COMMON_OPTION
*BdsOption
;
1311 LIST_ENTRY TempList
;
1312 BBS_BBS_DEVICE_PATH
* BbsDevicePath
= NULL
;
1313 // REFIT_VOLUME Volume;
1315 InitializeListHead (&TempList
);
1316 ZeroMem (Buffer
, sizeof (Buffer
));
1318 // If LegacyBios protocol is not implemented on this platform, then
1319 //we do not support this type of legacy boot on this machine.
1320 Status
= gBS
->LocateProtocol(&gEfiLegacyBootProtocolGuid
, NULL
, (VOID
**) &LegacyBios
);
1321 if (EFI_ERROR (Status
))
1324 // Grab the boot order
1325 BootOrder
= BdsLibGetVariableAndSize(L
"BootOrder", &gEfiGlobalVariableGuid
, &BootOrderSize
);
1326 if (BootOrder
== NULL
) {
1331 while (Index
< BootOrderSize
/ sizeof (UINT16
))
1333 // Grab each boot option variable from the boot order, and convert
1334 // the variable into a BDS boot option
1335 UnicodeSPrint (BootOption
, sizeof (BootOption
), L
"Boot%04x", BootOrder
[Index
]);
1336 BdsOption
= BdsLibVariableToOption (&TempList
, BootOption
);
1338 if (BdsOption
!= NULL
) {
1339 BbsDevicePath
= (BBS_BBS_DEVICE_PATH
*)BdsOption
->DevicePath
;
1341 // Only add the entry if it is of a requested type (e.g. USB, HD)
1343 // Two checks necessary because some systems return EFI boot loaders
1344 // with a DeviceType value that would inappropriately include them
1345 // as legacy loaders....
1346 if ((BbsDevicePath
->DeviceType
== DiskType
) && (BdsOption
->DevicePath
->Type
== DEVICE_TYPE_BIOS
)) {
1347 AddLegacyEntryUEFI(BdsOption
, BbsDevicePath
->DeviceType
);
1352 } /* static VOID ScanLegacyUEFI() */
1354 static VOID
ScanLegacyUEFI(IN UINTN DiskType
){}
1355 #endif // __MAKEWITH_TIANO
1357 static VOID
ScanLegacyVolume(REFIT_VOLUME
*Volume
, UINTN VolumeIndex
) {
1359 BOOLEAN ShowVolume
, HideIfOthersFound
;
1362 HideIfOthersFound
= FALSE
;
1363 if (Volume
->IsAppleLegacy
) {
1365 HideIfOthersFound
= TRUE
;
1366 } else if (Volume
->HasBootCode
) {
1368 if (Volume
->BlockIO
== Volume
->WholeDiskBlockIO
&&
1369 Volume
->BlockIOOffset
== 0 &&
1370 Volume
->OSName
== NULL
)
1371 // this is a whole disk (MBR) entry; hide if we have entries for partitions
1372 HideIfOthersFound
= TRUE
;
1374 if (HideIfOthersFound
) {
1375 // check for other bootable entries on the same disk
1376 for (VolumeIndex2
= 0; VolumeIndex2
< VolumesCount
; VolumeIndex2
++) {
1377 if (VolumeIndex2
!= VolumeIndex
&& Volumes
[VolumeIndex2
]->HasBootCode
&&
1378 Volumes
[VolumeIndex2
]->WholeDiskBlockIO
== Volume
->WholeDiskBlockIO
)
1384 AddLegacyEntry(NULL
, Volume
);
1385 } // static VOID ScanLegacyVolume()
1387 // Scan attached optical discs for legacy (BIOS) boot code
1388 // and add anything found to the list....
1389 static VOID
ScanLegacyDisc(VOID
)
1392 REFIT_VOLUME
*Volume
;
1394 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
) {
1395 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1396 Volume
= Volumes
[VolumeIndex
];
1397 if (Volume
->DiskKind
== DISK_KIND_OPTICAL
)
1398 ScanLegacyVolume(Volume
, VolumeIndex
);
1400 } else if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
1401 ScanLegacyUEFI(BBS_CDROM
);
1403 } /* static VOID ScanLegacyDisc() */
1405 // Scan internal hard disks for legacy (BIOS) boot code
1406 // and add anything found to the list....
1407 static VOID
ScanLegacyInternal(VOID
)
1410 REFIT_VOLUME
*Volume
;
1412 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
) {
1413 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1414 Volume
= Volumes
[VolumeIndex
];
1415 if (Volume
->DiskKind
== DISK_KIND_INTERNAL
)
1416 ScanLegacyVolume(Volume
, VolumeIndex
);
1418 } else if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
1419 ScanLegacyUEFI(BBS_HARDDISK
);
1421 } /* static VOID ScanLegacyInternal() */
1423 // Scan external disks for legacy (BIOS) boot code
1424 // and add anything found to the list....
1425 static VOID
ScanLegacyExternal(VOID
)
1428 REFIT_VOLUME
*Volume
;
1430 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
) {
1431 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1432 Volume
= Volumes
[VolumeIndex
];
1433 if (Volume
->DiskKind
== DISK_KIND_EXTERNAL
)
1434 ScanLegacyVolume(Volume
, VolumeIndex
);
1436 } else if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
1437 ScanLegacyUEFI(BBS_USB
);
1439 } /* static VOID ScanLegacyExternal() */
1442 // pre-boot tool functions
1445 static VOID
StartTool(IN LOADER_ENTRY
*Entry
)
1447 BeginExternalScreen(Entry
->UseGraphicsMode
, Entry
->me
.Title
+ 6); // assumes "Start <title>" as assigned below
1448 StartEFIImage(Entry
->DevicePath
, Entry
->LoadOptions
, Basename(Entry
->LoaderPath
),
1449 Basename(Entry
->LoaderPath
), NULL
, TRUE
);
1450 FinishExternalScreen();
1451 } /* static VOID StartTool() */
1453 static LOADER_ENTRY
* AddToolEntry(IN CHAR16
*LoaderPath
, IN CHAR16
*LoaderTitle
, IN EG_IMAGE
*Image
,
1454 IN CHAR16 ShortcutLetter
, IN BOOLEAN UseGraphicsMode
)
1456 LOADER_ENTRY
*Entry
;
1457 CHAR16
*TitleStr
= NULL
;
1459 Entry
= AllocateZeroPool(sizeof(LOADER_ENTRY
));
1461 MergeStrings(&TitleStr
, L
"Start ", 0);
1462 MergeStrings(&TitleStr
, LoaderTitle
, 0);
1463 Entry
->me
.Title
= TitleStr
;
1464 Entry
->me
.Tag
= TAG_TOOL
;
1466 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1467 Entry
->me
.Image
= Image
;
1468 Entry
->LoaderPath
= (LoaderPath
) ? StrDuplicate(LoaderPath
) : NULL
;
1469 Entry
->DevicePath
= FileDevicePath(SelfLoadedImage
->DeviceHandle
, Entry
->LoaderPath
);
1470 Entry
->UseGraphicsMode
= UseGraphicsMode
;
1472 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1474 } /* static LOADER_ENTRY * AddToolEntry() */
1477 // pre-boot driver functions
1480 static UINTN
ScanDriverDir(IN CHAR16
*Path
)
1483 REFIT_DIR_ITER DirIter
;
1485 EFI_FILE_INFO
*DirEntry
;
1486 CHAR16 FileName
[256];
1488 CleanUpPathNameSlashes(Path
);
1489 // look through contents of the directory
1490 DirIterOpen(SelfRootDir
, Path
, &DirIter
);
1491 while (DirIterNext(&DirIter
, 2, LOADER_MATCH_PATTERNS
, &DirEntry
)) {
1492 if (DirEntry
->FileName
[0] == '.')
1493 continue; // skip this
1495 SPrint(FileName
, 255, L
"%s\\%s", Path
, DirEntry
->FileName
);
1497 Status
= StartEFIImage(FileDevicePath(SelfLoadedImage
->DeviceHandle
, FileName
),
1498 L
"", DirEntry
->FileName
, DirEntry
->FileName
, NULL
, FALSE
);
1500 Status
= DirIterClose(&DirIter
);
1501 if (Status
!= EFI_NOT_FOUND
) {
1502 SPrint(FileName
, 255, L
"while scanning the %s directory", Path
);
1503 CheckError(Status
, FileName
);
1508 #ifdef __MAKEWITH_GNUEFI
1509 static EFI_STATUS
ConnectAllDriversToAllControllers(VOID
)
1512 UINTN AllHandleCount
;
1513 EFI_HANDLE
*AllHandleBuffer
;
1516 EFI_HANDLE
*HandleBuffer
;
1522 Status
= LibLocateHandle(AllHandles
,
1527 if (EFI_ERROR(Status
))
1530 for (Index
= 0; Index
< AllHandleCount
; Index
++) {
1532 // Scan the handle database
1534 Status
= LibScanHandleDatabase(NULL
,
1536 AllHandleBuffer
[Index
],
1541 if (EFI_ERROR (Status
))
1545 if (HandleType
[Index
] & EFI_HANDLE_TYPE_DRIVER_BINDING_HANDLE
)
1547 if (HandleType
[Index
] & EFI_HANDLE_TYPE_IMAGE_HANDLE
)
1552 for (HandleIndex
= 0; HandleIndex
< HandleCount
; HandleIndex
++) {
1553 if (HandleType
[HandleIndex
] & EFI_HANDLE_TYPE_PARENT_HANDLE
)
1558 if (HandleType
[Index
] & EFI_HANDLE_TYPE_DEVICE_HANDLE
) {
1559 Status
= refit_call4_wrapper(BS
->ConnectController
,
1560 AllHandleBuffer
[Index
],
1568 MyFreePool (HandleBuffer
);
1569 MyFreePool (HandleType
);
1573 MyFreePool (AllHandleBuffer
);
1575 } /* EFI_STATUS ConnectAllDriversToAllControllers() */
1577 static EFI_STATUS
ConnectAllDriversToAllControllers(VOID
) {
1578 BdsLibConnectAllDriversToAllControllers();
1583 // Load all EFI drivers from rEFInd's "drivers" subdirectory and from the
1584 // directories specified by the user in the "scan_driver_dirs" configuration
1586 static VOID
LoadDrivers(VOID
)
1588 CHAR16
*Directory
, *SelfDirectory
;
1589 UINTN i
= 0, Length
, NumFound
= 0;
1591 // load drivers from the subdirectories of rEFInd's home directory specified
1592 // in the DRIVER_DIRS constant.
1593 while ((Directory
= FindCommaDelimited(DRIVER_DIRS
, i
++)) != NULL
) {
1594 SelfDirectory
= SelfDirPath
? StrDuplicate(SelfDirPath
) : NULL
;
1595 CleanUpPathNameSlashes(SelfDirectory
);
1596 MergeStrings(&SelfDirectory
, Directory
, L
'\\');
1597 NumFound
+= ScanDriverDir(SelfDirectory
);
1598 MyFreePool(Directory
);
1599 MyFreePool(SelfDirectory
);
1602 // Scan additional user-specified driver directories....
1604 while ((Directory
= FindCommaDelimited(GlobalConfig
.DriverDirs
, i
++)) != NULL
) {
1605 CleanUpPathNameSlashes(Directory
);
1606 Length
= StrLen(Directory
);
1608 NumFound
+= ScanDriverDir(Directory
);
1609 MyFreePool(Directory
);
1612 // connect all devices
1614 ConnectAllDriversToAllControllers();
1615 } /* static VOID LoadDrivers() */
1617 // Determine what (if any) type of legacy (BIOS) boot support is available
1618 static VOID
FindLegacyBootType(VOID
) {
1619 #ifdef __MAKEWITH_TIANO
1621 EFI_LEGACY_BIOS_PROTOCOL
*LegacyBios
;
1624 GlobalConfig
.LegacyType
= LEGACY_TYPE_NONE
;
1626 // UEFI-style legacy BIOS support is available only with the TianoCore EDK2
1627 // build environment, and then only with some implementations....
1628 #ifdef __MAKEWITH_TIANO
1629 Status
= gBS
->LocateProtocol (&gEfiLegacyBootProtocolGuid
, NULL
, (VOID
**) &LegacyBios
);
1630 if (!EFI_ERROR (Status
))
1631 GlobalConfig
.LegacyType
= LEGACY_TYPE_UEFI
;
1634 // Macs have their own system. If the firmware vendor code contains the
1635 // string "Apple", assume it's available. Note that this overrides the
1636 // UEFI type, and might yield false positives if the vendor string
1637 // contains "Apple" as part of something bigger, so this isn't 100%
1639 if (StriSubCmp(L
"Apple", ST
->FirmwareVendor
))
1640 GlobalConfig
.LegacyType
= LEGACY_TYPE_MAC
;
1641 } // static VOID FindLegacyBootType
1643 // Warn the user if legacy OS scans are enabled but the firmware or this
1644 // application can't support them....
1645 static VOID
WarnIfLegacyProblems() {
1646 BOOLEAN found
= FALSE
;
1649 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_NONE
) {
1651 if (GlobalConfig
.ScanFor
[i
] == 'h' || GlobalConfig
.ScanFor
[i
] == 'b' || GlobalConfig
.ScanFor
[i
] == 'c')
1654 } while ((i
< NUM_SCAN_OPTIONS
) && (!found
));
1656 Print(L
"NOTE: refind.conf's 'scanfor' line specifies scanning for one or more legacy\n");
1657 Print(L
"(BIOS) boot options; however, this is not possible because ");
1658 #ifdef __MAKEWITH_TIANO
1659 Print(L
"your computer lacks\n");
1660 Print(L
"the necessary Compatibility Support Module (CSM) support.\n");
1662 Print(L
"this program was\n");
1663 Print(L
"compiled without the necessary support. Please visit\n");
1664 Print(L
"http://www.rodsbooks.com/refind/getting.html and download and install a rEFInd\n");
1665 Print(L
"binary built with the TianoCore EDK2 to enable legacy boot support.\n");
1669 } // if no legacy support
1670 } // static VOID WarnIfLegacyProblems()
1672 // Locates boot loaders. NOTE: This assumes that GlobalConfig.LegacyType is set correctly.
1673 static VOID
ScanForBootloaders(VOID
) {
1678 // scan for loaders and tools, add them to the menu
1679 for (i
= 0; i
< NUM_SCAN_OPTIONS
; i
++) {
1680 switch(GlobalConfig
.ScanFor
[i
]) {
1685 ScanLegacyInternal();
1688 ScanLegacyExternal();
1691 ScanUserConfigured();
1705 // assign shortcut keys
1706 for (i
= 0; i
< MainMenu
.EntryCount
&& MainMenu
.Entries
[i
]->Row
== 0 && i
< 9; i
++)
1707 MainMenu
.Entries
[i
]->ShortcutDigit
= (CHAR16
)('1' + i
);
1709 // wait for user ACK when there were errors
1710 FinishTextScreen(FALSE
);
1711 } // static VOID ScanForBootloaders()
1713 // Add the second-row tags containing built-in and external tools (EFI shell,
1715 static VOID
ScanForTools(VOID
) {
1716 CHAR16
*FileName
= NULL
;
1717 REFIT_MENU_ENTRY
*TempMenuEntry
;
1720 for (i
= 0; i
< NUM_TOOLS
; i
++) {
1721 switch(GlobalConfig
.ShowTools
[i
]) {
1723 TempMenuEntry
= CopyMenuEntry(&MenuEntryShutdown
);
1724 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_SHUTDOWN
);
1725 AddMenuEntry(&MainMenu
, TempMenuEntry
);
1728 TempMenuEntry
= CopyMenuEntry(&MenuEntryReset
);
1729 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_RESET
);
1730 AddMenuEntry(&MainMenu
, TempMenuEntry
);
1733 TempMenuEntry
= CopyMenuEntry(&MenuEntryAbout
);
1734 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT
);
1735 AddMenuEntry(&MainMenu
, TempMenuEntry
);
1738 TempMenuEntry
= CopyMenuEntry(&MenuEntryExit
);
1739 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_EXIT
);
1740 AddMenuEntry(&MainMenu
, TempMenuEntry
);
1744 while ((FileName
= FindCommaDelimited(SHELL_NAMES
, j
++)) != NULL
) {
1745 if (FileExists(SelfRootDir
, FileName
)) {
1746 AddToolEntry(FileName
, L
"EFI Shell", BuiltinIcon(BUILTIN_ICON_TOOL_SHELL
), 'S', FALSE
);
1751 MergeStrings(&FileName
, L
"\\efi\\tools\\gptsync.efi", 0);
1752 if (FileExists(SelfRootDir
, FileName
)) {
1753 AddToolEntry(FileName
, L
"Make Hybrid MBR", BuiltinIcon(BUILTIN_ICON_TOOL_PART
), 'P', FALSE
);
1757 MyFreePool(FileName
);
1760 } // static VOID ScanForTools
1762 // Rescan for boot loaders
1763 VOID
RescanAll(VOID
) {
1770 egDisplayMessage(L
"Scanning for new boot loaders; please wait....", &BGColor
);
1771 FreeList((VOID
***) &(MainMenu
.Entries
), &MainMenu
.EntryCount
);
1772 MainMenu
.Entries
= NULL
;
1773 MainMenu
.EntryCount
= 0;
1775 ConnectAllDriversToAllControllers();
1776 ScanForBootloaders();
1779 } // VOID RescanAll()
1781 #ifndef __MAKEWITH_GNUEFI
1783 // Minimal initialization function
1784 static VOID
InitializeLib(IN EFI_HANDLE ImageHandle
, IN EFI_SYSTEM_TABLE
*SystemTable
) {
1786 // gImageHandle = ImageHandle;
1787 gBS
= SystemTable
->BootServices
;
1788 // gRS = SystemTable->RuntimeServices;
1789 gRT
= SystemTable
->RuntimeServices
; // Some BDS functions need gRT to be set
1790 EfiGetSystemConfigurationTable (&gEfiDxeServicesTableGuid
, (VOID
**) &gDS
);
1792 InitializeConsoleSim();
1802 efi_main (IN EFI_HANDLE ImageHandle
, IN EFI_SYSTEM_TABLE
*SystemTable
)
1805 BOOLEAN MainLoopRunning
= TRUE
;
1806 REFIT_MENU_ENTRY
*ChosenEntry
;
1812 InitializeLib(ImageHandle
, SystemTable
);
1814 Status
= InitRefitLib(ImageHandle
);
1815 if (EFI_ERROR(Status
))
1818 // read configuration
1819 CopyMem(GlobalConfig
.ScanFor
, "ieom ", NUM_SCAN_OPTIONS
);
1820 FindLegacyBootType();
1821 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
)
1822 CopyMem(GlobalConfig
.ScanFor
, "ihebocm ", NUM_SCAN_OPTIONS
);
1824 WarnIfLegacyProblems();
1825 MainMenu
.TimeoutSeconds
= GlobalConfig
.Timeout
;
1827 // disable EFI watchdog timer
1828 refit_call4_wrapper(BS
->SetWatchdogTimer
, 0x0000, 0x0000, 0x0000, NULL
);
1830 // further bootstrap (now with config available)
1832 if (GlobalConfig
.ScanDelay
> 0) {
1837 egDisplayMessage(L
"Pausing before disk scan; please wait....", &BGColor
);
1838 for (i
= 0; i
< GlobalConfig
.ScanDelay
; i
++)
1839 refit_call1_wrapper(BS
->Stall
, 1000000);
1842 ScanForBootloaders();
1845 Selection
= StrDuplicate(GlobalConfig
.DefaultSelection
);
1846 while (MainLoopRunning
) {
1847 MenuExit
= RunMainMenu(&MainMenu
, Selection
, &ChosenEntry
);
1849 // The Escape key triggers a re-scan operation....
1850 if (MenuExit
== MENU_EXIT_ESCAPE
) {
1855 switch (ChosenEntry
->Tag
) {
1857 case TAG_REBOOT
: // Reboot
1859 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);
1860 MainLoopRunning
= FALSE
; // just in case we get this far
1863 case TAG_SHUTDOWN
: // Shut Down
1865 refit_call4_wrapper(RT
->ResetSystem
, EfiResetShutdown
, EFI_SUCCESS
, 0, NULL
);
1866 MainLoopRunning
= FALSE
; // just in case we get this far
1869 case TAG_ABOUT
: // About rEFInd
1873 case TAG_LOADER
: // Boot OS via .EFI loader
1874 StartLoader((LOADER_ENTRY
*)ChosenEntry
);
1877 case TAG_LEGACY
: // Boot legacy OS
1878 StartLegacy((LEGACY_ENTRY
*)ChosenEntry
);
1881 #ifdef __MAKEWITH_TIANO
1882 case TAG_LEGACY_UEFI
: // Boot a legacy OS on a non-Mac
1883 StartLegacyUEFI((LEGACY_ENTRY
*)ChosenEntry
);
1885 #endif // __MAKEWITH_TIANO
1887 case TAG_TOOL
: // Start a EFI tool
1888 StartTool((LOADER_ENTRY
*)ChosenEntry
);
1891 case TAG_EXIT
: // Terminate rEFInd
1892 BeginTextScreen(L
" ");
1897 MyFreePool(Selection
);
1898 Selection
= (ChosenEntry
->Title
) ? StrDuplicate(ChosenEntry
->Title
) : NULL
;
1901 // If we end up here, things have gone wrong. Try to reboot, and if that
1902 // fails, go into an endless loop.
1903 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);