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*"
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;
733 FileName
= Basename(LoaderPath
);
734 PathOnly
= FindPath(LoaderPath
);
736 // locate a custom icon for the loader
737 StrCpy(IconFileName
, LoaderPath
);
738 ReplaceEfiExtension(IconFileName
, L
".icns");
739 if (FileExists(Volume
->RootDir
, IconFileName
)) {
740 Entry
->me
.Image
= LoadIcns(Volume
->RootDir
, IconFileName
, 128);
741 } else if ((StrLen(PathOnly
) == 0) && (Volume
->VolIconImage
!= NULL
)) {
742 Entry
->me
.Image
= Volume
->VolIconImage
;
743 } // icon matched to loader or volume
745 Temp
= FindLastDirName(LoaderPath
);
746 MergeStrings(&OSIconName
, Temp
, L
',');
749 if (OSIconName
!= NULL
) {
750 ShortcutLetter
= OSIconName
[0];
753 // detect specific loaders
754 if (StriSubCmp(L
"bzImage", LoaderPath
) || StriSubCmp(L
"vmlinuz", LoaderPath
)) {
755 MergeStrings(&OSIconName
, L
"linux", L
',');
757 if (ShortcutLetter
== 0)
758 ShortcutLetter
= 'L';
759 Entry
->LoadOptions
= GetMainLinuxOptions(LoaderPath
, Volume
);
760 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_LINUX
;
761 } else if (StriSubCmp(L
"refit", LoaderPath
)) {
762 MergeStrings(&OSIconName
, L
"refit", L
',');
764 ShortcutLetter
= 'R';
765 } else if (StriCmp(LoaderPath
, MACOSX_LOADER_PATH
) == 0) {
766 if (Volume
->VolIconImage
!= NULL
) { // custom icon file found
767 Entry
->me
.Image
= Volume
->VolIconImage
;
769 MergeStrings(&OSIconName
, L
"mac", L
',');
771 ShortcutLetter
= 'M';
772 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
773 } else if (StriCmp(FileName
, L
"diags.efi") == 0) {
774 MergeStrings(&OSIconName
, L
"hwtest", L
',');
775 } else if (StriCmp(FileName
, L
"e.efi") == 0 || StriCmp(FileName
, L
"elilo.efi") == 0 || StriSubCmp(L
"elilo", FileName
)) {
776 MergeStrings(&OSIconName
, L
"elilo,linux", L
',');
778 if (ShortcutLetter
== 0)
779 ShortcutLetter
= 'L';
780 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
781 } else if (StriSubCmp(L
"grub", FileName
)) {
783 ShortcutLetter
= 'G';
784 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_GRUB
;
785 } else if (StriCmp(FileName
, L
"cdboot.efi") == 0 ||
786 StriCmp(FileName
, L
"bootmgr.efi") == 0 ||
787 StriCmp(FileName
, L
"Bootmgfw.efi") == 0) {
788 MergeStrings(&OSIconName
, L
"win", L
',');
790 ShortcutLetter
= 'W';
791 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
792 } else if (StriCmp(FileName
, L
"xom.efi") == 0) {
793 MergeStrings(&OSIconName
, L
"xom,win", L
',');
794 Entry
->UseGraphicsMode
= TRUE
;
796 ShortcutLetter
= 'W';
797 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
800 if ((ShortcutLetter
>= 'a') && (ShortcutLetter
<= 'z'))
801 ShortcutLetter
= ShortcutLetter
- 'a' + 'A'; // convert lowercase to uppercase
802 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
803 if (Entry
->me
.Image
== NULL
)
804 Entry
->me
.Image
= LoadOSIcon(OSIconName
, L
"unknown", FALSE
);
805 MyFreePool(PathOnly
);
806 } // VOID SetLoaderDefaults()
808 // Add a specified EFI boot loader to the list, using automatic settings
809 // for icons, options, etc.
810 LOADER_ENTRY
* AddLoaderEntry(IN CHAR16
*LoaderPath
, IN CHAR16
*LoaderTitle
, IN REFIT_VOLUME
*Volume
) {
813 CleanUpPathNameSlashes(LoaderPath
);
814 Entry
= InitializeLoaderEntry(NULL
);
816 Entry
->Title
= StrDuplicate((LoaderTitle
!= NULL
) ? LoaderTitle
: LoaderPath
);
817 Entry
->me
.Title
= AllocateZeroPool(sizeof(CHAR16
) * 256);
818 SPrint(Entry
->me
.Title
, 255, L
"Boot %s from %s", (LoaderTitle
!= NULL
) ? LoaderTitle
: LoaderPath
, Volume
->VolName
);
820 Entry
->me
.BadgeImage
= Volume
->VolBadgeImage
;
821 if ((LoaderPath
!= NULL
) && (LoaderPath
[0] != L
'\\')) {
822 Entry
->LoaderPath
= StrDuplicate(L
"\\");
824 Entry
->LoaderPath
= NULL
;
826 MergeStrings(&(Entry
->LoaderPath
), LoaderPath
, 0);
827 Entry
->VolName
= Volume
->VolName
;
828 Entry
->DevicePath
= FileDevicePath(Volume
->DeviceHandle
, Entry
->LoaderPath
);
829 SetLoaderDefaults(Entry
, LoaderPath
, Volume
);
830 GenerateSubScreen(Entry
, Volume
);
831 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
835 } // LOADER_ENTRY * AddLoaderEntry()
837 // Returns -1 if (Time1 < Time2), +1 if (Time1 > Time2), or 0 if
838 // (Time1 == Time2). Precision is only to the nearest second; since
839 // this is used for sorting boot loader entries, differences smaller
840 // than this are likely to be meaningless (and unlikely!).
841 INTN
TimeComp(EFI_TIME
*Time1
, EFI_TIME
*Time2
) {
842 INT64 Time1InSeconds
, Time2InSeconds
;
844 // Following values are overestimates; I'm assuming 31 days in every month.
845 // This is fine for the purpose of this function, which is limited
846 Time1InSeconds
= Time1
->Second
+ (Time1
->Minute
* 60) + (Time1
->Hour
* 3600) + (Time1
->Day
* 86400) +
847 (Time1
->Month
* 2678400) + ((Time1
->Year
- 1998) * 32140800);
848 Time2InSeconds
= Time2
->Second
+ (Time2
->Minute
* 60) + (Time2
->Hour
* 3600) + (Time2
->Day
* 86400) +
849 (Time2
->Month
* 2678400) + ((Time2
->Year
- 1998) * 32140800);
850 if (Time1InSeconds
< Time2InSeconds
)
852 else if (Time1InSeconds
> Time2InSeconds
)
858 // Adds a loader list element, keeping it sorted by date. Returns the new
859 // first element (the one with the most recent date).
860 static struct LOADER_LIST
* AddLoaderListEntry(struct LOADER_LIST
*LoaderList
, struct LOADER_LIST
*NewEntry
) {
861 struct LOADER_LIST
*LatestEntry
, *CurrentEntry
, *PrevEntry
= NULL
;
863 LatestEntry
= CurrentEntry
= LoaderList
;
864 if (LoaderList
== NULL
) {
865 LatestEntry
= NewEntry
;
867 while ((CurrentEntry
!= NULL
) && (TimeComp(&(NewEntry
->TimeStamp
), &(CurrentEntry
->TimeStamp
)) < 0)) {
868 PrevEntry
= CurrentEntry
;
869 CurrentEntry
= CurrentEntry
->NextEntry
;
871 NewEntry
->NextEntry
= CurrentEntry
;
872 if (PrevEntry
== NULL
) {
873 LatestEntry
= NewEntry
;
875 PrevEntry
->NextEntry
= NewEntry
;
878 return (LatestEntry
);
879 } // static VOID AddLoaderListEntry()
881 // Delete the LOADER_LIST linked list
882 static VOID
CleanUpLoaderList(struct LOADER_LIST
*LoaderList
) {
883 struct LOADER_LIST
*Temp
;
885 while (LoaderList
!= NULL
) {
887 LoaderList
= LoaderList
->NextEntry
;
888 MyFreePool(Temp
->FileName
);
891 } // static VOID CleanUpLoaderList()
893 // Scan an individual directory for EFI boot loader files and, if found,
894 // add them to the list. Sorts the entries within the loader directory
895 // so that the most recent one appears first in the list.
896 static VOID
ScanLoaderDir(IN REFIT_VOLUME
*Volume
, IN CHAR16
*Path
, IN CHAR16
*Pattern
)
899 REFIT_DIR_ITER DirIter
;
900 EFI_FILE_INFO
*DirEntry
;
901 CHAR16 FileName
[256], *Extension
;
902 struct LOADER_LIST
*LoaderList
= NULL
, *NewLoader
;
904 if ((!SelfDirPath
|| !Path
|| ((StriCmp(Path
, SelfDirPath
) == 0) && (Volume
->DeviceHandle
!= SelfVolume
->DeviceHandle
)) ||
905 (StriCmp(Path
, SelfDirPath
) != 0)) &&
906 (!IsIn(Path
, GlobalConfig
.DontScanDirs
)) &&
907 (!IsIn(Volume
->VolName
, GlobalConfig
.DontScanVolumes
))) {
908 // look through contents of the directory
909 DirIterOpen(Volume
->RootDir
, Path
, &DirIter
);
910 while (DirIterNext(&DirIter
, 2, Pattern
, &DirEntry
)) {
911 Extension
= FindExtension(DirEntry
->FileName
);
912 if (DirEntry
->FileName
[0] == '.' ||
913 StriCmp(Extension
, L
".icns") == 0 ||
914 StriSubCmp(L
"shell", DirEntry
->FileName
) ||
915 IsIn(DirEntry
->FileName
, GlobalConfig
.DontScanFiles
))
916 continue; // skip this
919 SPrint(FileName
, 255, L
"\\%s\\%s", Path
, DirEntry
->FileName
);
921 SPrint(FileName
, 255, L
"\\%s", DirEntry
->FileName
);
922 CleanUpPathNameSlashes(FileName
);
923 NewLoader
= AllocateZeroPool(sizeof(struct LOADER_LIST
));
924 if (NewLoader
!= NULL
) {
925 NewLoader
->FileName
= StrDuplicate(FileName
);
926 NewLoader
->TimeStamp
= DirEntry
->ModificationTime
;
927 LoaderList
= AddLoaderListEntry(LoaderList
, NewLoader
);
929 MyFreePool(Extension
);
931 NewLoader
= LoaderList
;
932 while (NewLoader
!= NULL
) {
933 AddLoaderEntry(NewLoader
->FileName
, NULL
, Volume
);
934 NewLoader
= NewLoader
->NextEntry
;
936 CleanUpLoaderList(LoaderList
);
937 Status
= DirIterClose(&DirIter
);
938 if (Status
!= EFI_NOT_FOUND
) {
940 SPrint(FileName
, 255, L
"while scanning the %s directory", Path
);
942 StrCpy(FileName
, L
"while scanning the root directory");
943 CheckError(Status
, FileName
);
944 } // if (Status != EFI_NOT_FOUND)
945 } // if not scanning our own directory
946 } /* static VOID ScanLoaderDir() */
948 static VOID
ScanEfiFiles(REFIT_VOLUME
*Volume
) {
950 REFIT_DIR_ITER EfiDirIter
;
951 EFI_FILE_INFO
*EfiDirEntry
;
952 CHAR16 FileName
[256], *Directory
, *MatchPatterns
;
955 MatchPatterns
= StrDuplicate(LOADER_MATCH_PATTERNS
);
956 if (GlobalConfig
.ScanAllLinux
)
957 MergeStrings(&MatchPatterns
, LINUX_MATCH_PATTERNS
, L
',');
959 if ((Volume
->RootDir
!= NULL
) && (Volume
->VolName
!= NULL
)) {
960 // check for Mac OS X boot loader
961 if (!IsIn(L
"System\\Library\\CoreServices", GlobalConfig
.DontScanDirs
)) {
962 StrCpy(FileName
, MACOSX_LOADER_PATH
);
963 if (FileExists(Volume
->RootDir
, FileName
) && !IsIn(L
"boot.efi", GlobalConfig
.DontScanFiles
)) {
964 AddLoaderEntry(FileName
, L
"Mac OS X", Volume
);
968 StrCpy(FileName
, L
"System\\Library\\CoreServices\\xom.efi");
969 if (FileExists(Volume
->RootDir
, FileName
) && !IsIn(L
"boot.efi", GlobalConfig
.DontScanFiles
)) {
970 AddLoaderEntry(FileName
, L
"Windows XP (XoM)", Volume
);
972 } // if Mac directory not in GlobalConfig.DontScanDirs list
974 // check for Microsoft boot loader/menu
975 StrCpy(FileName
, L
"EFI\\Microsoft\\Boot\\Bootmgfw.efi");
976 if (FileExists(Volume
->RootDir
, FileName
) && !IsIn(L
"EFI\\Microsoft\\Boot", GlobalConfig
.DontScanDirs
) &&
977 !IsIn(L
"bootmgfw.efi", GlobalConfig
.DontScanFiles
)) {
978 AddLoaderEntry(FileName
, L
"Microsoft EFI boot", Volume
);
981 // scan the root directory for EFI executables
982 ScanLoaderDir(Volume
, L
"\\", MatchPatterns
);
984 // scan subdirectories of the EFI directory (as per the standard)
985 DirIterOpen(Volume
->RootDir
, L
"EFI", &EfiDirIter
);
986 while (DirIterNext(&EfiDirIter
, 1, NULL
, &EfiDirEntry
)) {
987 if (StriCmp(EfiDirEntry
->FileName
, L
"tools") == 0 || EfiDirEntry
->FileName
[0] == '.')
988 continue; // skip this, doesn't contain boot loaders
989 SPrint(FileName
, 255, L
"EFI\\%s", EfiDirEntry
->FileName
);
990 ScanLoaderDir(Volume
, FileName
, MatchPatterns
);
992 Status
= DirIterClose(&EfiDirIter
);
993 if (Status
!= EFI_NOT_FOUND
)
994 CheckError(Status
, L
"while scanning the EFI directory");
996 // Scan user-specified (or additional default) directories....
998 while ((Directory
= FindCommaDelimited(GlobalConfig
.AlsoScan
, i
++)) != NULL
) {
999 CleanUpPathNameSlashes(Directory
);
1000 Length
= StrLen(Directory
);
1002 ScanLoaderDir(Volume
, Directory
, MatchPatterns
);
1003 MyFreePool(Directory
);
1006 } // static VOID ScanEfiFiles()
1008 // Scan internal disks for valid EFI boot loaders....
1009 static VOID
ScanInternal(VOID
) {
1012 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1013 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_INTERNAL
) {
1014 ScanEfiFiles(Volumes
[VolumeIndex
]);
1017 } // static VOID ScanInternal()
1019 // Scan external disks for valid EFI boot loaders....
1020 static VOID
ScanExternal(VOID
) {
1023 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1024 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_EXTERNAL
) {
1025 ScanEfiFiles(Volumes
[VolumeIndex
]);
1028 } // static VOID ScanExternal()
1030 // Scan internal disks for valid EFI boot loaders....
1031 static VOID
ScanOptical(VOID
) {
1034 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1035 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_OPTICAL
) {
1036 ScanEfiFiles(Volumes
[VolumeIndex
]);
1039 } // static VOID ScanOptical()
1042 // legacy boot functions
1045 static EFI_STATUS
ActivateMbrPartition(IN EFI_BLOCK_IO
*BlockIO
, IN UINTN PartitionIndex
)
1048 UINT8 SectorBuffer
[512];
1049 MBR_PARTITION_INFO
*MbrTable
, *EMbrTable
;
1050 UINT32 ExtBase
, ExtCurrent
, NextExtCurrent
;
1051 UINTN LogicalPartitionIndex
= 4;
1053 BOOLEAN HaveBootCode
;
1056 Status
= refit_call5_wrapper(BlockIO
->ReadBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, 0, 512, SectorBuffer
);
1057 if (EFI_ERROR(Status
))
1059 if (*((UINT16
*)(SectorBuffer
+ 510)) != 0xaa55)
1060 return EFI_NOT_FOUND
; // safety measure #1
1062 // add boot code if necessary
1063 HaveBootCode
= FALSE
;
1064 for (i
= 0; i
< MBR_BOOTCODE_SIZE
; i
++) {
1065 if (SectorBuffer
[i
] != 0) {
1066 HaveBootCode
= TRUE
;
1070 if (!HaveBootCode
) {
1071 // no boot code found in the MBR, add the syslinux MBR code
1072 SetMem(SectorBuffer
, MBR_BOOTCODE_SIZE
, 0);
1073 CopyMem(SectorBuffer
, syslinux_mbr
, SYSLINUX_MBR_SIZE
);
1076 // set the partition active
1077 MbrTable
= (MBR_PARTITION_INFO
*)(SectorBuffer
+ 446);
1079 for (i
= 0; i
< 4; i
++) {
1080 if (MbrTable
[i
].Flags
!= 0x00 && MbrTable
[i
].Flags
!= 0x80)
1081 return EFI_NOT_FOUND
; // safety measure #2
1082 if (i
== PartitionIndex
)
1083 MbrTable
[i
].Flags
= 0x80;
1084 else if (PartitionIndex
>= 4 && IS_EXTENDED_PART_TYPE(MbrTable
[i
].Type
)) {
1085 MbrTable
[i
].Flags
= 0x80;
1086 ExtBase
= MbrTable
[i
].StartLBA
;
1088 MbrTable
[i
].Flags
= 0x00;
1092 Status
= refit_call5_wrapper(BlockIO
->WriteBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, 0, 512, SectorBuffer
);
1093 if (EFI_ERROR(Status
))
1096 if (PartitionIndex
>= 4) {
1097 // we have to activate a logical partition, so walk the EMBR chain
1099 // NOTE: ExtBase was set above while looking at the MBR table
1100 for (ExtCurrent
= ExtBase
; ExtCurrent
; ExtCurrent
= NextExtCurrent
) {
1101 // read current EMBR
1102 Status
= refit_call5_wrapper(BlockIO
->ReadBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, ExtCurrent
, 512, SectorBuffer
);
1103 if (EFI_ERROR(Status
))
1105 if (*((UINT16
*)(SectorBuffer
+ 510)) != 0xaa55)
1106 return EFI_NOT_FOUND
; // safety measure #3
1108 // scan EMBR, set appropriate partition active
1109 EMbrTable
= (MBR_PARTITION_INFO
*)(SectorBuffer
+ 446);
1111 for (i
= 0; i
< 4; i
++) {
1112 if (EMbrTable
[i
].Flags
!= 0x00 && EMbrTable
[i
].Flags
!= 0x80)
1113 return EFI_NOT_FOUND
; // safety measure #4
1114 if (EMbrTable
[i
].StartLBA
== 0 || EMbrTable
[i
].Size
== 0)
1116 if (IS_EXTENDED_PART_TYPE(EMbrTable
[i
].Type
)) {
1117 // link to next EMBR
1118 NextExtCurrent
= ExtBase
+ EMbrTable
[i
].StartLBA
;
1119 EMbrTable
[i
].Flags
= (PartitionIndex
>= LogicalPartitionIndex
) ? 0x80 : 0x00;
1122 // logical partition
1123 EMbrTable
[i
].Flags
= (PartitionIndex
== LogicalPartitionIndex
) ? 0x80 : 0x00;
1124 LogicalPartitionIndex
++;
1128 // write current EMBR
1129 Status
= refit_call5_wrapper(BlockIO
->WriteBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, ExtCurrent
, 512, SectorBuffer
);
1130 if (EFI_ERROR(Status
))
1133 if (PartitionIndex
< LogicalPartitionIndex
)
1134 break; // stop the loop, no need to touch further EMBRs
1140 } /* static EFI_STATUS ActivateMbrPartition() */
1142 // early 2006 Core Duo / Core Solo models
1143 static UINT8 LegacyLoaderDevicePath1Data
[] = {
1144 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1145 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1146 0xFF, 0xFF, 0xF9, 0xFF, 0x00, 0x00, 0x00, 0x00,
1147 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1148 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1149 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1151 // mid-2006 Mac Pro (and probably other Core 2 models)
1152 static UINT8 LegacyLoaderDevicePath2Data
[] = {
1153 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1154 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1155 0xFF, 0xFF, 0xF7, 0xFF, 0x00, 0x00, 0x00, 0x00,
1156 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1157 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1158 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1160 // mid-2007 MBP ("Santa Rosa" based models)
1161 static UINT8 LegacyLoaderDevicePath3Data
[] = {
1162 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1163 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1164 0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
1165 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1166 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1167 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1170 static UINT8 LegacyLoaderDevicePath4Data
[] = {
1171 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1172 0x00, 0x00, 0xC0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1173 0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
1174 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1175 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1176 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1178 // late-2008 MB/MBP (NVidia chipset)
1179 static UINT8 LegacyLoaderDevicePath5Data
[] = {
1180 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1181 0x00, 0x40, 0xCB, 0xFF, 0x00, 0x00, 0x00, 0x00,
1182 0xFF, 0xBF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
1183 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1184 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1185 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1188 static EFI_DEVICE_PATH
*LegacyLoaderList
[] = {
1189 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath1Data
,
1190 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath2Data
,
1191 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath3Data
,
1192 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath4Data
,
1193 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath5Data
,
1197 #define MAX_DISCOVERED_PATHS (16)
1199 static VOID
StartLegacy(IN LEGACY_ENTRY
*Entry
)
1202 EG_IMAGE
*BootLogoImage
;
1203 UINTN ErrorInStep
= 0;
1204 EFI_DEVICE_PATH
*DiscoveredPathList
[MAX_DISCOVERED_PATHS
];
1206 BeginExternalScreen(TRUE
, L
"Booting Legacy OS (Mac mode)");
1208 BootLogoImage
= LoadOSIcon(Entry
->Volume
->OSIconName
, L
"legacy", TRUE
);
1209 if (BootLogoImage
!= NULL
)
1210 BltImageAlpha(BootLogoImage
,
1211 (UGAWidth
- BootLogoImage
->Width
) >> 1,
1212 (UGAHeight
- BootLogoImage
->Height
) >> 1,
1213 &StdBackgroundPixel
);
1215 if (Entry
->Volume
->IsMbrPartition
) {
1216 ActivateMbrPartition(Entry
->Volume
->WholeDiskBlockIO
, Entry
->Volume
->MbrPartitionIndex
);
1219 ExtractLegacyLoaderPaths(DiscoveredPathList
, MAX_DISCOVERED_PATHS
, LegacyLoaderList
);
1221 Status
= StartEFIImageList(DiscoveredPathList
, Entry
->LoadOptions
, NULL
, L
"legacy loader", 0, &ErrorInStep
, TRUE
);
1222 if (Status
== EFI_NOT_FOUND
) {
1223 if (ErrorInStep
== 1) {
1224 Print(L
"\nPlease make sure that you have the latest firmware update installed.\n");
1225 } else if (ErrorInStep
== 3) {
1226 Print(L
"\nThe firmware refused to boot from the selected volume. Note that external\n"
1227 L
"hard drives are not well-supported by Apple's firmware for legacy OS booting.\n");
1230 FinishExternalScreen();
1231 } /* static VOID StartLegacy() */
1233 // Start a device on a non-Mac using the EFI_LEGACY_BIOS_PROTOCOL
1234 #ifdef __MAKEWITH_TIANO
1235 static VOID
StartLegacyUEFI(IN LEGACY_ENTRY
*Entry
)
1237 BeginExternalScreen(TRUE
, L
"Booting Legacy OS (UEFI mode)");
1239 BdsLibConnectDevicePath (Entry
->BdsOption
->DevicePath
);
1240 BdsLibDoLegacyBoot(Entry
->BdsOption
);
1242 // If we get here, it means that there was a failure....
1243 Print(L
"Failure booting legacy (BIOS) OS.");
1245 FinishExternalScreen();
1246 } // static VOID StartLegacyUEFI()
1247 #endif // __MAKEWITH_TIANO
1249 static LEGACY_ENTRY
* AddLegacyEntry(IN CHAR16
*LoaderTitle
, IN REFIT_VOLUME
*Volume
)
1251 LEGACY_ENTRY
*Entry
, *SubEntry
;
1252 REFIT_MENU_SCREEN
*SubScreen
;
1254 CHAR16 ShortcutLetter
= 0;
1256 if (LoaderTitle
== NULL
) {
1257 if (Volume
->OSName
!= NULL
) {
1258 LoaderTitle
= Volume
->OSName
;
1259 if (LoaderTitle
[0] == 'W' || LoaderTitle
[0] == 'L')
1260 ShortcutLetter
= LoaderTitle
[0];
1262 LoaderTitle
= L
"Legacy OS";
1264 if (Volume
->VolName
!= NULL
)
1265 VolDesc
= Volume
->VolName
;
1267 VolDesc
= (Volume
->DiskKind
== DISK_KIND_OPTICAL
) ? L
"CD" : L
"HD";
1269 // prepare the menu entry
1270 Entry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1271 Entry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1272 SPrint(Entry
->me
.Title
, 255, L
"Boot %s from %s", LoaderTitle
, VolDesc
);
1273 Entry
->me
.Tag
= TAG_LEGACY
;
1275 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1276 Entry
->me
.Image
= LoadOSIcon(Volume
->OSIconName
, L
"legacy", FALSE
);
1277 Entry
->me
.BadgeImage
= Volume
->VolBadgeImage
;
1278 Entry
->Volume
= Volume
;
1279 Entry
->LoadOptions
= (Volume
->DiskKind
== DISK_KIND_OPTICAL
) ? L
"CD" :
1280 ((Volume
->DiskKind
== DISK_KIND_EXTERNAL
) ? L
"USB" : L
"HD");
1281 Entry
->Enabled
= TRUE
;
1283 // create the submenu
1284 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
1285 SubScreen
->Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1286 SPrint(SubScreen
->Title
, 255, L
"Boot Options for %s on %s", LoaderTitle
, VolDesc
);
1287 SubScreen
->TitleImage
= Entry
->me
.Image
;
1288 SubScreen
->Hint1
= StrDuplicate(SUBSCREEN_HINT1
);
1289 if (GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_EDITOR
) {
1290 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2_NO_EDITOR
);
1292 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2
);
1296 SubEntry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1297 SubEntry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1298 SPrint(SubEntry
->me
.Title
, 255, L
"Boot %s", LoaderTitle
);
1299 SubEntry
->me
.Tag
= TAG_LEGACY
;
1300 SubEntry
->Volume
= Entry
->Volume
;
1301 SubEntry
->LoadOptions
= Entry
->LoadOptions
;
1302 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
1304 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
1305 Entry
->me
.SubScreen
= SubScreen
;
1306 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1308 } /* static LEGACY_ENTRY * AddLegacyEntry() */
1311 #ifdef __MAKEWITH_TIANO
1312 // default volume badge icon based on disk kind
1313 static EG_IMAGE
* GetDiskBadge(IN UINTN DiskType
) {
1314 EG_IMAGE
* Badge
= NULL
;
1318 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_INTERNAL
);
1321 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_EXTERNAL
);
1324 Badge
= BuiltinIcon(BUILTIN_ICON_VOL_OPTICAL
);
1328 } // static EG_IMAGE * GetDiskBadge()
1331 Create a rEFInd boot option from a Legacy BIOS protocol option.
1333 static LEGACY_ENTRY
* AddLegacyEntryUEFI(BDS_COMMON_OPTION
*BdsOption
, IN UINT16 DiskType
)
1335 LEGACY_ENTRY
*Entry
, *SubEntry
;
1336 REFIT_MENU_SCREEN
*SubScreen
;
1337 CHAR16 ShortcutLetter
= 0;
1338 CHAR16
*LegacyDescription
= BdsOption
->Description
;
1340 // ScanVolume(Volume);
1342 // prepare the menu entry
1343 Entry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1344 Entry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1345 SPrint(Entry
->me
.Title
, 255, L
"Boot legacy target %s", LegacyDescription
);
1346 Entry
->me
.Tag
= TAG_LEGACY_UEFI
;
1348 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1349 Entry
->me
.Image
= LoadOSIcon(L
"legacy", L
"legacy", TRUE
);
1350 Entry
->LoadOptions
= (DiskType
== BBS_CDROM
) ? L
"CD" :
1351 ((DiskType
== BBS_USB
) ? L
"USB" : L
"HD");
1352 Entry
->me
.BadgeImage
= GetDiskBadge(DiskType
);
1353 // Entry->me.BadgeImage = Volume->VolBadgeImage;
1354 Entry
->BdsOption
= BdsOption
;
1355 Entry
->Enabled
= TRUE
;
1357 // create the submenu
1358 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
1359 SubScreen
->Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1360 SPrint(SubScreen
->Title
, 255, L
"No boot options for legacy target");
1361 SubScreen
->TitleImage
= Entry
->me
.Image
;
1362 SubScreen
->Hint1
= StrDuplicate(SUBSCREEN_HINT1
);
1363 if (GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_EDITOR
) {
1364 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2_NO_EDITOR
);
1366 SubScreen
->Hint2
= StrDuplicate(SUBSCREEN_HINT2
);
1370 SubEntry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1371 SubEntry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1372 SPrint(SubEntry
->me
.Title
, 255, L
"Boot %s", LegacyDescription
);
1373 SubEntry
->me
.Tag
= TAG_LEGACY_UEFI
;
1374 Entry
->BdsOption
= BdsOption
;
1375 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
1377 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
1378 Entry
->me
.SubScreen
= SubScreen
;
1379 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1381 } /* static LEGACY_ENTRY * AddLegacyEntryUEFI() */
1384 Scan for legacy BIOS targets on machines that implement EFI_LEGACY_BIOS_PROTOCOL.
1385 In testing, protocol has not been implemented on Macs but has been
1386 implemented on several Dell PCs and an ASUS motherboard.
1387 Restricts output to disks of the specified DiskType.
1389 static VOID
ScanLegacyUEFI(IN UINTN DiskType
)
1392 EFI_LEGACY_BIOS_PROTOCOL
*LegacyBios
;
1393 UINT16
*BootOrder
= NULL
;
1395 CHAR16 BootOption
[10];
1396 UINTN BootOrderSize
= 0;
1398 BDS_COMMON_OPTION
*BdsOption
;
1399 LIST_ENTRY TempList
;
1400 BBS_BBS_DEVICE_PATH
* BbsDevicePath
= NULL
;
1401 // REFIT_VOLUME Volume;
1403 InitializeListHead (&TempList
);
1404 ZeroMem (Buffer
, sizeof (Buffer
));
1406 // If LegacyBios protocol is not implemented on this platform, then
1407 //we do not support this type of legacy boot on this machine.
1408 Status
= gBS
->LocateProtocol(&gEfiLegacyBootProtocolGuid
, NULL
, (VOID
**) &LegacyBios
);
1409 if (EFI_ERROR (Status
))
1412 // Grab the boot order
1413 BootOrder
= BdsLibGetVariableAndSize(L
"BootOrder", &gEfiGlobalVariableGuid
, &BootOrderSize
);
1414 if (BootOrder
== NULL
) {
1419 while (Index
< BootOrderSize
/ sizeof (UINT16
))
1421 // Grab each boot option variable from the boot order, and convert
1422 // the variable into a BDS boot option
1423 UnicodeSPrint (BootOption
, sizeof (BootOption
), L
"Boot%04x", BootOrder
[Index
]);
1424 BdsOption
= BdsLibVariableToOption (&TempList
, BootOption
);
1426 if (BdsOption
!= NULL
) {
1427 BbsDevicePath
= (BBS_BBS_DEVICE_PATH
*)BdsOption
->DevicePath
;
1429 // Only add the entry if it is of a requested type (e.g. USB, HD)
1431 // Two checks necessary because some systems return EFI boot loaders
1432 // with a DeviceType value that would inappropriately include them
1433 // as legacy loaders....
1434 if ((BbsDevicePath
->DeviceType
== DiskType
) && (BdsOption
->DevicePath
->Type
== DEVICE_TYPE_BIOS
)) {
1435 AddLegacyEntryUEFI(BdsOption
, BbsDevicePath
->DeviceType
);
1440 } /* static VOID ScanLegacyUEFI() */
1442 static VOID
ScanLegacyUEFI(IN UINTN DiskType
){}
1443 #endif // __MAKEWITH_TIANO
1445 static VOID
ScanLegacyVolume(REFIT_VOLUME
*Volume
, UINTN VolumeIndex
) {
1447 BOOLEAN ShowVolume
, HideIfOthersFound
;
1450 HideIfOthersFound
= FALSE
;
1451 if (Volume
->IsAppleLegacy
) {
1453 HideIfOthersFound
= TRUE
;
1454 } else if (Volume
->HasBootCode
) {
1456 if (Volume
->BlockIO
== Volume
->WholeDiskBlockIO
&&
1457 Volume
->BlockIOOffset
== 0 &&
1458 Volume
->OSName
== NULL
)
1459 // this is a whole disk (MBR) entry; hide if we have entries for partitions
1460 HideIfOthersFound
= TRUE
;
1462 if (HideIfOthersFound
) {
1463 // check for other bootable entries on the same disk
1464 for (VolumeIndex2
= 0; VolumeIndex2
< VolumesCount
; VolumeIndex2
++) {
1465 if (VolumeIndex2
!= VolumeIndex
&& Volumes
[VolumeIndex2
]->HasBootCode
&&
1466 Volumes
[VolumeIndex2
]->WholeDiskBlockIO
== Volume
->WholeDiskBlockIO
)
1472 AddLegacyEntry(NULL
, Volume
);
1473 } // static VOID ScanLegacyVolume()
1475 // Scan attached optical discs for legacy (BIOS) boot code
1476 // and add anything found to the list....
1477 static VOID
ScanLegacyDisc(VOID
)
1480 REFIT_VOLUME
*Volume
;
1482 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
) {
1483 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1484 Volume
= Volumes
[VolumeIndex
];
1485 if (Volume
->DiskKind
== DISK_KIND_OPTICAL
)
1486 ScanLegacyVolume(Volume
, VolumeIndex
);
1488 } else if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
1489 ScanLegacyUEFI(BBS_CDROM
);
1491 } /* static VOID ScanLegacyDisc() */
1493 // Scan internal hard disks for legacy (BIOS) boot code
1494 // and add anything found to the list....
1495 static VOID
ScanLegacyInternal(VOID
)
1498 REFIT_VOLUME
*Volume
;
1500 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
) {
1501 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1502 Volume
= Volumes
[VolumeIndex
];
1503 if (Volume
->DiskKind
== DISK_KIND_INTERNAL
)
1504 ScanLegacyVolume(Volume
, VolumeIndex
);
1506 } else if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
1507 ScanLegacyUEFI(BBS_HARDDISK
);
1509 } /* static VOID ScanLegacyInternal() */
1511 // Scan external disks for legacy (BIOS) boot code
1512 // and add anything found to the list....
1513 static VOID
ScanLegacyExternal(VOID
)
1516 REFIT_VOLUME
*Volume
;
1518 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
) {
1519 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1520 Volume
= Volumes
[VolumeIndex
];
1521 if (Volume
->DiskKind
== DISK_KIND_EXTERNAL
)
1522 ScanLegacyVolume(Volume
, VolumeIndex
);
1524 } else if (GlobalConfig
.LegacyType
== LEGACY_TYPE_UEFI
) {
1525 ScanLegacyUEFI(BBS_USB
);
1527 } /* static VOID ScanLegacyExternal() */
1530 // pre-boot tool functions
1533 static VOID
StartTool(IN LOADER_ENTRY
*Entry
)
1535 BeginExternalScreen(Entry
->UseGraphicsMode
, Entry
->me
.Title
+ 6); // assumes "Start <title>" as assigned below
1536 StartEFIImage(Entry
->DevicePath
, Entry
->LoadOptions
, Basename(Entry
->LoaderPath
),
1537 Basename(Entry
->LoaderPath
), Entry
->OSType
, NULL
, TRUE
);
1538 FinishExternalScreen();
1539 } /* static VOID StartTool() */
1541 static LOADER_ENTRY
* AddToolEntry(EFI_HANDLE DeviceHandle
, IN CHAR16
*LoaderPath
, IN CHAR16
*LoaderTitle
, IN EG_IMAGE
*Image
,
1542 IN CHAR16 ShortcutLetter
, IN BOOLEAN UseGraphicsMode
)
1544 LOADER_ENTRY
*Entry
;
1545 CHAR16
*TitleStr
= NULL
;
1547 Entry
= AllocateZeroPool(sizeof(LOADER_ENTRY
));
1549 MergeStrings(&TitleStr
, L
"Start ", 0);
1550 MergeStrings(&TitleStr
, LoaderTitle
, 0);
1551 Entry
->me
.Title
= TitleStr
;
1552 Entry
->me
.Tag
= TAG_TOOL
;
1554 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1555 Entry
->me
.Image
= Image
;
1556 Entry
->LoaderPath
= (LoaderPath
) ? StrDuplicate(LoaderPath
) : NULL
;
1557 Entry
->DevicePath
= FileDevicePath(DeviceHandle
, Entry
->LoaderPath
);
1558 Entry
->UseGraphicsMode
= UseGraphicsMode
;
1560 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1562 } /* static LOADER_ENTRY * AddToolEntry() */
1565 // pre-boot driver functions
1568 static UINTN
ScanDriverDir(IN CHAR16
*Path
)
1571 REFIT_DIR_ITER DirIter
;
1573 EFI_FILE_INFO
*DirEntry
;
1574 CHAR16 FileName
[256];
1576 CleanUpPathNameSlashes(Path
);
1577 // look through contents of the directory
1578 DirIterOpen(SelfRootDir
, Path
, &DirIter
);
1579 while (DirIterNext(&DirIter
, 2, LOADER_MATCH_PATTERNS
, &DirEntry
)) {
1580 if (DirEntry
->FileName
[0] == '.')
1581 continue; // skip this
1583 SPrint(FileName
, 255, L
"%s\\%s", Path
, DirEntry
->FileName
);
1585 Status
= StartEFIImage(FileDevicePath(SelfLoadedImage
->DeviceHandle
, FileName
),
1586 L
"", DirEntry
->FileName
, DirEntry
->FileName
, 0, NULL
, FALSE
);
1588 Status
= DirIterClose(&DirIter
);
1589 if (Status
!= EFI_NOT_FOUND
) {
1590 SPrint(FileName
, 255, L
"while scanning the %s directory", Path
);
1591 CheckError(Status
, FileName
);
1596 #ifdef __MAKEWITH_GNUEFI
1597 static EFI_STATUS
ConnectAllDriversToAllControllers(VOID
)
1600 UINTN AllHandleCount
;
1601 EFI_HANDLE
*AllHandleBuffer
;
1604 EFI_HANDLE
*HandleBuffer
;
1610 Status
= LibLocateHandle(AllHandles
,
1615 if (EFI_ERROR(Status
))
1618 for (Index
= 0; Index
< AllHandleCount
; Index
++) {
1620 // Scan the handle database
1622 Status
= LibScanHandleDatabase(NULL
,
1624 AllHandleBuffer
[Index
],
1629 if (EFI_ERROR (Status
))
1633 if (HandleType
[Index
] & EFI_HANDLE_TYPE_DRIVER_BINDING_HANDLE
)
1635 if (HandleType
[Index
] & EFI_HANDLE_TYPE_IMAGE_HANDLE
)
1640 for (HandleIndex
= 0; HandleIndex
< HandleCount
; HandleIndex
++) {
1641 if (HandleType
[HandleIndex
] & EFI_HANDLE_TYPE_PARENT_HANDLE
)
1646 if (HandleType
[Index
] & EFI_HANDLE_TYPE_DEVICE_HANDLE
) {
1647 Status
= refit_call4_wrapper(BS
->ConnectController
,
1648 AllHandleBuffer
[Index
],
1656 MyFreePool (HandleBuffer
);
1657 MyFreePool (HandleType
);
1661 MyFreePool (AllHandleBuffer
);
1663 } /* EFI_STATUS ConnectAllDriversToAllControllers() */
1665 static EFI_STATUS
ConnectAllDriversToAllControllers(VOID
) {
1666 BdsLibConnectAllDriversToAllControllers();
1671 // Load all EFI drivers from rEFInd's "drivers" subdirectory and from the
1672 // directories specified by the user in the "scan_driver_dirs" configuration
1674 static VOID
LoadDrivers(VOID
)
1676 CHAR16
*Directory
, *SelfDirectory
;
1677 UINTN i
= 0, Length
, NumFound
= 0;
1679 // load drivers from the subdirectories of rEFInd's home directory specified
1680 // in the DRIVER_DIRS constant.
1681 while ((Directory
= FindCommaDelimited(DRIVER_DIRS
, i
++)) != NULL
) {
1682 SelfDirectory
= SelfDirPath
? StrDuplicate(SelfDirPath
) : NULL
;
1683 CleanUpPathNameSlashes(SelfDirectory
);
1684 MergeStrings(&SelfDirectory
, Directory
, L
'\\');
1685 NumFound
+= ScanDriverDir(SelfDirectory
);
1686 MyFreePool(Directory
);
1687 MyFreePool(SelfDirectory
);
1690 // Scan additional user-specified driver directories....
1692 while ((Directory
= FindCommaDelimited(GlobalConfig
.DriverDirs
, i
++)) != NULL
) {
1693 CleanUpPathNameSlashes(Directory
);
1694 Length
= StrLen(Directory
);
1696 NumFound
+= ScanDriverDir(Directory
);
1698 MyFreePool(Directory
);
1701 // connect all devices
1703 ConnectAllDriversToAllControllers();
1704 } /* static VOID LoadDrivers() */
1706 // Determine what (if any) type of legacy (BIOS) boot support is available
1707 static VOID
FindLegacyBootType(VOID
) {
1708 #ifdef __MAKEWITH_TIANO
1710 EFI_LEGACY_BIOS_PROTOCOL
*LegacyBios
;
1713 GlobalConfig
.LegacyType
= LEGACY_TYPE_NONE
;
1715 // UEFI-style legacy BIOS support is available only with the TianoCore EDK2
1716 // build environment, and then only with some implementations....
1717 #ifdef __MAKEWITH_TIANO
1718 Status
= gBS
->LocateProtocol (&gEfiLegacyBootProtocolGuid
, NULL
, (VOID
**) &LegacyBios
);
1719 if (!EFI_ERROR (Status
))
1720 GlobalConfig
.LegacyType
= LEGACY_TYPE_UEFI
;
1723 // Macs have their own system. If the firmware vendor code contains the
1724 // string "Apple", assume it's available. Note that this overrides the
1725 // UEFI type, and might yield false positives if the vendor string
1726 // contains "Apple" as part of something bigger, so this isn't 100%
1728 if (StriSubCmp(L
"Apple", ST
->FirmwareVendor
))
1729 GlobalConfig
.LegacyType
= LEGACY_TYPE_MAC
;
1730 } // static VOID FindLegacyBootType
1732 // Warn the user if legacy OS scans are enabled but the firmware or this
1733 // application can't support them....
1734 static VOID
WarnIfLegacyProblems() {
1735 BOOLEAN found
= FALSE
;
1738 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_NONE
) {
1740 if (GlobalConfig
.ScanFor
[i
] == 'h' || GlobalConfig
.ScanFor
[i
] == 'b' || GlobalConfig
.ScanFor
[i
] == 'c')
1743 } while ((i
< NUM_SCAN_OPTIONS
) && (!found
));
1745 Print(L
"NOTE: refind.conf's 'scanfor' line specifies scanning for one or more legacy\n");
1746 Print(L
"(BIOS) boot options; however, this is not possible because ");
1747 #ifdef __MAKEWITH_TIANO
1748 Print(L
"your computer lacks\n");
1749 Print(L
"the necessary Compatibility Support Module (CSM) support.\n");
1751 Print(L
"this program was\n");
1752 Print(L
"compiled without the necessary support. Please visit\n");
1753 Print(L
"http://www.rodsbooks.com/refind/getting.html and download and install a rEFInd\n");
1754 Print(L
"binary built with the TianoCore EDK2 to enable legacy boot support.\n");
1758 } // if no legacy support
1759 } // static VOID WarnIfLegacyProblems()
1761 // Locates boot loaders. NOTE: This assumes that GlobalConfig.LegacyType is set correctly.
1762 static VOID
ScanForBootloaders(VOID
) {
1767 // scan for loaders and tools, add them to the menu
1768 for (i
= 0; i
< NUM_SCAN_OPTIONS
; i
++) {
1769 switch(GlobalConfig
.ScanFor
[i
]) {
1774 ScanLegacyInternal();
1777 ScanLegacyExternal();
1780 ScanUserConfigured();
1794 // assign shortcut keys
1795 for (i
= 0; i
< MainMenu
.EntryCount
&& MainMenu
.Entries
[i
]->Row
== 0 && i
< 9; i
++)
1796 MainMenu
.Entries
[i
]->ShortcutDigit
= (CHAR16
)('1' + i
);
1798 // wait for user ACK when there were errors
1799 FinishTextScreen(FALSE
);
1800 } // static VOID ScanForBootloaders()
1802 // Add the second-row tags containing built-in and external tools (EFI shell,
1804 static VOID
ScanForTools(VOID
) {
1805 CHAR16
*FileName
= NULL
, Description
[256];
1806 REFIT_MENU_ENTRY
*TempMenuEntry
;
1807 UINTN i
, j
, VolumeIndex
;
1809 for (i
= 0; i
< NUM_TOOLS
; i
++) {
1810 switch(GlobalConfig
.ShowTools
[i
]) {
1812 TempMenuEntry
= CopyMenuEntry(&MenuEntryShutdown
);
1813 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_SHUTDOWN
);
1814 AddMenuEntry(&MainMenu
, TempMenuEntry
);
1817 TempMenuEntry
= CopyMenuEntry(&MenuEntryReset
);
1818 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_RESET
);
1819 AddMenuEntry(&MainMenu
, TempMenuEntry
);
1822 TempMenuEntry
= CopyMenuEntry(&MenuEntryAbout
);
1823 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT
);
1824 AddMenuEntry(&MainMenu
, TempMenuEntry
);
1827 TempMenuEntry
= CopyMenuEntry(&MenuEntryExit
);
1828 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_EXIT
);
1829 AddMenuEntry(&MainMenu
, TempMenuEntry
);
1833 MyFreePool(FileName
);
1834 while ((FileName
= FindCommaDelimited(SHELL_NAMES
, j
++)) != NULL
) {
1835 if (FileExists(SelfRootDir
, FileName
)) {
1836 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, L
"EFI Shell", BuiltinIcon(BUILTIN_ICON_TOOL_SHELL
),
1842 MyFreePool(FileName
);
1843 FileName
= StrDuplicate(L
"\\efi\\tools\\gptsync.efi");
1844 // MergeStrings(&FileName, L"\\efi\\tools\\gptsync.efi", 0);
1845 if (FileExists(SelfRootDir
, FileName
)) {
1846 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, L
"Make Hybrid MBR", BuiltinIcon(BUILTIN_ICON_TOOL_PART
), 'P', FALSE
);
1849 case TAG_APPLE_RECOVERY
:
1850 MyFreePool(FileName
);
1851 FileName
= StrDuplicate(L
"\\com.apple.recovery.boot\\boot.efi");
1852 // MergeStrings(&FileName, L"\\com.apple.recovery.boot\\boot.efi", 0);
1853 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1854 if ((Volumes
[VolumeIndex
]->RootDir
!= NULL
) && (FileExists(Volumes
[VolumeIndex
]->RootDir
, FileName
))) {
1855 SPrint(Description
, 255, L
"Apple Recovery on %s", Volumes
[VolumeIndex
]->VolName
);
1856 AddToolEntry(Volumes
[VolumeIndex
]->DeviceHandle
, FileName
, Description
,
1857 BuiltinIcon(BUILTIN_ICON_TOOL_APPLE_RESCUE
), 'R', TRUE
);
1863 MyFreePool(FileName
);
1864 while ((FileName
= FindCommaDelimited(MOK_NAMES
, j
++)) != NULL
) {
1865 if (FileExists(SelfRootDir
, FileName
)) {
1866 SPrint(Description
, 255, L
"MOK Key Manager at %s", FileName
);
1867 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, Description
,
1868 BuiltinIcon(BUILTIN_ICON_TOOL_MOK_TOOL
), 'S', FALSE
);
1871 if (FileExists(SelfDir
, L
"MokManager.efi")) {
1872 MyFreePool(FileName
);
1873 FileName
= StrDuplicate(SelfDirPath
);
1874 MergeStrings(&FileName
, L
"\\MokManager.efi", 0);
1875 SPrint(Description
, 255, L
"MOK Key Manager at %s", FileName
);
1876 AddToolEntry(SelfLoadedImage
->DeviceHandle
, FileName
, Description
,
1877 BuiltinIcon(BUILTIN_ICON_TOOL_MOK_TOOL
), 'S', FALSE
);
1881 MyFreePool(FileName
);
1884 } // static VOID ScanForTools
1886 // Rescan for boot loaders
1887 VOID
RescanAll(VOID
) {
1894 egDisplayMessage(L
"Scanning for new boot loaders; please wait....", &BGColor
);
1895 FreeList((VOID
***) &(MainMenu
.Entries
), &MainMenu
.EntryCount
);
1896 MainMenu
.Entries
= NULL
;
1897 MainMenu
.EntryCount
= 0;
1898 ReadConfig(CONFIG_FILE_NAME
);
1899 ConnectAllDriversToAllControllers();
1901 ScanForBootloaders();
1904 } // VOID RescanAll()
1906 #ifndef __MAKEWITH_GNUEFI
1908 // Minimal initialization function
1909 static VOID
InitializeLib(IN EFI_HANDLE ImageHandle
, IN EFI_SYSTEM_TABLE
*SystemTable
) {
1911 // gImageHandle = ImageHandle;
1912 gBS
= SystemTable
->BootServices
;
1913 // gRS = SystemTable->RuntimeServices;
1914 gRT
= SystemTable
->RuntimeServices
; // Some BDS functions need gRT to be set
1915 EfiGetSystemConfigurationTable (&gEfiDxeServicesTableGuid
, (VOID
**) &gDS
);
1917 InitializeConsoleSim();
1927 efi_main (IN EFI_HANDLE ImageHandle
, IN EFI_SYSTEM_TABLE
*SystemTable
)
1930 BOOLEAN MainLoopRunning
= TRUE
;
1931 REFIT_MENU_ENTRY
*ChosenEntry
;
1937 InitializeLib(ImageHandle
, SystemTable
);
1939 Status
= InitRefitLib(ImageHandle
);
1940 if (EFI_ERROR(Status
))
1943 // read configuration
1944 CopyMem(GlobalConfig
.ScanFor
, "ieom ", NUM_SCAN_OPTIONS
);
1945 FindLegacyBootType();
1946 if (GlobalConfig
.LegacyType
== LEGACY_TYPE_MAC
)
1947 CopyMem(GlobalConfig
.ScanFor
, "ihebocm ", NUM_SCAN_OPTIONS
);
1948 ReadConfig(CONFIG_FILE_NAME
);
1949 WarnIfLegacyProblems();
1950 MainMenu
.TimeoutSeconds
= GlobalConfig
.Timeout
;
1952 // disable EFI watchdog timer
1953 refit_call4_wrapper(BS
->SetWatchdogTimer
, 0x0000, 0x0000, 0x0000, NULL
);
1955 // further bootstrap (now with config available)
1959 ScanForBootloaders();
1962 if (GlobalConfig
.ScanDelay
> 0) {
1967 egDisplayMessage(L
"Pausing before disk scan; please wait....", &BGColor
);
1968 for (i
= 0; i
< GlobalConfig
.ScanDelay
; i
++)
1969 refit_call1_wrapper(BS
->Stall
, 1000000);
1973 Selection
= StrDuplicate(GlobalConfig
.DefaultSelection
);
1974 while (MainLoopRunning
) {
1975 MenuExit
= RunMainMenu(&MainMenu
, Selection
, &ChosenEntry
);
1977 // The Escape key triggers a re-scan operation....
1978 if (MenuExit
== MENU_EXIT_ESCAPE
) {
1983 switch (ChosenEntry
->Tag
) {
1985 case TAG_REBOOT
: // Reboot
1987 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);
1988 MainLoopRunning
= FALSE
; // just in case we get this far
1991 case TAG_SHUTDOWN
: // Shut Down
1993 refit_call4_wrapper(RT
->ResetSystem
, EfiResetShutdown
, EFI_SUCCESS
, 0, NULL
);
1994 MainLoopRunning
= FALSE
; // just in case we get this far
1997 case TAG_ABOUT
: // About rEFInd
2001 case TAG_LOADER
: // Boot OS via .EFI loader
2002 StartLoader((LOADER_ENTRY
*)ChosenEntry
);
2005 case TAG_LEGACY
: // Boot legacy OS
2006 StartLegacy((LEGACY_ENTRY
*)ChosenEntry
);
2009 #ifdef __MAKEWITH_TIANO
2010 case TAG_LEGACY_UEFI
: // Boot a legacy OS on a non-Mac
2011 StartLegacyUEFI((LEGACY_ENTRY
*)ChosenEntry
);
2013 #endif // __MAKEWITH_TIANO
2015 case TAG_TOOL
: // Start a EFI tool
2016 StartTool((LOADER_ENTRY
*)ChosenEntry
);
2019 case TAG_EXIT
: // Terminate rEFInd
2020 BeginTextScreen(L
" ");
2025 MyFreePool(Selection
);
2026 Selection
= (ChosenEntry
->Title
) ? StrDuplicate(ChosenEntry
->Title
) : NULL
;
2029 // If we end up here, things have gone wrong. Try to reboot, and if that
2030 // fails, go into an endless loop.
2031 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);