3 * Main code for the boot menu
5 * Copyright (c) 2006-2010 Christoph Pfisterer
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions are
12 * * Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
15 * * Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the
20 * * Neither the name of Christoph Pfisterer nor the names of the
21 * contributors may be used to endorse or promote products derived
22 * from this software without specific prior written permission.
24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
26 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
27 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
28 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
29 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
30 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
31 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
32 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
33 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
34 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37 * Modifications copyright (c) 2012 Roderick W. Smith
39 * Modifications distributed under the terms of the GNU General Public
40 * License (GPL) version 3 (GPLv3), a copy of which must be distributed
41 * with this source code or binaries made from it.
52 #include "../include/Handle.h"
53 #include "../include/refit_call_wrapper.h"
54 #include "driver_support.h"
55 #include "../include/syslinux_mbr.h"
57 #ifdef __MAKEWITH_TIANO
58 #include "../EfiLib/BdsHelper.h"
59 #endif // __MAKEWITH_TIANO
64 #define MACOSX_LOADER_PATH L"System\\Library\\CoreServices\\boot.efi"
66 #define SHELL_NAMES L"\\EFI\\tools\\shell.efi,\\EFI\\tools\\shellx64.efi,\\shellx64.efi"
67 #define DRIVER_DIRS L"drivers,drivers_x64"
69 #define SHELL_NAMES L"\\EFI\\tools\\shell.efi,\\EFI\\tools\shellia32.efi,\\shellia32.efi"
70 #define DRIVER_DIRS L"drivers,drivers_ia32"
72 #define SHELL_NAMES L"\\EFI\\tools\\shell.efi"
73 #define DRIVER_DIRS L"drivers"
76 #define MOK_NAMES L"\\EFI\\tools\\MokManager.efi,\\EFI\\redhat\\MokManager.efi,\\EFI\\ubuntu\\MokManager.efi,\\EFI\\suse\\MokManager"
78 // Filename patterns that identify EFI boot loaders. Note that a single case (either L"*.efi" or
79 // L"*.EFI") is fine for most systems; but Gigabyte's buggy Hybrid EFI does a case-sensitive
80 // comparison when it should do a case-insensitive comparison, so I'm doubling this up. It does
81 // no harm on other computers, AFAIK. In theory, every case variation should be done for
82 // completeness, but that's ridiculous....
83 #define LOADER_MATCH_PATTERNS L"*.efi,*.EFI"
85 // Patterns that identify Linux kernels. Added to the loader match pattern when the
86 // scan_all_linux_kernels option is set in the configuration file. Causes kernels WITHOUT
87 // a ".efi" extension to be found when scanning for boot loaders.
88 #define LINUX_MATCH_PATTERNS L"vmlinuz*,bzImage*"
90 // Default hint text for program-launch submenus
91 #define SUBSCREEN_HINT1 L"Use arrow keys to move cursor; Enter to boot;"
92 #define SUBSCREEN_HINT2 L"Insert or F2 to edit options; Esc to return to main menu"
93 #define SUBSCREEN_HINT2_NO_EDITOR L"Esc to return to main menu"
95 static REFIT_MENU_ENTRY MenuEntryAbout
= { L
"About rEFInd", TAG_ABOUT
, 1, 0, 'A', NULL
, NULL
, NULL
};
96 static REFIT_MENU_ENTRY MenuEntryReset
= { L
"Reboot Computer", TAG_REBOOT
, 1, 0, 'R', NULL
, NULL
, NULL
};
97 static REFIT_MENU_ENTRY MenuEntryShutdown
= { L
"Shut Down Computer", TAG_SHUTDOWN
, 1, 0, 'U', NULL
, NULL
, NULL
};
98 static REFIT_MENU_ENTRY MenuEntryReturn
= { L
"Return to Main Menu", TAG_RETURN
, 0, 0, 0, NULL
, NULL
, NULL
};
99 static REFIT_MENU_ENTRY MenuEntryExit
= { L
"Exit rEFInd", TAG_EXIT
, 1, 0, 0, NULL
, NULL
, NULL
};
101 static REFIT_MENU_SCREEN MainMenu
= { L
"Main Menu", NULL
, 0, NULL
, 0, NULL
, 0, L
"Automatic boot",
102 L
"Use arrow keys to move cursor; Enter to boot;",
103 L
"Insert or F2 for more options; Esc to refresh" };
104 static REFIT_MENU_SCREEN AboutMenu
= { L
"About", NULL
, 0, NULL
, 0, NULL
, 0, NULL
, L
"Press Enter to return to main menu", L
"" };
106 REFIT_CONFIG GlobalConfig
= { FALSE
, FALSE
, 0, 0, 0, 20, 0, 0, GRAPHICS_FOR_OSX
, LEGACY_TYPE_MAC
, 0,
107 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
108 {TAG_SHELL
, TAG_APPLE_RECOVERY
, TAG_MOK_TOOL
, TAG_ABOUT
, TAG_SHUTDOWN
, TAG_REBOOT
, 0, 0, 0, 0, 0 }};
110 // Structure used to hold boot loader filenames and time stamps in
111 // a linked list; used to sort entries within a directory.
115 struct LOADER_LIST
*NextEntry
;
122 static VOID
AboutrEFInd(VOID
)
124 CHAR16
*TempStr
; // Note: Don't deallocate; moved to menu structure
126 if (AboutMenu
.EntryCount
== 0) {
127 AboutMenu
.TitleImage
= BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT
);
128 AddMenuInfoLine(&AboutMenu
, L
"rEFInd Version 0.6.0.2");
129 AddMenuInfoLine(&AboutMenu
, L
"");
130 AddMenuInfoLine(&AboutMenu
, L
"Copyright (c) 2006-2010 Christoph Pfisterer");
131 AddMenuInfoLine(&AboutMenu
, L
"Copyright (c) 2012 Roderick W. Smith");
132 AddMenuInfoLine(&AboutMenu
, L
"Portions Copyright (c) Intel Corporation and others");
133 AddMenuInfoLine(&AboutMenu
, L
"Distributed under the terms of the GNU GPLv3 license");
134 AddMenuInfoLine(&AboutMenu
, L
"");
135 AddMenuInfoLine(&AboutMenu
, L
"Running on:");
136 TempStr
= AllocateZeroPool(256 * sizeof(CHAR16
));
137 SPrint(TempStr
, 255, L
" EFI Revision %d.%02d", ST
->Hdr
.Revision
>> 16, ST
->Hdr
.Revision
& ((1 << 16) - 1));
138 AddMenuInfoLine(&AboutMenu
, TempStr
);
140 AddMenuInfoLine(&AboutMenu
, L
" Platform: x86 (32 bit)");
141 #elif defined(EFIX64)
142 TempStr
= AllocateZeroPool(256 * sizeof(CHAR16
));
143 SPrint(TempStr
, 255, L
" Platform: x86_64 (64 bit); Secure Boot %s", secure_mode() ? L
"active" : L
"inactive");
144 AddMenuInfoLine(&AboutMenu
, TempStr
);
146 AddMenuInfoLine(&AboutMenu
, L
" Platform: unknown");
148 TempStr
= AllocateZeroPool(256 * sizeof(CHAR16
));
149 SPrint(TempStr
, 255, L
" Firmware: %s %d.%02d",
150 ST
->FirmwareVendor
, ST
->FirmwareRevision
>> 16, ST
->FirmwareRevision
& ((1 << 16) - 1));
151 AddMenuInfoLine(&AboutMenu
, TempStr
);
152 TempStr
= AllocateZeroPool(256 * sizeof(CHAR16
));
153 SPrint(TempStr
, 255, L
" Screen Output: %s", egScreenDescription());
154 AddMenuInfoLine(&AboutMenu
, TempStr
);
155 AddMenuInfoLine(&AboutMenu
, L
"");
156 #if defined(__MAKEWITH_GNUEFI)
157 AddMenuInfoLine(&AboutMenu
, L
"Built with GNU-EFI");
159 AddMenuInfoLine(&AboutMenu
, L
"Built with TianoCore EDK2");
161 AddMenuInfoLine(&AboutMenu
, L
"");
162 AddMenuInfoLine(&AboutMenu
, L
"For more information, see the rEFInd Web site:");
163 AddMenuInfoLine(&AboutMenu
, L
"http://www.rodsbooks.com/refind/");
164 AddMenuEntry(&AboutMenu
, &MenuEntryReturn
);
167 RunMenu(&AboutMenu
, NULL
);
168 } /* VOID AboutrEFInd() */
170 // Launch an EFI binary.
171 static EFI_STATUS
StartEFIImageList(IN EFI_DEVICE_PATH
**DevicePaths
,
172 IN CHAR16
*LoadOptions
, IN CHAR16
*LoadOptionsPrefix
,
173 IN CHAR16
*ImageTitle
, IN CHAR8 OSType
,
174 OUT UINTN
*ErrorInStep
,
177 EFI_STATUS Status
, ReturnStatus
;
178 EFI_HANDLE ChildImageHandle
;
179 EFI_LOADED_IMAGE
*ChildLoadedImage
= NULL
;
181 VOID
*ImageData
= NULL
;
183 REFIT_VOLUME
*DeviceVolume
= NULL
;
184 UINTN DevicePathIndex
;
185 CHAR16 ErrorInfo
[256];
186 CHAR16
*FullLoadOptions
= NULL
;
187 CHAR16
*loader
= NULL
;
188 BOOLEAN UseMok
= FALSE
;
190 if (ErrorInStep
!= NULL
)
194 if (LoadOptions
!= NULL
) {
195 if (LoadOptionsPrefix
!= NULL
) {
196 // MergeStrings(&FullLoadOptions, LoadOptionsPrefix, 0);
197 MergeStrings(&FullLoadOptions
, LoadOptions
, L
' ');
199 MergeStrings(&FullLoadOptions
, L
" ", 0);
200 // NOTE: That last space is also added by the EFI shell and seems to be significant
201 // when passing options to Apple's boot.efi...
204 MergeStrings(&FullLoadOptions
, LoadOptions
, 0);
206 } else { // LoadOptions == NULL
207 // NOTE: We provide a non-null string when no options are specified for safety;
208 // some systems (at least DUET) can hang when launching some programs (such as
209 // an EFI shell) without this.
210 FullLoadOptions
= StrDuplicate(L
" ");
213 Print(L
"Starting %s\nUsing load options '%s'\n", ImageTitle
, FullLoadOptions
);
215 // load the image into memory (and execute it, in the case of a shim/MOK image).
216 ReturnStatus
= Status
= EFI_NOT_FOUND
; // in case the list is empty
217 for (DevicePathIndex
= 0; DevicePaths
[DevicePathIndex
] != NULL
; DevicePathIndex
++) {
218 // NOTE: Below commented-out line could be more efficient if the ReadFile() and
219 // FindVolumeAndFilename() calls were moved earlier, but it doesn't work on my
220 // 32-bit Mac Mini or my 64-bit Intel box when launching a Linux kernel; the
221 // kernel returns a "Failed to handle fs_proto" error message.
222 // TODO: Track down the cause of this error and fix it, if possible.
223 // ReturnStatus = Status = refit_call6_wrapper(BS->LoadImage, FALSE, SelfImageHandle, DevicePaths[DevicePathIndex],
224 // ImageData, ImageSize, &ChildImageHandle);
225 ReturnStatus
= Status
= refit_call6_wrapper(BS
->LoadImage
, FALSE
, SelfImageHandle
, DevicePaths
[DevicePathIndex
],
226 NULL
, 0, &ChildImageHandle
);
227 if ((Status
== EFI_ACCESS_DENIED
) && (ShimLoaded())) {
228 FindVolumeAndFilename(DevicePaths
[DevicePathIndex
], &DeviceVolume
, &loader
);
229 if (DeviceVolume
!= NULL
) {
230 Status
= ReadFile(DeviceVolume
->RootDir
, loader
, &File
, &ImageSize
);
231 ImageData
= File
.Buffer
;
233 Status
= EFI_NOT_FOUND
;
234 Print(L
"Error: device volume not found!\n");
236 if (Status
!= EFI_NOT_FOUND
) {
237 ReturnStatus
= Status
= start_image(SelfImageHandle
, loader
, ImageData
, ImageSize
, FullLoadOptions
,
238 DeviceVolume
, FileDevicePath(DeviceVolume
->DeviceHandle
, loader
));
239 // ReturnStatus = Status = start_image(SelfImageHandle, loader, ImageData, ImageSize, FullLoadOptions,
240 // DeviceVolume, DevicePaths[DevicePathIndex]);
242 if (ReturnStatus
== EFI_SUCCESS
) {
245 } // if (UEFI SB failed; use shim)
246 if (ReturnStatus
!= EFI_NOT_FOUND
) {
250 SPrint(ErrorInfo
, 255, L
"while loading %s", ImageTitle
);
251 if (CheckError(Status
, ErrorInfo
)) {
252 if (ErrorInStep
!= NULL
)
258 ReturnStatus
= Status
= refit_call3_wrapper(BS
->HandleProtocol
, ChildImageHandle
, &LoadedImageProtocol
,
259 (VOID
**) &ChildLoadedImage
);
260 if (CheckError(Status
, L
"while getting a LoadedImageProtocol handle")) {
261 if (ErrorInStep
!= NULL
)
265 ChildLoadedImage
->LoadOptions
= (VOID
*)FullLoadOptions
;
266 ChildLoadedImage
->LoadOptionsSize
= ((UINT32
)StrLen(FullLoadOptions
) + 1) * sizeof(CHAR16
);
267 // turn control over to the image
268 // TODO: (optionally) re-enable the EFI watchdog timer!
270 // close open file handles
272 ReturnStatus
= Status
= refit_call3_wrapper(BS
->StartImage
, ChildImageHandle
, NULL
, NULL
);
273 // control returns here when the child image calls Exit()
274 SPrint(ErrorInfo
, 255, L
"returned from %s", ImageTitle
);
275 if (CheckError(Status
, ErrorInfo
)) {
276 if (ErrorInStep
!= NULL
)
280 // re-open file handles
285 // unload the image, we don't care if it works or not...
287 Status
= refit_call1_wrapper(BS
->UnloadImage
, ChildImageHandle
);
290 MyFreePool(FullLoadOptions
);
292 } /* static EFI_STATUS StartEFIImageList() */
294 static EFI_STATUS
StartEFIImage(IN EFI_DEVICE_PATH
*DevicePath
,
295 IN CHAR16
*LoadOptions
, IN CHAR16
*LoadOptionsPrefix
,
296 IN CHAR16
*ImageTitle
, IN CHAR8 OSType
,
297 OUT UINTN
*ErrorInStep
,
300 EFI_DEVICE_PATH
*DevicePaths
[2];
302 DevicePaths
[0] = DevicePath
;
303 DevicePaths
[1] = NULL
;
304 return StartEFIImageList(DevicePaths
, LoadOptions
, LoadOptionsPrefix
, ImageTitle
, OSType
, ErrorInStep
, Verbose
);
305 } /* static EFI_STATUS StartEFIImage() */
308 // EFI OS loader functions
311 static VOID
StartLoader(IN LOADER_ENTRY
*Entry
)
313 UINTN ErrorInStep
= 0;
315 BeginExternalScreen(Entry
->UseGraphicsMode
, L
"Booting OS");
316 StartEFIImage(Entry
->DevicePath
, Entry
->LoadOptions
, Basename(Entry
->LoaderPath
),
317 Basename(Entry
->LoaderPath
), Entry
->OSType
, &ErrorInStep
, !Entry
->UseGraphicsMode
);
318 FinishExternalScreen();
321 // Locate an initrd or initramfs file that matches the kernel specified by LoaderPath.
322 // The matching file has a name that begins with "init" and includes the same version
323 // number string as is found in LoaderPath -- but not a longer version number string.
324 // For instance, if LoaderPath is \EFI\kernels\bzImage-3.3.0.efi, and if \EFI\kernels
325 // has a file called initramfs-3.3.0.img, this function will return the string
326 // '\EFI\kernels\initramfs-3.3.0.img'. If the directory ALSO contains the file
327 // initramfs-3.3.0-rc7.img or initramfs-13.3.0.img, those files will NOT match;
328 // however, initmine-3.3.0.img might match. (FindInitrd() returns the first match it
329 // finds). Thus, care should be taken to avoid placing duplicate matching files in
330 // the kernel's directory.
331 // If no matching init file can be found, returns NULL.
332 static CHAR16
* FindInitrd(IN CHAR16
*LoaderPath
, IN REFIT_VOLUME
*Volume
) {
333 CHAR16
*InitrdName
= NULL
, *FileName
, *KernelVersion
, *InitrdVersion
, *Path
;
334 REFIT_DIR_ITER DirIter
;
335 EFI_FILE_INFO
*DirEntry
;
337 FileName
= Basename(LoaderPath
);
338 KernelVersion
= FindNumbers(FileName
);
339 Path
= FindPath(LoaderPath
);
341 // Add trailing backslash for root directory; necessary on some systems, but must
342 // NOT be added to all directories, since on other systems, a trailing backslash on
343 // anything but the root directory causes them to flake out!
344 if (StrLen(Path
) == 0) {
345 MergeStrings(&Path
, L
"\\", 0);
347 DirIterOpen(Volume
->RootDir
, Path
, &DirIter
);
348 // Now add a trailing backslash if it was NOT added earlier, for consistency in
349 // building the InitrdName later....
350 if ((StrLen(Path
) > 0) && (Path
[StrLen(Path
) - 1] != L
'\\'))
351 MergeStrings(&Path
, L
"\\", 0);
352 while ((DirIterNext(&DirIter
, 2, L
"init*", &DirEntry
)) && (InitrdName
== NULL
)) {
353 InitrdVersion
= FindNumbers(DirEntry
->FileName
);
354 if (KernelVersion
!= NULL
) {
355 if (StriCmp(InitrdVersion
, KernelVersion
) == 0) {
356 MergeStrings(&InitrdName
, Path
, 0);
357 MergeStrings(&InitrdName
, DirEntry
->FileName
, 0);
360 if (InitrdVersion
== NULL
) {
361 MergeStrings(&InitrdName
, Path
, 0);
362 MergeStrings(&InitrdName
, DirEntry
->FileName
, 0);
365 MyFreePool(InitrdVersion
);
367 DirIterClose(&DirIter
);
369 // Note: Don't FreePool(FileName), since Basename returns a pointer WITHIN the string it's passed.
370 MyFreePool(KernelVersion
);
373 } // static CHAR16 * FindInitrd()
375 LOADER_ENTRY
* AddPreparedLoaderEntry(LOADER_ENTRY
*Entry
) {
376 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
379 } // LOADER_ENTRY * AddPreparedLoaderEntry()
381 // Creates a copy of a menu screen.
382 // Returns a pointer to the copy of the menu screen.
383 static REFIT_MENU_SCREEN
* CopyMenuScreen(REFIT_MENU_SCREEN
*Entry
) {
384 REFIT_MENU_SCREEN
*NewEntry
;
387 NewEntry
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
388 if ((Entry
!= NULL
) && (NewEntry
!= NULL
)) {
389 CopyMem(NewEntry
, Entry
, sizeof(REFIT_MENU_SCREEN
));
390 NewEntry
->Title
= (Entry
->Title
) ? StrDuplicate(Entry
->Title
) : NULL
;
391 NewEntry
->TimeoutText
= (Entry
->TimeoutText
) ? StrDuplicate(Entry
->TimeoutText
) : NULL
;
392 if (Entry
->TitleImage
!= NULL
) {
393 NewEntry
->TitleImage
= AllocatePool(sizeof(EG_IMAGE
));
394 if (NewEntry
->TitleImage
!= NULL
)
395 CopyMem(NewEntry
->TitleImage
, Entry
->TitleImage
, sizeof(EG_IMAGE
));
397 NewEntry
->InfoLines
= (CHAR16
**) AllocateZeroPool(Entry
->InfoLineCount
* (sizeof(CHAR16
*)));
398 for (i
= 0; i
< Entry
->InfoLineCount
&& NewEntry
->InfoLines
; i
++) {
399 NewEntry
->InfoLines
[i
] = (Entry
->InfoLines
[i
]) ? StrDuplicate(Entry
->InfoLines
[i
]) : NULL
;
401 NewEntry
->Entries
= (REFIT_MENU_ENTRY
**) AllocateZeroPool(Entry
->EntryCount
* (sizeof (REFIT_MENU_ENTRY
*)));
402 for (i
= 0; i
< Entry
->EntryCount
&& NewEntry
->Entries
; i
++) {
403 AddMenuEntry(NewEntry
, Entry
->Entries
[i
]);
405 NewEntry
->Hint1
= (Entry
->Hint1
) ? StrDuplicate(Entry
->Hint1
) : NULL
;
406 NewEntry
->Hint2
= (Entry
->Hint2
) ? StrDuplicate(Entry
->Hint2
) : NULL
;
409 } // static REFIT_MENU_SCREEN* CopyMenuScreen()
411 // Creates a copy of a menu entry. Intended to enable moving a stack-based
412 // menu entry (such as the ones for the "reboot" and "exit" functions) to
413 // to the heap. This enables easier deletion of the whole set of menu
414 // entries when re-scanning.
415 // Returns a pointer to the copy of the menu entry.
416 static REFIT_MENU_ENTRY
* CopyMenuEntry(REFIT_MENU_ENTRY
*Entry
) {
417 REFIT_MENU_ENTRY
*NewEntry
;
419 NewEntry
= AllocateZeroPool(sizeof(REFIT_MENU_ENTRY
));
420 if ((Entry
!= NULL
) && (NewEntry
!= NULL
)) {
421 CopyMem(NewEntry
, Entry
, sizeof(REFIT_MENU_ENTRY
));
422 NewEntry
->Title
= (Entry
->Title
) ? StrDuplicate(Entry
->Title
) : NULL
;
423 if (Entry
->BadgeImage
!= NULL
) {
424 NewEntry
->BadgeImage
= AllocatePool(sizeof(EG_IMAGE
));
425 if (NewEntry
->BadgeImage
!= NULL
)
426 CopyMem(NewEntry
->BadgeImage
, Entry
->BadgeImage
, sizeof(EG_IMAGE
));
428 if (Entry
->Image
!= NULL
) {
429 NewEntry
->Image
= AllocatePool(sizeof(EG_IMAGE
));
430 if (NewEntry
->Image
!= NULL
)
431 CopyMem(NewEntry
->Image
, Entry
->Image
, sizeof(EG_IMAGE
));
433 if (Entry
->SubScreen
!= NULL
) {
434 NewEntry
->SubScreen
= CopyMenuScreen(Entry
->SubScreen
);
438 } // REFIT_MENU_ENTRY* CopyMenuEntry()
440 // Creates a new LOADER_ENTRY data structure and populates it with
441 // default values from the specified Entry, or NULL values if Entry
442 // is unspecified (NULL).
443 // Returns a pointer to the new data structure, or NULL if it
444 // couldn't be allocated
445 LOADER_ENTRY
*InitializeLoaderEntry(IN LOADER_ENTRY
*Entry
) {
446 LOADER_ENTRY
*NewEntry
= NULL
;
448 NewEntry
= AllocateZeroPool(sizeof(LOADER_ENTRY
));
449 if (NewEntry
!= NULL
) {
450 NewEntry
->me
.Title
= NULL
;
451 NewEntry
->me
.Tag
= TAG_LOADER
;
452 NewEntry
->Enabled
= TRUE
;
453 NewEntry
->UseGraphicsMode
= FALSE
;
454 NewEntry
->OSType
= 0;
456 NewEntry
->LoaderPath
= (Entry
->LoaderPath
) ? StrDuplicate(Entry
->LoaderPath
) : NULL
;
457 NewEntry
->VolName
= (Entry
->VolName
) ? StrDuplicate(Entry
->VolName
) : NULL
;
458 NewEntry
->DevicePath
= Entry
->DevicePath
;
459 NewEntry
->UseGraphicsMode
= Entry
->UseGraphicsMode
;
460 NewEntry
->LoadOptions
= (Entry
->LoadOptions
) ? StrDuplicate(Entry
->LoadOptions
) : NULL
;
461 NewEntry
->InitrdPath
= (Entry
->InitrdPath
) ? StrDuplicate(Entry
->InitrdPath
) : NULL
;
465 } // LOADER_ENTRY *InitializeLoaderEntry()
467 // Adds InitrdPath to Options, but only if Options doesn't already include an
468 // initrd= line. Done to enable overriding the default initrd selection in a
469 // refind_linux.conf file's options list.
470 // Returns a pointer to a new string. The calling function is responsible for
471 // freeing its memory.
472 static CHAR16
*AddInitrdToOptions(CHAR16
*Options
, CHAR16
*InitrdPath
) {
473 CHAR16
*NewOptions
= NULL
;
476 NewOptions
= StrDuplicate(Options
);
477 if ((InitrdPath
!= NULL
) && !StriSubCmp(L
"initrd=", Options
)) {
478 MergeStrings(&NewOptions
, L
"initrd=", L
' ');
479 MergeStrings(&NewOptions
, InitrdPath
, 0);
482 } // CHAR16 *AddInitrdToOptions()
484 // Prepare a REFIT_MENU_SCREEN data structure for a subscreen entry. This sets up
485 // the default entry that launches the boot loader using the same options as the
486 // main Entry does. Subsequent options can be added by the calling function.
487 // If a subscreen already exists in the Entry that's passed to this function,
488 // it's left unchanged and a pointer to it is returned.
489 // Returns a pointer to the new subscreen data structure, or NULL if there
490 // were problems allocating memory.
491 REFIT_MENU_SCREEN
*InitializeSubScreen(IN LOADER_ENTRY
*Entry
) {
492 CHAR16
*FileName
, *MainOptions
= NULL
;
493 REFIT_MENU_SCREEN
*SubScreen
= NULL
;
494 LOADER_ENTRY
*SubEntry
;
496 FileName
= Basename(Entry
->LoaderPath
);
497 if (Entry
->me
.SubScreen
== NULL
) { // No subscreen yet; initialize default entry....
498 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
499 if (SubScreen
!= NULL
) {
500 SubScreen
->Title
= AllocateZeroPool(sizeof(CHAR16
) * 256);
501 SPrint(SubScreen
->Title
, 255, L
"Boot Options for %s on %s",
502 (Entry
->Title
!= NULL
) ? Entry
->Title
: FileName
, Entry
->VolName
);
503 SubScreen
->TitleImage
= Entry
->me
.Image
;
505 SubEntry
= InitializeLoaderEntry(Entry
);
506 if (SubEntry
!= NULL
) {
507 SubEntry
->me
.Title
= StrDuplicate(L
"Boot using default options");
508 MainOptions
= SubEntry
->LoadOptions
;
509 SubEntry
->LoadOptions
= AddInitrdToOptions(MainOptions
, SubEntry
->InitrdPath
);
510 MyFreePool(MainOptions
);
511 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
512 } // if (SubEntry != NULL)
513 SubScreen
->Hint1
= StrDuplicate(SUBSCREEN_HINT1
);
514 if (GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_EDITOR
) {
515 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2_NO_EDITOR
);
517 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2
);
519 } // if (SubScreen != NULL)
520 } else { // existing subscreen; less initialization, and just add new entry later....
521 SubScreen
= Entry
->me
.SubScreen
;
524 } // REFIT_MENU_SCREEN *InitializeSubScreen()
526 VOID
GenerateSubScreen(LOADER_ENTRY
*Entry
, IN REFIT_VOLUME
*Volume
) {
527 REFIT_MENU_SCREEN
*SubScreen
;
528 LOADER_ENTRY
*SubEntry
;
530 CHAR16 DiagsFileName
[256];
535 // create the submenu
536 if (StrLen(Entry
->Title
) == 0) {
537 MyFreePool(Entry
->Title
);
540 SubScreen
= InitializeSubScreen(Entry
);
542 // loader-specific submenu entries
543 if (Entry
->OSType
== 'M') { // entries for Mac OS X
545 SubEntry
= InitializeLoaderEntry(Entry
);
546 if (SubEntry
!= NULL
) {
547 SubEntry
->me
.Title
= L
"Boot Mac OS X with a 64-bit kernel";
548 SubEntry
->LoadOptions
= L
"arch=x86_64";
549 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
550 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
553 SubEntry
= InitializeLoaderEntry(Entry
);
554 if (SubEntry
!= NULL
) {
555 SubEntry
->me
.Title
= L
"Boot Mac OS X with a 32-bit kernel";
556 SubEntry
->LoadOptions
= L
"arch=i386";
557 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
558 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
562 if (!(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_SINGLEUSER
)) {
563 SubEntry
= InitializeLoaderEntry(Entry
);
564 if (SubEntry
!= NULL
) {
565 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode";
566 SubEntry
->UseGraphicsMode
= FALSE
;
567 SubEntry
->LoadOptions
= L
"-v";
568 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
572 SubEntry
= InitializeLoaderEntry(Entry
);
573 if (SubEntry
!= NULL
) {
574 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode (64-bit)";
575 SubEntry
->UseGraphicsMode
= FALSE
;
576 SubEntry
->LoadOptions
= L
"-v arch=x86_64";
577 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
580 SubEntry
= InitializeLoaderEntry(Entry
);
581 if (SubEntry
!= NULL
) {
582 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode (32-bit)";
583 SubEntry
->UseGraphicsMode
= FALSE
;
584 SubEntry
->LoadOptions
= L
"-v arch=i386";
585 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
589 SubEntry
= InitializeLoaderEntry(Entry
);
590 if (SubEntry
!= NULL
) {
591 SubEntry
->me
.Title
= L
"Boot Mac OS X in single user mode";
592 SubEntry
->UseGraphicsMode
= FALSE
;
593 SubEntry
->LoadOptions
= L
"-v -s";
594 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
598 // check for Apple hardware diagnostics
599 StrCpy(DiagsFileName
, L
"System\\Library\\CoreServices\\.diagnostics\\diags.efi");
600 if (FileExists(Volume
->RootDir
, DiagsFileName
) && !(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_HWTEST
)) {
601 SubEntry
= InitializeLoaderEntry(Entry
);
602 if (SubEntry
!= NULL
) {
603 SubEntry
->me
.Title
= L
"Run Apple Hardware Test";
604 MyFreePool(SubEntry
->LoaderPath
);
605 SubEntry
->LoaderPath
= StrDuplicate(DiagsFileName
);
606 SubEntry
->DevicePath
= FileDevicePath(Volume
->DeviceHandle
, SubEntry
->LoaderPath
);
607 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
608 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
610 } // if diagnostics entry found
612 } else if (Entry
->OSType
== 'L') { // entries for Linux kernels with EFI stub loaders
613 File
= ReadLinuxOptionsFile(Entry
->LoaderPath
, Volume
);
615 InitrdName
= FindInitrd(Entry
->LoaderPath
, Volume
);
616 TokenCount
= ReadTokenLine(File
, &TokenList
);
617 // first entry requires special processing, since it was initially set
618 // up with a default title but correct options by InitializeSubScreen(),
620 if ((SubScreen
->Entries
!= NULL
) && (SubScreen
->Entries
[0] != NULL
)) {
621 MyFreePool(SubScreen
->Entries
[0]->Title
);
622 SubScreen
->Entries
[0]->Title
= StrDuplicate(TokenList
[0]);
624 FreeTokenLine(&TokenList
, &TokenCount
);
625 while ((TokenCount
= ReadTokenLine(File
, &TokenList
)) > 1) {
626 SubEntry
= InitializeLoaderEntry(Entry
);
627 SubEntry
->me
.Title
= StrDuplicate(TokenList
[0]);
628 MyFreePool(SubEntry
->LoadOptions
);
629 SubEntry
->LoadOptions
= AddInitrdToOptions(TokenList
[1], InitrdName
);
630 FreeTokenLine(&TokenList
, &TokenCount
);
631 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_LINUX
;
632 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
634 MyFreePool(InitrdName
);
636 } // if Linux options file exists
638 } else if (Entry
->OSType
== 'E') { // entries for ELILO
639 SubEntry
= InitializeLoaderEntry(Entry
);
640 if (SubEntry
!= NULL
) {
641 SubEntry
->me
.Title
= L
"Run ELILO in interactive mode";
642 SubEntry
->LoadOptions
= L
"-p";
643 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
644 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
647 SubEntry
= InitializeLoaderEntry(Entry
);
648 if (SubEntry
!= NULL
) {
649 SubEntry
->me
.Title
= L
"Boot Linux for a 17\" iMac or a 15\" MacBook Pro (*)";
650 SubEntry
->UseGraphicsMode
= TRUE
;
651 SubEntry
->LoadOptions
= L
"-d 0 i17";
652 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
653 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
656 SubEntry
= InitializeLoaderEntry(Entry
);
657 if (SubEntry
!= NULL
) {
658 SubEntry
->me
.Title
= L
"Boot Linux for a 20\" iMac (*)";
659 SubEntry
->UseGraphicsMode
= TRUE
;
660 SubEntry
->LoadOptions
= L
"-d 0 i20";
661 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
662 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
665 SubEntry
= InitializeLoaderEntry(Entry
);
666 if (SubEntry
!= NULL
) {
667 SubEntry
->me
.Title
= L
"Boot Linux for a Mac Mini (*)";
668 SubEntry
->UseGraphicsMode
= TRUE
;
669 SubEntry
->LoadOptions
= L
"-d 0 mini";
670 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
671 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
674 AddMenuInfoLine(SubScreen
, L
"NOTE: This is an example. Entries");
675 AddMenuInfoLine(SubScreen
, L
"marked with (*) may not work.");
677 } else if (Entry
->OSType
== 'X') { // entries for xom.efi
678 // by default, skip the built-in selection and boot from hard disk only
679 Entry
->LoadOptions
= L
"-s -h";
681 SubEntry
= InitializeLoaderEntry(Entry
);
682 if (SubEntry
!= NULL
) {
683 SubEntry
->me
.Title
= L
"Boot Windows from Hard Disk";
684 SubEntry
->LoadOptions
= L
"-s -h";
685 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
686 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
689 SubEntry
= InitializeLoaderEntry(Entry
);
690 if (SubEntry
!= NULL
) {
691 SubEntry
->me
.Title
= L
"Boot Windows from CD-ROM";
692 SubEntry
->LoadOptions
= L
"-s -c";
693 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
694 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
697 SubEntry
= InitializeLoaderEntry(Entry
);
698 if (SubEntry
!= NULL
) {
699 SubEntry
->me
.Title
= L
"Run XOM in text mode";
700 SubEntry
->UseGraphicsMode
= FALSE
;
701 SubEntry
->LoadOptions
= L
"-v";
702 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
703 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
705 } // entries for xom.efi
706 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
707 Entry
->me
.SubScreen
= SubScreen
;
708 } // VOID GenerateSubScreen()
710 // Returns options for a Linux kernel. Reads them from an options file in the
711 // kernel's directory; and if present, adds an initrd= option for an initial
712 // RAM disk file with the same version number as the kernel file.
713 static CHAR16
* GetMainLinuxOptions(IN CHAR16
* LoaderPath
, IN REFIT_VOLUME
*Volume
) {
714 CHAR16
*Options
= NULL
, *InitrdName
, *FullOptions
= NULL
;
716 Options
= GetFirstOptionsFromFile(LoaderPath
, Volume
);
717 InitrdName
= FindInitrd(LoaderPath
, Volume
);
718 FullOptions
= AddInitrdToOptions(Options
, InitrdName
);
721 MyFreePool(InitrdName
);
722 return (FullOptions
);
723 } // static CHAR16 * GetMainLinuxOptions()
725 // Sets a few defaults for a loader entry -- mainly the icon, but also the OS type
726 // code and shortcut letter. For Linux EFI stub loaders, also sets kernel options
727 // that will (with luck) work fairly automatically.
728 VOID
SetLoaderDefaults(LOADER_ENTRY
*Entry
, CHAR16
*LoaderPath
, IN REFIT_VOLUME
*Volume
) {
729 CHAR16 IconFileName
[256];
730 CHAR16
*FileName
, *PathOnly
, *OSIconName
= NULL
, *Temp
;
731 CHAR16 ShortcutLetter
= 0;
734 FileName
= Basename(LoaderPath
);
735 PathOnly
= FindPath(LoaderPath
);
737 // locate a custom icon for the loader
738 StrCpy(IconFileName
, LoaderPath
);
739 ReplaceEfiExtension(IconFileName
, L
".icns");
740 if (FileExists(Volume
->RootDir
, IconFileName
)) {
741 Entry
->me
.Image
= LoadIcns(Volume
->RootDir
, IconFileName
, 128);
742 } else if ((StrLen(PathOnly
) == 0) && (Volume
->VolIconImage
!= NULL
)) {
743 Entry
->me
.Image
= Volume
->VolIconImage
;
744 } // icon matched to loader or volume
746 Temp
= FindLastDirName(LoaderPath
);
747 MergeStrings(&OSIconName
, Temp
, L
',');
750 if (OSIconName
!= NULL
) {
751 ShortcutLetter
= OSIconName
[0];
754 // Add the volume's label up to the first space, dash, or underscore (if present)
755 // as a potential base for finding an icon
756 if ((Volume
->VolName
) && (StrLen(Volume
->VolName
) > 0)) {
757 Temp
= StrDuplicate(Volume
->VolName
);
760 Length
= StrLen(Temp
);
762 if ((Temp
[i
] == L
' ') || (Temp
[i
] == L
'_') || (Temp
[i
] == L
'-'))
764 } while ((Temp
[i
] != 0) && (++i
< Length
));
765 MergeStrings(&OSIconName
, Temp
, L
',');
770 // detect specific loaders
771 if (StriSubCmp(L
"bzImage", LoaderPath
) || StriSubCmp(L
"vmlinuz", LoaderPath
)) {
772 MergeStrings(&OSIconName
, L
"linux", L
',');
774 if (ShortcutLetter
== 0)
775 ShortcutLetter
= 'L';
776 Entry
->LoadOptions
= GetMainLinuxOptions(LoaderPath
, Volume
);
777 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_LINUX
;
778 } else if (StriSubCmp(L
"refit", LoaderPath
)) {
779 MergeStrings(&OSIconName
, L
"refit", L
',');
781 ShortcutLetter
= 'R';
782 } else if (StriCmp(LoaderPath
, MACOSX_LOADER_PATH
) == 0) {
783 if (Volume
->VolIconImage
!= NULL
) { // custom icon file found
784 Entry
->me
.Image
= Volume
->VolIconImage
;
786 MergeStrings(&OSIconName
, L
"mac", L
',');
788 ShortcutLetter
= 'M';
789 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
790 } else if (StriCmp(FileName
, L
"diags.efi") == 0) {
791 MergeStrings(&OSIconName
, L
"hwtest", L
',');
792 } else if (StriCmp(FileName
, L
"e.efi") == 0 || StriCmp(FileName
, L
"elilo.efi") == 0 || StriSubCmp(L
"elilo", FileName
)) {
793 MergeStrings(&OSIconName
, L
"elilo,linux", L
',');
795 if (ShortcutLetter
== 0)
796 ShortcutLetter
= 'L';
797 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
798 } else if (StriSubCmp(L
"grub", FileName
)) {
800 ShortcutLetter
= 'G';
801 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_GRUB
;
802 } else if (StriCmp(FileName
, L
"cdboot.efi") == 0 ||
803 StriCmp(FileName
, L
"bootmgr.efi") == 0 ||
804 StriCmp(FileName
, L
"Bootmgfw.efi") == 0) {
805 MergeStrings(&OSIconName
, L
"win", L
',');
807 ShortcutLetter
= 'W';
808 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
809 } else if (StriCmp(FileName
, L
"xom.efi") == 0) {
810 MergeStrings(&OSIconName
, L
"xom,win", L
',');
811 Entry
->UseGraphicsMode
= TRUE
;
813 ShortcutLetter
= 'W';
814 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
817 if ((ShortcutLetter
>= 'a') && (ShortcutLetter
<= 'z'))
818 ShortcutLetter
= ShortcutLetter
- 'a' + 'A'; // convert lowercase to uppercase
819 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
820 if (Entry
->me
.Image
== NULL
)
821 Entry
->me
.Image
= LoadOSIcon(OSIconName
, L
"unknown", FALSE
);
822 MyFreePool(PathOnly
);
823 } // VOID SetLoaderDefaults()
825 // Add a specified EFI boot loader to the list, using automatic settings
826 // for icons, options, etc.
827 LOADER_ENTRY
* AddLoaderEntry(IN CHAR16
*LoaderPath
, IN CHAR16
*LoaderTitle
, IN REFIT_VOLUME
*Volume
) {
830 CleanUpPathNameSlashes(LoaderPath
);
831 Entry
= InitializeLoaderEntry(NULL
);
833 Entry
->Title
= StrDuplicate((LoaderTitle
!= NULL
) ? LoaderTitle
: LoaderPath
);
834 Entry
->me
.Title
= AllocateZeroPool(sizeof(CHAR16
) * 256);
835 SPrint(Entry
->me
.Title
, 255, L
"Boot %s from %s", (LoaderTitle
!= NULL
) ? LoaderTitle
: LoaderPath
, Volume
->VolName
);
837 Entry
->me
.BadgeImage
= Volume
->VolBadgeImage
;
838 if ((LoaderPath
!= NULL
) && (LoaderPath
[0] != L
'\\')) {
839 Entry
->LoaderPath
= StrDuplicate(L
"\\");
841 Entry
->LoaderPath
= NULL
;
843 MergeStrings(&(Entry
->LoaderPath
), LoaderPath
, 0);
844 Entry
->VolName
= Volume
->VolName
;
845 Entry
->DevicePath
= FileDevicePath(Volume
->DeviceHandle
, Entry
->LoaderPath
);
846 SetLoaderDefaults(Entry
, LoaderPath
, Volume
);
847 GenerateSubScreen(Entry
, Volume
);
848 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
852 } // LOADER_ENTRY * AddLoaderEntry()
854 // Returns -1 if (Time1 < Time2), +1 if (Time1 > Time2), or 0 if
855 // (Time1 == Time2). Precision is only to the nearest second; since
856 // this is used for sorting boot loader entries, differences smaller
857 // than this are likely to be meaningless (and unlikely!).
858 INTN
TimeComp(IN EFI_TIME
*Time1
, IN EFI_TIME
*Time2
) {
859 INT64 Time1InSeconds
, Time2InSeconds
;
861 // Following values are overestimates; I'm assuming 31 days in every month.
862 // This is fine for the purpose of this function, which is limited
863 Time1InSeconds
= Time1
->Second
+ (Time1
->Minute
* 60) + (Time1
->Hour
* 3600) + (Time1
->Day
* 86400) +
864 (Time1
->Month
* 2678400) + ((Time1
->Year
- 1998) * 32140800);
865 Time2InSeconds
= Time2
->Second
+ (Time2
->Minute
* 60) + (Time2
->Hour
* 3600) + (Time2
->Day
* 86400) +
866 (Time2
->Month
* 2678400) + ((Time2
->Year
- 1998) * 32140800);
867 if (Time1InSeconds
< Time2InSeconds
)
869 else if (Time1InSeconds
> Time2InSeconds
)
875 // Adds a loader list element, keeping it sorted by date. Returns the new
876 // first element (the one with the most recent date).
877 static struct LOADER_LIST
* AddLoaderListEntry(struct LOADER_LIST
*LoaderList
, struct LOADER_LIST
*NewEntry
) {
878 struct LOADER_LIST
*LatestEntry
, *CurrentEntry
, *PrevEntry
= NULL
;
880 LatestEntry
= CurrentEntry
= LoaderList
;
881 if (LoaderList
== NULL
) {
882 LatestEntry
= NewEntry
;
884 while ((CurrentEntry
!= NULL
) && (TimeComp(&(NewEntry
->TimeStamp
), &(CurrentEntry
->TimeStamp
)) < 0)) {
885 PrevEntry
= CurrentEntry
;
886 CurrentEntry
= CurrentEntry
->NextEntry
;
888 NewEntry
->NextEntry
= CurrentEntry
;
889 if (PrevEntry
== NULL
) {
890 LatestEntry
= NewEntry
;
892 PrevEntry
->NextEntry
= NewEntry
;
895 return (LatestEntry
);
896 } // static VOID AddLoaderListEntry()
898 // Delete the LOADER_LIST linked list
899 static VOID
CleanUpLoaderList(struct LOADER_LIST
*LoaderList
) {
900 struct LOADER_LIST
*Temp
;
902 while (LoaderList
!= NULL
) {
904 LoaderList
= LoaderList
->NextEntry
;
905 MyFreePool(Temp
->FileName
);
908 } // static VOID CleanUpLoaderList()
910 // Scan an individual directory for EFI boot loader files and, if found,
911 // add them to the list. Sorts the entries within the loader directory
912 // so that the most recent one appears first in the list.
913 static VOID
ScanLoaderDir(IN REFIT_VOLUME
*Volume
, IN CHAR16
*Path
, IN CHAR16
*Pattern
)
916 REFIT_DIR_ITER DirIter
;
917 EFI_FILE_INFO
*DirEntry
;
918 CHAR16 FileName
[256], *Extension
;
919 struct LOADER_LIST
*LoaderList
= NULL
, *NewLoader
;
921 if ((!SelfDirPath
|| !Path
|| ((StriCmp(Path
, SelfDirPath
) == 0) && (Volume
->DeviceHandle
!= SelfVolume
->DeviceHandle
)) ||
922 (StriCmp(Path
, SelfDirPath
) != 0)) &&
923 (!IsIn(Path
, GlobalConfig
.DontScanDirs
)) &&
924 (!IsIn(Volume
->VolName
, GlobalConfig
.DontScanVolumes
))) {
925 // look through contents of the directory
926 DirIterOpen(Volume
->RootDir
, Path
, &DirIter
);
927 while (DirIterNext(&DirIter
, 2, Pattern
, &DirEntry
)) {
928 Extension
= FindExtension(DirEntry
->FileName
);
929 if (DirEntry
->FileName
[0] == '.' ||
930 StriCmp(Extension
, L
".icns") == 0 ||
931 StriSubCmp(L
"shell", DirEntry
->FileName
) ||
932 IsIn(DirEntry
->FileName
, GlobalConfig
.DontScanFiles
))
933 continue; // skip this
936 SPrint(FileName
, 255, L
"\\%s\\%s", Path
, DirEntry
->FileName
);
938 SPrint(FileName
, 255, L
"\\%s", DirEntry
->FileName
);
939 CleanUpPathNameSlashes(FileName
);
940 NewLoader
= AllocateZeroPool(sizeof(struct LOADER_LIST
));
941 if (NewLoader
!= NULL
) {
942 NewLoader
->FileName
= StrDuplicate(FileName
);
943 NewLoader
->TimeStamp
= DirEntry
->ModificationTime
;
944 LoaderList
= AddLoaderListEntry(LoaderList
, NewLoader
);
946 MyFreePool(Extension
);
948 NewLoader
= LoaderList
;
949 while (NewLoader
!= NULL
) {
950 AddLoaderEntry(NewLoader
->FileName
, NULL
, Volume
);
951 NewLoader
= NewLoader
->NextEntry
;
953 CleanUpLoaderList(LoaderList
);
954 Status
= DirIterClose(&DirIter
);
955 if (Status
!= EFI_NOT_FOUND
) {
957 SPrint(FileName
, 255, L
"while scanning the %s directory", Path
);
959 StrCpy(FileName
, L
"while scanning the root directory");
960 CheckError(Status
, FileName
);
961 } // if (Status != EFI_NOT_FOUND)
962 } // if not scanning our own directory
963 } /* static VOID ScanLoaderDir() */
965 static VOID
ScanEfiFiles(REFIT_VOLUME
*Volume
) {
967 REFIT_DIR_ITER EfiDirIter
;
968 EFI_FILE_INFO
*EfiDirEntry
;
969 CHAR16 FileName
[256], *Directory
, *MatchPatterns
;
972 MatchPatterns
= StrDuplicate(LOADER_MATCH_PATTERNS
);
973 if (GlobalConfig
.ScanAllLinux
)
974 MergeStrings(&MatchPatterns
, LINUX_MATCH_PATTERNS
, L
',');
976 if ((Volume
->RootDir
!= NULL
) && (Volume
->VolName
!= NULL
)) {
977 // check for Mac OS X boot loader
978 if (!IsIn(L
"System\\Library\\CoreServices", GlobalConfig
.DontScanDirs
)) {
979 StrCpy(FileName
, MACOSX_LOADER_PATH
);
980 if (FileExists(Volume
->RootDir
, FileName
) && !IsIn(L
"boot.efi", GlobalConfig
.DontScanFiles
)) {
981 AddLoaderEntry(FileName
, L
"Mac OS X", Volume
);
985 StrCpy(FileName
, L
"System\\Library\\CoreServices\\xom.efi");
986 if (FileExists(Volume
->RootDir
, FileName
) && !IsIn(L
"boot.efi", GlobalConfig
.DontScanFiles
)) {
987 AddLoaderEntry(FileName
, L
"Windows XP (XoM)", Volume
);
989 } // if Mac directory not in GlobalConfig.DontScanDirs list
991 // check for Microsoft boot loader/menu
992 StrCpy(FileName
, L
"EFI\\Microsoft\\Boot\\Bootmgfw.efi");
993 if (FileExists(Volume
->RootDir
, FileName
) && !IsIn(L
"EFI\\Microsoft\\Boot", GlobalConfig
.DontScanDirs
) &&
994 !IsIn(L
"bootmgfw.efi", GlobalConfig
.DontScanFiles
)) {
995 AddLoaderEntry(FileName
, L
"Microsoft EFI boot", Volume
);
998 // scan the root directory for EFI executables
999 ScanLoaderDir(Volume
, L
"\\", MatchPatterns
);
1001 // scan subdirectories of the EFI directory (as per the standard)
1002 DirIterOpen(Volume
->RootDir
, L
"EFI", &EfiDirIter
);
1003 while (DirIterNext(&EfiDirIter
, 1, NULL
, &EfiDirEntry
)) {
1004 if (StriCmp(EfiDirEntry
->FileName
, L
"tools") == 0 || EfiDirEntry
->FileName
[0] == '.')
1005 continue; // skip this, doesn't contain boot loaders
1006 SPrint(FileName
, 255, L
"EFI\\%s", EfiDirEntry
->FileName
);
1007 ScanLoaderDir(Volume
, FileName
, MatchPatterns
);
1009 Status
= DirIterClose(&EfiDirIter
);
1010 if (Status
!= EFI_NOT_FOUND
)
1011 CheckError(Status
, L
"while scanning the EFI directory");
1013 // Scan user-specified (or additional default) directories....
1015 while ((Directory
= FindCommaDelimited(GlobalConfig
.AlsoScan
, i
++)) != NULL
) {
1016 CleanUpPathNameSlashes(Directory
);
1017 Length
= StrLen(Directory
);
1019 ScanLoaderDir(Volume
, Directory
, MatchPatterns
);
1020 MyFreePool(Directory
);
1023 } // static VOID ScanEfiFiles()
1025 // Scan internal disks for valid EFI boot loaders....
1026 static VOID
ScanInternal(VOID
) {
1029 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1030 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_INTERNAL
) {
1031 ScanEfiFiles(Volumes
[VolumeIndex
]);
1034 } // static VOID ScanInternal()
1036 // Scan external disks for valid EFI boot loaders....
1037 static VOID
ScanExternal(VOID
) {
1040 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1041 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_EXTERNAL
) {
1042 ScanEfiFiles(Volumes
[VolumeIndex
]);
1045 } // static VOID ScanExternal()
1047 // Scan internal disks for valid EFI boot loaders....
1048 static VOID
ScanOptical(VOID
) {
1051 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1052 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_OPTICAL
) {
1053 ScanEfiFiles(Volumes
[VolumeIndex
]);
1056 } // static VOID ScanOptical()
1059 // legacy boot functions
1062 static EFI_STATUS
ActivateMbrPartition(IN EFI_BLOCK_IO
*BlockIO
, IN UINTN PartitionIndex
)
1065 UINT8 SectorBuffer
[512];
1066 MBR_PARTITION_INFO
*MbrTable
, *EMbrTable
;
1067 UINT32 ExtBase
, ExtCurrent
, NextExtCurrent
;
1068 UINTN LogicalPartitionIndex
= 4;
1070 BOOLEAN HaveBootCode
;
1073 Status
= refit_call5_wrapper(BlockIO
->ReadBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, 0, 512, SectorBuffer
);
1074 if (EFI_ERROR(Status
))
1076 if (*((UINT16
*)(SectorBuffer
+ 510)) != 0xaa55)
1077 return EFI_NOT_FOUND
; // safety measure #1
1079 // add boot code if necessary
1080 HaveBootCode
= FALSE
;
1081 for (i
= 0; i
< MBR_BOOTCODE_SIZE
; i
++) {
1082 if (SectorBuffer
[i
] != 0) {
1083 HaveBootCode
= TRUE
;
1087 if (!HaveBootCode
) {
1088 // no boot code found in the MBR, add the syslinux MBR code
1089 SetMem(SectorBuffer
, MBR_BOOTCODE_SIZE
, 0);
1090 CopyMem(SectorBuffer
, syslinux_mbr
, SYSLINUX_MBR_SIZE
);
1093 // set the partition active
1094 MbrTable
= (MBR_PARTITION_INFO
*)(SectorBuffer
+ 446);
1096 for (i
= 0; i
< 4; i
++) {
1097 if (MbrTable
[i
].Flags
!= 0x00 && MbrTable
[i
].Flags
!= 0x80)
1098 return EFI_NOT_FOUND
; // safety measure #2
1099 if (i
== PartitionIndex
)
1100 MbrTable
[i
].Flags
= 0x80;
1101 else if (PartitionIndex
>= 4 && IS_EXTENDED_PART_TYPE(MbrTable
[i
].Type
)) {
1102 MbrTable
[i
].Flags
= 0x80;
1103 ExtBase
= MbrTable
[i
].StartLBA
;
1105 MbrTable
[i
].Flags
= 0x00;
1109 Status
= refit_call5_wrapper(BlockIO
->WriteBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, 0, 512, SectorBuffer
);
1110 if (EFI_ERROR(Status
))
1113 if (PartitionIndex
>= 4) {
1114 // we have to activate a logical partition, so walk the EMBR chain
1116 // NOTE: ExtBase was set above while looking at the MBR table
1117 for (ExtCurrent
= ExtBase
; ExtCurrent
; ExtCurrent
= NextExtCurrent
) {
1118 // read current EMBR
1119 Status
= refit_call5_wrapper(BlockIO
->ReadBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, ExtCurrent
, 512, SectorBuffer
);
1120 if (EFI_ERROR(Status
))
1122 if (*((UINT16
*)(SectorBuffer
+ 510)) != 0xaa55)
1123 return EFI_NOT_FOUND
; // safety measure #3
1125 // scan EMBR, set appropriate partition active
1126 EMbrTable
= (MBR_PARTITION_INFO
*)(SectorBuffer
+ 446);
1128 for (i
= 0; i
< 4; i
++) {
1129 if (EMbrTable
[i
].Flags
!= 0x00 && EMbrTable
[i
].Flags
!= 0x80)
1130 return EFI_NOT_FOUND
; // safety measure #4
1131 if (EMbrTable
[i
].StartLBA
== 0 || EMbrTable
[i
].Size
== 0)
1133 if (IS_EXTENDED_PART_TYPE(EMbrTable
[i
].Type
)) {
1134 // link to next EMBR
1135 NextExtCurrent
= ExtBase
+ EMbrTable
[i
].StartLBA
;
1136 EMbrTable
[i
].Flags
= (PartitionIndex
>= LogicalPartitionIndex
) ? 0x80 : 0x00;
1139 // logical partition
1140 EMbrTable
[i
].Flags
= (PartitionIndex
== LogicalPartitionIndex
) ? 0x80 : 0x00;
1141 LogicalPartitionIndex
++;
1145 // write current EMBR
1146 Status
= refit_call5_wrapper(BlockIO
->WriteBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, ExtCurrent
, 512, SectorBuffer
);
1147 if (EFI_ERROR(Status
))
1150 if (PartitionIndex
< LogicalPartitionIndex
)
1151 break; // stop the loop, no need to touch further EMBRs
1157 } /* static EFI_STATUS ActivateMbrPartition() */
1159 // early 2006 Core Duo / Core Solo models
1160 static UINT8 LegacyLoaderDevicePath1Data
[] = {
1161 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1162 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1163 0xFF, 0xFF, 0xF9, 0xFF, 0x00, 0x00, 0x00, 0x00,
1164 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1165 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1166 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1168 // mid-2006 Mac Pro (and probably other Core 2 models)
1169 static UINT8 LegacyLoaderDevicePath2Data
[] = {
1170 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1171 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1172 0xFF, 0xFF, 0xF7, 0xFF, 0x00, 0x00, 0x00, 0x00,
1173 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1174 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1175 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1177 // mid-2007 MBP ("Santa Rosa" based models)
1178 static UINT8 LegacyLoaderDevicePath3Data
[] = {
1179 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1180 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1181 0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
1182 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1183 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1184 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1187 static UINT8 LegacyLoaderDevicePath4Data
[] = {
1188 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1189 0x00, 0x00, 0xC0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1190 0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
1191 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1192 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1193 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1195 // late-2008 MB/MBP (NVidia chipset)
1196 static UINT8 LegacyLoaderDevicePath5Data
[] = {
1197 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1198 0x00, 0x40, 0xCB, 0xFF, 0x00, 0x00, 0x00, 0x00,
1199 0xFF, 0xBF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
1200 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1201 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1202 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1205 static EFI_DEVICE_PATH
*LegacyLoaderList
[] = {
1206 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath1Data
,
1207 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath2Data
,
1208 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath3Data
,
1209 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath4Data
,
1210 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath5Data
,
1214 #define MAX_DISCOVERED_PATHS (16)
1216 static VOID
StartLegacy(IN LEGACY_ENTRY
*Entry
)
1219 EG_IMAGE
*BootLogoImage
;
1220 UINTN ErrorInStep
= 0;
1221 EFI_DEVICE_PATH
*DiscoveredPathList
[MAX_DISCOVERED_PATHS
];
1223 BeginExternalScreen(TRUE
, L
"Booting Legacy OS (Mac mode)");
1225 BootLogoImage
= LoadOSIcon(Entry
->Volume
->OSIconName
, L
"legacy", TRUE
);
1226 if (BootLogoImage
!= NULL
)
1227 BltImageAlpha(BootLogoImage
,
1228 (UGAWidth
- BootLogoImage
->Width
) >> 1,
1229 (UGAHeight
- BootLogoImage
->Height
) >> 1,
1230 &StdBackgroundPixel
);
1232 if (Entry
->Volume
->IsMbrPartition
) {
1233 ActivateMbrPartition(Entry
->Volume
->WholeDiskBlockIO
, Entry
->Volume
->MbrPartitionIndex
);
1236 ExtractLegacyLoaderPaths(DiscoveredPathList
, MAX_DISCOVERED_PATHS
, LegacyLoaderList
);
1238 Status
= StartEFIImageList(DiscoveredPathList
, Entry
->LoadOptions
, NULL
, L
"legacy loader", 0, &ErrorInStep
, TRUE
);
1239 if (Status
== EFI_NOT_FOUND
) {
1240 if (ErrorInStep
== 1) {
1241 Print(L
"\nPlease make sure that you have the latest firmware update installed.\n");
1242 } else if (ErrorInStep
== 3) {
1243 Print(L
"\nThe firmware refused to boot from the selected volume. Note that external\n"
1244 L
"hard drives are not well-supported by Apple's firmware for legacy OS booting.\n");
1247 FinishExternalScreen();
1248 } /* static VOID StartLegacy() */
1250 // Start a device on a non-Mac using the EFI_LEGACY_BIOS_PROTOCOL
1251 #ifdef __MAKEWITH_TIANO
1252 static VOID
StartLegacyUEFI(IN LEGACY_ENTRY
*Entry
)
1254 BeginExternalScreen(TRUE
, L
"Booting Legacy OS (UEFI mode)");
1256 BdsLibConnectDevicePath (Entry
->BdsOption
->DevicePath
);
1257 BdsLibDoLegacyBoot(Entry
->BdsOption
);
1259 // If we get here, it means that there was a failure....
1260 Print(L
"Failure booting legacy (BIOS) OS.");
1262 FinishExternalScreen();
1263 } // static VOID StartLegacyUEFI()
1264 #endif // __MAKEWITH_TIANO
1266 static LEGACY_ENTRY
* AddLegacyEntry(IN CHAR16
*LoaderTitle
, IN REFIT_VOLUME
*Volume
)
1268 LEGACY_ENTRY
*Entry
, *SubEntry
;
1269 REFIT_MENU_SCREEN
*SubScreen
;
1271 CHAR16 ShortcutLetter
= 0;
1273 if (LoaderTitle
== NULL
) {
1274 if (Volume
->OSName
!= NULL
) {
1275 LoaderTitle
= Volume
->OSName
;
1276 if (LoaderTitle
[0] == 'W' || LoaderTitle
[0] == 'L')
1277 ShortcutLetter
= LoaderTitle
[0];
1279 LoaderTitle
= L
"Legacy OS";
1281 if (Volume
->VolName
!= NULL
)
1282 VolDesc
= Volume
->VolName
;
1284 VolDesc
= (Volume
->DiskKind
== DISK_KIND_OPTICAL
) ? L
"CD" : L
"HD";
1286 // prepare the menu entry
1287 Entry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1288 Entry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1289 SPrint(Entry
->me
.Title
, 255, L
"Boot %s from %s", LoaderTitle
, VolDesc
);
1290 Entry
->me
.Tag
= TAG_LEGACY
;
1292 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1293 Entry
->me
.Image
= LoadOSIcon(Volume
->OSIconName
, L
"legacy", FALSE
);
1294 Entry
->me
.BadgeImage
= Volume
->VolBadgeImage
;
1295 Entry
->Volume
= Volume
;
1296 Entry
->LoadOptions
= (Volume
->DiskKind
== DISK_KIND_OPTICAL
) ? L
"CD" :
1297 ((Volume
->DiskKind
== DISK_KIND_EXTERNAL
) ? L
"USB" : L
"HD");
1298 Entry
->Enabled
= TRUE
;
1300 // create the submenu
1301 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
1302 SubScreen
->Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1303 SPrint(SubScreen
->Title
, 255, L
"Boot Options for %s on %s", LoaderTitle
, VolDesc
);
1304 SubScreen
->TitleImage
= Entry
->me
.Image
;
1305 SubScreen
->Hint1
= StrDuplicate(SUBSCREEN_HINT1
);
1306 if (GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_EDITOR
) {
1307 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2_NO_EDITOR
);
1309 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2
);
1313 SubEntry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1314 SubEntry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1315 SPrint(SubEntry
->me
.Title
, 255, L
"Boot %s", LoaderTitle
);
1316 SubEntry
->me
.Tag
= TAG_LEGACY
;
1317 SubEntry
->Volume
= Entry
->Volume
;
1318 SubEntry
->LoadOptions
= Entry
->LoadOptions
;
1319 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
1321 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
1322 Entry
->me
.SubScreen
= SubScreen
;
1323 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1325 } /* static LEGACY_ENTRY * AddLegacyEntry() */
1328 #ifdef __MAKEWITH_TIANO
1329 // default volume badge icon based on disk kind
1330 static EG_IMAGE
* GetDiskBadge(IN UINTN DiskType
) {
1331 EG_IMAGE
* Badge
= NULL
;
1335 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_INTERNAL
);
1338 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_EXTERNAL
);
1341 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_OPTICAL
);
1345 } // static EG_IMAGE * GetDiskBadge()
1348 Create a rEFInd boot option from a Legacy BIOS protocol option.
1350 static LEGACY_ENTRY
* AddLegacyEntryUEFI(BDS_COMMON_OPTION
*BdsOption
, IN UINT16 DiskType
)
1352 LEGACY_ENTRY
*Entry
, *SubEntry
;
1353 REFIT_MENU_SCREEN
*SubScreen
;
1354 CHAR16 ShortcutLetter
= 0;
1355 CHAR16
*LegacyDescription
= BdsOption
->Description
;
1357 // ScanVolume(Volume);
1359 // prepare the menu entry
1360 Entry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1361 Entry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1362 SPrint(Entry
->me
.Title
, 255, L
"Boot legacy target %s", LegacyDescription
);
1363 Entry
->me
.Tag
= TAG_LEGACY_UEFI
;
1365 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1366 Entry
->me
.Image
= LoadOSIcon(L
"legacy", L
"legacy", TRUE
);
1367 Entry
->LoadOptions
= (DiskType
== BBS_CDROM
) ? L
"CD" :
1368 ((DiskType
== BBS_USB
) ? L
"USB" : L
"HD");
1369 Entry
->me
.BadgeImage
= GetDiskBadge(DiskType
);
1370 // Entry->me.BadgeImage = Volume->VolBadgeImage;
1371 Entry
->BdsOption
= BdsOption
;
1372 Entry
->Enabled
= TRUE
;
1374 // create the submenu
1375 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
1376 SubScreen
->Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1377 SPrint(SubScreen
->Title
, 255, L
"No boot options for legacy target");
1378 SubScreen
->TitleImage
= Entry
->me
.Image
;
1379 SubScreen
->Hint1
= StrDuplicate(SUBSCREEN_HINT1
);
1380 if (GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_EDITOR
) {
1381 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2_NO_EDITOR
);
1383 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2
);
1387 SubEntry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1388 SubEntry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1389 SPrint(SubEntry
->me
.Title
, 255, L
"Boot %s", LegacyDescription
);
1390 SubEntry
->me
.Tag
= TAG_LEGACY_UEFI
;
1391 Entry
->BdsOption
= BdsOption
;
1392 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
1394 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
1395 Entry
->me
.SubScreen
= SubScreen
;
1396 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1398 } /* static LEGACY_ENTRY * AddLegacyEntryUEFI() */
1401 Scan for legacy BIOS targets on machines that implement EFI_LEGACY_BIOS_PROTOCOL.
1402 In testing, protocol has not been implemented on Macs but has been
1403 implemented on several Dell PCs and an ASUS motherboard.
1404 Restricts output to disks of the specified DiskType.
1406 static VOID
ScanLegacyUEFI(IN UINTN DiskType
)
1409 EFI_LEGACY_BIOS_PROTOCOL
*LegacyBios
;
1410 UINT16
*BootOrder
= NULL
;
1412 CHAR16 BootOption
[10];
1413 UINTN BootOrderSize
= 0;
1415 BDS_COMMON_OPTION
*BdsOption
;
1416 LIST_ENTRY TempList
;
1417 BBS_BBS_DEVICE_PATH
* BbsDevicePath
= NULL
;
1418 // REFIT_VOLUME Volume;
1420 InitializeListHead (&TempList
);
1421 ZeroMem (Buffer
, sizeof (Buffer
));
1423 // If LegacyBios protocol is not implemented on this platform, then
1424 //we do not support this type of legacy boot on this machine.
1425 Status
= gBS
->LocateProtocol(&gEfiLegacyBootProtocolGuid
, NULL
, (VOID
**) &LegacyBios
);
1426 if (EFI_ERROR (Status
))
1429 // Grab the boot order
1430 BootOrder
= BdsLibGetVariableAndSize(L
"BootOrder", &gEfiGlobalVariableGuid
, &BootOrderSize
);
1431 if (BootOrder
== NULL
) {
1436 while (Index
< BootOrderSize
/ sizeof (UINT16
))
1438 // Grab each boot option variable from the boot order, and convert
1439 // the variable into a BDS boot option
1440 UnicodeSPrint (BootOption
, sizeof (BootOption
), L
"Boot%04x", BootOrder
[Index
]);
1441 BdsOption
= BdsLibVariableToOption (&TempList
, BootOption
);
1443 if (BdsOption
!= NULL
) {
1444 BbsDevicePath
= (BBS_BBS_DEVICE_PATH
*)BdsOption
->DevicePath
;
1446 // Only add the entry if it is of a requested type (e.g. USB, HD)
1448 // Two checks necessary because some systems return EFI boot loaders
1449 // with a DeviceType value that would inappropriately include them
1450 // as legacy loaders....
1451 if ((BbsDevicePath
->DeviceType
== DiskType
) && (BdsOption
->DevicePath
->Type
== DEVICE_TYPE_BIOS
)) {
1452 AddLegacyEntryUEFI(BdsOption
, BbsDevicePath
->DeviceType
);
1457 } /* static VOID ScanLegacyUEFI() */
1459 static VOID
ScanLegacyUEFI(IN UINTN DiskType
){}
1460 #endif // __MAKEWITH_TIANO
1462 static VOID
ScanLegacyVolume(REFIT_VOLUME
*Volume
, UINTN VolumeIndex
) {
1464 BOOLEAN ShowVolume
, HideIfOthersFound
;
1467 HideIfOthersFound
= FALSE
;
1468 if (Volume
->IsAppleLegacy
) {
1470 HideIfOthersFound
= TRUE
;
1471 } else if (Volume
->HasBootCode
) {
1473 if (Volume
->BlockIO
== Volume
->WholeDiskBlockIO
&&
1474 Volume
->BlockIOOffset
== 0 &&
1475 Volume
->OSName
== NULL
)
1476 // this is a whole disk (MBR) entry; hide if we have entries for partitions
1477 HideIfOthersFound
= TRUE
;
1479 if (HideIfOthersFound
) {
1480 // check for other bootable entries on the same disk
1481 for (VolumeIndex2
= 0; VolumeIndex2
< VolumesCount
; VolumeIndex2
++) {
1482 if (VolumeIndex2
!= VolumeIndex
&& Volumes
[VolumeIndex2
]->HasBootCode
&&
1483 Volumes
[VolumeIndex2
]->WholeDiskBlockIO
== Volume
->WholeDiskBlockIO
)
1489 AddLegacyEntry(NULL
, Volume
);
1490 } // static VOID ScanLegacyVolume()
1492 // Scan attached optical discs for legacy (BIOS) boot code
1493 // and add anything found to the list....
1494 static VOID
ScanLegacyDisc(VOID
)
1497 REFIT_VOLUME
*Volume
;
1499 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
) {
1500 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1501 Volume
= Volumes
[VolumeIndex
];
1502 if (Volume
->DiskKind
== DISK_KIND_OPTICAL
)
1503 ScanLegacyVolume(Volume
, VolumeIndex
);
1505 } else if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
1506 ScanLegacyUEFI(BBS_CDROM
);
1508 } /* static VOID ScanLegacyDisc() */
1510 // Scan internal hard disks for legacy (BIOS) boot code
1511 // and add anything found to the list....
1512 static VOID
ScanLegacyInternal(VOID
)
1515 REFIT_VOLUME
*Volume
;
1517 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
) {
1518 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1519 Volume
= Volumes
[VolumeIndex
];
1520 if (Volume
->DiskKind
== DISK_KIND_INTERNAL
)
1521 ScanLegacyVolume(Volume
, VolumeIndex
);
1523 } else if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
1524 ScanLegacyUEFI(BBS_HARDDISK
);
1526 } /* static VOID ScanLegacyInternal() */
1528 // Scan external disks for legacy (BIOS) boot code
1529 // and add anything found to the list....
1530 static VOID
ScanLegacyExternal(VOID
)
1533 REFIT_VOLUME
*Volume
;
1535 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
) {
1536 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1537 Volume
= Volumes
[VolumeIndex
];
1538 if (Volume
->DiskKind
== DISK_KIND_EXTERNAL
)
1539 ScanLegacyVolume(Volume
, VolumeIndex
);
1541 } else if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
1542 ScanLegacyUEFI(BBS_USB
);
1544 } /* static VOID ScanLegacyExternal() */
1547 // pre-boot tool functions
1550 static VOID
StartTool(IN LOADER_ENTRY
*Entry
)
1552 BeginExternalScreen(Entry
->UseGraphicsMode
, Entry
->me
.Title
+ 6); // assumes "Start <title>" as assigned below
1553 StartEFIImage(Entry
->DevicePath
, Entry
->LoadOptions
, Basename(Entry
->LoaderPath
),
1554 Basename(Entry
->LoaderPath
), Entry
->OSType
, NULL
, TRUE
);
1555 FinishExternalScreen();
1556 } /* static VOID StartTool() */
1558 static LOADER_ENTRY
* AddToolEntry(EFI_HANDLE DeviceHandle
, IN CHAR16
*LoaderPath
, IN CHAR16
*LoaderTitle
, IN EG_IMAGE
*Image
,
1559 IN CHAR16 ShortcutLetter
, IN BOOLEAN UseGraphicsMode
)
1561 LOADER_ENTRY
*Entry
;
1562 CHAR16
*TitleStr
= NULL
;
1564 Entry
= AllocateZeroPool(sizeof(LOADER_ENTRY
));
1566 MergeStrings(&TitleStr
, L
"Start ", 0);
1567 MergeStrings(&TitleStr
, LoaderTitle
, 0);
1568 Entry
->me
.Title
= TitleStr
;
1569 Entry
->me
.Tag
= TAG_TOOL
;
1571 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1572 Entry
->me
.Image
= Image
;
1573 Entry
->LoaderPath
= (LoaderPath
) ? StrDuplicate(LoaderPath
) : NULL
;
1574 Entry
->DevicePath
= FileDevicePath(DeviceHandle
, Entry
->LoaderPath
);
1575 Entry
->UseGraphicsMode
= UseGraphicsMode
;
1577 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1579 } /* static LOADER_ENTRY * AddToolEntry() */
1582 // pre-boot driver functions
1585 static UINTN
ScanDriverDir(IN CHAR16
*Path
)
1588 REFIT_DIR_ITER DirIter
;
1590 EFI_FILE_INFO
*DirEntry
;
1591 CHAR16 FileName
[256];
1593 CleanUpPathNameSlashes(Path
);
1594 // look through contents of the directory
1595 DirIterOpen(SelfRootDir
, Path
, &DirIter
);
1596 while (DirIterNext(&DirIter
, 2, LOADER_MATCH_PATTERNS
, &DirEntry
)) {
1597 if (DirEntry
->FileName
[0] == '.')
1598 continue; // skip this
1600 SPrint(FileName
, 255, L
"%s\\%s", Path
, DirEntry
->FileName
);
1602 Status
= StartEFIImage(FileDevicePath(SelfLoadedImage
->DeviceHandle
, FileName
),
1603 L
"", DirEntry
->FileName
, DirEntry
->FileName
, 0, NULL
, FALSE
);
1605 Status
= DirIterClose(&DirIter
);
1606 if (Status
!= EFI_NOT_FOUND
) {
1607 SPrint(FileName
, 255, L
"while scanning the %s directory", Path
);
1608 CheckError(Status
, FileName
);
1613 #ifdef __MAKEWITH_GNUEFI
1614 static EFI_STATUS
ConnectAllDriversToAllControllers(VOID
)
1617 UINTN AllHandleCount
;
1618 EFI_HANDLE
*AllHandleBuffer
;
1621 EFI_HANDLE
*HandleBuffer
;
1627 Status
= LibLocateHandle(AllHandles
,
1632 if (EFI_ERROR(Status
))
1635 for (Index
= 0; Index
< AllHandleCount
; Index
++) {
1637 // Scan the handle database
1639 Status
= LibScanHandleDatabase(NULL
,
1641 AllHandleBuffer
[Index
],
1646 if (EFI_ERROR (Status
))
1650 if (HandleType
[Index
] & EFI_HANDLE_TYPE_DRIVER_BINDING_HANDLE
)
1652 if (HandleType
[Index
] & EFI_HANDLE_TYPE_IMAGE_HANDLE
)
1657 for (HandleIndex
= 0; HandleIndex
< HandleCount
; HandleIndex
++) {
1658 if (HandleType
[HandleIndex
] & EFI_HANDLE_TYPE_PARENT_HANDLE
)
1663 if (HandleType
[Index
] & EFI_HANDLE_TYPE_DEVICE_HANDLE
) {
1664 Status
= refit_call4_wrapper(BS
->ConnectController
,
1665 AllHandleBuffer
[Index
],
1673 MyFreePool (HandleBuffer
);
1674 MyFreePool (HandleType
);
1678 MyFreePool (AllHandleBuffer
);
1680 } /* EFI_STATUS ConnectAllDriversToAllControllers() */
1682 static EFI_STATUS
ConnectAllDriversToAllControllers(VOID
) {
1683 BdsLibConnectAllDriversToAllControllers();
1688 // Load all EFI drivers from rEFInd's "drivers" subdirectory and from the
1689 // directories specified by the user in the "scan_driver_dirs" configuration
1691 static VOID
LoadDrivers(VOID
)
1693 CHAR16
*Directory
, *SelfDirectory
;
1694 UINTN i
= 0, Length
, NumFound
= 0;
1696 // load drivers from the subdirectories of rEFInd's home directory specified
1697 // in the DRIVER_DIRS constant.
1698 while ((Directory
= FindCommaDelimited(DRIVER_DIRS
, i
++)) != NULL
) {
1699 SelfDirectory
= SelfDirPath
? StrDuplicate(SelfDirPath
) : NULL
;
1700 CleanUpPathNameSlashes(SelfDirectory
);
1701 MergeStrings(&SelfDirectory
, Directory
, L
'\\');
1702 NumFound
+= ScanDriverDir(SelfDirectory
);
1703 MyFreePool(Directory
);
1704 MyFreePool(SelfDirectory
);
1707 // Scan additional user-specified driver directories....
1709 while ((Directory
= FindCommaDelimited(GlobalConfig
.DriverDirs
, i
++)) != NULL
) {
1710 CleanUpPathNameSlashes(Directory
);
1711 Length
= StrLen(Directory
);
1713 NumFound
+= ScanDriverDir(Directory
);
1715 MyFreePool(Directory
);
1718 // connect all devices
1720 ConnectAllDriversToAllControllers();
1721 } /* static VOID LoadDrivers() */
1723 // Determine what (if any) type of legacy (BIOS) boot support is available
1724 static VOID
FindLegacyBootType(VOID
) {
1725 #ifdef __MAKEWITH_TIANO
1727 EFI_LEGACY_BIOS_PROTOCOL
*LegacyBios
;
1730 GlobalConfig
.LegacyType
= LEGACY_TYPE_NONE
;
1732 // UEFI-style legacy BIOS support is available only with the TianoCore EDK2
1733 // build environment, and then only with some implementations....
1734 #ifdef __MAKEWITH_TIANO
1735 Status
= gBS
->LocateProtocol (&gEfiLegacyBootProtocolGuid
, NULL
, (VOID
**) &LegacyBios
);
1736 if (!EFI_ERROR (Status
))
1737 GlobalConfig
.LegacyType
= LEGACY_TYPE_UEFI
;
1740 // Macs have their own system. If the firmware vendor code contains the
1741 // string "Apple", assume it's available. Note that this overrides the
1742 // UEFI type, and might yield false positives if the vendor string
1743 // contains "Apple" as part of something bigger, so this isn't 100%
1745 if (StriSubCmp(L
"Apple", ST
->FirmwareVendor
))
1746 GlobalConfig
.LegacyType
= LEGACY_TYPE_MAC
;
1747 } // static VOID FindLegacyBootType
1749 // Warn the user if legacy OS scans are enabled but the firmware or this
1750 // application can't support them....
1751 static VOID
WarnIfLegacyProblems() {
1752 BOOLEAN found
= FALSE
;
1755 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_NONE
) {
1757 if (GlobalConfig
.ScanFor
[i
] == 'h' || GlobalConfig
.ScanFor
[i
] == 'b' || GlobalConfig
.ScanFor
[i
] == 'c')
1760 } while ((i
< NUM_SCAN_OPTIONS
) && (!found
));
1762 Print(L
"NOTE: refind.conf's 'scanfor' line specifies scanning for one or more legacy\n");
1763 Print(L
"(BIOS) boot options; however, this is not possible because ");
1764 #ifdef __MAKEWITH_TIANO
1765 Print(L
"your computer lacks\n");
1766 Print(L
"the necessary Compatibility Support Module (CSM) support.\n");
1768 Print(L
"this program was\n");
1769 Print(L
"compiled without the necessary support. Please visit\n");
1770 Print(L
"http://www.rodsbooks.com/refind/getting.html and download and install a rEFInd\n");
1771 Print(L
"binary built with the TianoCore EDK2 to enable legacy boot support.\n");
1775 } // if no legacy support
1776 } // static VOID WarnIfLegacyProblems()
1778 // Locates boot loaders. NOTE: This assumes that GlobalConfig.LegacyType is set correctly.
1779 static VOID
ScanForBootloaders(VOID
) {
1784 // scan for loaders and tools, add them to the menu
1785 for (i
= 0; i
< NUM_SCAN_OPTIONS
; i
++) {
1786 switch(GlobalConfig
.ScanFor
[i
]) {
1791 ScanLegacyInternal();
1794 ScanLegacyExternal();
1797 ScanUserConfigured();
1811 // assign shortcut keys
1812 for (i
= 0; i
< MainMenu
.EntryCount
&& MainMenu
.Entries
[i
]->Row
== 0 && i
< 9; i
++)
1813 MainMenu
.Entries
[i
]->ShortcutDigit
= (CHAR16
)('1' + i
);
1815 // wait for user ACK when there were errors
1816 FinishTextScreen(FALSE
);
1817 } // static VOID ScanForBootloaders()
1819 // Add the second-row tags containing built-in and external tools (EFI shell,
1821 static VOID
ScanForTools(VOID
) {
1822 CHAR16
*FileName
= NULL
, Description
[256];
1823 REFIT_MENU_ENTRY
*TempMenuEntry
;
1824 UINTN i
, j
, VolumeIndex
;
1826 for (i
= 0; i
< NUM_TOOLS
; i
++) {
1827 switch(GlobalConfig
.ShowTools
[i
]) {
1829 TempMenuEntry
= CopyMenuEntry(&MenuEntryShutdown
);
1830 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_SHUTDOWN
);
1831 AddMenuEntry(&MainMenu
, TempMenuEntry
);
1834 TempMenuEntry
= CopyMenuEntry(&MenuEntryReset
);
1835 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_RESET
);
1836 AddMenuEntry(&MainMenu
, TempMenuEntry
);
1839 TempMenuEntry
= CopyMenuEntry(&MenuEntryAbout
);
1840 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT
);
1841 AddMenuEntry(&MainMenu
, TempMenuEntry
);
1844 TempMenuEntry
= CopyMenuEntry(&MenuEntryExit
);
1845 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_EXIT
);
1846 AddMenuEntry(&MainMenu
, TempMenuEntry
);
1850 MyFreePool(FileName
);
1851 while ((FileName
= FindCommaDelimited(SHELL_NAMES
, j
++)) != NULL
) {
1852 if (FileExists(SelfRootDir
, FileName
)) {
1853 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, L
"EFI Shell", BuiltinIcon(BUILTIN_ICON_TOOL_SHELL
),
1859 MyFreePool(FileName
);
1860 FileName
= StrDuplicate(L
"\\efi\\tools\\gptsync.efi");
1861 // MergeStrings(&FileName, L"\\efi\\tools\\gptsync.efi", 0);
1862 if (FileExists(SelfRootDir
, FileName
)) {
1863 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, L
"Make Hybrid MBR", BuiltinIcon(BUILTIN_ICON_TOOL_PART
), 'P', FALSE
);
1866 case TAG_APPLE_RECOVERY
:
1867 MyFreePool(FileName
);
1868 FileName
= StrDuplicate(L
"\\com.apple.recovery.boot\\boot.efi");
1869 // MergeStrings(&FileName, L"\\com.apple.recovery.boot\\boot.efi", 0);
1870 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1871 if ((Volumes
[VolumeIndex
]->RootDir
!= NULL
) && (FileExists(Volumes
[VolumeIndex
]->RootDir
, FileName
))) {
1872 SPrint(Description
, 255, L
"Apple Recovery on %s", Volumes
[VolumeIndex
]->VolName
);
1873 AddToolEntry(Volumes
[VolumeIndex
]->DeviceHandle
, FileName
, Description
,
1874 BuiltinIcon(BUILTIN_ICON_TOOL_APPLE_RESCUE
), 'R', TRUE
);
1880 MyFreePool(FileName
);
1881 while ((FileName
= FindCommaDelimited(MOK_NAMES
, j
++)) != NULL
) {
1882 if (FileExists(SelfRootDir
, FileName
)) {
1883 SPrint(Description
, 255, L
"MOK Key Manager at %s", FileName
);
1884 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, Description
,
1885 BuiltinIcon(BUILTIN_ICON_TOOL_MOK_TOOL
), 'S', FALSE
);
1888 if (FileExists(SelfDir
, L
"MokManager.efi")) {
1889 MyFreePool(FileName
);
1890 FileName
= StrDuplicate(SelfDirPath
);
1891 MergeStrings(&FileName
, L
"\\MokManager.efi", 0);
1892 SPrint(Description
, 255, L
"MOK Key Manager at %s", FileName
);
1893 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, Description
,
1894 BuiltinIcon(BUILTIN_ICON_TOOL_MOK_TOOL
), 'S', FALSE
);
1898 MyFreePool(FileName
);
1901 } // static VOID ScanForTools
1903 // Rescan for boot loaders
1904 VOID
RescanAll(VOID
) {
1911 egDisplayMessage(L
"Scanning for new boot loaders; please wait....", &BGColor
);
1912 FreeList((VOID
***) &(MainMenu
.Entries
), &MainMenu
.EntryCount
);
1913 MainMenu
.Entries
= NULL
;
1914 MainMenu
.EntryCount
= 0;
1915 ReadConfig(CONFIG_FILE_NAME
);
1916 ConnectAllDriversToAllControllers();
1918 ScanForBootloaders();
1921 } // VOID RescanAll()
1923 #ifndef __MAKEWITH_GNUEFI
1925 // Minimal initialization function
1926 static VOID
InitializeLib(IN EFI_HANDLE ImageHandle
, IN EFI_SYSTEM_TABLE
*SystemTable
) {
1928 // gImageHandle = ImageHandle;
1929 gBS
= SystemTable
->BootServices
;
1930 // gRS = SystemTable->RuntimeServices;
1931 gRT
= SystemTable
->RuntimeServices
; // Some BDS functions need gRT to be set
1932 EfiGetSystemConfigurationTable (&gEfiDxeServicesTableGuid
, (VOID
**) &gDS
);
1934 InitializeConsoleSim();
1944 efi_main (IN EFI_HANDLE ImageHandle
, IN EFI_SYSTEM_TABLE
*SystemTable
)
1947 BOOLEAN MainLoopRunning
= TRUE
;
1948 REFIT_MENU_ENTRY
*ChosenEntry
;
1954 InitializeLib(ImageHandle
, SystemTable
);
1956 Status
= InitRefitLib(ImageHandle
);
1957 if (EFI_ERROR(Status
))
1960 // read configuration
1961 CopyMem(GlobalConfig
.ScanFor
, "ieom ", NUM_SCAN_OPTIONS
);
1962 FindLegacyBootType();
1963 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
)
1964 CopyMem(GlobalConfig
.ScanFor
, "ihebocm ", NUM_SCAN_OPTIONS
);
1965 ReadConfig(CONFIG_FILE_NAME
);
1966 WarnIfLegacyProblems();
1967 MainMenu
.TimeoutSeconds
= GlobalConfig
.Timeout
;
1969 // disable EFI watchdog timer
1970 refit_call4_wrapper(BS
->SetWatchdogTimer
, 0x0000, 0x0000, 0x0000, NULL
);
1972 // further bootstrap (now with config available)
1976 ScanForBootloaders();
1979 if (GlobalConfig
.ScanDelay
> 0) {
1984 egDisplayMessage(L
"Pausing before disk scan; please wait....", &BGColor
);
1985 for (i
= 0; i
< GlobalConfig
.ScanDelay
; i
++)
1986 refit_call1_wrapper(BS
->Stall
, 1000000);
1990 Selection
= StrDuplicate(GlobalConfig
.DefaultSelection
);
1991 while (MainLoopRunning
) {
1992 MenuExit
= RunMainMenu(&MainMenu
, Selection
, &ChosenEntry
);
1994 // The Escape key triggers a re-scan operation....
1995 if (MenuExit
== MENU_EXIT_ESCAPE
) {
2000 switch (ChosenEntry
->Tag
) {
2002 case TAG_REBOOT
: // Reboot
2004 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);
2005 MainLoopRunning
= FALSE
; // just in case we get this far
2008 case TAG_SHUTDOWN
: // Shut Down
2010 refit_call4_wrapper(RT
->ResetSystem
, EfiResetShutdown
, EFI_SUCCESS
, 0, NULL
);
2011 MainLoopRunning
= FALSE
; // just in case we get this far
2014 case TAG_ABOUT
: // About rEFInd
2018 case TAG_LOADER
: // Boot OS via .EFI loader
2019 StartLoader((LOADER_ENTRY
*)ChosenEntry
);
2022 case TAG_LEGACY
: // Boot legacy OS
2023 StartLegacy((LEGACY_ENTRY
*)ChosenEntry
);
2026 #ifdef __MAKEWITH_TIANO
2027 case TAG_LEGACY_UEFI
: // Boot a legacy OS on a non-Mac
2028 StartLegacyUEFI((LEGACY_ENTRY
*)ChosenEntry
);
2030 #endif // __MAKEWITH_TIANO
2032 case TAG_TOOL
: // Start a EFI tool
2033 StartTool((LOADER_ENTRY
*)ChosenEntry
);
2036 case TAG_EXIT
: // Terminate rEFInd
2037 BeginTextScreen(L
" ");
2042 MyFreePool(Selection
);
2043 Selection
= (ChosenEntry
->Title
) ? StrDuplicate(ChosenEntry
->Title
) : NULL
;
2046 // If we end up here, things have gone wrong. Try to reboot, and if that
2047 // fails, go into an endless loop.
2048 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);