3 * Main code for the boot menu
5 * Copyright (c) 2006-2010 Christoph Pfisterer
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions are
12 * * Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
15 * * Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the
20 * * Neither the name of Christoph Pfisterer nor the names of the
21 * contributors may be used to endorse or promote products derived
22 * from this software without specific prior written permission.
24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
26 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
27 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
28 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
29 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
30 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
31 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
32 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
33 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
34 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37 * Modifications copyright (c) 2012 Roderick W. Smith
39 * Modifications distributed under the terms of the GNU General Public
40 * License (GPL) version 3 (GPLv3), a copy of which must be distributed
41 * with this source code or binaries made from it.
51 #include "../include/Handle.h"
52 #include "../include/refit_call_wrapper.h"
53 #include "driver_support.h"
54 #include "../include/syslinux_mbr.h"
59 #define MACOSX_LOADER_PATH L"System\\Library\\CoreServices\\boot.efi"
61 #define SHELL_NAMES L"\\EFI\\tools\\shell.efi,\\shellx64.efi"
62 #define DRIVER_DIRS L"drivers,drivers_x64"
64 #define SHELL_NAMES L"\\EFI\\tools\\shell.efi,\\shellia32.efi"
65 #define DRIVER_DIRS L"drivers,drivers_ia32"
67 #define SHELL_NAMES L"\\EFI\\tools\\shell.efi"
68 #define DRIVER_DIRS L"drivers"
71 // Filename patterns that identify EFI boot loaders. Note that a single case (either L"*.efi" or
72 // L"*.EFI") is fine for most systems; but Gigabyte's buggy Hybrid EFI does a case-sensitive
73 // comparison when it should do a case-insensitive comparison, so I'm doubling this up. It does
74 // no harm on other computers, AFAIK. In theory, every case variation should be done for
75 // completeness, but that's ridiculous....
76 #define LOADER_MATCH_PATTERNS L"*.efi,*.EFI"
78 // Patterns that identify Linux kernels. Added to the loader match pattern when the
79 // scan_all_linux_kernels option is set in the configuration file. Causes kernels WITHOUT
80 // a ".efi" extension to be found when scanning for boot loaders.
81 #define LINUX_MATCH_PATTERNS L"vmlinuz*,bzImage*"
83 static REFIT_MENU_ENTRY MenuEntryAbout
= { L
"About rEFInd", TAG_ABOUT
, 1, 0, 'A', NULL
, NULL
, NULL
};
84 static REFIT_MENU_ENTRY MenuEntryReset
= { L
"Reboot Computer", TAG_REBOOT
, 1, 0, 'R', NULL
, NULL
, NULL
};
85 static REFIT_MENU_ENTRY MenuEntryShutdown
= { L
"Shut Down Computer", TAG_SHUTDOWN
, 1, 0, 'U', NULL
, NULL
, NULL
};
86 static REFIT_MENU_ENTRY MenuEntryReturn
= { L
"Return to Main Menu", TAG_RETURN
, 0, 0, 0, NULL
, NULL
, NULL
};
87 static REFIT_MENU_ENTRY MenuEntryExit
= { L
"Exit rEFInd", TAG_EXIT
, 1, 0, 0, NULL
, NULL
, NULL
};
89 static REFIT_MENU_SCREEN MainMenu
= { L
"Main Menu", NULL
, 0, NULL
, 0, NULL
, 0, L
"Automatic boot" };
90 static REFIT_MENU_SCREEN AboutMenu
= { L
"About", NULL
, 0, NULL
, 0, NULL
, 0, NULL
};
92 REFIT_CONFIG GlobalConfig
= { FALSE
, FALSE
, 0, 0, 20, 0, 0, GRAPHICS_FOR_OSX
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
93 {TAG_SHELL
, TAG_ABOUT
, TAG_SHUTDOWN
, TAG_REBOOT
, 0, 0, 0, 0, 0 }};
95 // Structure used to hold boot loader filenames and time stamps in
96 // a linked list; used to sort entries within a directory.
100 struct LOADER_LIST
*NextEntry
;
107 static VOID
AboutrEFInd(VOID
)
109 CHAR16
*TempStr
; // Note: Don't deallocate; moved to menu structure
111 if (AboutMenu
.EntryCount
== 0) {
112 AboutMenu
.TitleImage
= BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT
);
113 AddMenuInfoLine(&AboutMenu
, L
"rEFInd Version 0.4.4");
114 AddMenuInfoLine(&AboutMenu
, L
"");
115 AddMenuInfoLine(&AboutMenu
, L
"Copyright (c) 2006-2010 Christoph Pfisterer");
116 AddMenuInfoLine(&AboutMenu
, L
"Copyright (c) 2012 Roderick W. Smith");
117 AddMenuInfoLine(&AboutMenu
, L
"Portions Copyright (c) Intel Corporation and others");
118 AddMenuInfoLine(&AboutMenu
, L
"Distributed under the terms of the GNU GPLv3 license");
119 AddMenuInfoLine(&AboutMenu
, L
"");
120 AddMenuInfoLine(&AboutMenu
, L
"Running on:");
121 TempStr
= AllocateZeroPool(256 * sizeof(CHAR16
));
122 SPrint(TempStr
, 255, L
" EFI Revision %d.%02d", ST
->Hdr
.Revision
>> 16, ST
->Hdr
.Revision
& ((1 << 16) - 1));
123 AddMenuInfoLine(&AboutMenu
, TempStr
);
125 AddMenuInfoLine(&AboutMenu
, L
" Platform: x86 (32 bit)");
126 #elif defined(EFIX64)
127 AddMenuInfoLine(&AboutMenu
, L
" Platform: x86_64 (64 bit)");
129 AddMenuInfoLine(&AboutMenu
, L
" Platform: unknown");
131 TempStr
= AllocateZeroPool(256 * sizeof(CHAR16
));
132 SPrint(TempStr
, 255, L
" Firmware: %s %d.%02d",
133 ST
->FirmwareVendor
, ST
->FirmwareRevision
>> 16, ST
->FirmwareRevision
& ((1 << 16) - 1));
134 AddMenuInfoLine(&AboutMenu
, TempStr
);
135 TempStr
= AllocateZeroPool(256 * sizeof(CHAR16
));
136 SPrint(TempStr
, 255, L
" Screen Output: %s", egScreenDescription());
137 AddMenuInfoLine(&AboutMenu
, TempStr
);
138 AddMenuInfoLine(&AboutMenu
, L
"");
139 #if defined(__MAKEWITH_GNUEFI)
140 AddMenuInfoLine(&AboutMenu
, L
"Built with GNU-EFI");
142 AddMenuInfoLine(&AboutMenu
, L
"Built with TianoCore EDK2");
144 AddMenuInfoLine(&AboutMenu
, L
"");
145 AddMenuInfoLine(&AboutMenu
, L
"For more information, see the rEFInd Web site:");
146 AddMenuInfoLine(&AboutMenu
, L
"http://www.rodsbooks.com/refind/");
147 AddMenuEntry(&AboutMenu
, &MenuEntryReturn
);
150 RunMenu(&AboutMenu
, NULL
);
151 } /* VOID AboutrEFInd() */
153 static EFI_STATUS
StartEFIImageList(IN EFI_DEVICE_PATH
**DevicePaths
,
154 IN CHAR16
*LoadOptions
, IN CHAR16
*LoadOptionsPrefix
,
155 IN CHAR16
*ImageTitle
,
156 OUT UINTN
*ErrorInStep
,
159 EFI_STATUS Status
, ReturnStatus
;
160 EFI_HANDLE ChildImageHandle
;
161 EFI_LOADED_IMAGE
*ChildLoadedImage
;
162 UINTN DevicePathIndex
;
163 CHAR16 ErrorInfo
[256];
164 CHAR16
*FullLoadOptions
= NULL
;
167 Print(L
"Starting %s\n", ImageTitle
);
168 if (ErrorInStep
!= NULL
)
171 // load the image into memory
172 ReturnStatus
= Status
= EFI_NOT_FOUND
; // in case the list is empty
173 for (DevicePathIndex
= 0; DevicePaths
[DevicePathIndex
] != NULL
; DevicePathIndex
++) {
174 ReturnStatus
= Status
= refit_call6_wrapper(BS
->LoadImage
, FALSE
, SelfImageHandle
, DevicePaths
[DevicePathIndex
], NULL
, 0, &ChildImageHandle
);
175 if (ReturnStatus
!= EFI_NOT_FOUND
) {
179 SPrint(ErrorInfo
, 255, L
"while loading %s", ImageTitle
);
180 if (CheckError(Status
, ErrorInfo
)) {
181 if (ErrorInStep
!= NULL
)
187 if (LoadOptions
!= NULL
) {
188 ReturnStatus
= Status
= refit_call3_wrapper(BS
->HandleProtocol
, ChildImageHandle
, &LoadedImageProtocol
, (VOID
**) &ChildLoadedImage
);
189 if (CheckError(Status
, L
"while getting a LoadedImageProtocol handle")) {
190 if (ErrorInStep
!= NULL
)
195 if (LoadOptionsPrefix
!= NULL
) {
196 MergeStrings(&FullLoadOptions
, LoadOptionsPrefix
, 0);
197 MergeStrings(&FullLoadOptions
, LoadOptions
, L
' ');
198 MergeStrings(&FullLoadOptions
, L
" ", 0);
199 // NOTE: That last space is also added by the EFI shell and seems to be significant
200 // when passing options to Apple's boot.efi...
202 // NOTE: We also include the terminating null in the length for safety.
203 ChildLoadedImage
->LoadOptions
= (VOID
*)FullLoadOptions
;
204 ChildLoadedImage
->LoadOptionsSize
= ((UINT32
)StrLen(FullLoadOptions
) + 1) * sizeof(CHAR16
);
206 Print(L
"Using load options '%s'\n", FullLoadOptions
);
209 // close open file handles
212 // turn control over to the image
213 // TODO: (optionally) re-enable the EFI watchdog timer!
214 ReturnStatus
= Status
= refit_call3_wrapper(BS
->StartImage
, ChildImageHandle
, NULL
, NULL
);
215 // control returns here when the child image calls Exit()
216 SPrint(ErrorInfo
, 255, L
"returned from %s", ImageTitle
);
217 if (CheckError(Status
, ErrorInfo
)) {
218 if (ErrorInStep
!= NULL
)
222 // re-open file handles
226 // unload the image, we don't care if it works or not...
227 Status
= refit_call1_wrapper(BS
->UnloadImage
, ChildImageHandle
);
229 if (FullLoadOptions
!= NULL
)
230 FreePool(FullLoadOptions
);
232 } /* static EFI_STATUS StartEFIImageList() */
234 static EFI_STATUS
StartEFIImage(IN EFI_DEVICE_PATH
*DevicePath
,
235 IN CHAR16
*LoadOptions
, IN CHAR16
*LoadOptionsPrefix
,
236 IN CHAR16
*ImageTitle
,
237 OUT UINTN
*ErrorInStep
,
240 EFI_DEVICE_PATH
*DevicePaths
[2];
242 DevicePaths
[0] = DevicePath
;
243 DevicePaths
[1] = NULL
;
244 return StartEFIImageList(DevicePaths
, LoadOptions
, LoadOptionsPrefix
, ImageTitle
, ErrorInStep
, Verbose
);
245 } /* static EFI_STATUS StartEFIImage() */
248 // EFI OS loader functions
251 static VOID
StartLoader(IN LOADER_ENTRY
*Entry
)
253 UINTN ErrorInStep
= 0;
255 BeginExternalScreen(Entry
->UseGraphicsMode
, L
"Booting OS");
256 StartEFIImage(Entry
->DevicePath
, Entry
->LoadOptions
,
257 Basename(Entry
->LoaderPath
), Basename(Entry
->LoaderPath
), &ErrorInStep
, !Entry
->UseGraphicsMode
);
258 FinishExternalScreen();
261 // Locate an initrd or initramfs file that matches the kernel specified by LoaderPath.
262 // The matching file has a name that begins with "init" and includes the same version
263 // number string as is found in LoaderPath -- but not a longer version number string.
264 // For instance, if LoaderPath is \EFI\kernels\bzImage-3.3.0.efi, and if \EFI\kernels
265 // has a file called initramfs-3.3.0.img, this function will return the string
266 // '\EFI\kernels\initramfs-3.3.0.img'. If the directory ALSO contains the file
267 // initramfs-3.3.0-rc7.img or initramfs-13.3.0.img, those files will NOT match;
268 // however, initmine-3.3.0.img might match. (FindInitrd() returns the first match it
269 // finds). Thus, care should be taken to avoid placing duplicate matching files in
270 // the kernel's directory.
271 // If no matching init file can be found, returns NULL.
272 static CHAR16
* FindInitrd(IN CHAR16
*LoaderPath
, IN REFIT_VOLUME
*Volume
) {
273 CHAR16
*InitrdName
= NULL
, *FileName
, *KernelVersion
, *InitrdVersion
, *Path
;
274 REFIT_DIR_ITER DirIter
;
275 EFI_FILE_INFO
*DirEntry
;
277 FileName
= Basename(LoaderPath
);
278 KernelVersion
= FindNumbers(FileName
);
279 Path
= FindPath(LoaderPath
);
281 // Add trailing backslash for root directory; necessary on some systems, but must
282 // NOT be added to all directories, since on other systems, a trailing backslash on
283 // anything but the root directory causes them to flake out!
284 if (StrLen(Path
) == 0) {
285 MergeStrings(&Path
, L
"\\", 0);
287 DirIterOpen(Volume
->RootDir
, Path
, &DirIter
);
288 // Now add a trailing backslash if it was NOT added earlier, for consistency in
289 // building the InitrdName later....
290 if ((StrLen(Path
) > 0) && (Path
[StrLen(Path
) - 1] != L
'\\'))
291 MergeStrings(&Path
, L
"\\", 0);
292 while ((DirIterNext(&DirIter
, 2, L
"init*", &DirEntry
)) && (InitrdName
== NULL
)) {
293 InitrdVersion
= FindNumbers(DirEntry
->FileName
);
294 if (KernelVersion
!= NULL
) {
295 if (StriCmp(InitrdVersion
, KernelVersion
) == 0) {
296 MergeStrings(&InitrdName
, Path
, 0);
297 MergeStrings(&InitrdName
, DirEntry
->FileName
, 0);
300 if (InitrdVersion
== NULL
) {
301 MergeStrings(&InitrdName
, Path
, 0);
302 MergeStrings(&InitrdName
, DirEntry
->FileName
, 0);
305 if (InitrdVersion
!= NULL
)
306 FreePool(InitrdVersion
);
308 DirIterClose(&DirIter
);
310 // Note: Don't FreePool(FileName), since Basename returns a pointer WITHIN the string it's passed.
311 FreePool(KernelVersion
);
314 } // static CHAR16 * FindInitrd()
316 LOADER_ENTRY
* AddPreparedLoaderEntry(LOADER_ENTRY
*Entry
) {
317 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
320 } // LOADER_ENTRY * AddPreparedLoaderEntry()
322 // Creates a copy of a menu screen.
323 // Returns a pointer to the copy of the menu screen.
324 static REFIT_MENU_SCREEN
* CopyMenuScreen(REFIT_MENU_SCREEN
*Entry
) {
325 REFIT_MENU_SCREEN
*NewEntry
;
328 NewEntry
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
329 if ((Entry
!= NULL
) && (NewEntry
!= NULL
)) {
330 CopyMem(NewEntry
, Entry
, sizeof(REFIT_MENU_SCREEN
));
331 NewEntry
->Title
= StrDuplicate(Entry
->Title
);
332 NewEntry
->TimeoutText
= StrDuplicate(Entry
->TimeoutText
);
333 if (Entry
->TitleImage
!= NULL
) {
334 NewEntry
->TitleImage
= AllocatePool(sizeof(EG_IMAGE
));
335 if (NewEntry
->TitleImage
!= NULL
)
336 CopyMem(NewEntry
->TitleImage
, Entry
->TitleImage
, sizeof(EG_IMAGE
));
338 NewEntry
->InfoLines
= (CHAR16
**) AllocateZeroPool(Entry
->InfoLineCount
* (sizeof(CHAR16
*)));
339 for (i
= 0; i
< Entry
->InfoLineCount
&& NewEntry
->InfoLines
; i
++) {
340 NewEntry
->InfoLines
[i
] = StrDuplicate(Entry
->InfoLines
[i
]);
342 NewEntry
->Entries
= (REFIT_MENU_ENTRY
**) AllocateZeroPool(Entry
->EntryCount
* (sizeof (REFIT_MENU_ENTRY
*)));
343 for (i
= 0; i
< Entry
->EntryCount
&& NewEntry
->Entries
; i
++) {
344 AddMenuEntry(NewEntry
, Entry
->Entries
[i
]);
348 } // static REFIT_MENU_SCREEN* CopyMenuScreen()
350 // Creates a copy of a menu entry. Intended to enable moving a stack-based
351 // menu entry (such as the ones for the "reboot" and "exit" functions) to
352 // to the heap. This enables easier deletion of the whole set of menu
353 // entries when re-scanning.
354 // Returns a pointer to the copy of the menu entry.
355 static REFIT_MENU_ENTRY
* CopyMenuEntry(REFIT_MENU_ENTRY
*Entry
) {
356 REFIT_MENU_ENTRY
*NewEntry
;
358 NewEntry
= AllocateZeroPool(sizeof(REFIT_MENU_ENTRY
));
359 if ((Entry
!= NULL
) && (NewEntry
!= NULL
)) {
360 CopyMem(NewEntry
, Entry
, sizeof(REFIT_MENU_ENTRY
));
361 NewEntry
->Title
= StrDuplicate(Entry
->Title
);
362 if (Entry
->BadgeImage
!= NULL
) {
363 NewEntry
->BadgeImage
= AllocatePool(sizeof(EG_IMAGE
));
364 if (NewEntry
->BadgeImage
!= NULL
)
365 CopyMem(NewEntry
->BadgeImage
, Entry
->BadgeImage
, sizeof(EG_IMAGE
));
367 if (Entry
->Image
!= NULL
) {
368 NewEntry
->Image
= AllocatePool(sizeof(EG_IMAGE
));
369 if (NewEntry
->Image
!= NULL
)
370 CopyMem(NewEntry
->Image
, Entry
->Image
, sizeof(EG_IMAGE
));
372 if (Entry
->SubScreen
!= NULL
) {
373 NewEntry
->SubScreen
= CopyMenuScreen(Entry
->SubScreen
);
377 } // REFIT_MENU_ENTRY* CopyMenuEntry()
379 // Creates a new LOADER_ENTRY data structure and populates it with
380 // default values from the specified Entry, or NULL values if Entry
381 // is unspecified (NULL).
382 // Returns a pointer to the new data structure, or NULL if it
383 // couldn't be allocated
384 LOADER_ENTRY
*InitializeLoaderEntry(IN LOADER_ENTRY
*Entry
) {
385 LOADER_ENTRY
*NewEntry
= NULL
;
387 NewEntry
= AllocateZeroPool(sizeof(LOADER_ENTRY
));
388 if (NewEntry
!= NULL
) {
389 NewEntry
->me
.Title
= NULL
;
390 NewEntry
->me
.Tag
= TAG_LOADER
;
391 NewEntry
->Enabled
= TRUE
;
392 NewEntry
->UseGraphicsMode
= FALSE
;
393 NewEntry
->OSType
= 0;
395 NewEntry
->LoaderPath
= StrDuplicate(Entry
->LoaderPath
);
396 NewEntry
->VolName
= StrDuplicate(Entry
->VolName
);
397 NewEntry
->DevicePath
= Entry
->DevicePath
;
398 NewEntry
->UseGraphicsMode
= Entry
->UseGraphicsMode
;
399 NewEntry
->LoadOptions
= StrDuplicate(Entry
->LoadOptions
);
400 NewEntry
->InitrdPath
= StrDuplicate(Entry
->InitrdPath
);
404 } // LOADER_ENTRY *InitializeLoaderEntry()
406 // Prepare a REFIT_MENU_SCREEN data structure for a subscreen entry. This sets up
407 // the default entry that launches the boot loader using the same options as the
408 // main Entry does. Subsequent options can be added by the calling function.
409 // If a subscreen already exists in the Entry that's passed to this function,
410 // it's left unchanged and a pointer to it is returned.
411 // Returns a pointer to the new subscreen data structure, or NULL if there
412 // were problems allocating memory.
413 REFIT_MENU_SCREEN
*InitializeSubScreen(IN LOADER_ENTRY
*Entry
) {
414 CHAR16
*FileName
, *Temp
= NULL
;
415 REFIT_MENU_SCREEN
*SubScreen
= NULL
;
416 LOADER_ENTRY
*SubEntry
;
418 FileName
= Basename(Entry
->LoaderPath
);
419 if (Entry
->me
.SubScreen
== NULL
) { // No subscreen yet; initialize default entry....
420 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
421 if (SubScreen
!= NULL
) {
422 SubScreen
->Title
= AllocateZeroPool(sizeof(CHAR16
) * 256);
423 SPrint(SubScreen
->Title
, 255, L
"Boot Options for %s on %s",
424 (Entry
->Title
!= NULL
) ? Entry
->Title
: FileName
, Entry
->VolName
);
425 SubScreen
->TitleImage
= Entry
->me
.Image
;
427 SubEntry
= InitializeLoaderEntry(Entry
);
428 if (SubEntry
!= NULL
) {
429 SubEntry
->me
.Title
= L
"Boot using default options";
430 if ((SubEntry
->InitrdPath
!= NULL
) && (StrLen(SubEntry
->InitrdPath
) > 0) && (!StriSubCmp(L
"initrd", SubEntry
->LoadOptions
))) {
431 MergeStrings(&Temp
, L
"initrd=", 0);
432 MergeStrings(&Temp
, SubEntry
->InitrdPath
, 0);
433 MergeStrings(&SubEntry
->LoadOptions
, Temp
, L
' ');
436 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
437 } // if (SubEntry != NULL)
438 } // if (SubScreen != NULL)
439 } else { // existing subscreen; less initialization, and just add new entry later....
440 SubScreen
= Entry
->me
.SubScreen
;
443 } // REFIT_MENU_SCREEN *InitializeSubScreen()
445 VOID
GenerateSubScreen(LOADER_ENTRY
*Entry
, IN REFIT_VOLUME
*Volume
) {
446 REFIT_MENU_SCREEN
*SubScreen
;
447 LOADER_ENTRY
*SubEntry
;
448 CHAR16
*InitrdOption
= NULL
, *Temp
;
449 CHAR16 DiagsFileName
[256];
454 // create the submenu
455 if (StrLen(Entry
->Title
) == 0) {
456 FreePool(Entry
->Title
);
459 SubScreen
= InitializeSubScreen(Entry
);
461 // loader-specific submenu entries
462 if (Entry
->OSType
== 'M') { // entries for Mac OS X
464 SubEntry
= InitializeLoaderEntry(Entry
);
465 if (SubEntry
!= NULL
) {
466 SubEntry
->me
.Title
= L
"Boot Mac OS X with a 64-bit kernel";
467 SubEntry
->LoadOptions
= L
"arch=x86_64";
468 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
469 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
472 SubEntry
= InitializeLoaderEntry(Entry
);
473 if (SubEntry
!= NULL
) {
474 SubEntry
->me
.Title
= L
"Boot Mac OS X with a 32-bit kernel";
475 SubEntry
->LoadOptions
= L
"arch=i386";
476 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
477 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
481 if (!(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_SINGLEUSER
)) {
482 SubEntry
= InitializeLoaderEntry(Entry
);
483 if (SubEntry
!= NULL
) {
484 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode";
485 SubEntry
->UseGraphicsMode
= FALSE
;
486 SubEntry
->LoadOptions
= L
"-v";
487 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
491 SubEntry
= InitializeLoaderEntry(Entry
);
492 if (SubEntry
!= NULL
) {
493 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode (64-bit)";
494 SubEntry
->UseGraphicsMode
= FALSE
;
495 SubEntry
->LoadOptions
= L
"-v arch=x86_64";
496 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
499 SubEntry
= InitializeLoaderEntry(Entry
);
500 if (SubEntry
!= NULL
) {
501 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode (32-bit)";
502 SubEntry
->UseGraphicsMode
= FALSE
;
503 SubEntry
->LoadOptions
= L
"-v arch=i386";
504 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
508 SubEntry
= InitializeLoaderEntry(Entry
);
509 if (SubEntry
!= NULL
) {
510 SubEntry
->me
.Title
= L
"Boot Mac OS X in single user mode";
511 SubEntry
->UseGraphicsMode
= FALSE
;
512 SubEntry
->LoadOptions
= L
"-v -s";
513 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
517 // check for Apple hardware diagnostics
518 StrCpy(DiagsFileName
, L
"\\System\\Library\\CoreServices\\.diagnostics\\diags.efi");
519 if (FileExists(Volume
->RootDir
, DiagsFileName
) && !(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_HWTEST
)) {
520 SubEntry
= InitializeLoaderEntry(Entry
);
521 if (SubEntry
!= NULL
) {
522 SubEntry
->me
.Title
= L
"Run Apple Hardware Test";
523 FreePool(SubEntry
->LoaderPath
);
524 SubEntry
->LoaderPath
= StrDuplicate(DiagsFileName
);
525 SubEntry
->DevicePath
= FileDevicePath(Volume
->DeviceHandle
, SubEntry
->LoaderPath
);
526 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
527 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
529 } // if diagnostics entry found
531 } else if (Entry
->OSType
== 'L') { // entries for Linux kernels with EFI stub loaders
532 File
= ReadLinuxOptionsFile(Entry
->LoaderPath
, Volume
);
534 if ((Temp
= FindInitrd(Entry
->LoaderPath
, Volume
)) != NULL
) {
535 MergeStrings(&InitrdOption
, L
"initrd=", 0);
536 MergeStrings(&InitrdOption
, Temp
, 0);
538 TokenCount
= ReadTokenLine(File
, &TokenList
); // read and discard first entry, since it's
539 FreeTokenLine(&TokenList
, &TokenCount
); // set up by InitializeSubScreen(), earlier....
540 while ((TokenCount
= ReadTokenLine(File
, &TokenList
)) > 1) {
541 SubEntry
= InitializeLoaderEntry(Entry
);
542 SubEntry
->me
.Title
= StrDuplicate(TokenList
[0]);
543 if (SubEntry
->LoadOptions
!= NULL
)
544 FreePool(SubEntry
->LoadOptions
);
545 SubEntry
->LoadOptions
= StrDuplicate(TokenList
[1]);
546 MergeStrings(&SubEntry
->LoadOptions
, InitrdOption
, L
' ');
547 FreeTokenLine(&TokenList
, &TokenCount
);
548 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_LINUX
;
549 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
552 FreePool(InitrdOption
);
556 } // if Linux options file exists
558 } else if (Entry
->OSType
== 'E') { // entries for ELILO
559 SubEntry
= InitializeLoaderEntry(Entry
);
560 if (SubEntry
!= NULL
) {
561 SubEntry
->me
.Title
= L
"Run ELILO in interactive mode";
562 SubEntry
->LoadOptions
= L
"-p";
563 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
564 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
567 SubEntry
= InitializeLoaderEntry(Entry
);
568 if (SubEntry
!= NULL
) {
569 SubEntry
->me
.Title
= L
"Boot Linux for a 17\" iMac or a 15\" MacBook Pro (*)";
570 SubEntry
->UseGraphicsMode
= TRUE
;
571 SubEntry
->LoadOptions
= L
"-d 0 i17";
572 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
573 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
576 SubEntry
= InitializeLoaderEntry(Entry
);
577 if (SubEntry
!= NULL
) {
578 SubEntry
->me
.Title
= L
"Boot Linux for a 20\" iMac (*)";
579 SubEntry
->UseGraphicsMode
= TRUE
;
580 SubEntry
->LoadOptions
= L
"-d 0 i20";
581 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
582 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
585 SubEntry
= InitializeLoaderEntry(Entry
);
586 if (SubEntry
!= NULL
) {
587 SubEntry
->me
.Title
= L
"Boot Linux for a Mac Mini (*)";
588 SubEntry
->UseGraphicsMode
= TRUE
;
589 SubEntry
->LoadOptions
= L
"-d 0 mini";
590 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
591 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
594 AddMenuInfoLine(SubScreen
, L
"NOTE: This is an example. Entries");
595 AddMenuInfoLine(SubScreen
, L
"marked with (*) may not work.");
597 } else if (Entry
->OSType
== 'X') { // entries for xom.efi
598 // by default, skip the built-in selection and boot from hard disk only
599 Entry
->LoadOptions
= L
"-s -h";
601 SubEntry
= InitializeLoaderEntry(Entry
);
602 if (SubEntry
!= NULL
) {
603 SubEntry
->me
.Title
= L
"Boot Windows from Hard Disk";
604 SubEntry
->LoadOptions
= L
"-s -h";
605 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
606 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
609 SubEntry
= InitializeLoaderEntry(Entry
);
610 if (SubEntry
!= NULL
) {
611 SubEntry
->me
.Title
= L
"Boot Windows from CD-ROM";
612 SubEntry
->LoadOptions
= L
"-s -c";
613 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
614 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
617 SubEntry
= InitializeLoaderEntry(Entry
);
618 if (SubEntry
!= NULL
) {
619 SubEntry
->me
.Title
= L
"Run XOM in text mode";
620 SubEntry
->UseGraphicsMode
= FALSE
;
621 SubEntry
->LoadOptions
= L
"-v";
622 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
623 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
625 } // entries for xom.efi
626 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
627 Entry
->me
.SubScreen
= SubScreen
;
628 } // VOID GenerateSubScreen()
630 // Returns options for a Linux kernel. Reads them from an options file in the
631 // kernel's directory; and if present, adds an initrd= option for an initial
632 // RAM disk file with the same version number as the kernel file.
633 static CHAR16
* GetMainLinuxOptions(IN CHAR16
* LoaderPath
, IN REFIT_VOLUME
*Volume
) {
634 CHAR16
*Options
= NULL
, *InitrdName
, *InitrdOption
= NULL
;
636 Options
= GetFirstOptionsFromFile(LoaderPath
, Volume
);
637 InitrdName
= FindInitrd(LoaderPath
, Volume
);
638 if (InitrdName
!= NULL
) {
639 MergeStrings(&InitrdOption
, L
"initrd=", 0);
640 MergeStrings(&InitrdOption
, InitrdName
, 0);
642 MergeStrings(&Options
, InitrdOption
, ' ');
643 if (InitrdOption
!= NULL
)
644 FreePool(InitrdOption
);
645 if (InitrdName
!= NULL
)
646 FreePool(InitrdName
);
648 } // static CHAR16 * GetMainLinuxOptions()
650 // Sets a few defaults for a loader entry -- mainly the icon, but also the OS type
651 // code and shortcut letter. For Linux EFI stub loaders, also sets kernel options
652 // that will (with luck) work fairly automatically.
653 VOID
SetLoaderDefaults(LOADER_ENTRY
*Entry
, CHAR16
*LoaderPath
, IN REFIT_VOLUME
*Volume
) {
654 CHAR16 IconFileName
[256];
655 CHAR16
*FileName
, *PathOnly
, *OSIconName
= NULL
, *Temp
;
656 CHAR16 ShortcutLetter
= 0;
658 FileName
= Basename(LoaderPath
);
659 PathOnly
= FindPath(LoaderPath
);
661 // locate a custom icon for the loader
662 StrCpy(IconFileName
, LoaderPath
);
663 ReplaceEfiExtension(IconFileName
, L
".icns");
664 if (FileExists(Volume
->RootDir
, IconFileName
)) {
665 Entry
->me
.Image
= LoadIcns(Volume
->RootDir
, IconFileName
, 128);
666 } else if ((StrLen(PathOnly
) == 0) && (Volume
->VolIconImage
!= NULL
)) {
667 Entry
->me
.Image
= Volume
->VolIconImage
;
668 } // icon matched to loader or volume
670 Temp
= FindLastDirName(LoaderPath
);
671 MergeStrings(&OSIconName
, Temp
, L
',');
673 if (OSIconName
!= NULL
) {
674 ShortcutLetter
= OSIconName
[0];
677 // detect specific loaders
678 if (StriSubCmp(L
"bzImage", LoaderPath
) || StriSubCmp(L
"vmlinuz", LoaderPath
)) {
679 MergeStrings(&OSIconName
, L
"linux", L
',');
681 if (ShortcutLetter
== 0)
682 ShortcutLetter
= 'L';
683 Entry
->LoadOptions
= GetMainLinuxOptions(LoaderPath
, Volume
);
684 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_LINUX
;
685 } else if (StriSubCmp(L
"refit", LoaderPath
)) {
686 MergeStrings(&OSIconName
, L
"refit", L
',');
688 ShortcutLetter
= 'R';
689 } else if (StriCmp(LoaderPath
, MACOSX_LOADER_PATH
) == 0) {
690 if (Volume
->VolIconImage
!= NULL
) { // custom icon file found
691 Entry
->me
.Image
= Volume
->VolIconImage
;
693 MergeStrings(&OSIconName
, L
"mac", L
',');
695 ShortcutLetter
= 'M';
696 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
697 } else if (StriCmp(FileName
, L
"diags.efi") == 0) {
698 MergeStrings(&OSIconName
, L
"hwtest", L
',');
699 } else if (StriCmp(FileName
, L
"e.efi") == 0 || StriCmp(FileName
, L
"elilo.efi") == 0) {
700 MergeStrings(&OSIconName
, L
"elilo,linux", L
',');
702 if (ShortcutLetter
== 0)
703 ShortcutLetter
= 'L';
704 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
705 } else if (StriSubCmp(L
"grub", FileName
)) {
707 ShortcutLetter
= 'G';
708 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_GRUB
;
709 } else if (StriCmp(FileName
, L
"cdboot.efi") == 0 ||
710 StriCmp(FileName
, L
"bootmgr.efi") == 0 ||
711 StriCmp(FileName
, L
"Bootmgfw.efi") == 0) {
712 MergeStrings(&OSIconName
, L
"win", L
',');
714 ShortcutLetter
= 'W';
715 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
716 } else if (StriCmp(FileName
, L
"xom.efi") == 0) {
717 MergeStrings(&OSIconName
, L
"xom,win", L
',');
718 Entry
->UseGraphicsMode
= TRUE
;
720 ShortcutLetter
= 'W';
721 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
724 if ((ShortcutLetter
>= 'a') && (ShortcutLetter
<= 'z'))
725 ShortcutLetter
= ShortcutLetter
- 'a' + 'A'; // convert lowercase to uppercase
726 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
727 if (Entry
->me
.Image
== NULL
)
728 Entry
->me
.Image
= LoadOSIcon(OSIconName
, L
"unknown", FALSE
);
729 if (PathOnly
!= NULL
)
731 } // VOID SetLoaderDefaults()
733 // Add a specified EFI boot loader to the list, using automatic settings
734 // for icons, options, etc.
735 LOADER_ENTRY
* AddLoaderEntry(IN CHAR16
*LoaderPath
, IN CHAR16
*LoaderTitle
, IN REFIT_VOLUME
*Volume
) {
738 CleanUpPathNameSlashes(LoaderPath
);
739 Entry
= InitializeLoaderEntry(NULL
);
741 Entry
->Title
= StrDuplicate((LoaderTitle
!= NULL
) ? LoaderTitle
: LoaderPath
);
742 Entry
->me
.Title
= AllocateZeroPool(sizeof(CHAR16
) * 256);
743 SPrint(Entry
->me
.Title
, 255, L
"Boot %s from %s", (LoaderTitle
!= NULL
) ? LoaderTitle
: LoaderPath
, Volume
->VolName
);
745 Entry
->me
.BadgeImage
= Volume
->VolBadgeImage
;
746 if ((LoaderPath
!= NULL
) && (LoaderPath
[0] != L
'\\')) {
747 Entry
->LoaderPath
= StrDuplicate(L
"\\");
749 Entry
->LoaderPath
= NULL
;
751 MergeStrings(&(Entry
->LoaderPath
), LoaderPath
, 0);
752 Entry
->VolName
= Volume
->VolName
;
753 Entry
->DevicePath
= FileDevicePath(Volume
->DeviceHandle
, Entry
->LoaderPath
);
754 SetLoaderDefaults(Entry
, LoaderPath
, Volume
);
755 GenerateSubScreen(Entry
, Volume
);
756 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
760 } // LOADER_ENTRY * AddLoaderEntry()
762 // Returns -1 if (Time1 < Time2), +1 if (Time1 > Time2), or 0 if
763 // (Time1 == Time2). Precision is only to the nearest second; since
764 // this is used for sorting boot loader entries, differences smaller
765 // than this are likely to be meaningless (and unlikely!).
766 INTN
TimeComp(EFI_TIME
*Time1
, EFI_TIME
*Time2
) {
767 INT64 Time1InSeconds
, Time2InSeconds
;
769 // Following values are overestimates; I'm assuming 31 days in every month.
770 // This is fine for the purpose of this function, which has a limited
772 Time1InSeconds
= Time1
->Second
+ (Time1
->Minute
* 60) + (Time1
->Hour
* 3600) + (Time1
->Day
* 86400) +
773 (Time1
->Month
* 2678400) + ((Time1
->Year
- 1998) * 32140800);
774 Time2InSeconds
= Time2
->Second
+ (Time2
->Minute
* 60) + (Time2
->Hour
* 3600) + (Time2
->Day
* 86400) +
775 (Time2
->Month
* 2678400) + ((Time2
->Year
- 1998) * 32140800);
776 if (Time1InSeconds
< Time2InSeconds
)
778 else if (Time1InSeconds
> Time2InSeconds
)
784 // Adds a loader list element, keeping it sorted by date. Returns the new
785 // first element (the one with the most recent date).
786 static struct LOADER_LIST
* AddLoaderListEntry(struct LOADER_LIST
*LoaderList
, struct LOADER_LIST
*NewEntry
) {
787 struct LOADER_LIST
*LatestEntry
, *CurrentEntry
, *PrevEntry
= NULL
;
789 LatestEntry
= CurrentEntry
= LoaderList
;
790 if (LoaderList
== NULL
) {
791 LatestEntry
= NewEntry
;
793 while ((CurrentEntry
!= NULL
) && (TimeComp(&(NewEntry
->TimeStamp
), &(CurrentEntry
->TimeStamp
)) < 0)) {
794 PrevEntry
= CurrentEntry
;
795 CurrentEntry
= CurrentEntry
->NextEntry
;
797 NewEntry
->NextEntry
= CurrentEntry
;
798 if (PrevEntry
== NULL
) {
799 LatestEntry
= NewEntry
;
801 PrevEntry
->NextEntry
= NewEntry
;
804 return (LatestEntry
);
805 } // static VOID AddLoaderListEntry()
807 // Delete the LOADER_LIST linked list
808 static VOID
CleanUpLoaderList(struct LOADER_LIST
*LoaderList
) {
809 struct LOADER_LIST
*Temp
;
811 while (LoaderList
!= NULL
) {
813 LoaderList
= LoaderList
->NextEntry
;
814 FreePool(Temp
->FileName
);
817 } // static VOID CleanUpLoaderList()
819 // Scan an individual directory for EFI boot loader files and, if found,
820 // add them to the list. Sorts the entries within the loader directory
821 // so that the most recent one appears first in the list.
822 static VOID
ScanLoaderDir(IN REFIT_VOLUME
*Volume
, IN CHAR16
*Path
, IN CHAR16
*Pattern
)
825 REFIT_DIR_ITER DirIter
;
826 EFI_FILE_INFO
*DirEntry
;
827 CHAR16 FileName
[256], *Extension
;
828 struct LOADER_LIST
*LoaderList
= NULL
, *NewLoader
;
830 if ((!SelfDirPath
|| !Path
|| ((StriCmp(Path
, SelfDirPath
) == 0) && Volume
->DeviceHandle
!= SelfVolume
->DeviceHandle
) ||
831 (StriCmp(Path
, SelfDirPath
) != 0)) && (!IsIn(Path
, GlobalConfig
.DontScan
))) {
832 // look through contents of the directory
833 DirIterOpen(Volume
->RootDir
, Path
, &DirIter
);
834 while (DirIterNext(&DirIter
, 2, Pattern
, &DirEntry
)) {
835 Extension
= FindExtension(DirEntry
->FileName
);
836 if (DirEntry
->FileName
[0] == '.' ||
837 StriCmp(DirEntry
->FileName
, L
"TextMode.efi") == 0 ||
838 StriCmp(DirEntry
->FileName
, L
"ebounce.efi") == 0 ||
839 StriCmp(DirEntry
->FileName
, L
"GraphicsConsole.efi") == 0 ||
840 StriCmp(Extension
, L
".icns") == 0 ||
841 StriSubCmp(L
"shell", DirEntry
->FileName
))
842 continue; // skip this
845 SPrint(FileName
, 255, L
"\\%s\\%s", Path
, DirEntry
->FileName
);
847 SPrint(FileName
, 255, L
"\\%s", DirEntry
->FileName
);
848 CleanUpPathNameSlashes(FileName
);
849 NewLoader
= AllocateZeroPool(sizeof(struct LOADER_LIST
));
850 if (NewLoader
!= NULL
) {
851 NewLoader
->FileName
= StrDuplicate(FileName
);
852 NewLoader
->TimeStamp
= DirEntry
->ModificationTime
;
853 LoaderList
= AddLoaderListEntry(LoaderList
, NewLoader
);
857 NewLoader
= LoaderList
;
858 while (NewLoader
!= NULL
) {
859 AddLoaderEntry(NewLoader
->FileName
, NULL
, Volume
);
860 NewLoader
= NewLoader
->NextEntry
;
862 CleanUpLoaderList(LoaderList
);
863 Status
= DirIterClose(&DirIter
);
864 if (Status
!= EFI_NOT_FOUND
) {
866 SPrint(FileName
, 255, L
"while scanning the %s directory", Path
);
868 StrCpy(FileName
, L
"while scanning the root directory");
869 CheckError(Status
, FileName
);
870 } // if (Status != EFI_NOT_FOUND)
871 } // if not scanning our own directory
872 } /* static VOID ScanLoaderDir() */
874 static VOID
ScanEfiFiles(REFIT_VOLUME
*Volume
) {
876 REFIT_DIR_ITER EfiDirIter
;
877 EFI_FILE_INFO
*EfiDirEntry
;
878 CHAR16 FileName
[256], *Directory
, *MatchPatterns
;
881 MatchPatterns
= StrDuplicate(LOADER_MATCH_PATTERNS
);
882 if (GlobalConfig
.ScanAllLinux
)
883 MergeStrings(&MatchPatterns
, LINUX_MATCH_PATTERNS
, L
',');
885 if ((Volume
->RootDir
!= NULL
) && (Volume
->VolName
!= NULL
)) {
886 // check for Mac OS X boot loader
887 if (!IsIn(L
"System\\Library\\CoreServices", GlobalConfig
.DontScan
)) {
888 StrCpy(FileName
, MACOSX_LOADER_PATH
);
889 if (FileExists(Volume
->RootDir
, FileName
)) {
890 AddLoaderEntry(FileName
, L
"Mac OS X", Volume
);
894 StrCpy(FileName
, L
"System\\Library\\CoreServices\\xom.efi");
895 if (FileExists(Volume
->RootDir
, FileName
)) {
896 AddLoaderEntry(FileName
, L
"Windows XP (XoM)", Volume
);
898 } // if Mac directory not in GlobalConfig.DontScan list
900 // check for Microsoft boot loader/menu
901 StrCpy(FileName
, L
"EFI\\Microsoft\\Boot\\Bootmgfw.efi");
902 if (FileExists(Volume
->RootDir
, FileName
) && !IsIn(L
"EFI\\Microsoft\\Boot", GlobalConfig
.DontScan
)) {
903 AddLoaderEntry(FileName
, L
"Microsoft EFI boot", Volume
);
906 // scan the root directory for EFI executables
907 ScanLoaderDir(Volume
, L
"\\", MatchPatterns
);
909 // scan subdirectories of the EFI directory (as per the standard)
910 DirIterOpen(Volume
->RootDir
, L
"EFI", &EfiDirIter
);
911 while (DirIterNext(&EfiDirIter
, 1, NULL
, &EfiDirEntry
)) {
912 if (StriCmp(EfiDirEntry
->FileName
, L
"tools") == 0 || EfiDirEntry
->FileName
[0] == '.')
913 continue; // skip this, doesn't contain boot loaders
914 SPrint(FileName
, 255, L
"EFI\\%s", EfiDirEntry
->FileName
);
915 ScanLoaderDir(Volume
, FileName
, MatchPatterns
);
917 Status
= DirIterClose(&EfiDirIter
);
918 if (Status
!= EFI_NOT_FOUND
)
919 CheckError(Status
, L
"while scanning the EFI directory");
921 // Scan user-specified (or additional default) directories....
923 while ((Directory
= FindCommaDelimited(GlobalConfig
.AlsoScan
, i
++)) != NULL
) {
924 CleanUpPathNameSlashes(Directory
);
925 Length
= StrLen(Directory
);
927 ScanLoaderDir(Volume
, Directory
, MatchPatterns
);
931 } // static VOID ScanEfiFiles()
933 // Scan internal disks for valid EFI boot loaders....
934 static VOID
ScanInternal(VOID
) {
937 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
938 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_INTERNAL
) {
939 ScanEfiFiles(Volumes
[VolumeIndex
]);
942 } // static VOID ScanInternal()
944 // Scan external disks for valid EFI boot loaders....
945 static VOID
ScanExternal(VOID
) {
948 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
949 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_EXTERNAL
) {
950 ScanEfiFiles(Volumes
[VolumeIndex
]);
953 } // static VOID ScanExternal()
955 // Scan internal disks for valid EFI boot loaders....
956 static VOID
ScanOptical(VOID
) {
959 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
960 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_OPTICAL
) {
961 ScanEfiFiles(Volumes
[VolumeIndex
]);
964 } // static VOID ScanOptical()
967 // legacy boot functions
970 static EFI_STATUS
ActivateMbrPartition(IN EFI_BLOCK_IO
*BlockIO
, IN UINTN PartitionIndex
)
973 UINT8 SectorBuffer
[512];
974 MBR_PARTITION_INFO
*MbrTable
, *EMbrTable
;
975 UINT32 ExtBase
, ExtCurrent
, NextExtCurrent
;
976 UINTN LogicalPartitionIndex
= 4;
978 BOOLEAN HaveBootCode
;
981 Status
= refit_call5_wrapper(BlockIO
->ReadBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, 0, 512, SectorBuffer
);
982 if (EFI_ERROR(Status
))
984 if (*((UINT16
*)(SectorBuffer
+ 510)) != 0xaa55)
985 return EFI_NOT_FOUND
; // safety measure #1
987 // add boot code if necessary
988 HaveBootCode
= FALSE
;
989 for (i
= 0; i
< MBR_BOOTCODE_SIZE
; i
++) {
990 if (SectorBuffer
[i
] != 0) {
996 // no boot code found in the MBR, add the syslinux MBR code
997 SetMem(SectorBuffer
, MBR_BOOTCODE_SIZE
, 0);
998 CopyMem(SectorBuffer
, syslinux_mbr
, SYSLINUX_MBR_SIZE
);
1001 // set the partition active
1002 MbrTable
= (MBR_PARTITION_INFO
*)(SectorBuffer
+ 446);
1004 for (i
= 0; i
< 4; i
++) {
1005 if (MbrTable
[i
].Flags
!= 0x00 && MbrTable
[i
].Flags
!= 0x80)
1006 return EFI_NOT_FOUND
; // safety measure #2
1007 if (i
== PartitionIndex
)
1008 MbrTable
[i
].Flags
= 0x80;
1009 else if (PartitionIndex
>= 4 && IS_EXTENDED_PART_TYPE(MbrTable
[i
].Type
)) {
1010 MbrTable
[i
].Flags
= 0x80;
1011 ExtBase
= MbrTable
[i
].StartLBA
;
1013 MbrTable
[i
].Flags
= 0x00;
1017 Status
= refit_call5_wrapper(BlockIO
->WriteBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, 0, 512, SectorBuffer
);
1018 if (EFI_ERROR(Status
))
1021 if (PartitionIndex
>= 4) {
1022 // we have to activate a logical partition, so walk the EMBR chain
1024 // NOTE: ExtBase was set above while looking at the MBR table
1025 for (ExtCurrent
= ExtBase
; ExtCurrent
; ExtCurrent
= NextExtCurrent
) {
1026 // read current EMBR
1027 Status
= refit_call5_wrapper(BlockIO
->ReadBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, ExtCurrent
, 512, SectorBuffer
);
1028 if (EFI_ERROR(Status
))
1030 if (*((UINT16
*)(SectorBuffer
+ 510)) != 0xaa55)
1031 return EFI_NOT_FOUND
; // safety measure #3
1033 // scan EMBR, set appropriate partition active
1034 EMbrTable
= (MBR_PARTITION_INFO
*)(SectorBuffer
+ 446);
1036 for (i
= 0; i
< 4; i
++) {
1037 if (EMbrTable
[i
].Flags
!= 0x00 && EMbrTable
[i
].Flags
!= 0x80)
1038 return EFI_NOT_FOUND
; // safety measure #4
1039 if (EMbrTable
[i
].StartLBA
== 0 || EMbrTable
[i
].Size
== 0)
1041 if (IS_EXTENDED_PART_TYPE(EMbrTable
[i
].Type
)) {
1042 // link to next EMBR
1043 NextExtCurrent
= ExtBase
+ EMbrTable
[i
].StartLBA
;
1044 EMbrTable
[i
].Flags
= (PartitionIndex
>= LogicalPartitionIndex
) ? 0x80 : 0x00;
1047 // logical partition
1048 EMbrTable
[i
].Flags
= (PartitionIndex
== LogicalPartitionIndex
) ? 0x80 : 0x00;
1049 LogicalPartitionIndex
++;
1053 // write current EMBR
1054 Status
= refit_call5_wrapper(BlockIO
->WriteBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, ExtCurrent
, 512, SectorBuffer
);
1055 if (EFI_ERROR(Status
))
1058 if (PartitionIndex
< LogicalPartitionIndex
)
1059 break; // stop the loop, no need to touch further EMBRs
1065 } /* static EFI_STATUS ActivateMbrPartition() */
1067 // early 2006 Core Duo / Core Solo models
1068 static UINT8 LegacyLoaderDevicePath1Data
[] = {
1069 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1070 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1071 0xFF, 0xFF, 0xF9, 0xFF, 0x00, 0x00, 0x00, 0x00,
1072 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1073 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1074 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1076 // mid-2006 Mac Pro (and probably other Core 2 models)
1077 static UINT8 LegacyLoaderDevicePath2Data
[] = {
1078 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1079 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1080 0xFF, 0xFF, 0xF7, 0xFF, 0x00, 0x00, 0x00, 0x00,
1081 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1082 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1083 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1085 // mid-2007 MBP ("Santa Rosa" based models)
1086 static UINT8 LegacyLoaderDevicePath3Data
[] = {
1087 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1088 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1089 0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
1090 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1091 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1092 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1095 static UINT8 LegacyLoaderDevicePath4Data
[] = {
1096 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1097 0x00, 0x00, 0xC0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1098 0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
1099 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1100 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1101 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1103 // late-2008 MB/MBP (NVidia chipset)
1104 static UINT8 LegacyLoaderDevicePath5Data
[] = {
1105 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1106 0x00, 0x40, 0xCB, 0xFF, 0x00, 0x00, 0x00, 0x00,
1107 0xFF, 0xBF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
1108 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1109 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1110 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1113 static EFI_DEVICE_PATH
*LegacyLoaderList
[] = {
1114 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath1Data
,
1115 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath2Data
,
1116 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath3Data
,
1117 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath4Data
,
1118 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath5Data
,
1122 #define MAX_DISCOVERED_PATHS (16)
1124 static VOID
StartLegacy(IN LEGACY_ENTRY
*Entry
)
1127 EG_IMAGE
*BootLogoImage
;
1128 UINTN ErrorInStep
= 0;
1129 EFI_DEVICE_PATH
*DiscoveredPathList
[MAX_DISCOVERED_PATHS
];
1131 BeginExternalScreen(TRUE
, L
"Booting Legacy OS");
1133 BootLogoImage
= LoadOSIcon(Entry
->Volume
->OSIconName
, L
"legacy", TRUE
);
1134 if (BootLogoImage
!= NULL
)
1135 BltImageAlpha(BootLogoImage
,
1136 (UGAWidth
- BootLogoImage
->Width
) >> 1,
1137 (UGAHeight
- BootLogoImage
->Height
) >> 1,
1138 &StdBackgroundPixel
);
1140 if (Entry
->Volume
->IsMbrPartition
)
1141 ActivateMbrPartition(Entry
->Volume
->WholeDiskBlockIO
, Entry
->Volume
->MbrPartitionIndex
);
1143 ExtractLegacyLoaderPaths(DiscoveredPathList
, MAX_DISCOVERED_PATHS
, LegacyLoaderList
);
1145 Status
= StartEFIImageList(DiscoveredPathList
, Entry
->LoadOptions
, NULL
, L
"legacy loader", &ErrorInStep
, TRUE
);
1146 if (Status
== EFI_NOT_FOUND
) {
1147 if (ErrorInStep
== 1) {
1148 Print(L
"\nPlease make sure that you have the latest firmware update installed.\n");
1149 } else if (ErrorInStep
== 3) {
1150 Print(L
"\nThe firmware refused to boot from the selected volume. Note that external\n"
1151 L
"hard drives are not well-supported by Apple's firmware for legacy OS booting.\n");
1154 FinishExternalScreen();
1155 } /* static VOID StartLegacy() */
1157 static LEGACY_ENTRY
* AddLegacyEntry(IN CHAR16
*LoaderTitle
, IN REFIT_VOLUME
*Volume
)
1159 LEGACY_ENTRY
*Entry
, *SubEntry
;
1160 REFIT_MENU_SCREEN
*SubScreen
;
1162 CHAR16 ShortcutLetter
= 0;
1164 if (LoaderTitle
== NULL
) {
1165 if (Volume
->OSName
!= NULL
) {
1166 LoaderTitle
= Volume
->OSName
;
1167 if (LoaderTitle
[0] == 'W' || LoaderTitle
[0] == 'L')
1168 ShortcutLetter
= LoaderTitle
[0];
1170 LoaderTitle
= L
"Legacy OS";
1172 if (Volume
->VolName
!= NULL
)
1173 VolDesc
= Volume
->VolName
;
1175 VolDesc
= (Volume
->DiskKind
== DISK_KIND_OPTICAL
) ? L
"CD" : L
"HD";
1177 // prepare the menu entry
1178 Entry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1179 Entry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1180 SPrint(Entry
->me
.Title
, 255, L
"Boot %s from %s", LoaderTitle
, VolDesc
);
1181 Entry
->me
.Tag
= TAG_LEGACY
;
1183 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1184 Entry
->me
.Image
= LoadOSIcon(Volume
->OSIconName
, L
"legacy", FALSE
);
1185 Entry
->me
.BadgeImage
= Volume
->VolBadgeImage
;
1186 Entry
->Volume
= Volume
;
1187 Entry
->LoadOptions
= (Volume
->DiskKind
== DISK_KIND_OPTICAL
) ? L
"CD" :
1188 ((Volume
->DiskKind
== DISK_KIND_EXTERNAL
) ? L
"USB" : L
"HD");
1189 Entry
->Enabled
= TRUE
;
1191 // create the submenu
1192 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
1193 SubScreen
->Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1194 SPrint(SubScreen
->Title
, 255, L
"Boot Options for %s on %s", LoaderTitle
, VolDesc
);
1195 SubScreen
->TitleImage
= Entry
->me
.Image
;
1198 SubEntry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1199 SubEntry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1200 SPrint(SubEntry
->me
.Title
, 255, L
"Boot %s", LoaderTitle
);
1201 SubEntry
->me
.Tag
= TAG_LEGACY
;
1202 SubEntry
->Volume
= Entry
->Volume
;
1203 SubEntry
->LoadOptions
= Entry
->LoadOptions
;
1204 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
1206 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
1207 Entry
->me
.SubScreen
= SubScreen
;
1208 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1210 } /* static LEGACY_ENTRY * AddLegacyEntry() */
1212 static VOID
ScanLegacyVolume(REFIT_VOLUME
*Volume
, UINTN VolumeIndex
) {
1214 BOOLEAN ShowVolume
, HideIfOthersFound
;
1217 HideIfOthersFound
= FALSE
;
1218 if (Volume
->IsAppleLegacy
) {
1220 HideIfOthersFound
= TRUE
;
1221 } else if (Volume
->HasBootCode
) {
1223 if (Volume
->BlockIO
== Volume
->WholeDiskBlockIO
&&
1224 Volume
->BlockIOOffset
== 0 &&
1225 Volume
->OSName
== NULL
)
1226 // this is a whole disk (MBR) entry; hide if we have entries for partitions
1227 HideIfOthersFound
= TRUE
;
1229 if (HideIfOthersFound
) {
1230 // check for other bootable entries on the same disk
1231 for (VolumeIndex2
= 0; VolumeIndex2
< VolumesCount
; VolumeIndex2
++) {
1232 if (VolumeIndex2
!= VolumeIndex
&& Volumes
[VolumeIndex2
]->HasBootCode
&&
1233 Volumes
[VolumeIndex2
]->WholeDiskBlockIO
== Volume
->WholeDiskBlockIO
)
1239 AddLegacyEntry(NULL
, Volume
);
1240 } // static VOID ScanLegacyVolume()
1242 // Scan attached optical discs for legacy (BIOS) boot code
1243 // and add anything found to the list....
1244 static VOID
ScanLegacyDisc(VOID
)
1247 REFIT_VOLUME
*Volume
;
1249 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1250 Volume
= Volumes
[VolumeIndex
];
1251 if (Volume
->DiskKind
== DISK_KIND_OPTICAL
)
1252 ScanLegacyVolume(Volume
, VolumeIndex
);
1254 } /* static VOID ScanLegacyDisc() */
1256 // Scan internal hard disks for legacy (BIOS) boot code
1257 // and add anything found to the list....
1258 static VOID
ScanLegacyInternal(VOID
)
1261 REFIT_VOLUME
*Volume
;
1263 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1264 Volume
= Volumes
[VolumeIndex
];
1265 if (Volume
->DiskKind
== DISK_KIND_INTERNAL
)
1266 ScanLegacyVolume(Volume
, VolumeIndex
);
1268 } /* static VOID ScanLegacyInternal() */
1270 // Scan external disks for legacy (BIOS) boot code
1271 // and add anything found to the list....
1272 static VOID
ScanLegacyExternal(VOID
)
1275 REFIT_VOLUME
*Volume
;
1277 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1278 Volume
= Volumes
[VolumeIndex
];
1279 if (Volume
->DiskKind
== DISK_KIND_EXTERNAL
)
1280 ScanLegacyVolume(Volume
, VolumeIndex
);
1282 } /* static VOID ScanLegacyExternal() */
1285 // pre-boot tool functions
1288 static VOID
StartTool(IN LOADER_ENTRY
*Entry
)
1290 BeginExternalScreen(Entry
->UseGraphicsMode
, Entry
->me
.Title
+ 6); // assumes "Start <title>" as assigned below
1291 StartEFIImage(Entry
->DevicePath
, Entry
->LoadOptions
, Basename(Entry
->LoaderPath
),
1292 Basename(Entry
->LoaderPath
), NULL
, TRUE
);
1293 FinishExternalScreen();
1294 } /* static VOID StartTool() */
1296 static LOADER_ENTRY
* AddToolEntry(IN CHAR16
*LoaderPath
, IN CHAR16
*LoaderTitle
, IN EG_IMAGE
*Image
,
1297 IN CHAR16 ShortcutLetter
, IN BOOLEAN UseGraphicsMode
)
1299 LOADER_ENTRY
*Entry
;
1300 CHAR16
*TitleStr
= NULL
;
1302 Entry
= AllocateZeroPool(sizeof(LOADER_ENTRY
));
1304 MergeStrings(&TitleStr
, L
"Start ", 0);
1305 MergeStrings(&TitleStr
, LoaderTitle
, 0);
1306 Entry
->me
.Title
= TitleStr
;
1307 Entry
->me
.Tag
= TAG_TOOL
;
1309 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1310 Entry
->me
.Image
= Image
;
1311 Entry
->LoaderPath
= StrDuplicate(LoaderPath
);
1312 Entry
->DevicePath
= FileDevicePath(SelfLoadedImage
->DeviceHandle
, Entry
->LoaderPath
);
1313 Entry
->UseGraphicsMode
= UseGraphicsMode
;
1315 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1317 } /* static LOADER_ENTRY * AddToolEntry() */
1320 // pre-boot driver functions
1323 static UINTN
ScanDriverDir(IN CHAR16
*Path
)
1326 REFIT_DIR_ITER DirIter
;
1328 EFI_FILE_INFO
*DirEntry
;
1329 CHAR16 FileName
[256];
1331 CleanUpPathNameSlashes(Path
);
1332 // look through contents of the directory
1333 DirIterOpen(SelfRootDir
, Path
, &DirIter
);
1334 while (DirIterNext(&DirIter
, 2, LOADER_MATCH_PATTERNS
, &DirEntry
)) {
1335 if (DirEntry
->FileName
[0] == '.')
1336 continue; // skip this
1338 SPrint(FileName
, 255, L
"%s\\%s", Path
, DirEntry
->FileName
);
1340 Status
= StartEFIImage(FileDevicePath(SelfLoadedImage
->DeviceHandle
, FileName
),
1341 L
"", DirEntry
->FileName
, DirEntry
->FileName
, NULL
, FALSE
);
1343 Status
= DirIterClose(&DirIter
);
1344 if (Status
!= EFI_NOT_FOUND
) {
1345 SPrint(FileName
, 255, L
"while scanning the %s directory", Path
);
1346 CheckError(Status
, FileName
);
1351 #ifdef __MAKEWITH_GNUEFI
1352 static EFI_STATUS
ConnectAllDriversToAllControllers(VOID
)
1355 UINTN AllHandleCount
;
1356 EFI_HANDLE
*AllHandleBuffer
;
1359 EFI_HANDLE
*HandleBuffer
;
1365 Status
= LibLocateHandle(AllHandles
,
1370 if (EFI_ERROR(Status
))
1373 for (Index
= 0; Index
< AllHandleCount
; Index
++) {
1375 // Scan the handle database
1377 Status
= LibScanHandleDatabase(NULL
,
1379 AllHandleBuffer
[Index
],
1384 if (EFI_ERROR (Status
))
1388 if (HandleType
[Index
] & EFI_HANDLE_TYPE_DRIVER_BINDING_HANDLE
)
1390 if (HandleType
[Index
] & EFI_HANDLE_TYPE_IMAGE_HANDLE
)
1395 for (HandleIndex
= 0; HandleIndex
< HandleCount
; HandleIndex
++) {
1396 if (HandleType
[HandleIndex
] & EFI_HANDLE_TYPE_PARENT_HANDLE
)
1401 if (HandleType
[Index
] & EFI_HANDLE_TYPE_DEVICE_HANDLE
) {
1402 Status
= refit_call4_wrapper(BS
->ConnectController
,
1403 AllHandleBuffer
[Index
],
1411 FreePool (HandleBuffer
);
1412 FreePool (HandleType
);
1416 FreePool (AllHandleBuffer
);
1418 } /* EFI_STATUS ConnectAllDriversToAllControllers() */
1420 static EFI_STATUS
ConnectAllDriversToAllControllers(VOID
) {
1421 BdsLibConnectAllDriversToAllControllers();
1426 // Load all EFI drivers from rEFInd's "drivers" subdirectory and from the
1427 // directories specified by the user in the "scan_driver_dirs" configuration
1429 static VOID
LoadDrivers(VOID
)
1431 CHAR16
*Directory
, *SelfDirectory
;
1432 UINTN i
= 0, Length
, NumFound
= 0;
1434 // load drivers from the subdirectories of rEFInd's home directory specified
1435 // in the DRIVER_DIRS constant.
1436 while ((Directory
= FindCommaDelimited(DRIVER_DIRS
, i
++)) != NULL
) {
1437 SelfDirectory
= StrDuplicate(SelfDirPath
);
1438 CleanUpPathNameSlashes(SelfDirectory
);
1439 MergeStrings(&SelfDirectory
, Directory
, L
'\\');
1440 NumFound
+= ScanDriverDir(SelfDirectory
);
1441 FreePool(Directory
);
1442 FreePool(SelfDirectory
);
1445 // Scan additional user-specified driver directories....
1447 while ((Directory
= FindCommaDelimited(GlobalConfig
.DriverDirs
, i
++)) != NULL
) {
1448 CleanUpPathNameSlashes(Directory
);
1449 Length
= StrLen(Directory
);
1451 NumFound
+= ScanDriverDir(Directory
);
1452 FreePool(Directory
);
1455 // connect all devices
1457 ConnectAllDriversToAllControllers();
1458 } /* static VOID LoadDrivers() */
1460 static VOID
ScanForBootloaders(VOID
) {
1465 // scan for loaders and tools, add them to the menu
1466 for (i
= 0; i
< NUM_SCAN_OPTIONS
; i
++) {
1467 switch(GlobalConfig
.ScanFor
[i
]) {
1472 ScanLegacyInternal();
1475 ScanLegacyExternal();
1478 ScanUserConfigured();
1492 // assign shortcut keys
1493 for (i
= 0; i
< MainMenu
.EntryCount
&& MainMenu
.Entries
[i
]->Row
== 0 && i
< 9; i
++)
1494 MainMenu
.Entries
[i
]->ShortcutDigit
= (CHAR16
)('1' + i
);
1496 // wait for user ACK when there were errors
1497 FinishTextScreen(FALSE
);
1498 } // static VOID ScanForBootloaders()
1500 // Add the second-row tags containing built-in and external tools (EFI shell,
1502 static VOID
ScanForTools(VOID
) {
1503 CHAR16
*FileName
= NULL
;
1504 REFIT_MENU_ENTRY
*TempMenuEntry
;
1507 for (i
= 0; i
< NUM_TOOLS
; i
++) {
1508 switch(GlobalConfig
.ShowTools
[i
]) {
1510 TempMenuEntry
= CopyMenuEntry(&MenuEntryShutdown
);
1511 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_SHUTDOWN
);
1512 AddMenuEntry(&MainMenu
, TempMenuEntry
);
1515 TempMenuEntry
= CopyMenuEntry(&MenuEntryReset
);
1516 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_RESET
);
1517 AddMenuEntry(&MainMenu
, TempMenuEntry
);
1520 TempMenuEntry
= CopyMenuEntry(&MenuEntryAbout
);
1521 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT
);
1522 AddMenuEntry(&MainMenu
, TempMenuEntry
);
1525 TempMenuEntry
= CopyMenuEntry(&MenuEntryExit
);
1526 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_EXIT
);
1527 AddMenuEntry(&MainMenu
, TempMenuEntry
);
1531 while ((FileName
= FindCommaDelimited(SHELL_NAMES
, j
++)) != NULL
) {
1532 if (FileExists(SelfRootDir
, FileName
)) {
1533 AddToolEntry(FileName
, L
"EFI Shell", BuiltinIcon(BUILTIN_ICON_TOOL_SHELL
), 'S', FALSE
);
1538 MergeStrings(&FileName
, L
"\\efi\\tools\\gptsync.efi", 0);
1539 if (FileExists(SelfRootDir
, FileName
)) {
1540 AddToolEntry(FileName
, L
"Make Hybrid MBR", BuiltinIcon(BUILTIN_ICON_TOOL_PART
), 'P', FALSE
);
1544 if (FileName
!= NULL
) {
1549 } // static VOID ScanForTools
1551 // Rescan for boot loaders
1552 VOID
RescanAll(VOID
) {
1559 egDisplayMessage(L
"Scanning for new boot loaders; please wait....", &BGColor
);
1560 FreeList((VOID
***) &(MainMenu
.Entries
), &MainMenu
.EntryCount
);
1561 MainMenu
.Entries
= NULL
;
1562 MainMenu
.EntryCount
= 0;
1564 ConnectAllDriversToAllControllers();
1565 ScanForBootloaders();
1568 } // VOID RescanAll()
1570 #ifndef __MAKEWITH_GNUEFI
1572 // Minimal initialization function
1573 static VOID
InitializeLib(IN EFI_HANDLE ImageHandle
, IN EFI_SYSTEM_TABLE
*SystemTable
) {
1575 // gImageHandle = ImageHandle;
1576 gBS
= SystemTable
->BootServices
;
1577 // gRS = SystemTable->RuntimeServices;
1578 EfiGetSystemConfigurationTable (&gEfiDxeServicesTableGuid
, (VOID
**) &gDS
);
1580 InitializeConsoleSim();
1590 efi_main (IN EFI_HANDLE ImageHandle
, IN EFI_SYSTEM_TABLE
*SystemTable
)
1593 BOOLEAN MainLoopRunning
= TRUE
;
1594 REFIT_MENU_ENTRY
*ChosenEntry
;
1599 InitializeLib(ImageHandle
, SystemTable
);
1601 Status
= InitRefitLib(ImageHandle
);
1602 if (EFI_ERROR(Status
))
1605 // read configuration
1606 CopyMem(GlobalConfig
.ScanFor
, "ieo ", NUM_SCAN_OPTIONS
);
1608 MainMenu
.TimeoutSeconds
= GlobalConfig
.Timeout
;
1610 // disable EFI watchdog timer
1611 refit_call4_wrapper(BS
->SetWatchdogTimer
, 0x0000, 0x0000, 0x0000, NULL
);
1613 // further bootstrap (now with config available)
1616 ScanForBootloaders();
1619 Selection
= StrDuplicate(GlobalConfig
.DefaultSelection
);
1620 while (MainLoopRunning
) {
1621 MenuExit
= RunMainMenu(&MainMenu
, Selection
, &ChosenEntry
);
1623 // The Escape key triggers a re-scan operation....
1624 if (MenuExit
== MENU_EXIT_ESCAPE
) {
1629 switch (ChosenEntry
->Tag
) {
1631 case TAG_REBOOT
: // Reboot
1633 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);
1634 MainLoopRunning
= FALSE
; // just in case we get this far
1637 case TAG_SHUTDOWN
: // Shut Down
1639 refit_call4_wrapper(RT
->ResetSystem
, EfiResetShutdown
, EFI_SUCCESS
, 0, NULL
);
1640 MainLoopRunning
= FALSE
; // just in case we get this far
1643 case TAG_ABOUT
: // About rEFInd
1647 case TAG_LOADER
: // Boot OS via .EFI loader
1648 StartLoader((LOADER_ENTRY
*)ChosenEntry
);
1651 case TAG_LEGACY
: // Boot legacy OS
1652 StartLegacy((LEGACY_ENTRY
*)ChosenEntry
);
1655 case TAG_TOOL
: // Start a EFI tool
1656 StartTool((LOADER_ENTRY
*)ChosenEntry
);
1659 case TAG_EXIT
: // Terminate rEFInd
1660 BeginTextScreen(L
" ");
1665 FreePool(Selection
);
1666 Selection
= StrDuplicate(ChosenEntry
->Title
);
1669 // If we end up here, things have gone wrong. Try to reboot, and if that
1670 // fails, go into an endless loop.
1671 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);