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
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
97 {TAG_SHELL
, TAG_ABOUT
, TAG_SHUTDOWN
, TAG_REBOOT
, 0, 0, 0, 0, 0 }};
99 // Structure used to hold boot loader filenames and time stamps in
100 // a linked list; used to sort entries within a directory.
104 struct LOADER_LIST
*NextEntry
;
111 static VOID
AboutrEFInd(VOID
)
113 CHAR16
*TempStr
; // Note: Don't deallocate; moved to menu structure
115 if (AboutMenu
.EntryCount
== 0) {
116 AboutMenu
.TitleImage
= BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT
);
117 AddMenuInfoLine(&AboutMenu
, L
"rEFInd Version 0.4.5.2");
118 AddMenuInfoLine(&AboutMenu
, L
"");
119 AddMenuInfoLine(&AboutMenu
, L
"Copyright (c) 2006-2010 Christoph Pfisterer");
120 AddMenuInfoLine(&AboutMenu
, L
"Copyright (c) 2012 Roderick W. Smith");
121 AddMenuInfoLine(&AboutMenu
, L
"Portions Copyright (c) Intel Corporation and others");
122 AddMenuInfoLine(&AboutMenu
, L
"Distributed under the terms of the GNU GPLv3 license");
123 AddMenuInfoLine(&AboutMenu
, L
"");
124 AddMenuInfoLine(&AboutMenu
, L
"Running on:");
125 TempStr
= AllocateZeroPool(256 * sizeof(CHAR16
));
126 SPrint(TempStr
, 255, L
" EFI Revision %d.%02d", ST
->Hdr
.Revision
>> 16, ST
->Hdr
.Revision
& ((1 << 16) - 1));
127 AddMenuInfoLine(&AboutMenu
, TempStr
);
129 AddMenuInfoLine(&AboutMenu
, L
" Platform: x86 (32 bit)");
130 #elif defined(EFIX64)
131 AddMenuInfoLine(&AboutMenu
, L
" Platform: x86_64 (64 bit)");
133 AddMenuInfoLine(&AboutMenu
, L
" Platform: unknown");
135 TempStr
= AllocateZeroPool(256 * sizeof(CHAR16
));
136 SPrint(TempStr
, 255, L
" Firmware: %s %d.%02d",
137 ST
->FirmwareVendor
, ST
->FirmwareRevision
>> 16, ST
->FirmwareRevision
& ((1 << 16) - 1));
138 AddMenuInfoLine(&AboutMenu
, TempStr
);
139 TempStr
= AllocateZeroPool(256 * sizeof(CHAR16
));
140 SPrint(TempStr
, 255, L
" Screen Output: %s", egScreenDescription());
141 AddMenuInfoLine(&AboutMenu
, TempStr
);
142 AddMenuInfoLine(&AboutMenu
, L
"");
143 #if defined(__MAKEWITH_GNUEFI)
144 AddMenuInfoLine(&AboutMenu
, L
"Built with GNU-EFI");
146 AddMenuInfoLine(&AboutMenu
, L
"Built with TianoCore EDK2");
148 AddMenuInfoLine(&AboutMenu
, L
"");
149 AddMenuInfoLine(&AboutMenu
, L
"For more information, see the rEFInd Web site:");
150 AddMenuInfoLine(&AboutMenu
, L
"http://www.rodsbooks.com/refind/");
151 AddMenuEntry(&AboutMenu
, &MenuEntryReturn
);
154 RunMenu(&AboutMenu
, NULL
);
155 } /* VOID AboutrEFInd() */
157 static EFI_STATUS
StartEFIImageList(IN EFI_DEVICE_PATH
**DevicePaths
,
158 IN CHAR16
*LoadOptions
, IN CHAR16
*LoadOptionsPrefix
,
159 IN CHAR16
*ImageTitle
,
160 OUT UINTN
*ErrorInStep
,
163 EFI_STATUS Status
, ReturnStatus
;
164 EFI_HANDLE ChildImageHandle
;
165 EFI_LOADED_IMAGE
*ChildLoadedImage
;
166 UINTN DevicePathIndex
;
167 CHAR16 ErrorInfo
[256];
168 CHAR16
*FullLoadOptions
= NULL
;
171 Print(L
"Starting %s\n", ImageTitle
);
172 if (ErrorInStep
!= NULL
)
175 // load the image into memory
176 ReturnStatus
= Status
= EFI_NOT_FOUND
; // in case the list is empty
177 for (DevicePathIndex
= 0; DevicePaths
[DevicePathIndex
] != NULL
; DevicePathIndex
++) {
178 ReturnStatus
= Status
= refit_call6_wrapper(BS
->LoadImage
, FALSE
, SelfImageHandle
, DevicePaths
[DevicePathIndex
], NULL
, 0, &ChildImageHandle
);
179 if (ReturnStatus
!= EFI_NOT_FOUND
) {
183 SPrint(ErrorInfo
, 255, L
"while loading %s", ImageTitle
);
184 if (CheckError(Status
, ErrorInfo
)) {
185 if (ErrorInStep
!= NULL
)
191 if (LoadOptions
!= NULL
) {
192 ReturnStatus
= Status
= refit_call3_wrapper(BS
->HandleProtocol
, ChildImageHandle
, &LoadedImageProtocol
, (VOID
**) &ChildLoadedImage
);
193 if (CheckError(Status
, L
"while getting a LoadedImageProtocol handle")) {
194 if (ErrorInStep
!= NULL
)
199 if (LoadOptionsPrefix
!= NULL
) {
200 MergeStrings(&FullLoadOptions
, LoadOptionsPrefix
, 0);
201 MergeStrings(&FullLoadOptions
, LoadOptions
, L
' ');
202 MergeStrings(&FullLoadOptions
, L
" ", 0);
203 // NOTE: That last space is also added by the EFI shell and seems to be significant
204 // when passing options to Apple's boot.efi...
206 MergeStrings(&FullLoadOptions
, LoadOptions
, 0);
208 // NOTE: We also include the terminating null in the length for safety.
209 ChildLoadedImage
->LoadOptions
= (VOID
*)FullLoadOptions
;
210 ChildLoadedImage
->LoadOptionsSize
= ((UINT32
)StrLen(FullLoadOptions
) + 1) * sizeof(CHAR16
);
212 Print(L
"Using load options '%s'\n", FullLoadOptions
);
215 // close open file handles
218 // turn control over to the image
219 // TODO: (optionally) re-enable the EFI watchdog timer!
220 ReturnStatus
= Status
= refit_call3_wrapper(BS
->StartImage
, ChildImageHandle
, NULL
, NULL
);
221 // control returns here when the child image calls Exit()
222 SPrint(ErrorInfo
, 255, L
"returned from %s", ImageTitle
);
223 if (CheckError(Status
, ErrorInfo
)) {
224 if (ErrorInStep
!= NULL
)
228 // re-open file handles
232 // unload the image, we don't care if it works or not...
233 Status
= refit_call1_wrapper(BS
->UnloadImage
, ChildImageHandle
);
235 if (FullLoadOptions
!= NULL
)
236 FreePool(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 if (InitrdVersion
!= NULL
)
312 FreePool(InitrdVersion
);
314 DirIterClose(&DirIter
);
316 // Note: Don't FreePool(FileName), since Basename returns a pointer WITHIN the string it's passed.
317 FreePool(KernelVersion
);
320 } // static CHAR16 * FindInitrd()
322 LOADER_ENTRY
* AddPreparedLoaderEntry(LOADER_ENTRY
*Entry
) {
323 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
326 } // LOADER_ENTRY * AddPreparedLoaderEntry()
328 // Creates a copy of a menu screen.
329 // Returns a pointer to the copy of the menu screen.
330 static REFIT_MENU_SCREEN
* CopyMenuScreen(REFIT_MENU_SCREEN
*Entry
) {
331 REFIT_MENU_SCREEN
*NewEntry
;
334 NewEntry
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
335 if ((Entry
!= NULL
) && (NewEntry
!= NULL
)) {
336 CopyMem(NewEntry
, Entry
, sizeof(REFIT_MENU_SCREEN
));
337 NewEntry
->Title
= StrDuplicate(Entry
->Title
);
338 NewEntry
->TimeoutText
= StrDuplicate(Entry
->TimeoutText
);
339 if (Entry
->TitleImage
!= NULL
) {
340 NewEntry
->TitleImage
= AllocatePool(sizeof(EG_IMAGE
));
341 if (NewEntry
->TitleImage
!= NULL
)
342 CopyMem(NewEntry
->TitleImage
, Entry
->TitleImage
, sizeof(EG_IMAGE
));
344 NewEntry
->InfoLines
= (CHAR16
**) AllocateZeroPool(Entry
->InfoLineCount
* (sizeof(CHAR16
*)));
345 for (i
= 0; i
< Entry
->InfoLineCount
&& NewEntry
->InfoLines
; i
++) {
346 NewEntry
->InfoLines
[i
] = StrDuplicate(Entry
->InfoLines
[i
]);
348 NewEntry
->Entries
= (REFIT_MENU_ENTRY
**) AllocateZeroPool(Entry
->EntryCount
* (sizeof (REFIT_MENU_ENTRY
*)));
349 for (i
= 0; i
< Entry
->EntryCount
&& NewEntry
->Entries
; i
++) {
350 AddMenuEntry(NewEntry
, Entry
->Entries
[i
]);
354 } // static REFIT_MENU_SCREEN* CopyMenuScreen()
356 // Creates a copy of a menu entry. Intended to enable moving a stack-based
357 // menu entry (such as the ones for the "reboot" and "exit" functions) to
358 // to the heap. This enables easier deletion of the whole set of menu
359 // entries when re-scanning.
360 // Returns a pointer to the copy of the menu entry.
361 static REFIT_MENU_ENTRY
* CopyMenuEntry(REFIT_MENU_ENTRY
*Entry
) {
362 REFIT_MENU_ENTRY
*NewEntry
;
364 NewEntry
= AllocateZeroPool(sizeof(REFIT_MENU_ENTRY
));
365 if ((Entry
!= NULL
) && (NewEntry
!= NULL
)) {
366 CopyMem(NewEntry
, Entry
, sizeof(REFIT_MENU_ENTRY
));
367 NewEntry
->Title
= StrDuplicate(Entry
->Title
);
368 if (Entry
->BadgeImage
!= NULL
) {
369 NewEntry
->BadgeImage
= AllocatePool(sizeof(EG_IMAGE
));
370 if (NewEntry
->BadgeImage
!= NULL
)
371 CopyMem(NewEntry
->BadgeImage
, Entry
->BadgeImage
, sizeof(EG_IMAGE
));
373 if (Entry
->Image
!= NULL
) {
374 NewEntry
->Image
= AllocatePool(sizeof(EG_IMAGE
));
375 if (NewEntry
->Image
!= NULL
)
376 CopyMem(NewEntry
->Image
, Entry
->Image
, sizeof(EG_IMAGE
));
378 if (Entry
->SubScreen
!= NULL
) {
379 NewEntry
->SubScreen
= CopyMenuScreen(Entry
->SubScreen
);
383 } // REFIT_MENU_ENTRY* CopyMenuEntry()
385 // Creates a new LOADER_ENTRY data structure and populates it with
386 // default values from the specified Entry, or NULL values if Entry
387 // is unspecified (NULL).
388 // Returns a pointer to the new data structure, or NULL if it
389 // couldn't be allocated
390 LOADER_ENTRY
*InitializeLoaderEntry(IN LOADER_ENTRY
*Entry
) {
391 LOADER_ENTRY
*NewEntry
= NULL
;
393 NewEntry
= AllocateZeroPool(sizeof(LOADER_ENTRY
));
394 if (NewEntry
!= NULL
) {
395 NewEntry
->me
.Title
= NULL
;
396 NewEntry
->me
.Tag
= TAG_LOADER
;
397 NewEntry
->Enabled
= TRUE
;
398 NewEntry
->UseGraphicsMode
= FALSE
;
399 NewEntry
->OSType
= 0;
401 NewEntry
->LoaderPath
= StrDuplicate(Entry
->LoaderPath
);
402 NewEntry
->VolName
= StrDuplicate(Entry
->VolName
);
403 NewEntry
->DevicePath
= Entry
->DevicePath
;
404 NewEntry
->UseGraphicsMode
= Entry
->UseGraphicsMode
;
405 NewEntry
->LoadOptions
= StrDuplicate(Entry
->LoadOptions
);
406 NewEntry
->InitrdPath
= StrDuplicate(Entry
->InitrdPath
);
410 } // LOADER_ENTRY *InitializeLoaderEntry()
412 // Prepare a REFIT_MENU_SCREEN data structure for a subscreen entry. This sets up
413 // the default entry that launches the boot loader using the same options as the
414 // main Entry does. Subsequent options can be added by the calling function.
415 // If a subscreen already exists in the Entry that's passed to this function,
416 // it's left unchanged and a pointer to it is returned.
417 // Returns a pointer to the new subscreen data structure, or NULL if there
418 // were problems allocating memory.
419 REFIT_MENU_SCREEN
*InitializeSubScreen(IN LOADER_ENTRY
*Entry
) {
420 CHAR16
*FileName
, *Temp
= NULL
;
421 REFIT_MENU_SCREEN
*SubScreen
= NULL
;
422 LOADER_ENTRY
*SubEntry
;
424 FileName
= Basename(Entry
->LoaderPath
);
425 if (Entry
->me
.SubScreen
== NULL
) { // No subscreen yet; initialize default entry....
426 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
427 if (SubScreen
!= NULL
) {
428 SubScreen
->Title
= AllocateZeroPool(sizeof(CHAR16
) * 256);
429 SPrint(SubScreen
->Title
, 255, L
"Boot Options for %s on %s",
430 (Entry
->Title
!= NULL
) ? Entry
->Title
: FileName
, Entry
->VolName
);
431 SubScreen
->TitleImage
= Entry
->me
.Image
;
433 SubEntry
= InitializeLoaderEntry(Entry
);
434 if (SubEntry
!= NULL
) {
435 SubEntry
->me
.Title
= L
"Boot using default options";
436 if ((SubEntry
->InitrdPath
!= NULL
) && (StrLen(SubEntry
->InitrdPath
) > 0) && (!StriSubCmp(L
"initrd", SubEntry
->LoadOptions
))) {
437 MergeStrings(&Temp
, L
"initrd=", 0);
438 MergeStrings(&Temp
, SubEntry
->InitrdPath
, 0);
439 MergeStrings(&SubEntry
->LoadOptions
, Temp
, L
' ');
442 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
443 } // if (SubEntry != NULL)
444 } // if (SubScreen != NULL)
445 } else { // existing subscreen; less initialization, and just add new entry later....
446 SubScreen
= Entry
->me
.SubScreen
;
449 } // REFIT_MENU_SCREEN *InitializeSubScreen()
451 VOID
GenerateSubScreen(LOADER_ENTRY
*Entry
, IN REFIT_VOLUME
*Volume
) {
452 REFIT_MENU_SCREEN
*SubScreen
;
453 LOADER_ENTRY
*SubEntry
;
454 CHAR16
*InitrdOption
= NULL
, *Temp
;
455 CHAR16 DiagsFileName
[256];
460 // create the submenu
461 if (StrLen(Entry
->Title
) == 0) {
462 FreePool(Entry
->Title
);
465 SubScreen
= InitializeSubScreen(Entry
);
467 // loader-specific submenu entries
468 if (Entry
->OSType
== 'M') { // entries for Mac OS X
470 SubEntry
= InitializeLoaderEntry(Entry
);
471 if (SubEntry
!= NULL
) {
472 SubEntry
->me
.Title
= L
"Boot Mac OS X with a 64-bit kernel";
473 SubEntry
->LoadOptions
= L
"arch=x86_64";
474 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
475 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
478 SubEntry
= InitializeLoaderEntry(Entry
);
479 if (SubEntry
!= NULL
) {
480 SubEntry
->me
.Title
= L
"Boot Mac OS X with a 32-bit kernel";
481 SubEntry
->LoadOptions
= L
"arch=i386";
482 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
483 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
487 if (!(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_SINGLEUSER
)) {
488 SubEntry
= InitializeLoaderEntry(Entry
);
489 if (SubEntry
!= NULL
) {
490 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode";
491 SubEntry
->UseGraphicsMode
= FALSE
;
492 SubEntry
->LoadOptions
= L
"-v";
493 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
497 SubEntry
= InitializeLoaderEntry(Entry
);
498 if (SubEntry
!= NULL
) {
499 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode (64-bit)";
500 SubEntry
->UseGraphicsMode
= FALSE
;
501 SubEntry
->LoadOptions
= L
"-v arch=x86_64";
502 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
505 SubEntry
= InitializeLoaderEntry(Entry
);
506 if (SubEntry
!= NULL
) {
507 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode (32-bit)";
508 SubEntry
->UseGraphicsMode
= FALSE
;
509 SubEntry
->LoadOptions
= L
"-v arch=i386";
510 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
514 SubEntry
= InitializeLoaderEntry(Entry
);
515 if (SubEntry
!= NULL
) {
516 SubEntry
->me
.Title
= L
"Boot Mac OS X in single user mode";
517 SubEntry
->UseGraphicsMode
= FALSE
;
518 SubEntry
->LoadOptions
= L
"-v -s";
519 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
523 // check for Apple hardware diagnostics
524 StrCpy(DiagsFileName
, L
"\\System\\Library\\CoreServices\\.diagnostics\\diags.efi");
525 if (FileExists(Volume
->RootDir
, DiagsFileName
) && !(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_HWTEST
)) {
526 SubEntry
= InitializeLoaderEntry(Entry
);
527 if (SubEntry
!= NULL
) {
528 SubEntry
->me
.Title
= L
"Run Apple Hardware Test";
529 FreePool(SubEntry
->LoaderPath
);
530 SubEntry
->LoaderPath
= StrDuplicate(DiagsFileName
);
531 SubEntry
->DevicePath
= FileDevicePath(Volume
->DeviceHandle
, SubEntry
->LoaderPath
);
532 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
533 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
535 } // if diagnostics entry found
537 } else if (Entry
->OSType
== 'L') { // entries for Linux kernels with EFI stub loaders
538 File
= ReadLinuxOptionsFile(Entry
->LoaderPath
, Volume
);
540 if ((Temp
= FindInitrd(Entry
->LoaderPath
, Volume
)) != NULL
) {
541 MergeStrings(&InitrdOption
, L
"initrd=", 0);
542 MergeStrings(&InitrdOption
, Temp
, 0);
544 TokenCount
= ReadTokenLine(File
, &TokenList
); // read and discard first entry, since it's
545 FreeTokenLine(&TokenList
, &TokenCount
); // set up by InitializeSubScreen(), earlier....
546 while ((TokenCount
= ReadTokenLine(File
, &TokenList
)) > 1) {
547 SubEntry
= InitializeLoaderEntry(Entry
);
548 SubEntry
->me
.Title
= StrDuplicate(TokenList
[0]);
549 if (SubEntry
->LoadOptions
!= NULL
)
550 FreePool(SubEntry
->LoadOptions
);
551 SubEntry
->LoadOptions
= StrDuplicate(TokenList
[1]);
552 MergeStrings(&SubEntry
->LoadOptions
, InitrdOption
, L
' ');
553 FreeTokenLine(&TokenList
, &TokenCount
);
554 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_LINUX
;
555 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
558 FreePool(InitrdOption
);
562 } // if Linux options file exists
564 } else if (Entry
->OSType
== 'E') { // entries for ELILO
565 SubEntry
= InitializeLoaderEntry(Entry
);
566 if (SubEntry
!= NULL
) {
567 SubEntry
->me
.Title
= L
"Run ELILO in interactive mode";
568 SubEntry
->LoadOptions
= L
"-p";
569 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
570 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
573 SubEntry
= InitializeLoaderEntry(Entry
);
574 if (SubEntry
!= NULL
) {
575 SubEntry
->me
.Title
= L
"Boot Linux for a 17\" iMac or a 15\" MacBook Pro (*)";
576 SubEntry
->UseGraphicsMode
= TRUE
;
577 SubEntry
->LoadOptions
= L
"-d 0 i17";
578 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
579 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
582 SubEntry
= InitializeLoaderEntry(Entry
);
583 if (SubEntry
!= NULL
) {
584 SubEntry
->me
.Title
= L
"Boot Linux for a 20\" iMac (*)";
585 SubEntry
->UseGraphicsMode
= TRUE
;
586 SubEntry
->LoadOptions
= L
"-d 0 i20";
587 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
588 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
591 SubEntry
= InitializeLoaderEntry(Entry
);
592 if (SubEntry
!= NULL
) {
593 SubEntry
->me
.Title
= L
"Boot Linux for a Mac Mini (*)";
594 SubEntry
->UseGraphicsMode
= TRUE
;
595 SubEntry
->LoadOptions
= L
"-d 0 mini";
596 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
597 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
600 AddMenuInfoLine(SubScreen
, L
"NOTE: This is an example. Entries");
601 AddMenuInfoLine(SubScreen
, L
"marked with (*) may not work.");
603 } else if (Entry
->OSType
== 'X') { // entries for xom.efi
604 // by default, skip the built-in selection and boot from hard disk only
605 Entry
->LoadOptions
= L
"-s -h";
607 SubEntry
= InitializeLoaderEntry(Entry
);
608 if (SubEntry
!= NULL
) {
609 SubEntry
->me
.Title
= L
"Boot Windows from Hard Disk";
610 SubEntry
->LoadOptions
= L
"-s -h";
611 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
612 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
615 SubEntry
= InitializeLoaderEntry(Entry
);
616 if (SubEntry
!= NULL
) {
617 SubEntry
->me
.Title
= L
"Boot Windows from CD-ROM";
618 SubEntry
->LoadOptions
= L
"-s -c";
619 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
620 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
623 SubEntry
= InitializeLoaderEntry(Entry
);
624 if (SubEntry
!= NULL
) {
625 SubEntry
->me
.Title
= L
"Run XOM in text mode";
626 SubEntry
->UseGraphicsMode
= FALSE
;
627 SubEntry
->LoadOptions
= L
"-v";
628 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
629 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
631 } // entries for xom.efi
632 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
633 Entry
->me
.SubScreen
= SubScreen
;
634 } // VOID GenerateSubScreen()
636 // Returns options for a Linux kernel. Reads them from an options file in the
637 // kernel's directory; and if present, adds an initrd= option for an initial
638 // RAM disk file with the same version number as the kernel file.
639 static CHAR16
* GetMainLinuxOptions(IN CHAR16
* LoaderPath
, IN REFIT_VOLUME
*Volume
) {
640 CHAR16
*Options
= NULL
, *InitrdName
, *InitrdOption
= NULL
;
642 Options
= GetFirstOptionsFromFile(LoaderPath
, Volume
);
643 InitrdName
= FindInitrd(LoaderPath
, Volume
);
644 if (InitrdName
!= NULL
) {
645 MergeStrings(&InitrdOption
, L
"initrd=", 0);
646 MergeStrings(&InitrdOption
, InitrdName
, 0);
648 MergeStrings(&Options
, InitrdOption
, ' ');
649 if (InitrdOption
!= NULL
)
650 FreePool(InitrdOption
);
651 if (InitrdName
!= NULL
)
652 FreePool(InitrdName
);
654 } // static CHAR16 * GetMainLinuxOptions()
656 // Sets a few defaults for a loader entry -- mainly the icon, but also the OS type
657 // code and shortcut letter. For Linux EFI stub loaders, also sets kernel options
658 // that will (with luck) work fairly automatically.
659 VOID
SetLoaderDefaults(LOADER_ENTRY
*Entry
, CHAR16
*LoaderPath
, IN REFIT_VOLUME
*Volume
) {
660 CHAR16 IconFileName
[256];
661 CHAR16
*FileName
, *PathOnly
, *OSIconName
= NULL
, *Temp
;
662 CHAR16 ShortcutLetter
= 0;
664 FileName
= Basename(LoaderPath
);
665 PathOnly
= FindPath(LoaderPath
);
667 // locate a custom icon for the loader
668 StrCpy(IconFileName
, LoaderPath
);
669 ReplaceEfiExtension(IconFileName
, L
".icns");
670 if (FileExists(Volume
->RootDir
, IconFileName
)) {
671 Entry
->me
.Image
= LoadIcns(Volume
->RootDir
, IconFileName
, 128);
672 } else if ((StrLen(PathOnly
) == 0) && (Volume
->VolIconImage
!= NULL
)) {
673 Entry
->me
.Image
= Volume
->VolIconImage
;
674 } // icon matched to loader or volume
676 Temp
= FindLastDirName(LoaderPath
);
677 MergeStrings(&OSIconName
, Temp
, L
',');
679 if (OSIconName
!= NULL
) {
680 ShortcutLetter
= OSIconName
[0];
683 // detect specific loaders
684 if (StriSubCmp(L
"bzImage", LoaderPath
) || StriSubCmp(L
"vmlinuz", LoaderPath
)) {
685 MergeStrings(&OSIconName
, L
"linux", L
',');
687 if (ShortcutLetter
== 0)
688 ShortcutLetter
= 'L';
689 Entry
->LoadOptions
= GetMainLinuxOptions(LoaderPath
, Volume
);
690 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_LINUX
;
691 } else if (StriSubCmp(L
"refit", LoaderPath
)) {
692 MergeStrings(&OSIconName
, L
"refit", L
',');
694 ShortcutLetter
= 'R';
695 } else if (StriCmp(LoaderPath
, MACOSX_LOADER_PATH
) == 0) {
696 if (Volume
->VolIconImage
!= NULL
) { // custom icon file found
697 Entry
->me
.Image
= Volume
->VolIconImage
;
699 MergeStrings(&OSIconName
, L
"mac", L
',');
701 ShortcutLetter
= 'M';
702 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
703 } else if (StriCmp(FileName
, L
"diags.efi") == 0) {
704 MergeStrings(&OSIconName
, L
"hwtest", L
',');
705 } else if (StriCmp(FileName
, L
"e.efi") == 0 || StriCmp(FileName
, L
"elilo.efi") == 0) {
706 MergeStrings(&OSIconName
, L
"elilo,linux", L
',');
708 if (ShortcutLetter
== 0)
709 ShortcutLetter
= 'L';
710 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
711 } else if (StriSubCmp(L
"grub", FileName
)) {
713 ShortcutLetter
= 'G';
714 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_GRUB
;
715 } else if (StriCmp(FileName
, L
"cdboot.efi") == 0 ||
716 StriCmp(FileName
, L
"bootmgr.efi") == 0 ||
717 StriCmp(FileName
, L
"Bootmgfw.efi") == 0) {
718 MergeStrings(&OSIconName
, L
"win", L
',');
720 ShortcutLetter
= 'W';
721 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
722 } else if (StriCmp(FileName
, L
"xom.efi") == 0) {
723 MergeStrings(&OSIconName
, L
"xom,win", L
',');
724 Entry
->UseGraphicsMode
= TRUE
;
726 ShortcutLetter
= 'W';
727 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
730 if ((ShortcutLetter
>= 'a') && (ShortcutLetter
<= 'z'))
731 ShortcutLetter
= ShortcutLetter
- 'a' + 'A'; // convert lowercase to uppercase
732 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
733 if (Entry
->me
.Image
== NULL
)
734 Entry
->me
.Image
= LoadOSIcon(OSIconName
, L
"unknown", FALSE
);
735 if (PathOnly
!= NULL
)
737 } // VOID SetLoaderDefaults()
739 // Add a specified EFI boot loader to the list, using automatic settings
740 // for icons, options, etc.
741 LOADER_ENTRY
* AddLoaderEntry(IN CHAR16
*LoaderPath
, IN CHAR16
*LoaderTitle
, IN REFIT_VOLUME
*Volume
) {
744 CleanUpPathNameSlashes(LoaderPath
);
745 Entry
= InitializeLoaderEntry(NULL
);
747 Entry
->Title
= StrDuplicate((LoaderTitle
!= NULL
) ? LoaderTitle
: LoaderPath
);
748 Entry
->me
.Title
= AllocateZeroPool(sizeof(CHAR16
) * 256);
749 SPrint(Entry
->me
.Title
, 255, L
"Boot %s from %s", (LoaderTitle
!= NULL
) ? LoaderTitle
: LoaderPath
, Volume
->VolName
);
751 Entry
->me
.BadgeImage
= Volume
->VolBadgeImage
;
752 if ((LoaderPath
!= NULL
) && (LoaderPath
[0] != L
'\\')) {
753 Entry
->LoaderPath
= StrDuplicate(L
"\\");
755 Entry
->LoaderPath
= NULL
;
757 MergeStrings(&(Entry
->LoaderPath
), LoaderPath
, 0);
758 Entry
->VolName
= Volume
->VolName
;
759 Entry
->DevicePath
= FileDevicePath(Volume
->DeviceHandle
, Entry
->LoaderPath
);
760 SetLoaderDefaults(Entry
, LoaderPath
, Volume
);
761 GenerateSubScreen(Entry
, Volume
);
762 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
766 } // LOADER_ENTRY * AddLoaderEntry()
768 // Returns -1 if (Time1 < Time2), +1 if (Time1 > Time2), or 0 if
769 // (Time1 == Time2). Precision is only to the nearest second; since
770 // this is used for sorting boot loader entries, differences smaller
771 // than this are likely to be meaningless (and unlikely!).
772 INTN
TimeComp(EFI_TIME
*Time1
, EFI_TIME
*Time2
) {
773 INT64 Time1InSeconds
, Time2InSeconds
;
775 // Following values are overestimates; I'm assuming 31 days in every month.
776 // This is fine for the purpose of this function, which has a limited
778 Time1InSeconds
= Time1
->Second
+ (Time1
->Minute
* 60) + (Time1
->Hour
* 3600) + (Time1
->Day
* 86400) +
779 (Time1
->Month
* 2678400) + ((Time1
->Year
- 1998) * 32140800);
780 Time2InSeconds
= Time2
->Second
+ (Time2
->Minute
* 60) + (Time2
->Hour
* 3600) + (Time2
->Day
* 86400) +
781 (Time2
->Month
* 2678400) + ((Time2
->Year
- 1998) * 32140800);
782 if (Time1InSeconds
< Time2InSeconds
)
784 else if (Time1InSeconds
> Time2InSeconds
)
790 // Adds a loader list element, keeping it sorted by date. Returns the new
791 // first element (the one with the most recent date).
792 static struct LOADER_LIST
* AddLoaderListEntry(struct LOADER_LIST
*LoaderList
, struct LOADER_LIST
*NewEntry
) {
793 struct LOADER_LIST
*LatestEntry
, *CurrentEntry
, *PrevEntry
= NULL
;
795 LatestEntry
= CurrentEntry
= LoaderList
;
796 if (LoaderList
== NULL
) {
797 LatestEntry
= NewEntry
;
799 while ((CurrentEntry
!= NULL
) && (TimeComp(&(NewEntry
->TimeStamp
), &(CurrentEntry
->TimeStamp
)) < 0)) {
800 PrevEntry
= CurrentEntry
;
801 CurrentEntry
= CurrentEntry
->NextEntry
;
803 NewEntry
->NextEntry
= CurrentEntry
;
804 if (PrevEntry
== NULL
) {
805 LatestEntry
= NewEntry
;
807 PrevEntry
->NextEntry
= NewEntry
;
810 return (LatestEntry
);
811 } // static VOID AddLoaderListEntry()
813 // Delete the LOADER_LIST linked list
814 static VOID
CleanUpLoaderList(struct LOADER_LIST
*LoaderList
) {
815 struct LOADER_LIST
*Temp
;
817 while (LoaderList
!= NULL
) {
819 LoaderList
= LoaderList
->NextEntry
;
820 FreePool(Temp
->FileName
);
823 } // static VOID CleanUpLoaderList()
825 // Scan an individual directory for EFI boot loader files and, if found,
826 // add them to the list. Sorts the entries within the loader directory
827 // so that the most recent one appears first in the list.
828 static VOID
ScanLoaderDir(IN REFIT_VOLUME
*Volume
, IN CHAR16
*Path
, IN CHAR16
*Pattern
)
831 REFIT_DIR_ITER DirIter
;
832 EFI_FILE_INFO
*DirEntry
;
833 CHAR16 FileName
[256], *Extension
;
834 struct LOADER_LIST
*LoaderList
= NULL
, *NewLoader
;
836 if ((!SelfDirPath
|| !Path
|| ((StriCmp(Path
, SelfDirPath
) == 0) && Volume
->DeviceHandle
!= SelfVolume
->DeviceHandle
) ||
837 (StriCmp(Path
, SelfDirPath
) != 0)) && (!IsIn(Path
, GlobalConfig
.DontScan
))) {
838 // look through contents of the directory
839 DirIterOpen(Volume
->RootDir
, Path
, &DirIter
);
840 while (DirIterNext(&DirIter
, 2, Pattern
, &DirEntry
)) {
841 Extension
= FindExtension(DirEntry
->FileName
);
842 if (DirEntry
->FileName
[0] == '.' ||
843 StriCmp(DirEntry
->FileName
, L
"TextMode.efi") == 0 ||
844 StriCmp(DirEntry
->FileName
, L
"ebounce.efi") == 0 ||
845 StriCmp(DirEntry
->FileName
, L
"GraphicsConsole.efi") == 0 ||
846 StriCmp(Extension
, L
".icns") == 0 ||
847 StriSubCmp(L
"shell", DirEntry
->FileName
))
848 continue; // skip this
851 SPrint(FileName
, 255, L
"\\%s\\%s", Path
, DirEntry
->FileName
);
853 SPrint(FileName
, 255, L
"\\%s", DirEntry
->FileName
);
854 CleanUpPathNameSlashes(FileName
);
855 NewLoader
= AllocateZeroPool(sizeof(struct LOADER_LIST
));
856 if (NewLoader
!= NULL
) {
857 NewLoader
->FileName
= StrDuplicate(FileName
);
858 NewLoader
->TimeStamp
= DirEntry
->ModificationTime
;
859 LoaderList
= AddLoaderListEntry(LoaderList
, NewLoader
);
863 NewLoader
= LoaderList
;
864 while (NewLoader
!= NULL
) {
865 AddLoaderEntry(NewLoader
->FileName
, NULL
, Volume
);
866 NewLoader
= NewLoader
->NextEntry
;
868 CleanUpLoaderList(LoaderList
);
869 Status
= DirIterClose(&DirIter
);
870 if (Status
!= EFI_NOT_FOUND
) {
872 SPrint(FileName
, 255, L
"while scanning the %s directory", Path
);
874 StrCpy(FileName
, L
"while scanning the root directory");
875 CheckError(Status
, FileName
);
876 } // if (Status != EFI_NOT_FOUND)
877 } // if not scanning our own directory
878 } /* static VOID ScanLoaderDir() */
880 static VOID
ScanEfiFiles(REFIT_VOLUME
*Volume
) {
882 REFIT_DIR_ITER EfiDirIter
;
883 EFI_FILE_INFO
*EfiDirEntry
;
884 CHAR16 FileName
[256], *Directory
, *MatchPatterns
;
887 MatchPatterns
= StrDuplicate(LOADER_MATCH_PATTERNS
);
888 if (GlobalConfig
.ScanAllLinux
)
889 MergeStrings(&MatchPatterns
, LINUX_MATCH_PATTERNS
, L
',');
891 if ((Volume
->RootDir
!= NULL
) && (Volume
->VolName
!= NULL
)) {
892 // check for Mac OS X boot loader
893 if (!IsIn(L
"System\\Library\\CoreServices", GlobalConfig
.DontScan
)) {
894 StrCpy(FileName
, MACOSX_LOADER_PATH
);
895 if (FileExists(Volume
->RootDir
, FileName
)) {
896 AddLoaderEntry(FileName
, L
"Mac OS X", Volume
);
900 StrCpy(FileName
, L
"System\\Library\\CoreServices\\xom.efi");
901 if (FileExists(Volume
->RootDir
, FileName
)) {
902 AddLoaderEntry(FileName
, L
"Windows XP (XoM)", Volume
);
904 } // if Mac directory not in GlobalConfig.DontScan list
906 // check for Microsoft boot loader/menu
907 StrCpy(FileName
, L
"EFI\\Microsoft\\Boot\\Bootmgfw.efi");
908 if (FileExists(Volume
->RootDir
, FileName
) && !IsIn(L
"EFI\\Microsoft\\Boot", GlobalConfig
.DontScan
)) {
909 AddLoaderEntry(FileName
, L
"Microsoft EFI boot", Volume
);
912 // scan the root directory for EFI executables
913 ScanLoaderDir(Volume
, L
"\\", MatchPatterns
);
915 // scan subdirectories of the EFI directory (as per the standard)
916 DirIterOpen(Volume
->RootDir
, L
"EFI", &EfiDirIter
);
917 while (DirIterNext(&EfiDirIter
, 1, NULL
, &EfiDirEntry
)) {
918 if (StriCmp(EfiDirEntry
->FileName
, L
"tools") == 0 || EfiDirEntry
->FileName
[0] == '.')
919 continue; // skip this, doesn't contain boot loaders
920 SPrint(FileName
, 255, L
"EFI\\%s", EfiDirEntry
->FileName
);
921 ScanLoaderDir(Volume
, FileName
, MatchPatterns
);
923 Status
= DirIterClose(&EfiDirIter
);
924 if (Status
!= EFI_NOT_FOUND
)
925 CheckError(Status
, L
"while scanning the EFI directory");
927 // Scan user-specified (or additional default) directories....
929 while ((Directory
= FindCommaDelimited(GlobalConfig
.AlsoScan
, i
++)) != NULL
) {
930 CleanUpPathNameSlashes(Directory
);
931 Length
= StrLen(Directory
);
933 ScanLoaderDir(Volume
, Directory
, MatchPatterns
);
937 } // static VOID ScanEfiFiles()
939 // Scan internal disks for valid EFI boot loaders....
940 static VOID
ScanInternal(VOID
) {
943 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
944 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_INTERNAL
) {
945 ScanEfiFiles(Volumes
[VolumeIndex
]);
948 } // static VOID ScanInternal()
950 // Scan external disks for valid EFI boot loaders....
951 static VOID
ScanExternal(VOID
) {
954 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
955 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_EXTERNAL
) {
956 ScanEfiFiles(Volumes
[VolumeIndex
]);
959 } // static VOID ScanExternal()
961 // Scan internal disks for valid EFI boot loaders....
962 static VOID
ScanOptical(VOID
) {
965 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
966 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_OPTICAL
) {
967 ScanEfiFiles(Volumes
[VolumeIndex
]);
970 } // static VOID ScanOptical()
973 // legacy boot functions
976 static EFI_STATUS
ActivateMbrPartition(IN EFI_BLOCK_IO
*BlockIO
, IN UINTN PartitionIndex
)
979 UINT8 SectorBuffer
[512];
980 MBR_PARTITION_INFO
*MbrTable
, *EMbrTable
;
981 UINT32 ExtBase
, ExtCurrent
, NextExtCurrent
;
982 UINTN LogicalPartitionIndex
= 4;
984 BOOLEAN HaveBootCode
;
987 Status
= refit_call5_wrapper(BlockIO
->ReadBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, 0, 512, SectorBuffer
);
988 if (EFI_ERROR(Status
))
990 if (*((UINT16
*)(SectorBuffer
+ 510)) != 0xaa55)
991 return EFI_NOT_FOUND
; // safety measure #1
993 // add boot code if necessary
994 HaveBootCode
= FALSE
;
995 for (i
= 0; i
< MBR_BOOTCODE_SIZE
; i
++) {
996 if (SectorBuffer
[i
] != 0) {
1001 if (!HaveBootCode
) {
1002 // no boot code found in the MBR, add the syslinux MBR code
1003 SetMem(SectorBuffer
, MBR_BOOTCODE_SIZE
, 0);
1004 CopyMem(SectorBuffer
, syslinux_mbr
, SYSLINUX_MBR_SIZE
);
1007 // set the partition active
1008 MbrTable
= (MBR_PARTITION_INFO
*)(SectorBuffer
+ 446);
1010 for (i
= 0; i
< 4; i
++) {
1011 if (MbrTable
[i
].Flags
!= 0x00 && MbrTable
[i
].Flags
!= 0x80)
1012 return EFI_NOT_FOUND
; // safety measure #2
1013 if (i
== PartitionIndex
)
1014 MbrTable
[i
].Flags
= 0x80;
1015 else if (PartitionIndex
>= 4 && IS_EXTENDED_PART_TYPE(MbrTable
[i
].Type
)) {
1016 MbrTable
[i
].Flags
= 0x80;
1017 ExtBase
= MbrTable
[i
].StartLBA
;
1019 MbrTable
[i
].Flags
= 0x00;
1023 Status
= refit_call5_wrapper(BlockIO
->WriteBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, 0, 512, SectorBuffer
);
1024 if (EFI_ERROR(Status
))
1027 if (PartitionIndex
>= 4) {
1028 // we have to activate a logical partition, so walk the EMBR chain
1030 // NOTE: ExtBase was set above while looking at the MBR table
1031 for (ExtCurrent
= ExtBase
; ExtCurrent
; ExtCurrent
= NextExtCurrent
) {
1032 // read current EMBR
1033 Status
= refit_call5_wrapper(BlockIO
->ReadBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, ExtCurrent
, 512, SectorBuffer
);
1034 if (EFI_ERROR(Status
))
1036 if (*((UINT16
*)(SectorBuffer
+ 510)) != 0xaa55)
1037 return EFI_NOT_FOUND
; // safety measure #3
1039 // scan EMBR, set appropriate partition active
1040 EMbrTable
= (MBR_PARTITION_INFO
*)(SectorBuffer
+ 446);
1042 for (i
= 0; i
< 4; i
++) {
1043 if (EMbrTable
[i
].Flags
!= 0x00 && EMbrTable
[i
].Flags
!= 0x80)
1044 return EFI_NOT_FOUND
; // safety measure #4
1045 if (EMbrTable
[i
].StartLBA
== 0 || EMbrTable
[i
].Size
== 0)
1047 if (IS_EXTENDED_PART_TYPE(EMbrTable
[i
].Type
)) {
1048 // link to next EMBR
1049 NextExtCurrent
= ExtBase
+ EMbrTable
[i
].StartLBA
;
1050 EMbrTable
[i
].Flags
= (PartitionIndex
>= LogicalPartitionIndex
) ? 0x80 : 0x00;
1053 // logical partition
1054 EMbrTable
[i
].Flags
= (PartitionIndex
== LogicalPartitionIndex
) ? 0x80 : 0x00;
1055 LogicalPartitionIndex
++;
1059 // write current EMBR
1060 Status
= refit_call5_wrapper(BlockIO
->WriteBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, ExtCurrent
, 512, SectorBuffer
);
1061 if (EFI_ERROR(Status
))
1064 if (PartitionIndex
< LogicalPartitionIndex
)
1065 break; // stop the loop, no need to touch further EMBRs
1071 } /* static EFI_STATUS ActivateMbrPartition() */
1073 // early 2006 Core Duo / Core Solo models
1074 static UINT8 LegacyLoaderDevicePath1Data
[] = {
1075 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1076 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1077 0xFF, 0xFF, 0xF9, 0xFF, 0x00, 0x00, 0x00, 0x00,
1078 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1079 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1080 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1082 // mid-2006 Mac Pro (and probably other Core 2 models)
1083 static UINT8 LegacyLoaderDevicePath2Data
[] = {
1084 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1085 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1086 0xFF, 0xFF, 0xF7, 0xFF, 0x00, 0x00, 0x00, 0x00,
1087 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1088 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1089 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1091 // mid-2007 MBP ("Santa Rosa" based models)
1092 static UINT8 LegacyLoaderDevicePath3Data
[] = {
1093 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1094 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1095 0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
1096 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1097 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1098 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1101 static UINT8 LegacyLoaderDevicePath4Data
[] = {
1102 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1103 0x00, 0x00, 0xC0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1104 0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
1105 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1106 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1107 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1109 // late-2008 MB/MBP (NVidia chipset)
1110 static UINT8 LegacyLoaderDevicePath5Data
[] = {
1111 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1112 0x00, 0x40, 0xCB, 0xFF, 0x00, 0x00, 0x00, 0x00,
1113 0xFF, 0xBF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
1114 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1115 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1116 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1119 static EFI_DEVICE_PATH
*LegacyLoaderList
[] = {
1120 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath1Data
,
1121 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath2Data
,
1122 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath3Data
,
1123 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath4Data
,
1124 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath5Data
,
1128 #define MAX_DISCOVERED_PATHS (16)
1130 static VOID
StartLegacy(IN LEGACY_ENTRY
*Entry
)
1133 EG_IMAGE
*BootLogoImage
;
1134 UINTN ErrorInStep
= 0;
1135 EFI_DEVICE_PATH
*DiscoveredPathList
[MAX_DISCOVERED_PATHS
];
1137 BeginExternalScreen(TRUE
, L
"Booting Legacy OS");
1139 BootLogoImage
= LoadOSIcon(Entry
->Volume
->OSIconName
, L
"legacy", TRUE
);
1140 if (BootLogoImage
!= NULL
)
1141 BltImageAlpha(BootLogoImage
,
1142 (UGAWidth
- BootLogoImage
->Width
) >> 1,
1143 (UGAHeight
- BootLogoImage
->Height
) >> 1,
1144 &StdBackgroundPixel
);
1146 if (Entry
->Volume
->IsMbrPartition
) {
1147 ActivateMbrPartition(Entry
->Volume
->WholeDiskBlockIO
, Entry
->Volume
->MbrPartitionIndex
);
1150 ExtractLegacyLoaderPaths(DiscoveredPathList
, MAX_DISCOVERED_PATHS
, LegacyLoaderList
);
1152 Status
= StartEFIImageList(DiscoveredPathList
, Entry
->LoadOptions
, NULL
, L
"legacy loader", &ErrorInStep
, TRUE
);
1153 if (Status
== EFI_NOT_FOUND
) {
1154 if (ErrorInStep
== 1) {
1155 Print(L
"\nPlease make sure that you have the latest firmware update installed.\n");
1156 } else if (ErrorInStep
== 3) {
1157 Print(L
"\nThe firmware refused to boot from the selected volume. Note that external\n"
1158 L
"hard drives are not well-supported by Apple's firmware for legacy OS booting.\n");
1161 FinishExternalScreen();
1162 } /* static VOID StartLegacy() */
1164 // Start a device on a non-Mac using the EFI_LEGACY_BIOS_PROTOCOL
1165 #ifdef __MAKEWITH_TIANO
1166 static VOID
StartLegacyNonMac(IN LEGACY_ENTRY
*Entry
)
1168 BdsLibConnectDevicePath (Entry
->BdsOption
->DevicePath
);
1169 BdsLibDoLegacyBoot(Entry
->BdsOption
);
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
1231 Create a rEFInd boot option from a Legacy BIOS protocol option.
1233 static LEGACY_ENTRY
* AddLegacyEntryNonMac(BDS_COMMON_OPTION
*BdsOption
, IN UINT16 DiskType
)
1235 LEGACY_ENTRY
*Entry
, *SubEntry
;
1236 REFIT_MENU_SCREEN
*SubScreen
;
1237 CHAR16 ShortcutLetter
= 0;
1238 CHAR16
*LegacyDescription
= BdsOption
->Description
;
1240 // prepare the menu entry
1241 Entry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1242 Entry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1243 SPrint(Entry
->me
.Title
, 255, L
"Boot legacy target %s", LegacyDescription
);
1244 Entry
->me
.Tag
= TAG_LEGACY_NON_MAC
;
1246 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1247 Entry
->me
.Image
= LoadOSIcon(L
"legacy", L
"legacy", TRUE
);
1248 Entry
->LoadOptions
= (DiskType
== BBS_CDROM
) ? L
"CD" :
1249 ((DiskType
== BBS_USB
) ? L
"USB" : L
"HD");
1250 Entry
->me
.BadgeImage
= NULL
;
1251 Entry
->BdsOption
= BdsOption
;
1252 Entry
->Enabled
= TRUE
;
1254 // create the submenu
1255 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
1256 SubScreen
->Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1257 SPrint(SubScreen
->Title
, 255, L
"No boot options for legacy target");
1258 SubScreen
->TitleImage
= Entry
->me
.Image
;
1261 SubEntry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1262 SubEntry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1263 SPrint(SubEntry
->me
.Title
, 255, L
"Boot %s", LegacyDescription
);
1264 SubEntry
->me
.Tag
= TAG_LEGACY_NON_MAC
;
1265 Entry
->BdsOption
= BdsOption
;
1266 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
1268 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
1269 Entry
->me
.SubScreen
= SubScreen
;
1270 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1272 } /* static LEGACY_ENTRY * AddLegacyEntryNonMac() */
1275 Scan for legacy BIOS targets on machines that implement EFI_LEGACY_BIOS_PROTOCOL.
1276 In testing, protocol has not been implemented on Macs but has been
1277 implemented on several Dell PCs.
1279 static VOID
ScanLegacyNonMac()
1282 EFI_LEGACY_BIOS_PROTOCOL
*LegacyBios
;
1283 EFI_GUID EfiLegacyBootProtocolGuid
= { 0xdb9a1e3d, 0x45cb, 0x4abb, { 0x85, 0x3b, 0xe5, 0x38, 0x7f, 0xdb, 0x2e, 0x2d }};
1284 EFI_GUID EfiGlobalVariableGuid
= { 0x8BE4DF61, 0x93CA, 0x11D2, { 0xAA, 0x0D, 0x00, 0xE0, 0x98, 0x03, 0x2B, 0x8C }};
1285 UINT16
*BootOrder
= NULL
;
1287 UINT16 BootOption
[10];
1288 UINTN BootOrderSize
= 0;
1290 BDS_COMMON_OPTION
*BdsOption
;
1291 LIST_ENTRY TempList
;
1292 BBS_BBS_DEVICE_PATH
* BbsDevicePath
= NULL
;
1294 InitializeListHead (&TempList
);
1295 ZeroMem (Buffer
, sizeof (Buffer
));
1297 // If LegacyBios protocol is not implemented on this platform, then
1298 //we do not support this type of legacy boot on this machine.
1299 Status
= gBS
->LocateProtocol (&EfiLegacyBootProtocolGuid
, NULL
, (VOID
**) &LegacyBios
);
1300 if (EFI_ERROR (Status
))
1303 // Grab the boot order
1304 BootOrder
= BdsLibGetVariableAndSize (L
"BootOrder", &EfiGlobalVariableGuid
, &BootOrderSize
);
1305 if (BootOrder
== NULL
) {
1310 while (Index
< BootOrderSize
/ sizeof (UINT16
))
1312 // Grab each boot option variable from the boot order, and convert
1313 // the variable into a BDS boot option
1314 UnicodeSPrint (BootOption
, sizeof (BootOption
), L
"Boot%04x", BootOrder
[Index
]);
1315 BdsOption
= BdsLibVariableToOption (&TempList
, BootOption
);
1317 //Print(L"Option description = '%s'\n", BdsOption->Description);
1318 BbsDevicePath
= (BBS_BBS_DEVICE_PATH
*)BdsOption
->DevicePath
;
1320 // Only add the entry if it is of a supported type (e.g. USB, HD)
1321 // See BdsHelper.c for currently supported types
1322 if(IsBbsDeviceTypeSupported(BbsDevicePath
->DeviceType
))
1324 // TODO: Find/build REFIT_VOLUME structure for volume and pass instead of NULL
1325 AddLegacyEntryNonMac(BdsOption
, BbsDevicePath
->DeviceType
);
1329 } /* static VOID ScanLegacyNonMac() */
1331 static VOID
ScanLegacyNonMac(){}
1332 #endif // __MAKEWITH_TIANO
1334 static VOID
ScanLegacyVolume(REFIT_VOLUME
*Volume
, UINTN VolumeIndex
) {
1336 BOOLEAN ShowVolume
, HideIfOthersFound
;
1339 HideIfOthersFound
= FALSE
;
1340 if (Volume
->IsAppleLegacy
) {
1342 HideIfOthersFound
= TRUE
;
1343 } else if (Volume
->HasBootCode
) {
1345 if (Volume
->BlockIO
== Volume
->WholeDiskBlockIO
&&
1346 Volume
->BlockIOOffset
== 0 &&
1347 Volume
->OSName
== NULL
)
1348 // this is a whole disk (MBR) entry; hide if we have entries for partitions
1349 HideIfOthersFound
= TRUE
;
1351 if (HideIfOthersFound
) {
1352 // check for other bootable entries on the same disk
1353 for (VolumeIndex2
= 0; VolumeIndex2
< VolumesCount
; VolumeIndex2
++) {
1354 if (VolumeIndex2
!= VolumeIndex
&& Volumes
[VolumeIndex2
]->HasBootCode
&&
1355 Volumes
[VolumeIndex2
]->WholeDiskBlockIO
== Volume
->WholeDiskBlockIO
)
1361 AddLegacyEntry(NULL
, Volume
);
1362 } // static VOID ScanLegacyVolume()
1364 // Scan attached optical discs for legacy (BIOS) boot code
1365 // and add anything found to the list....
1366 static VOID
ScanLegacyDisc(VOID
)
1369 REFIT_VOLUME
*Volume
;
1371 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1372 Volume
= Volumes
[VolumeIndex
];
1373 if (Volume
->DiskKind
== DISK_KIND_OPTICAL
)
1374 ScanLegacyVolume(Volume
, VolumeIndex
);
1376 } /* static VOID ScanLegacyDisc() */
1378 // Scan internal hard disks for legacy (BIOS) boot code
1379 // and add anything found to the list....
1380 static VOID
ScanLegacyInternal(VOID
)
1383 REFIT_VOLUME
*Volume
;
1385 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1386 Volume
= Volumes
[VolumeIndex
];
1387 if (Volume
->DiskKind
== DISK_KIND_INTERNAL
)
1388 ScanLegacyVolume(Volume
, VolumeIndex
);
1390 } /* static VOID ScanLegacyInternal() */
1392 // Scan external disks for legacy (BIOS) boot code
1393 // and add anything found to the list....
1394 static VOID
ScanLegacyExternal(VOID
)
1397 REFIT_VOLUME
*Volume
;
1399 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1400 Volume
= Volumes
[VolumeIndex
];
1401 if (Volume
->DiskKind
== DISK_KIND_EXTERNAL
)
1402 ScanLegacyVolume(Volume
, VolumeIndex
);
1404 } /* static VOID ScanLegacyExternal() */
1407 // pre-boot tool functions
1410 static VOID
StartTool(IN LOADER_ENTRY
*Entry
)
1412 BeginExternalScreen(Entry
->UseGraphicsMode
, Entry
->me
.Title
+ 6); // assumes "Start <title>" as assigned below
1413 StartEFIImage(Entry
->DevicePath
, Entry
->LoadOptions
, Basename(Entry
->LoaderPath
),
1414 Basename(Entry
->LoaderPath
), NULL
, TRUE
);
1415 FinishExternalScreen();
1416 } /* static VOID StartTool() */
1418 static LOADER_ENTRY
* AddToolEntry(IN CHAR16
*LoaderPath
, IN CHAR16
*LoaderTitle
, IN EG_IMAGE
*Image
,
1419 IN CHAR16 ShortcutLetter
, IN BOOLEAN UseGraphicsMode
)
1421 LOADER_ENTRY
*Entry
;
1422 CHAR16
*TitleStr
= NULL
;
1424 Entry
= AllocateZeroPool(sizeof(LOADER_ENTRY
));
1426 MergeStrings(&TitleStr
, L
"Start ", 0);
1427 MergeStrings(&TitleStr
, LoaderTitle
, 0);
1428 Entry
->me
.Title
= TitleStr
;
1429 Entry
->me
.Tag
= TAG_TOOL
;
1431 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1432 Entry
->me
.Image
= Image
;
1433 Entry
->LoaderPath
= StrDuplicate(LoaderPath
);
1434 Entry
->DevicePath
= FileDevicePath(SelfLoadedImage
->DeviceHandle
, Entry
->LoaderPath
);
1435 Entry
->UseGraphicsMode
= UseGraphicsMode
;
1437 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1439 } /* static LOADER_ENTRY * AddToolEntry() */
1442 // pre-boot driver functions
1445 static UINTN
ScanDriverDir(IN CHAR16
*Path
)
1448 REFIT_DIR_ITER DirIter
;
1450 EFI_FILE_INFO
*DirEntry
;
1451 CHAR16 FileName
[256];
1453 CleanUpPathNameSlashes(Path
);
1454 // look through contents of the directory
1455 DirIterOpen(SelfRootDir
, Path
, &DirIter
);
1456 while (DirIterNext(&DirIter
, 2, LOADER_MATCH_PATTERNS
, &DirEntry
)) {
1457 if (DirEntry
->FileName
[0] == '.')
1458 continue; // skip this
1460 SPrint(FileName
, 255, L
"%s\\%s", Path
, DirEntry
->FileName
);
1462 Status
= StartEFIImage(FileDevicePath(SelfLoadedImage
->DeviceHandle
, FileName
),
1463 L
"", DirEntry
->FileName
, DirEntry
->FileName
, NULL
, FALSE
);
1465 Status
= DirIterClose(&DirIter
);
1466 if (Status
!= EFI_NOT_FOUND
) {
1467 SPrint(FileName
, 255, L
"while scanning the %s directory", Path
);
1468 CheckError(Status
, FileName
);
1473 #ifdef __MAKEWITH_GNUEFI
1474 static EFI_STATUS
ConnectAllDriversToAllControllers(VOID
)
1477 UINTN AllHandleCount
;
1478 EFI_HANDLE
*AllHandleBuffer
;
1481 EFI_HANDLE
*HandleBuffer
;
1487 Status
= LibLocateHandle(AllHandles
,
1492 if (EFI_ERROR(Status
))
1495 for (Index
= 0; Index
< AllHandleCount
; Index
++) {
1497 // Scan the handle database
1499 Status
= LibScanHandleDatabase(NULL
,
1501 AllHandleBuffer
[Index
],
1506 if (EFI_ERROR (Status
))
1510 if (HandleType
[Index
] & EFI_HANDLE_TYPE_DRIVER_BINDING_HANDLE
)
1512 if (HandleType
[Index
] & EFI_HANDLE_TYPE_IMAGE_HANDLE
)
1517 for (HandleIndex
= 0; HandleIndex
< HandleCount
; HandleIndex
++) {
1518 if (HandleType
[HandleIndex
] & EFI_HANDLE_TYPE_PARENT_HANDLE
)
1523 if (HandleType
[Index
] & EFI_HANDLE_TYPE_DEVICE_HANDLE
) {
1524 Status
= refit_call4_wrapper(BS
->ConnectController
,
1525 AllHandleBuffer
[Index
],
1533 FreePool (HandleBuffer
);
1534 FreePool (HandleType
);
1538 FreePool (AllHandleBuffer
);
1540 } /* EFI_STATUS ConnectAllDriversToAllControllers() */
1542 static EFI_STATUS
ConnectAllDriversToAllControllers(VOID
) {
1543 BdsLibConnectAllDriversToAllControllers();
1548 // Load all EFI drivers from rEFInd's "drivers" subdirectory and from the
1549 // directories specified by the user in the "scan_driver_dirs" configuration
1551 static VOID
LoadDrivers(VOID
)
1553 CHAR16
*Directory
, *SelfDirectory
;
1554 UINTN i
= 0, Length
, NumFound
= 0;
1556 // load drivers from the subdirectories of rEFInd's home directory specified
1557 // in the DRIVER_DIRS constant.
1558 while ((Directory
= FindCommaDelimited(DRIVER_DIRS
, i
++)) != NULL
) {
1559 SelfDirectory
= StrDuplicate(SelfDirPath
);
1560 CleanUpPathNameSlashes(SelfDirectory
);
1561 MergeStrings(&SelfDirectory
, Directory
, L
'\\');
1562 NumFound
+= ScanDriverDir(SelfDirectory
);
1563 FreePool(Directory
);
1564 FreePool(SelfDirectory
);
1567 // Scan additional user-specified driver directories....
1569 while ((Directory
= FindCommaDelimited(GlobalConfig
.DriverDirs
, i
++)) != NULL
) {
1570 CleanUpPathNameSlashes(Directory
);
1571 Length
= StrLen(Directory
);
1573 NumFound
+= ScanDriverDir(Directory
);
1574 FreePool(Directory
);
1577 // connect all devices
1579 ConnectAllDriversToAllControllers();
1580 } /* static VOID LoadDrivers() */
1583 static VOID
ScanForBootloaders(VOID
) {
1588 // scan for loaders and tools, add them to the menu
1589 for (i
= 0; i
< NUM_SCAN_OPTIONS
; i
++) {
1590 switch(GlobalConfig
.ScanFor
[i
]) {
1595 ScanLegacyInternal();
1598 ScanLegacyExternal();
1601 ScanUserConfigured();
1618 // assign shortcut keys
1619 for (i
= 0; i
< MainMenu
.EntryCount
&& MainMenu
.Entries
[i
]->Row
== 0 && i
< 9; i
++)
1620 MainMenu
.Entries
[i
]->ShortcutDigit
= (CHAR16
)('1' + i
);
1622 // wait for user ACK when there were errors
1623 FinishTextScreen(FALSE
);
1624 } // static VOID ScanForBootloaders()
1626 // Add the second-row tags containing built-in and external tools (EFI shell,
1628 static VOID
ScanForTools(VOID
) {
1629 CHAR16
*FileName
= NULL
;
1630 REFIT_MENU_ENTRY
*TempMenuEntry
;
1633 for (i
= 0; i
< NUM_TOOLS
; i
++) {
1634 switch(GlobalConfig
.ShowTools
[i
]) {
1636 TempMenuEntry
= CopyMenuEntry(&MenuEntryShutdown
);
1637 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_SHUTDOWN
);
1638 AddMenuEntry(&MainMenu
, TempMenuEntry
);
1641 TempMenuEntry
= CopyMenuEntry(&MenuEntryReset
);
1642 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_RESET
);
1643 AddMenuEntry(&MainMenu
, TempMenuEntry
);
1646 TempMenuEntry
= CopyMenuEntry(&MenuEntryAbout
);
1647 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT
);
1648 AddMenuEntry(&MainMenu
, TempMenuEntry
);
1651 TempMenuEntry
= CopyMenuEntry(&MenuEntryExit
);
1652 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_EXIT
);
1653 AddMenuEntry(&MainMenu
, TempMenuEntry
);
1657 while ((FileName
= FindCommaDelimited(SHELL_NAMES
, j
++)) != NULL
) {
1658 if (FileExists(SelfRootDir
, FileName
)) {
1659 AddToolEntry(FileName
, L
"EFI Shell", BuiltinIcon(BUILTIN_ICON_TOOL_SHELL
), 'S', FALSE
);
1664 MergeStrings(&FileName
, L
"\\efi\\tools\\gptsync.efi", 0);
1665 if (FileExists(SelfRootDir
, FileName
)) {
1666 AddToolEntry(FileName
, L
"Make Hybrid MBR", BuiltinIcon(BUILTIN_ICON_TOOL_PART
), 'P', FALSE
);
1670 if (FileName
!= NULL
) {
1675 } // static VOID ScanForTools
1677 // Rescan for boot loaders
1678 VOID
RescanAll(VOID
) {
1685 egDisplayMessage(L
"Scanning for new boot loaders; please wait....", &BGColor
);
1686 FreeList((VOID
***) &(MainMenu
.Entries
), &MainMenu
.EntryCount
);
1687 MainMenu
.Entries
= NULL
;
1688 MainMenu
.EntryCount
= 0;
1690 ConnectAllDriversToAllControllers();
1691 ScanForBootloaders();
1694 } // VOID RescanAll()
1696 #ifndef __MAKEWITH_GNUEFI
1698 // Minimal initialization function
1699 static VOID
InitializeLib(IN EFI_HANDLE ImageHandle
, IN EFI_SYSTEM_TABLE
*SystemTable
) {
1701 // gImageHandle = ImageHandle;
1702 gBS
= SystemTable
->BootServices
;
1703 // gRS = SystemTable->RuntimeServices;
1704 gRT
= SystemTable
->RuntimeServices
; // Some BDS functions need gRT to be set
1705 EfiGetSystemConfigurationTable (&gEfiDxeServicesTableGuid
, (VOID
**) &gDS
);
1707 InitializeConsoleSim();
1717 efi_main (IN EFI_HANDLE ImageHandle
, IN EFI_SYSTEM_TABLE
*SystemTable
)
1720 BOOLEAN MainLoopRunning
= TRUE
;
1721 REFIT_MENU_ENTRY
*ChosenEntry
;
1726 InitializeLib(ImageHandle
, SystemTable
);
1728 Status
= InitRefitLib(ImageHandle
);
1729 if (EFI_ERROR(Status
))
1732 // read configuration
1733 CopyMem(GlobalConfig
.ScanFor
, "ieo ", NUM_SCAN_OPTIONS
);
1735 MainMenu
.TimeoutSeconds
= GlobalConfig
.Timeout
;
1737 // disable EFI watchdog timer
1738 refit_call4_wrapper(BS
->SetWatchdogTimer
, 0x0000, 0x0000, 0x0000, NULL
);
1740 // further bootstrap (now with config available)
1743 ScanForBootloaders();
1746 Selection
= StrDuplicate(GlobalConfig
.DefaultSelection
);
1747 while (MainLoopRunning
) {
1748 MenuExit
= RunMainMenu(&MainMenu
, Selection
, &ChosenEntry
);
1750 // The Escape key triggers a re-scan operation....
1751 if (MenuExit
== MENU_EXIT_ESCAPE
) {
1756 switch (ChosenEntry
->Tag
) {
1758 case TAG_REBOOT
: // Reboot
1760 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);
1761 MainLoopRunning
= FALSE
; // just in case we get this far
1764 case TAG_SHUTDOWN
: // Shut Down
1766 refit_call4_wrapper(RT
->ResetSystem
, EfiResetShutdown
, EFI_SUCCESS
, 0, NULL
);
1767 MainLoopRunning
= FALSE
; // just in case we get this far
1770 case TAG_ABOUT
: // About rEFInd
1774 case TAG_LOADER
: // Boot OS via .EFI loader
1775 StartLoader((LOADER_ENTRY
*)ChosenEntry
);
1778 case TAG_LEGACY
: // Boot legacy OS
1779 StartLegacy((LEGACY_ENTRY
*)ChosenEntry
);
1782 #ifdef __MAKEWITH_TIANO
1783 case TAG_LEGACY_NON_MAC
: // Boot a legacy OS on a non-Mac
1784 StartLegacyNonMac((LEGACY_ENTRY
*)ChosenEntry
);
1786 #endif // __MAKEWITH_TIANO
1788 case TAG_TOOL
: // Start a EFI tool
1789 StartTool((LOADER_ENTRY
*)ChosenEntry
);
1792 case TAG_EXIT
: // Terminate rEFInd
1793 BeginTextScreen(L
" ");
1798 FreePool(Selection
);
1799 Selection
= StrDuplicate(ChosenEntry
->Title
);
1802 // If we end up here, things have gone wrong. Try to reboot, and if that
1803 // fails, go into an endless loop.
1804 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);