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.5.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 if (FullLoadOptions
!= NULL
)
237 FreePool(FullLoadOptions
);
239 } /* static EFI_STATUS StartEFIImageList() */
241 static EFI_STATUS
StartEFIImage(IN EFI_DEVICE_PATH
*DevicePath
,
242 IN CHAR16
*LoadOptions
, IN CHAR16
*LoadOptionsPrefix
,
243 IN CHAR16
*ImageTitle
,
244 OUT UINTN
*ErrorInStep
,
247 EFI_DEVICE_PATH
*DevicePaths
[2];
249 DevicePaths
[0] = DevicePath
;
250 DevicePaths
[1] = NULL
;
251 return StartEFIImageList(DevicePaths
, LoadOptions
, LoadOptionsPrefix
, ImageTitle
, ErrorInStep
, Verbose
);
252 } /* static EFI_STATUS StartEFIImage() */
255 // EFI OS loader functions
258 static VOID
StartLoader(IN LOADER_ENTRY
*Entry
)
260 UINTN ErrorInStep
= 0;
262 BeginExternalScreen(Entry
->UseGraphicsMode
, L
"Booting OS");
263 StartEFIImage(Entry
->DevicePath
, Entry
->LoadOptions
,
264 Basename(Entry
->LoaderPath
), Basename(Entry
->LoaderPath
), &ErrorInStep
, !Entry
->UseGraphicsMode
);
265 FinishExternalScreen();
268 // Locate an initrd or initramfs file that matches the kernel specified by LoaderPath.
269 // The matching file has a name that begins with "init" and includes the same version
270 // number string as is found in LoaderPath -- but not a longer version number string.
271 // For instance, if LoaderPath is \EFI\kernels\bzImage-3.3.0.efi, and if \EFI\kernels
272 // has a file called initramfs-3.3.0.img, this function will return the string
273 // '\EFI\kernels\initramfs-3.3.0.img'. If the directory ALSO contains the file
274 // initramfs-3.3.0-rc7.img or initramfs-13.3.0.img, those files will NOT match;
275 // however, initmine-3.3.0.img might match. (FindInitrd() returns the first match it
276 // finds). Thus, care should be taken to avoid placing duplicate matching files in
277 // the kernel's directory.
278 // If no matching init file can be found, returns NULL.
279 static CHAR16
* FindInitrd(IN CHAR16
*LoaderPath
, IN REFIT_VOLUME
*Volume
) {
280 CHAR16
*InitrdName
= NULL
, *FileName
, *KernelVersion
, *InitrdVersion
, *Path
;
281 REFIT_DIR_ITER DirIter
;
282 EFI_FILE_INFO
*DirEntry
;
284 FileName
= Basename(LoaderPath
);
285 KernelVersion
= FindNumbers(FileName
);
286 Path
= FindPath(LoaderPath
);
288 // Add trailing backslash for root directory; necessary on some systems, but must
289 // NOT be added to all directories, since on other systems, a trailing backslash on
290 // anything but the root directory causes them to flake out!
291 if (StrLen(Path
) == 0) {
292 MergeStrings(&Path
, L
"\\", 0);
294 DirIterOpen(Volume
->RootDir
, Path
, &DirIter
);
295 // Now add a trailing backslash if it was NOT added earlier, for consistency in
296 // building the InitrdName later....
297 if ((StrLen(Path
) > 0) && (Path
[StrLen(Path
) - 1] != L
'\\'))
298 MergeStrings(&Path
, L
"\\", 0);
299 while ((DirIterNext(&DirIter
, 2, L
"init*", &DirEntry
)) && (InitrdName
== NULL
)) {
300 InitrdVersion
= FindNumbers(DirEntry
->FileName
);
301 if (KernelVersion
!= NULL
) {
302 if (StriCmp(InitrdVersion
, KernelVersion
) == 0) {
303 MergeStrings(&InitrdName
, Path
, 0);
304 MergeStrings(&InitrdName
, DirEntry
->FileName
, 0);
307 if (InitrdVersion
== NULL
) {
308 MergeStrings(&InitrdName
, Path
, 0);
309 MergeStrings(&InitrdName
, DirEntry
->FileName
, 0);
312 if (InitrdVersion
!= NULL
)
313 FreePool(InitrdVersion
);
315 DirIterClose(&DirIter
);
317 // Note: Don't FreePool(FileName), since Basename returns a pointer WITHIN the string it's passed.
318 FreePool(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
= StrDuplicate(Entry
->Title
);
339 NewEntry
->TimeoutText
= StrDuplicate(Entry
->TimeoutText
);
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
] = StrDuplicate(Entry
->InfoLines
[i
]);
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
= StrDuplicate(Entry
->Title
);
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
= StrDuplicate(Entry
->LoaderPath
);
403 NewEntry
->VolName
= StrDuplicate(Entry
->VolName
);
404 NewEntry
->DevicePath
= Entry
->DevicePath
;
405 NewEntry
->UseGraphicsMode
= Entry
->UseGraphicsMode
;
406 NewEntry
->LoadOptions
= StrDuplicate(Entry
->LoadOptions
);
407 NewEntry
->InitrdPath
= StrDuplicate(Entry
->InitrdPath
);
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 FreePool(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 FreePool(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 if (SubEntry
->LoadOptions
!= NULL
)
551 FreePool(SubEntry
->LoadOptions
);
552 SubEntry
->LoadOptions
= StrDuplicate(TokenList
[1]);
553 MergeStrings(&SubEntry
->LoadOptions
, InitrdOption
, L
' ');
554 FreeTokenLine(&TokenList
, &TokenCount
);
555 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_LINUX
;
556 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
559 FreePool(InitrdOption
);
563 } // if Linux options file exists
565 } else if (Entry
->OSType
== 'E') { // entries for ELILO
566 SubEntry
= InitializeLoaderEntry(Entry
);
567 if (SubEntry
!= NULL
) {
568 SubEntry
->me
.Title
= L
"Run ELILO in interactive mode";
569 SubEntry
->LoadOptions
= L
"-p";
570 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
571 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
574 SubEntry
= InitializeLoaderEntry(Entry
);
575 if (SubEntry
!= NULL
) {
576 SubEntry
->me
.Title
= L
"Boot Linux for a 17\" iMac or a 15\" MacBook Pro (*)";
577 SubEntry
->UseGraphicsMode
= TRUE
;
578 SubEntry
->LoadOptions
= L
"-d 0 i17";
579 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
580 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
583 SubEntry
= InitializeLoaderEntry(Entry
);
584 if (SubEntry
!= NULL
) {
585 SubEntry
->me
.Title
= L
"Boot Linux for a 20\" iMac (*)";
586 SubEntry
->UseGraphicsMode
= TRUE
;
587 SubEntry
->LoadOptions
= L
"-d 0 i20";
588 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
589 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
592 SubEntry
= InitializeLoaderEntry(Entry
);
593 if (SubEntry
!= NULL
) {
594 SubEntry
->me
.Title
= L
"Boot Linux for a Mac Mini (*)";
595 SubEntry
->UseGraphicsMode
= TRUE
;
596 SubEntry
->LoadOptions
= L
"-d 0 mini";
597 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
598 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
601 AddMenuInfoLine(SubScreen
, L
"NOTE: This is an example. Entries");
602 AddMenuInfoLine(SubScreen
, L
"marked with (*) may not work.");
604 } else if (Entry
->OSType
== 'X') { // entries for xom.efi
605 // by default, skip the built-in selection and boot from hard disk only
606 Entry
->LoadOptions
= L
"-s -h";
608 SubEntry
= InitializeLoaderEntry(Entry
);
609 if (SubEntry
!= NULL
) {
610 SubEntry
->me
.Title
= L
"Boot Windows from Hard Disk";
611 SubEntry
->LoadOptions
= L
"-s -h";
612 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
613 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
616 SubEntry
= InitializeLoaderEntry(Entry
);
617 if (SubEntry
!= NULL
) {
618 SubEntry
->me
.Title
= L
"Boot Windows from CD-ROM";
619 SubEntry
->LoadOptions
= L
"-s -c";
620 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
621 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
624 SubEntry
= InitializeLoaderEntry(Entry
);
625 if (SubEntry
!= NULL
) {
626 SubEntry
->me
.Title
= L
"Run XOM in text mode";
627 SubEntry
->UseGraphicsMode
= FALSE
;
628 SubEntry
->LoadOptions
= L
"-v";
629 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
630 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
632 } // entries for xom.efi
633 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
634 Entry
->me
.SubScreen
= SubScreen
;
635 } // VOID GenerateSubScreen()
637 // Returns options for a Linux kernel. Reads them from an options file in the
638 // kernel's directory; and if present, adds an initrd= option for an initial
639 // RAM disk file with the same version number as the kernel file.
640 static CHAR16
* GetMainLinuxOptions(IN CHAR16
* LoaderPath
, IN REFIT_VOLUME
*Volume
) {
641 CHAR16
*Options
= NULL
, *InitrdName
, *InitrdOption
= NULL
;
643 Options
= GetFirstOptionsFromFile(LoaderPath
, Volume
);
644 InitrdName
= FindInitrd(LoaderPath
, Volume
);
645 if (InitrdName
!= NULL
) {
646 MergeStrings(&InitrdOption
, L
"initrd=", 0);
647 MergeStrings(&InitrdOption
, InitrdName
, 0);
649 MergeStrings(&Options
, InitrdOption
, ' ');
650 if (InitrdOption
!= NULL
)
651 FreePool(InitrdOption
);
652 if (InitrdName
!= NULL
)
653 FreePool(InitrdName
);
655 } // static CHAR16 * GetMainLinuxOptions()
657 // Sets a few defaults for a loader entry -- mainly the icon, but also the OS type
658 // code and shortcut letter. For Linux EFI stub loaders, also sets kernel options
659 // that will (with luck) work fairly automatically.
660 VOID
SetLoaderDefaults(LOADER_ENTRY
*Entry
, CHAR16
*LoaderPath
, IN REFIT_VOLUME
*Volume
) {
661 CHAR16 IconFileName
[256];
662 CHAR16
*FileName
, *PathOnly
, *OSIconName
= NULL
, *Temp
;
663 CHAR16 ShortcutLetter
= 0;
665 FileName
= Basename(LoaderPath
);
666 PathOnly
= FindPath(LoaderPath
);
668 // locate a custom icon for the loader
669 StrCpy(IconFileName
, LoaderPath
);
670 ReplaceEfiExtension(IconFileName
, L
".icns");
671 if (FileExists(Volume
->RootDir
, IconFileName
)) {
672 Entry
->me
.Image
= LoadIcns(Volume
->RootDir
, IconFileName
, 128);
673 } else if ((StrLen(PathOnly
) == 0) && (Volume
->VolIconImage
!= NULL
)) {
674 Entry
->me
.Image
= Volume
->VolIconImage
;
675 } // icon matched to loader or volume
677 Temp
= FindLastDirName(LoaderPath
);
678 MergeStrings(&OSIconName
, Temp
, L
',');
680 if (OSIconName
!= NULL
) {
681 ShortcutLetter
= OSIconName
[0];
684 // detect specific loaders
685 if (StriSubCmp(L
"bzImage", LoaderPath
) || StriSubCmp(L
"vmlinuz", LoaderPath
)) {
686 MergeStrings(&OSIconName
, L
"linux", L
',');
688 if (ShortcutLetter
== 0)
689 ShortcutLetter
= 'L';
690 Entry
->LoadOptions
= GetMainLinuxOptions(LoaderPath
, Volume
);
691 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_LINUX
;
692 } else if (StriSubCmp(L
"refit", LoaderPath
)) {
693 MergeStrings(&OSIconName
, L
"refit", L
',');
695 ShortcutLetter
= 'R';
696 } else if (StriCmp(LoaderPath
, MACOSX_LOADER_PATH
) == 0) {
697 if (Volume
->VolIconImage
!= NULL
) { // custom icon file found
698 Entry
->me
.Image
= Volume
->VolIconImage
;
700 MergeStrings(&OSIconName
, L
"mac", L
',');
702 ShortcutLetter
= 'M';
703 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
704 } else if (StriCmp(FileName
, L
"diags.efi") == 0) {
705 MergeStrings(&OSIconName
, L
"hwtest", L
',');
706 } else if (StriCmp(FileName
, L
"e.efi") == 0 || StriCmp(FileName
, L
"elilo.efi") == 0) {
707 MergeStrings(&OSIconName
, L
"elilo,linux", L
',');
709 if (ShortcutLetter
== 0)
710 ShortcutLetter
= 'L';
711 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
712 } else if (StriSubCmp(L
"grub", FileName
)) {
714 ShortcutLetter
= 'G';
715 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_GRUB
;
716 } else if (StriCmp(FileName
, L
"cdboot.efi") == 0 ||
717 StriCmp(FileName
, L
"bootmgr.efi") == 0 ||
718 StriCmp(FileName
, L
"Bootmgfw.efi") == 0) {
719 MergeStrings(&OSIconName
, L
"win", L
',');
721 ShortcutLetter
= 'W';
722 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
723 } else if (StriCmp(FileName
, L
"xom.efi") == 0) {
724 MergeStrings(&OSIconName
, L
"xom,win", L
',');
725 Entry
->UseGraphicsMode
= TRUE
;
727 ShortcutLetter
= 'W';
728 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
731 if ((ShortcutLetter
>= 'a') && (ShortcutLetter
<= 'z'))
732 ShortcutLetter
= ShortcutLetter
- 'a' + 'A'; // convert lowercase to uppercase
733 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
734 if (Entry
->me
.Image
== NULL
)
735 Entry
->me
.Image
= LoadOSIcon(OSIconName
, L
"unknown", FALSE
);
736 if (PathOnly
!= NULL
)
738 } // VOID SetLoaderDefaults()
740 // Add a specified EFI boot loader to the list, using automatic settings
741 // for icons, options, etc.
742 LOADER_ENTRY
* AddLoaderEntry(IN CHAR16
*LoaderPath
, IN CHAR16
*LoaderTitle
, IN REFIT_VOLUME
*Volume
) {
745 CleanUpPathNameSlashes(LoaderPath
);
746 Entry
= InitializeLoaderEntry(NULL
);
748 Entry
->Title
= StrDuplicate((LoaderTitle
!= NULL
) ? LoaderTitle
: LoaderPath
);
749 Entry
->me
.Title
= AllocateZeroPool(sizeof(CHAR16
) * 256);
750 SPrint(Entry
->me
.Title
, 255, L
"Boot %s from %s", (LoaderTitle
!= NULL
) ? LoaderTitle
: LoaderPath
, Volume
->VolName
);
752 Entry
->me
.BadgeImage
= Volume
->VolBadgeImage
;
753 if ((LoaderPath
!= NULL
) && (LoaderPath
[0] != L
'\\')) {
754 Entry
->LoaderPath
= StrDuplicate(L
"\\");
756 Entry
->LoaderPath
= NULL
;
758 MergeStrings(&(Entry
->LoaderPath
), LoaderPath
, 0);
759 Entry
->VolName
= Volume
->VolName
;
760 Entry
->DevicePath
= FileDevicePath(Volume
->DeviceHandle
, Entry
->LoaderPath
);
761 SetLoaderDefaults(Entry
, LoaderPath
, Volume
);
762 GenerateSubScreen(Entry
, Volume
);
763 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
767 } // LOADER_ENTRY * AddLoaderEntry()
769 // Returns -1 if (Time1 < Time2), +1 if (Time1 > Time2), or 0 if
770 // (Time1 == Time2). Precision is only to the nearest second; since
771 // this is used for sorting boot loader entries, differences smaller
772 // than this are likely to be meaningless (and unlikely!).
773 INTN
TimeComp(EFI_TIME
*Time1
, EFI_TIME
*Time2
) {
774 INT64 Time1InSeconds
, Time2InSeconds
;
776 // Following values are overestimates; I'm assuming 31 days in every month.
777 // This is fine for the purpose of this function, which has a limited
779 Time1InSeconds
= Time1
->Second
+ (Time1
->Minute
* 60) + (Time1
->Hour
* 3600) + (Time1
->Day
* 86400) +
780 (Time1
->Month
* 2678400) + ((Time1
->Year
- 1998) * 32140800);
781 Time2InSeconds
= Time2
->Second
+ (Time2
->Minute
* 60) + (Time2
->Hour
* 3600) + (Time2
->Day
* 86400) +
782 (Time2
->Month
* 2678400) + ((Time2
->Year
- 1998) * 32140800);
783 if (Time1InSeconds
< Time2InSeconds
)
785 else if (Time1InSeconds
> Time2InSeconds
)
791 // Adds a loader list element, keeping it sorted by date. Returns the new
792 // first element (the one with the most recent date).
793 static struct LOADER_LIST
* AddLoaderListEntry(struct LOADER_LIST
*LoaderList
, struct LOADER_LIST
*NewEntry
) {
794 struct LOADER_LIST
*LatestEntry
, *CurrentEntry
, *PrevEntry
= NULL
;
796 LatestEntry
= CurrentEntry
= LoaderList
;
797 if (LoaderList
== NULL
) {
798 LatestEntry
= NewEntry
;
800 while ((CurrentEntry
!= NULL
) && (TimeComp(&(NewEntry
->TimeStamp
), &(CurrentEntry
->TimeStamp
)) < 0)) {
801 PrevEntry
= CurrentEntry
;
802 CurrentEntry
= CurrentEntry
->NextEntry
;
804 NewEntry
->NextEntry
= CurrentEntry
;
805 if (PrevEntry
== NULL
) {
806 LatestEntry
= NewEntry
;
808 PrevEntry
->NextEntry
= NewEntry
;
811 return (LatestEntry
);
812 } // static VOID AddLoaderListEntry()
814 // Delete the LOADER_LIST linked list
815 static VOID
CleanUpLoaderList(struct LOADER_LIST
*LoaderList
) {
816 struct LOADER_LIST
*Temp
;
818 while (LoaderList
!= NULL
) {
820 LoaderList
= LoaderList
->NextEntry
;
821 FreePool(Temp
->FileName
);
824 } // static VOID CleanUpLoaderList()
826 // Scan an individual directory for EFI boot loader files and, if found,
827 // add them to the list. Sorts the entries within the loader directory
828 // so that the most recent one appears first in the list.
829 static VOID
ScanLoaderDir(IN REFIT_VOLUME
*Volume
, IN CHAR16
*Path
, IN CHAR16
*Pattern
)
832 REFIT_DIR_ITER DirIter
;
833 EFI_FILE_INFO
*DirEntry
;
834 CHAR16 FileName
[256], *Extension
;
835 struct LOADER_LIST
*LoaderList
= NULL
, *NewLoader
;
837 if ((!SelfDirPath
|| !Path
|| ((StriCmp(Path
, SelfDirPath
) == 0) && Volume
->DeviceHandle
!= SelfVolume
->DeviceHandle
) ||
838 (StriCmp(Path
, SelfDirPath
) != 0)) && (!IsIn(Path
, GlobalConfig
.DontScan
))) {
839 // look through contents of the directory
840 DirIterOpen(Volume
->RootDir
, Path
, &DirIter
);
841 while (DirIterNext(&DirIter
, 2, Pattern
, &DirEntry
)) {
842 Extension
= FindExtension(DirEntry
->FileName
);
843 if (DirEntry
->FileName
[0] == '.' ||
844 StriCmp(DirEntry
->FileName
, L
"TextMode.efi") == 0 ||
845 StriCmp(DirEntry
->FileName
, L
"ebounce.efi") == 0 ||
846 StriCmp(DirEntry
->FileName
, L
"GraphicsConsole.efi") == 0 ||
847 StriCmp(Extension
, L
".icns") == 0 ||
848 StriSubCmp(L
"shell", DirEntry
->FileName
))
849 continue; // skip this
852 SPrint(FileName
, 255, L
"\\%s\\%s", Path
, DirEntry
->FileName
);
854 SPrint(FileName
, 255, L
"\\%s", DirEntry
->FileName
);
855 CleanUpPathNameSlashes(FileName
);
856 NewLoader
= AllocateZeroPool(sizeof(struct LOADER_LIST
));
857 if (NewLoader
!= NULL
) {
858 NewLoader
->FileName
= StrDuplicate(FileName
);
859 NewLoader
->TimeStamp
= DirEntry
->ModificationTime
;
860 LoaderList
= AddLoaderListEntry(LoaderList
, NewLoader
);
864 NewLoader
= LoaderList
;
865 while (NewLoader
!= NULL
) {
866 AddLoaderEntry(NewLoader
->FileName
, NULL
, Volume
);
867 NewLoader
= NewLoader
->NextEntry
;
869 CleanUpLoaderList(LoaderList
);
870 Status
= DirIterClose(&DirIter
);
871 if (Status
!= EFI_NOT_FOUND
) {
873 SPrint(FileName
, 255, L
"while scanning the %s directory", Path
);
875 StrCpy(FileName
, L
"while scanning the root directory");
876 CheckError(Status
, FileName
);
877 } // if (Status != EFI_NOT_FOUND)
878 } // if not scanning our own directory
879 } /* static VOID ScanLoaderDir() */
881 static VOID
ScanEfiFiles(REFIT_VOLUME
*Volume
) {
883 REFIT_DIR_ITER EfiDirIter
;
884 EFI_FILE_INFO
*EfiDirEntry
;
885 CHAR16 FileName
[256], *Directory
, *MatchPatterns
;
888 MatchPatterns
= StrDuplicate(LOADER_MATCH_PATTERNS
);
889 if (GlobalConfig
.ScanAllLinux
)
890 MergeStrings(&MatchPatterns
, LINUX_MATCH_PATTERNS
, L
',');
892 if ((Volume
->RootDir
!= NULL
) && (Volume
->VolName
!= NULL
)) {
893 // check for Mac OS X boot loader
894 if (!IsIn(L
"System\\Library\\CoreServices", GlobalConfig
.DontScan
)) {
895 StrCpy(FileName
, MACOSX_LOADER_PATH
);
896 if (FileExists(Volume
->RootDir
, FileName
)) {
897 AddLoaderEntry(FileName
, L
"Mac OS X", Volume
);
901 StrCpy(FileName
, L
"System\\Library\\CoreServices\\xom.efi");
902 if (FileExists(Volume
->RootDir
, FileName
)) {
903 AddLoaderEntry(FileName
, L
"Windows XP (XoM)", Volume
);
905 } // if Mac directory not in GlobalConfig.DontScan list
907 // check for Microsoft boot loader/menu
908 StrCpy(FileName
, L
"EFI\\Microsoft\\Boot\\Bootmgfw.efi");
909 if (FileExists(Volume
->RootDir
, FileName
) && !IsIn(L
"EFI\\Microsoft\\Boot", GlobalConfig
.DontScan
)) {
910 AddLoaderEntry(FileName
, L
"Microsoft EFI boot", Volume
);
913 // scan the root directory for EFI executables
914 ScanLoaderDir(Volume
, L
"\\", MatchPatterns
);
916 // scan subdirectories of the EFI directory (as per the standard)
917 DirIterOpen(Volume
->RootDir
, L
"EFI", &EfiDirIter
);
918 while (DirIterNext(&EfiDirIter
, 1, NULL
, &EfiDirEntry
)) {
919 if (StriCmp(EfiDirEntry
->FileName
, L
"tools") == 0 || EfiDirEntry
->FileName
[0] == '.')
920 continue; // skip this, doesn't contain boot loaders
921 SPrint(FileName
, 255, L
"EFI\\%s", EfiDirEntry
->FileName
);
922 ScanLoaderDir(Volume
, FileName
, MatchPatterns
);
924 Status
= DirIterClose(&EfiDirIter
);
925 if (Status
!= EFI_NOT_FOUND
)
926 CheckError(Status
, L
"while scanning the EFI directory");
928 // Scan user-specified (or additional default) directories....
930 while ((Directory
= FindCommaDelimited(GlobalConfig
.AlsoScan
, i
++)) != NULL
) {
931 CleanUpPathNameSlashes(Directory
);
932 Length
= StrLen(Directory
);
934 ScanLoaderDir(Volume
, Directory
, MatchPatterns
);
938 } // static VOID ScanEfiFiles()
940 // Scan internal disks for valid EFI boot loaders....
941 static VOID
ScanInternal(VOID
) {
944 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
945 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_INTERNAL
) {
946 ScanEfiFiles(Volumes
[VolumeIndex
]);
949 } // static VOID ScanInternal()
951 // Scan external disks for valid EFI boot loaders....
952 static VOID
ScanExternal(VOID
) {
955 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
956 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_EXTERNAL
) {
957 ScanEfiFiles(Volumes
[VolumeIndex
]);
960 } // static VOID ScanExternal()
962 // Scan internal disks for valid EFI boot loaders....
963 static VOID
ScanOptical(VOID
) {
966 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
967 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_OPTICAL
) {
968 ScanEfiFiles(Volumes
[VolumeIndex
]);
971 } // static VOID ScanOptical()
974 // legacy boot functions
977 static EFI_STATUS
ActivateMbrPartition(IN EFI_BLOCK_IO
*BlockIO
, IN UINTN PartitionIndex
)
980 UINT8 SectorBuffer
[512];
981 MBR_PARTITION_INFO
*MbrTable
, *EMbrTable
;
982 UINT32 ExtBase
, ExtCurrent
, NextExtCurrent
;
983 UINTN LogicalPartitionIndex
= 4;
985 BOOLEAN HaveBootCode
;
988 Status
= refit_call5_wrapper(BlockIO
->ReadBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, 0, 512, SectorBuffer
);
989 if (EFI_ERROR(Status
))
991 if (*((UINT16
*)(SectorBuffer
+ 510)) != 0xaa55)
992 return EFI_NOT_FOUND
; // safety measure #1
994 // add boot code if necessary
995 HaveBootCode
= FALSE
;
996 for (i
= 0; i
< MBR_BOOTCODE_SIZE
; i
++) {
997 if (SectorBuffer
[i
] != 0) {
1002 if (!HaveBootCode
) {
1003 // no boot code found in the MBR, add the syslinux MBR code
1004 SetMem(SectorBuffer
, MBR_BOOTCODE_SIZE
, 0);
1005 CopyMem(SectorBuffer
, syslinux_mbr
, SYSLINUX_MBR_SIZE
);
1008 // set the partition active
1009 MbrTable
= (MBR_PARTITION_INFO
*)(SectorBuffer
+ 446);
1011 for (i
= 0; i
< 4; i
++) {
1012 if (MbrTable
[i
].Flags
!= 0x00 && MbrTable
[i
].Flags
!= 0x80)
1013 return EFI_NOT_FOUND
; // safety measure #2
1014 if (i
== PartitionIndex
)
1015 MbrTable
[i
].Flags
= 0x80;
1016 else if (PartitionIndex
>= 4 && IS_EXTENDED_PART_TYPE(MbrTable
[i
].Type
)) {
1017 MbrTable
[i
].Flags
= 0x80;
1018 ExtBase
= MbrTable
[i
].StartLBA
;
1020 MbrTable
[i
].Flags
= 0x00;
1024 Status
= refit_call5_wrapper(BlockIO
->WriteBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, 0, 512, SectorBuffer
);
1025 if (EFI_ERROR(Status
))
1028 if (PartitionIndex
>= 4) {
1029 // we have to activate a logical partition, so walk the EMBR chain
1031 // NOTE: ExtBase was set above while looking at the MBR table
1032 for (ExtCurrent
= ExtBase
; ExtCurrent
; ExtCurrent
= NextExtCurrent
) {
1033 // read current EMBR
1034 Status
= refit_call5_wrapper(BlockIO
->ReadBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, ExtCurrent
, 512, SectorBuffer
);
1035 if (EFI_ERROR(Status
))
1037 if (*((UINT16
*)(SectorBuffer
+ 510)) != 0xaa55)
1038 return EFI_NOT_FOUND
; // safety measure #3
1040 // scan EMBR, set appropriate partition active
1041 EMbrTable
= (MBR_PARTITION_INFO
*)(SectorBuffer
+ 446);
1043 for (i
= 0; i
< 4; i
++) {
1044 if (EMbrTable
[i
].Flags
!= 0x00 && EMbrTable
[i
].Flags
!= 0x80)
1045 return EFI_NOT_FOUND
; // safety measure #4
1046 if (EMbrTable
[i
].StartLBA
== 0 || EMbrTable
[i
].Size
== 0)
1048 if (IS_EXTENDED_PART_TYPE(EMbrTable
[i
].Type
)) {
1049 // link to next EMBR
1050 NextExtCurrent
= ExtBase
+ EMbrTable
[i
].StartLBA
;
1051 EMbrTable
[i
].Flags
= (PartitionIndex
>= LogicalPartitionIndex
) ? 0x80 : 0x00;
1054 // logical partition
1055 EMbrTable
[i
].Flags
= (PartitionIndex
== LogicalPartitionIndex
) ? 0x80 : 0x00;
1056 LogicalPartitionIndex
++;
1060 // write current EMBR
1061 Status
= refit_call5_wrapper(BlockIO
->WriteBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, ExtCurrent
, 512, SectorBuffer
);
1062 if (EFI_ERROR(Status
))
1065 if (PartitionIndex
< LogicalPartitionIndex
)
1066 break; // stop the loop, no need to touch further EMBRs
1072 } /* static EFI_STATUS ActivateMbrPartition() */
1074 // early 2006 Core Duo / Core Solo models
1075 static UINT8 LegacyLoaderDevicePath1Data
[] = {
1076 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1077 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1078 0xFF, 0xFF, 0xF9, 0xFF, 0x00, 0x00, 0x00, 0x00,
1079 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1080 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1081 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1083 // mid-2006 Mac Pro (and probably other Core 2 models)
1084 static UINT8 LegacyLoaderDevicePath2Data
[] = {
1085 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1086 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1087 0xFF, 0xFF, 0xF7, 0xFF, 0x00, 0x00, 0x00, 0x00,
1088 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1089 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1090 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1092 // mid-2007 MBP ("Santa Rosa" based models)
1093 static UINT8 LegacyLoaderDevicePath3Data
[] = {
1094 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1095 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1096 0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
1097 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1098 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1099 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1102 static UINT8 LegacyLoaderDevicePath4Data
[] = {
1103 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1104 0x00, 0x00, 0xC0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1105 0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
1106 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1107 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1108 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1110 // late-2008 MB/MBP (NVidia chipset)
1111 static UINT8 LegacyLoaderDevicePath5Data
[] = {
1112 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1113 0x00, 0x40, 0xCB, 0xFF, 0x00, 0x00, 0x00, 0x00,
1114 0xFF, 0xBF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
1115 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1116 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1117 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1120 static EFI_DEVICE_PATH
*LegacyLoaderList
[] = {
1121 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath1Data
,
1122 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath2Data
,
1123 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath3Data
,
1124 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath4Data
,
1125 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath5Data
,
1129 #define MAX_DISCOVERED_PATHS (16)
1131 static VOID
StartLegacy(IN LEGACY_ENTRY
*Entry
)
1134 EG_IMAGE
*BootLogoImage
;
1135 UINTN ErrorInStep
= 0;
1136 EFI_DEVICE_PATH
*DiscoveredPathList
[MAX_DISCOVERED_PATHS
];
1138 BeginExternalScreen(TRUE
, L
"Booting Legacy OS (Mac mode)");
1140 BootLogoImage
= LoadOSIcon(Entry
->Volume
->OSIconName
, L
"legacy", TRUE
);
1141 if (BootLogoImage
!= NULL
)
1142 BltImageAlpha(BootLogoImage
,
1143 (UGAWidth
- BootLogoImage
->Width
) >> 1,
1144 (UGAHeight
- BootLogoImage
->Height
) >> 1,
1145 &StdBackgroundPixel
);
1147 if (Entry
->Volume
->IsMbrPartition
) {
1148 ActivateMbrPartition(Entry
->Volume
->WholeDiskBlockIO
, Entry
->Volume
->MbrPartitionIndex
);
1151 ExtractLegacyLoaderPaths(DiscoveredPathList
, MAX_DISCOVERED_PATHS
, LegacyLoaderList
);
1153 Status
= StartEFIImageList(DiscoveredPathList
, Entry
->LoadOptions
, NULL
, L
"legacy loader", &ErrorInStep
, TRUE
);
1154 if (Status
== EFI_NOT_FOUND
) {
1155 if (ErrorInStep
== 1) {
1156 Print(L
"\nPlease make sure that you have the latest firmware update installed.\n");
1157 } else if (ErrorInStep
== 3) {
1158 Print(L
"\nThe firmware refused to boot from the selected volume. Note that external\n"
1159 L
"hard drives are not well-supported by Apple's firmware for legacy OS booting.\n");
1162 FinishExternalScreen();
1163 } /* static VOID StartLegacy() */
1165 // Start a device on a non-Mac using the EFI_LEGACY_BIOS_PROTOCOL
1166 #ifdef __MAKEWITH_TIANO
1167 static VOID
StartLegacyUEFI(IN LEGACY_ENTRY
*Entry
)
1169 BeginExternalScreen(TRUE
, L
"Booting Legacy OS (UEFI mode)");
1171 BdsLibConnectDevicePath (Entry
->BdsOption
->DevicePath
);
1172 BdsLibDoLegacyBoot(Entry
->BdsOption
);
1174 // If we get here, it means that there was a failure....
1175 Print(L
"Failure booting legacy (BIOS) OS.");
1177 FinishExternalScreen();
1178 } // static VOID StartLegacyUEFI()
1179 #endif // __MAKEWITH_TIANO
1181 static LEGACY_ENTRY
* AddLegacyEntry(IN CHAR16
*LoaderTitle
, IN REFIT_VOLUME
*Volume
)
1183 LEGACY_ENTRY
*Entry
, *SubEntry
;
1184 REFIT_MENU_SCREEN
*SubScreen
;
1186 CHAR16 ShortcutLetter
= 0;
1188 if (LoaderTitle
== NULL
) {
1189 if (Volume
->OSName
!= NULL
) {
1190 LoaderTitle
= Volume
->OSName
;
1191 if (LoaderTitle
[0] == 'W' || LoaderTitle
[0] == 'L')
1192 ShortcutLetter
= LoaderTitle
[0];
1194 LoaderTitle
= L
"Legacy OS";
1196 if (Volume
->VolName
!= NULL
)
1197 VolDesc
= Volume
->VolName
;
1199 VolDesc
= (Volume
->DiskKind
== DISK_KIND_OPTICAL
) ? L
"CD" : L
"HD";
1201 // prepare the menu entry
1202 Entry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1203 Entry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1204 SPrint(Entry
->me
.Title
, 255, L
"Boot %s from %s", LoaderTitle
, VolDesc
);
1205 Entry
->me
.Tag
= TAG_LEGACY
;
1207 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1208 Entry
->me
.Image
= LoadOSIcon(Volume
->OSIconName
, L
"legacy", FALSE
);
1209 Entry
->me
.BadgeImage
= Volume
->VolBadgeImage
;
1210 Entry
->Volume
= Volume
;
1211 Entry
->LoadOptions
= (Volume
->DiskKind
== DISK_KIND_OPTICAL
) ? L
"CD" :
1212 ((Volume
->DiskKind
== DISK_KIND_EXTERNAL
) ? L
"USB" : L
"HD");
1213 Entry
->Enabled
= TRUE
;
1215 // create the submenu
1216 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
1217 SubScreen
->Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1218 SPrint(SubScreen
->Title
, 255, L
"Boot Options for %s on %s", LoaderTitle
, VolDesc
);
1219 SubScreen
->TitleImage
= Entry
->me
.Image
;
1222 SubEntry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1223 SubEntry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1224 SPrint(SubEntry
->me
.Title
, 255, L
"Boot %s", LoaderTitle
);
1225 SubEntry
->me
.Tag
= TAG_LEGACY
;
1226 SubEntry
->Volume
= Entry
->Volume
;
1227 SubEntry
->LoadOptions
= Entry
->LoadOptions
;
1228 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
1230 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
1231 Entry
->me
.SubScreen
= SubScreen
;
1232 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1234 } /* static LEGACY_ENTRY * AddLegacyEntry() */
1237 #ifdef __MAKEWITH_TIANO
1238 // default volume badge icon based on disk kind
1239 static EG_IMAGE
* GetDiskBadge(IN UINTN DiskType
) {
1240 EG_IMAGE
* Badge
= NULL
;
1244 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_INTERNAL
);
1247 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_EXTERNAL
);
1250 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_OPTICAL
);
1254 } // static EG_IMAGE * GetDiskBadge()
1257 Create a rEFInd boot option from a Legacy BIOS protocol option.
1259 static LEGACY_ENTRY
* AddLegacyEntryUEFI(BDS_COMMON_OPTION
*BdsOption
, IN UINT16 DiskType
)
1261 LEGACY_ENTRY
*Entry
, *SubEntry
;
1262 REFIT_MENU_SCREEN
*SubScreen
;
1263 CHAR16 ShortcutLetter
= 0;
1264 CHAR16
*LegacyDescription
= BdsOption
->Description
;
1266 // ScanVolume(Volume);
1268 // prepare the menu entry
1269 Entry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1270 Entry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1271 SPrint(Entry
->me
.Title
, 255, L
"Boot legacy target %s", LegacyDescription
);
1272 Entry
->me
.Tag
= TAG_LEGACY_NON_MAC
;
1274 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1275 Entry
->me
.Image
= LoadOSIcon(L
"legacy", L
"legacy", TRUE
);
1276 Entry
->LoadOptions
= (DiskType
== BBS_CDROM
) ? L
"CD" :
1277 ((DiskType
== BBS_USB
) ? L
"USB" : L
"HD");
1278 Entry
->me
.BadgeImage
= GetDiskBadge(DiskType
);
1279 // Entry->me.BadgeImage = Volume->VolBadgeImage;
1280 Entry
->BdsOption
= BdsOption
;
1281 Entry
->Enabled
= TRUE
;
1283 // create the submenu
1284 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
1285 SubScreen
->Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1286 SPrint(SubScreen
->Title
, 255, L
"No boot options for legacy target");
1287 SubScreen
->TitleImage
= Entry
->me
.Image
;
1290 SubEntry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1291 SubEntry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1292 SPrint(SubEntry
->me
.Title
, 255, L
"Boot %s", LegacyDescription
);
1293 SubEntry
->me
.Tag
= TAG_LEGACY_NON_MAC
;
1294 Entry
->BdsOption
= BdsOption
;
1295 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
1297 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
1298 Entry
->me
.SubScreen
= SubScreen
;
1299 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1301 } /* static LEGACY_ENTRY * AddLegacyEntryUEFI() */
1304 Scan for legacy BIOS targets on machines that implement EFI_LEGACY_BIOS_PROTOCOL.
1305 In testing, protocol has not been implemented on Macs but has been
1306 implemented on several Dell PCs and an ASUS motherboard.
1307 Restricts output to disks of the specified DiskType.
1309 static VOID
ScanLegacyUEFI(IN UINTN DiskType
)
1312 EFI_LEGACY_BIOS_PROTOCOL
*LegacyBios
;
1313 UINT16
*BootOrder
= NULL
;
1315 CHAR16 BootOption
[10];
1316 UINTN BootOrderSize
= 0;
1318 BDS_COMMON_OPTION
*BdsOption
;
1319 LIST_ENTRY TempList
;
1320 BBS_BBS_DEVICE_PATH
* BbsDevicePath
= NULL
;
1321 // REFIT_VOLUME Volume;
1323 InitializeListHead (&TempList
);
1324 ZeroMem (Buffer
, sizeof (Buffer
));
1326 // If LegacyBios protocol is not implemented on this platform, then
1327 //we do not support this type of legacy boot on this machine.
1328 Status
= gBS
->LocateProtocol(&gEfiLegacyBootProtocolGuid
, NULL
, (VOID
**) &LegacyBios
);
1329 if (EFI_ERROR (Status
))
1332 // Grab the boot order
1333 BootOrder
= BdsLibGetVariableAndSize(L
"BootOrder", &gEfiGlobalVariableGuid
, &BootOrderSize
);
1334 if (BootOrder
== NULL
) {
1339 while (Index
< BootOrderSize
/ sizeof (UINT16
))
1341 // Grab each boot option variable from the boot order, and convert
1342 // the variable into a BDS boot option
1343 UnicodeSPrint (BootOption
, sizeof (BootOption
), L
"Boot%04x", BootOrder
[Index
]);
1344 Print(L
"Scanning '%s'\n", BootOption
);
1345 BdsOption
= BdsLibVariableToOption (&TempList
, BootOption
);
1347 if (BdsOption
!= NULL
) {
1348 BbsDevicePath
= (BBS_BBS_DEVICE_PATH
*)BdsOption
->DevicePath
;
1350 // Only add the entry if it is of a requested type (e.g. USB, HD)
1352 // Two checks necessary because some systems return EFI boot loaders
1353 // with a DeviceType value that would inappropriately include them
1354 // as legacy loaders....
1355 if ((BbsDevicePath
->DeviceType
== DiskType
) && (BdsOption
->DevicePath
->Type
== DEVICE_TYPE_BIOS
)) {
1356 AddLegacyEntryUEFI(BdsOption
, BbsDevicePath
->DeviceType
);
1361 } /* static VOID ScanLegacyUEFI() */
1363 static VOID
ScanLegacyUEFI(IN UINTN DiskType
){}
1364 #endif // __MAKEWITH_TIANO
1366 static VOID
ScanLegacyVolume(REFIT_VOLUME
*Volume
, UINTN VolumeIndex
) {
1368 BOOLEAN ShowVolume
, HideIfOthersFound
;
1371 HideIfOthersFound
= FALSE
;
1372 if (Volume
->IsAppleLegacy
) {
1374 HideIfOthersFound
= TRUE
;
1375 } else if (Volume
->HasBootCode
) {
1377 if (Volume
->BlockIO
== Volume
->WholeDiskBlockIO
&&
1378 Volume
->BlockIOOffset
== 0 &&
1379 Volume
->OSName
== NULL
)
1380 // this is a whole disk (MBR) entry; hide if we have entries for partitions
1381 HideIfOthersFound
= TRUE
;
1383 if (HideIfOthersFound
) {
1384 // check for other bootable entries on the same disk
1385 for (VolumeIndex2
= 0; VolumeIndex2
< VolumesCount
; VolumeIndex2
++) {
1386 if (VolumeIndex2
!= VolumeIndex
&& Volumes
[VolumeIndex2
]->HasBootCode
&&
1387 Volumes
[VolumeIndex2
]->WholeDiskBlockIO
== Volume
->WholeDiskBlockIO
)
1393 AddLegacyEntry(NULL
, Volume
);
1394 } // static VOID ScanLegacyVolume()
1396 // Scan attached optical discs for legacy (BIOS) boot code
1397 // and add anything found to the list....
1398 static VOID
ScanLegacyDisc(VOID
)
1401 REFIT_VOLUME
*Volume
;
1403 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
) {
1404 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1405 Volume
= Volumes
[VolumeIndex
];
1406 if (Volume
->DiskKind
== DISK_KIND_OPTICAL
)
1407 ScanLegacyVolume(Volume
, VolumeIndex
);
1409 } else if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
1410 ScanLegacyUEFI(BBS_CDROM
);
1412 } /* static VOID ScanLegacyDisc() */
1414 // Scan internal hard disks for legacy (BIOS) boot code
1415 // and add anything found to the list....
1416 static VOID
ScanLegacyInternal(VOID
)
1419 REFIT_VOLUME
*Volume
;
1421 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
) {
1422 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1423 Volume
= Volumes
[VolumeIndex
];
1424 if (Volume
->DiskKind
== DISK_KIND_INTERNAL
)
1425 ScanLegacyVolume(Volume
, VolumeIndex
);
1427 } else if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
1428 ScanLegacyUEFI(BBS_HARDDISK
);
1430 } /* static VOID ScanLegacyInternal() */
1432 // Scan external disks for legacy (BIOS) boot code
1433 // and add anything found to the list....
1434 static VOID
ScanLegacyExternal(VOID
)
1437 REFIT_VOLUME
*Volume
;
1439 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
) {
1440 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1441 Volume
= Volumes
[VolumeIndex
];
1442 if (Volume
->DiskKind
== DISK_KIND_EXTERNAL
)
1443 ScanLegacyVolume(Volume
, VolumeIndex
);
1445 } else if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
1446 ScanLegacyUEFI(BBS_USB
);
1448 } /* static VOID ScanLegacyExternal() */
1451 // pre-boot tool functions
1454 static VOID
StartTool(IN LOADER_ENTRY
*Entry
)
1456 BeginExternalScreen(Entry
->UseGraphicsMode
, Entry
->me
.Title
+ 6); // assumes "Start <title>" as assigned below
1457 StartEFIImage(Entry
->DevicePath
, Entry
->LoadOptions
, Basename(Entry
->LoaderPath
),
1458 Basename(Entry
->LoaderPath
), NULL
, TRUE
);
1459 FinishExternalScreen();
1460 } /* static VOID StartTool() */
1462 static LOADER_ENTRY
* AddToolEntry(IN CHAR16
*LoaderPath
, IN CHAR16
*LoaderTitle
, IN EG_IMAGE
*Image
,
1463 IN CHAR16 ShortcutLetter
, IN BOOLEAN UseGraphicsMode
)
1465 LOADER_ENTRY
*Entry
;
1466 CHAR16
*TitleStr
= NULL
;
1468 Entry
= AllocateZeroPool(sizeof(LOADER_ENTRY
));
1470 MergeStrings(&TitleStr
, L
"Start ", 0);
1471 MergeStrings(&TitleStr
, LoaderTitle
, 0);
1472 Entry
->me
.Title
= TitleStr
;
1473 Entry
->me
.Tag
= TAG_TOOL
;
1475 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1476 Entry
->me
.Image
= Image
;
1477 Entry
->LoaderPath
= StrDuplicate(LoaderPath
);
1478 Entry
->DevicePath
= FileDevicePath(SelfLoadedImage
->DeviceHandle
, Entry
->LoaderPath
);
1479 Entry
->UseGraphicsMode
= UseGraphicsMode
;
1481 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1483 } /* static LOADER_ENTRY * AddToolEntry() */
1486 // pre-boot driver functions
1489 static UINTN
ScanDriverDir(IN CHAR16
*Path
)
1492 REFIT_DIR_ITER DirIter
;
1494 EFI_FILE_INFO
*DirEntry
;
1495 CHAR16 FileName
[256];
1497 CleanUpPathNameSlashes(Path
);
1498 // look through contents of the directory
1499 DirIterOpen(SelfRootDir
, Path
, &DirIter
);
1500 while (DirIterNext(&DirIter
, 2, LOADER_MATCH_PATTERNS
, &DirEntry
)) {
1501 if (DirEntry
->FileName
[0] == '.')
1502 continue; // skip this
1504 SPrint(FileName
, 255, L
"%s\\%s", Path
, DirEntry
->FileName
);
1506 Status
= StartEFIImage(FileDevicePath(SelfLoadedImage
->DeviceHandle
, FileName
),
1507 L
"", DirEntry
->FileName
, DirEntry
->FileName
, NULL
, FALSE
);
1509 Status
= DirIterClose(&DirIter
);
1510 if (Status
!= EFI_NOT_FOUND
) {
1511 SPrint(FileName
, 255, L
"while scanning the %s directory", Path
);
1512 CheckError(Status
, FileName
);
1517 #ifdef __MAKEWITH_GNUEFI
1518 static EFI_STATUS
ConnectAllDriversToAllControllers(VOID
)
1521 UINTN AllHandleCount
;
1522 EFI_HANDLE
*AllHandleBuffer
;
1525 EFI_HANDLE
*HandleBuffer
;
1531 Status
= LibLocateHandle(AllHandles
,
1536 if (EFI_ERROR(Status
))
1539 for (Index
= 0; Index
< AllHandleCount
; Index
++) {
1541 // Scan the handle database
1543 Status
= LibScanHandleDatabase(NULL
,
1545 AllHandleBuffer
[Index
],
1550 if (EFI_ERROR (Status
))
1554 if (HandleType
[Index
] & EFI_HANDLE_TYPE_DRIVER_BINDING_HANDLE
)
1556 if (HandleType
[Index
] & EFI_HANDLE_TYPE_IMAGE_HANDLE
)
1561 for (HandleIndex
= 0; HandleIndex
< HandleCount
; HandleIndex
++) {
1562 if (HandleType
[HandleIndex
] & EFI_HANDLE_TYPE_PARENT_HANDLE
)
1567 if (HandleType
[Index
] & EFI_HANDLE_TYPE_DEVICE_HANDLE
) {
1568 Status
= refit_call4_wrapper(BS
->ConnectController
,
1569 AllHandleBuffer
[Index
],
1577 FreePool (HandleBuffer
);
1578 FreePool (HandleType
);
1582 FreePool (AllHandleBuffer
);
1584 } /* EFI_STATUS ConnectAllDriversToAllControllers() */
1586 static EFI_STATUS
ConnectAllDriversToAllControllers(VOID
) {
1587 BdsLibConnectAllDriversToAllControllers();
1592 // Load all EFI drivers from rEFInd's "drivers" subdirectory and from the
1593 // directories specified by the user in the "scan_driver_dirs" configuration
1595 static VOID
LoadDrivers(VOID
)
1597 CHAR16
*Directory
, *SelfDirectory
;
1598 UINTN i
= 0, Length
, NumFound
= 0;
1600 // load drivers from the subdirectories of rEFInd's home directory specified
1601 // in the DRIVER_DIRS constant.
1602 while ((Directory
= FindCommaDelimited(DRIVER_DIRS
, i
++)) != NULL
) {
1603 SelfDirectory
= StrDuplicate(SelfDirPath
);
1604 CleanUpPathNameSlashes(SelfDirectory
);
1605 MergeStrings(&SelfDirectory
, Directory
, L
'\\');
1606 NumFound
+= ScanDriverDir(SelfDirectory
);
1607 FreePool(Directory
);
1608 FreePool(SelfDirectory
);
1611 // Scan additional user-specified driver directories....
1613 while ((Directory
= FindCommaDelimited(GlobalConfig
.DriverDirs
, i
++)) != NULL
) {
1614 CleanUpPathNameSlashes(Directory
);
1615 Length
= StrLen(Directory
);
1617 NumFound
+= ScanDriverDir(Directory
);
1618 FreePool(Directory
);
1621 // connect all devices
1623 ConnectAllDriversToAllControllers();
1624 } /* static VOID LoadDrivers() */
1626 // Determine what (if any) type of legacy (BIOS) boot support is available
1627 static VOID
FindLegacyBootType(VOID
) {
1628 #ifdef __MAKEWITH_TIANO
1630 EFI_LEGACY_BIOS_PROTOCOL
*LegacyBios
;
1633 GlobalConfig
.LegacyType
= LEGACY_TYPE_NONE
;
1635 // UEFI-style legacy BIOS support is available only with the TianoCore EDK2
1636 // build environment, and then only with some implementations....
1637 #ifdef __MAKEWITH_TIANO
1638 Status
= gBS
->LocateProtocol (&gEfiLegacyBootProtocolGuid
, NULL
, (VOID
**) &LegacyBios
);
1639 if (!EFI_ERROR (Status
))
1640 GlobalConfig
.LegacyType
= LEGACY_TYPE_UEFI
;
1643 // Macs have their own system. If the firmware vendor code contains the
1644 // string "Apple", assume it's available. Note that this overrides the
1645 // UEFI type, and might yield false positives if the vendor string
1646 // contains "Apple" as part of something bigger, so this isn't 100%
1648 if (StriSubCmp(L
"Apple", ST
->FirmwareVendor
))
1649 GlobalConfig
.LegacyType
= LEGACY_TYPE_MAC
;
1650 } // static VOID FindLegacyBootType
1652 // Locates boot loaders. NOTE: This assumes that GlobalConfig.LegacyType is set correctly.
1653 static VOID
ScanForBootloaders(VOID
) {
1658 // scan for loaders and tools, add them to the menu
1659 for (i
= 0; i
< NUM_SCAN_OPTIONS
; i
++) {
1660 switch(GlobalConfig
.ScanFor
[i
]) {
1665 ScanLegacyInternal();
1668 ScanLegacyExternal();
1671 ScanUserConfigured();
1685 // assign shortcut keys
1686 for (i
= 0; i
< MainMenu
.EntryCount
&& MainMenu
.Entries
[i
]->Row
== 0 && i
< 9; i
++)
1687 MainMenu
.Entries
[i
]->ShortcutDigit
= (CHAR16
)('1' + i
);
1689 // wait for user ACK when there were errors
1690 FinishTextScreen(FALSE
);
1691 } // static VOID ScanForBootloaders()
1693 // Add the second-row tags containing built-in and external tools (EFI shell,
1695 static VOID
ScanForTools(VOID
) {
1696 CHAR16
*FileName
= NULL
;
1697 REFIT_MENU_ENTRY
*TempMenuEntry
;
1700 for (i
= 0; i
< NUM_TOOLS
; i
++) {
1701 switch(GlobalConfig
.ShowTools
[i
]) {
1703 TempMenuEntry
= CopyMenuEntry(&MenuEntryShutdown
);
1704 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_SHUTDOWN
);
1705 AddMenuEntry(&MainMenu
, TempMenuEntry
);
1708 TempMenuEntry
= CopyMenuEntry(&MenuEntryReset
);
1709 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_RESET
);
1710 AddMenuEntry(&MainMenu
, TempMenuEntry
);
1713 TempMenuEntry
= CopyMenuEntry(&MenuEntryAbout
);
1714 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT
);
1715 AddMenuEntry(&MainMenu
, TempMenuEntry
);
1718 TempMenuEntry
= CopyMenuEntry(&MenuEntryExit
);
1719 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_EXIT
);
1720 AddMenuEntry(&MainMenu
, TempMenuEntry
);
1724 while ((FileName
= FindCommaDelimited(SHELL_NAMES
, j
++)) != NULL
) {
1725 if (FileExists(SelfRootDir
, FileName
)) {
1726 AddToolEntry(FileName
, L
"EFI Shell", BuiltinIcon(BUILTIN_ICON_TOOL_SHELL
), 'S', FALSE
);
1731 MergeStrings(&FileName
, L
"\\efi\\tools\\gptsync.efi", 0);
1732 if (FileExists(SelfRootDir
, FileName
)) {
1733 AddToolEntry(FileName
, L
"Make Hybrid MBR", BuiltinIcon(BUILTIN_ICON_TOOL_PART
), 'P', FALSE
);
1737 if (FileName
!= NULL
) {
1742 } // static VOID ScanForTools
1744 // Rescan for boot loaders
1745 VOID
RescanAll(VOID
) {
1752 egDisplayMessage(L
"Scanning for new boot loaders; please wait....", &BGColor
);
1753 FreeList((VOID
***) &(MainMenu
.Entries
), &MainMenu
.EntryCount
);
1754 MainMenu
.Entries
= NULL
;
1755 MainMenu
.EntryCount
= 0;
1757 ConnectAllDriversToAllControllers();
1758 ScanForBootloaders();
1761 } // VOID RescanAll()
1763 #ifndef __MAKEWITH_GNUEFI
1765 // Minimal initialization function
1766 static VOID
InitializeLib(IN EFI_HANDLE ImageHandle
, IN EFI_SYSTEM_TABLE
*SystemTable
) {
1768 // gImageHandle = ImageHandle;
1769 gBS
= SystemTable
->BootServices
;
1770 // gRS = SystemTable->RuntimeServices;
1771 gRT
= SystemTable
->RuntimeServices
; // Some BDS functions need gRT to be set
1772 EfiGetSystemConfigurationTable (&gEfiDxeServicesTableGuid
, (VOID
**) &gDS
);
1774 InitializeConsoleSim();
1784 efi_main (IN EFI_HANDLE ImageHandle
, IN EFI_SYSTEM_TABLE
*SystemTable
)
1787 BOOLEAN MainLoopRunning
= TRUE
;
1788 REFIT_MENU_ENTRY
*ChosenEntry
;
1794 InitializeLib(ImageHandle
, SystemTable
);
1796 Status
= InitRefitLib(ImageHandle
);
1797 if (EFI_ERROR(Status
))
1800 // read configuration
1801 FindLegacyBootType();
1802 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
)
1803 CopyMem(GlobalConfig
.ScanFor
, "ihebocm ", NUM_SCAN_OPTIONS
);
1805 CopyMem(GlobalConfig
.ScanFor
, "ieom ", NUM_SCAN_OPTIONS
);
1807 MainMenu
.TimeoutSeconds
= GlobalConfig
.Timeout
;
1809 // disable EFI watchdog timer
1810 refit_call4_wrapper(BS
->SetWatchdogTimer
, 0x0000, 0x0000, 0x0000, NULL
);
1812 // further bootstrap (now with config available)
1814 if (GlobalConfig
.ScanDelay
> 0) {
1819 egDisplayMessage(L
"Pausing before disk scan; please wait....", &BGColor
);
1820 for (i
= 0; i
< GlobalConfig
.ScanDelay
; i
++)
1821 refit_call1_wrapper(BS
->Stall
, 1000000);
1824 ScanForBootloaders();
1827 Selection
= StrDuplicate(GlobalConfig
.DefaultSelection
);
1828 while (MainLoopRunning
) {
1829 MenuExit
= RunMainMenu(&MainMenu
, Selection
, &ChosenEntry
);
1831 // The Escape key triggers a re-scan operation....
1832 if (MenuExit
== MENU_EXIT_ESCAPE
) {
1837 switch (ChosenEntry
->Tag
) {
1839 case TAG_REBOOT
: // Reboot
1841 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);
1842 MainLoopRunning
= FALSE
; // just in case we get this far
1845 case TAG_SHUTDOWN
: // Shut Down
1847 refit_call4_wrapper(RT
->ResetSystem
, EfiResetShutdown
, EFI_SUCCESS
, 0, NULL
);
1848 MainLoopRunning
= FALSE
; // just in case we get this far
1851 case TAG_ABOUT
: // About rEFInd
1855 case TAG_LOADER
: // Boot OS via .EFI loader
1856 StartLoader((LOADER_ENTRY
*)ChosenEntry
);
1859 case TAG_LEGACY
: // Boot legacy OS
1860 StartLegacy((LEGACY_ENTRY
*)ChosenEntry
);
1863 #ifdef __MAKEWITH_TIANO
1864 case TAG_LEGACY_NON_MAC
: // Boot a legacy OS on a non-Mac
1865 StartLegacyUEFI((LEGACY_ENTRY
*)ChosenEntry
);
1867 #endif // __MAKEWITH_TIANO
1869 case TAG_TOOL
: // Start a EFI tool
1870 StartTool((LOADER_ENTRY
*)ChosenEntry
);
1873 case TAG_EXIT
: // Terminate rEFInd
1874 BeginTextScreen(L
" ");
1879 FreePool(Selection
);
1880 Selection
= StrDuplicate(ChosenEntry
->Title
);
1883 // If we end up here, things have gone wrong. Try to reboot, and if that
1884 // fails, go into an endless loop.
1885 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);