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.1");
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 MergeStrings(&FullLoadOptions
, LoadOptions
, 0);
204 // NOTE: We also include the terminating null in the length for safety.
205 ChildLoadedImage
->LoadOptions
= (VOID
*)FullLoadOptions
;
206 ChildLoadedImage
->LoadOptionsSize
= ((UINT32
)StrLen(FullLoadOptions
) + 1) * sizeof(CHAR16
);
208 Print(L
"Using load options '%s'\n", FullLoadOptions
);
211 // close open file handles
214 // turn control over to the image
215 // TODO: (optionally) re-enable the EFI watchdog timer!
216 ReturnStatus
= Status
= refit_call3_wrapper(BS
->StartImage
, ChildImageHandle
, NULL
, NULL
);
217 // control returns here when the child image calls Exit()
218 SPrint(ErrorInfo
, 255, L
"returned from %s", ImageTitle
);
219 if (CheckError(Status
, ErrorInfo
)) {
220 if (ErrorInStep
!= NULL
)
224 // re-open file handles
228 // unload the image, we don't care if it works or not...
229 Status
= refit_call1_wrapper(BS
->UnloadImage
, ChildImageHandle
);
231 if (FullLoadOptions
!= NULL
)
232 FreePool(FullLoadOptions
);
234 } /* static EFI_STATUS StartEFIImageList() */
236 static EFI_STATUS
StartEFIImage(IN EFI_DEVICE_PATH
*DevicePath
,
237 IN CHAR16
*LoadOptions
, IN CHAR16
*LoadOptionsPrefix
,
238 IN CHAR16
*ImageTitle
,
239 OUT UINTN
*ErrorInStep
,
242 EFI_DEVICE_PATH
*DevicePaths
[2];
244 DevicePaths
[0] = DevicePath
;
245 DevicePaths
[1] = NULL
;
246 return StartEFIImageList(DevicePaths
, LoadOptions
, LoadOptionsPrefix
, ImageTitle
, ErrorInStep
, Verbose
);
247 } /* static EFI_STATUS StartEFIImage() */
250 // EFI OS loader functions
253 static VOID
StartLoader(IN LOADER_ENTRY
*Entry
)
255 UINTN ErrorInStep
= 0;
257 BeginExternalScreen(Entry
->UseGraphicsMode
, L
"Booting OS");
258 StartEFIImage(Entry
->DevicePath
, Entry
->LoadOptions
,
259 Basename(Entry
->LoaderPath
), Basename(Entry
->LoaderPath
), &ErrorInStep
, !Entry
->UseGraphicsMode
);
260 FinishExternalScreen();
263 // Locate an initrd or initramfs file that matches the kernel specified by LoaderPath.
264 // The matching file has a name that begins with "init" and includes the same version
265 // number string as is found in LoaderPath -- but not a longer version number string.
266 // For instance, if LoaderPath is \EFI\kernels\bzImage-3.3.0.efi, and if \EFI\kernels
267 // has a file called initramfs-3.3.0.img, this function will return the string
268 // '\EFI\kernels\initramfs-3.3.0.img'. If the directory ALSO contains the file
269 // initramfs-3.3.0-rc7.img or initramfs-13.3.0.img, those files will NOT match;
270 // however, initmine-3.3.0.img might match. (FindInitrd() returns the first match it
271 // finds). Thus, care should be taken to avoid placing duplicate matching files in
272 // the kernel's directory.
273 // If no matching init file can be found, returns NULL.
274 static CHAR16
* FindInitrd(IN CHAR16
*LoaderPath
, IN REFIT_VOLUME
*Volume
) {
275 CHAR16
*InitrdName
= NULL
, *FileName
, *KernelVersion
, *InitrdVersion
, *Path
;
276 REFIT_DIR_ITER DirIter
;
277 EFI_FILE_INFO
*DirEntry
;
279 FileName
= Basename(LoaderPath
);
280 KernelVersion
= FindNumbers(FileName
);
281 Path
= FindPath(LoaderPath
);
283 // Add trailing backslash for root directory; necessary on some systems, but must
284 // NOT be added to all directories, since on other systems, a trailing backslash on
285 // anything but the root directory causes them to flake out!
286 if (StrLen(Path
) == 0) {
287 MergeStrings(&Path
, L
"\\", 0);
289 DirIterOpen(Volume
->RootDir
, Path
, &DirIter
);
290 // Now add a trailing backslash if it was NOT added earlier, for consistency in
291 // building the InitrdName later....
292 if ((StrLen(Path
) > 0) && (Path
[StrLen(Path
) - 1] != L
'\\'))
293 MergeStrings(&Path
, L
"\\", 0);
294 while ((DirIterNext(&DirIter
, 2, L
"init*", &DirEntry
)) && (InitrdName
== NULL
)) {
295 InitrdVersion
= FindNumbers(DirEntry
->FileName
);
296 if (KernelVersion
!= NULL
) {
297 if (StriCmp(InitrdVersion
, KernelVersion
) == 0) {
298 MergeStrings(&InitrdName
, Path
, 0);
299 MergeStrings(&InitrdName
, DirEntry
->FileName
, 0);
302 if (InitrdVersion
== NULL
) {
303 MergeStrings(&InitrdName
, Path
, 0);
304 MergeStrings(&InitrdName
, DirEntry
->FileName
, 0);
307 if (InitrdVersion
!= NULL
)
308 FreePool(InitrdVersion
);
310 DirIterClose(&DirIter
);
312 // Note: Don't FreePool(FileName), since Basename returns a pointer WITHIN the string it's passed.
313 FreePool(KernelVersion
);
316 } // static CHAR16 * FindInitrd()
318 LOADER_ENTRY
* AddPreparedLoaderEntry(LOADER_ENTRY
*Entry
) {
319 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
322 } // LOADER_ENTRY * AddPreparedLoaderEntry()
324 // Creates a copy of a menu screen.
325 // Returns a pointer to the copy of the menu screen.
326 static REFIT_MENU_SCREEN
* CopyMenuScreen(REFIT_MENU_SCREEN
*Entry
) {
327 REFIT_MENU_SCREEN
*NewEntry
;
330 NewEntry
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
331 if ((Entry
!= NULL
) && (NewEntry
!= NULL
)) {
332 CopyMem(NewEntry
, Entry
, sizeof(REFIT_MENU_SCREEN
));
333 NewEntry
->Title
= StrDuplicate(Entry
->Title
);
334 NewEntry
->TimeoutText
= StrDuplicate(Entry
->TimeoutText
);
335 if (Entry
->TitleImage
!= NULL
) {
336 NewEntry
->TitleImage
= AllocatePool(sizeof(EG_IMAGE
));
337 if (NewEntry
->TitleImage
!= NULL
)
338 CopyMem(NewEntry
->TitleImage
, Entry
->TitleImage
, sizeof(EG_IMAGE
));
340 NewEntry
->InfoLines
= (CHAR16
**) AllocateZeroPool(Entry
->InfoLineCount
* (sizeof(CHAR16
*)));
341 for (i
= 0; i
< Entry
->InfoLineCount
&& NewEntry
->InfoLines
; i
++) {
342 NewEntry
->InfoLines
[i
] = StrDuplicate(Entry
->InfoLines
[i
]);
344 NewEntry
->Entries
= (REFIT_MENU_ENTRY
**) AllocateZeroPool(Entry
->EntryCount
* (sizeof (REFIT_MENU_ENTRY
*)));
345 for (i
= 0; i
< Entry
->EntryCount
&& NewEntry
->Entries
; i
++) {
346 AddMenuEntry(NewEntry
, Entry
->Entries
[i
]);
350 } // static REFIT_MENU_SCREEN* CopyMenuScreen()
352 // Creates a copy of a menu entry. Intended to enable moving a stack-based
353 // menu entry (such as the ones for the "reboot" and "exit" functions) to
354 // to the heap. This enables easier deletion of the whole set of menu
355 // entries when re-scanning.
356 // Returns a pointer to the copy of the menu entry.
357 static REFIT_MENU_ENTRY
* CopyMenuEntry(REFIT_MENU_ENTRY
*Entry
) {
358 REFIT_MENU_ENTRY
*NewEntry
;
360 NewEntry
= AllocateZeroPool(sizeof(REFIT_MENU_ENTRY
));
361 if ((Entry
!= NULL
) && (NewEntry
!= NULL
)) {
362 CopyMem(NewEntry
, Entry
, sizeof(REFIT_MENU_ENTRY
));
363 NewEntry
->Title
= StrDuplicate(Entry
->Title
);
364 if (Entry
->BadgeImage
!= NULL
) {
365 NewEntry
->BadgeImage
= AllocatePool(sizeof(EG_IMAGE
));
366 if (NewEntry
->BadgeImage
!= NULL
)
367 CopyMem(NewEntry
->BadgeImage
, Entry
->BadgeImage
, sizeof(EG_IMAGE
));
369 if (Entry
->Image
!= NULL
) {
370 NewEntry
->Image
= AllocatePool(sizeof(EG_IMAGE
));
371 if (NewEntry
->Image
!= NULL
)
372 CopyMem(NewEntry
->Image
, Entry
->Image
, sizeof(EG_IMAGE
));
374 if (Entry
->SubScreen
!= NULL
) {
375 NewEntry
->SubScreen
= CopyMenuScreen(Entry
->SubScreen
);
379 } // REFIT_MENU_ENTRY* CopyMenuEntry()
381 // Creates a new LOADER_ENTRY data structure and populates it with
382 // default values from the specified Entry, or NULL values if Entry
383 // is unspecified (NULL).
384 // Returns a pointer to the new data structure, or NULL if it
385 // couldn't be allocated
386 LOADER_ENTRY
*InitializeLoaderEntry(IN LOADER_ENTRY
*Entry
) {
387 LOADER_ENTRY
*NewEntry
= NULL
;
389 NewEntry
= AllocateZeroPool(sizeof(LOADER_ENTRY
));
390 if (NewEntry
!= NULL
) {
391 NewEntry
->me
.Title
= NULL
;
392 NewEntry
->me
.Tag
= TAG_LOADER
;
393 NewEntry
->Enabled
= TRUE
;
394 NewEntry
->UseGraphicsMode
= FALSE
;
395 NewEntry
->OSType
= 0;
397 NewEntry
->LoaderPath
= StrDuplicate(Entry
->LoaderPath
);
398 NewEntry
->VolName
= StrDuplicate(Entry
->VolName
);
399 NewEntry
->DevicePath
= Entry
->DevicePath
;
400 NewEntry
->UseGraphicsMode
= Entry
->UseGraphicsMode
;
401 NewEntry
->LoadOptions
= StrDuplicate(Entry
->LoadOptions
);
402 NewEntry
->InitrdPath
= StrDuplicate(Entry
->InitrdPath
);
406 } // LOADER_ENTRY *InitializeLoaderEntry()
408 // Prepare a REFIT_MENU_SCREEN data structure for a subscreen entry. This sets up
409 // the default entry that launches the boot loader using the same options as the
410 // main Entry does. Subsequent options can be added by the calling function.
411 // If a subscreen already exists in the Entry that's passed to this function,
412 // it's left unchanged and a pointer to it is returned.
413 // Returns a pointer to the new subscreen data structure, or NULL if there
414 // were problems allocating memory.
415 REFIT_MENU_SCREEN
*InitializeSubScreen(IN LOADER_ENTRY
*Entry
) {
416 CHAR16
*FileName
, *Temp
= NULL
;
417 REFIT_MENU_SCREEN
*SubScreen
= NULL
;
418 LOADER_ENTRY
*SubEntry
;
420 FileName
= Basename(Entry
->LoaderPath
);
421 if (Entry
->me
.SubScreen
== NULL
) { // No subscreen yet; initialize default entry....
422 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
423 if (SubScreen
!= NULL
) {
424 SubScreen
->Title
= AllocateZeroPool(sizeof(CHAR16
) * 256);
425 SPrint(SubScreen
->Title
, 255, L
"Boot Options for %s on %s",
426 (Entry
->Title
!= NULL
) ? Entry
->Title
: FileName
, Entry
->VolName
);
427 SubScreen
->TitleImage
= Entry
->me
.Image
;
429 SubEntry
= InitializeLoaderEntry(Entry
);
430 if (SubEntry
!= NULL
) {
431 SubEntry
->me
.Title
= L
"Boot using default options";
432 if ((SubEntry
->InitrdPath
!= NULL
) && (StrLen(SubEntry
->InitrdPath
) > 0) && (!StriSubCmp(L
"initrd", SubEntry
->LoadOptions
))) {
433 MergeStrings(&Temp
, L
"initrd=", 0);
434 MergeStrings(&Temp
, SubEntry
->InitrdPath
, 0);
435 MergeStrings(&SubEntry
->LoadOptions
, Temp
, L
' ');
438 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
439 } // if (SubEntry != NULL)
440 } // if (SubScreen != NULL)
441 } else { // existing subscreen; less initialization, and just add new entry later....
442 SubScreen
= Entry
->me
.SubScreen
;
445 } // REFIT_MENU_SCREEN *InitializeSubScreen()
447 VOID
GenerateSubScreen(LOADER_ENTRY
*Entry
, IN REFIT_VOLUME
*Volume
) {
448 REFIT_MENU_SCREEN
*SubScreen
;
449 LOADER_ENTRY
*SubEntry
;
450 CHAR16
*InitrdOption
= NULL
, *Temp
;
451 CHAR16 DiagsFileName
[256];
456 // create the submenu
457 if (StrLen(Entry
->Title
) == 0) {
458 FreePool(Entry
->Title
);
461 SubScreen
= InitializeSubScreen(Entry
);
463 // loader-specific submenu entries
464 if (Entry
->OSType
== 'M') { // entries for Mac OS X
466 SubEntry
= InitializeLoaderEntry(Entry
);
467 if (SubEntry
!= NULL
) {
468 SubEntry
->me
.Title
= L
"Boot Mac OS X with a 64-bit kernel";
469 SubEntry
->LoadOptions
= L
"arch=x86_64";
470 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
471 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
474 SubEntry
= InitializeLoaderEntry(Entry
);
475 if (SubEntry
!= NULL
) {
476 SubEntry
->me
.Title
= L
"Boot Mac OS X with a 32-bit kernel";
477 SubEntry
->LoadOptions
= L
"arch=i386";
478 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
479 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
483 if (!(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_SINGLEUSER
)) {
484 SubEntry
= InitializeLoaderEntry(Entry
);
485 if (SubEntry
!= NULL
) {
486 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode";
487 SubEntry
->UseGraphicsMode
= FALSE
;
488 SubEntry
->LoadOptions
= L
"-v";
489 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
493 SubEntry
= InitializeLoaderEntry(Entry
);
494 if (SubEntry
!= NULL
) {
495 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode (64-bit)";
496 SubEntry
->UseGraphicsMode
= FALSE
;
497 SubEntry
->LoadOptions
= L
"-v arch=x86_64";
498 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
501 SubEntry
= InitializeLoaderEntry(Entry
);
502 if (SubEntry
!= NULL
) {
503 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode (32-bit)";
504 SubEntry
->UseGraphicsMode
= FALSE
;
505 SubEntry
->LoadOptions
= L
"-v arch=i386";
506 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
510 SubEntry
= InitializeLoaderEntry(Entry
);
511 if (SubEntry
!= NULL
) {
512 SubEntry
->me
.Title
= L
"Boot Mac OS X in single user mode";
513 SubEntry
->UseGraphicsMode
= FALSE
;
514 SubEntry
->LoadOptions
= L
"-v -s";
515 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
519 // check for Apple hardware diagnostics
520 StrCpy(DiagsFileName
, L
"\\System\\Library\\CoreServices\\.diagnostics\\diags.efi");
521 if (FileExists(Volume
->RootDir
, DiagsFileName
) && !(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_HWTEST
)) {
522 SubEntry
= InitializeLoaderEntry(Entry
);
523 if (SubEntry
!= NULL
) {
524 SubEntry
->me
.Title
= L
"Run Apple Hardware Test";
525 FreePool(SubEntry
->LoaderPath
);
526 SubEntry
->LoaderPath
= StrDuplicate(DiagsFileName
);
527 SubEntry
->DevicePath
= FileDevicePath(Volume
->DeviceHandle
, SubEntry
->LoaderPath
);
528 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
529 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
531 } // if diagnostics entry found
533 } else if (Entry
->OSType
== 'L') { // entries for Linux kernels with EFI stub loaders
534 File
= ReadLinuxOptionsFile(Entry
->LoaderPath
, Volume
);
536 if ((Temp
= FindInitrd(Entry
->LoaderPath
, Volume
)) != NULL
) {
537 MergeStrings(&InitrdOption
, L
"initrd=", 0);
538 MergeStrings(&InitrdOption
, Temp
, 0);
540 TokenCount
= ReadTokenLine(File
, &TokenList
); // read and discard first entry, since it's
541 FreeTokenLine(&TokenList
, &TokenCount
); // set up by InitializeSubScreen(), earlier....
542 while ((TokenCount
= ReadTokenLine(File
, &TokenList
)) > 1) {
543 SubEntry
= InitializeLoaderEntry(Entry
);
544 SubEntry
->me
.Title
= StrDuplicate(TokenList
[0]);
545 if (SubEntry
->LoadOptions
!= NULL
)
546 FreePool(SubEntry
->LoadOptions
);
547 SubEntry
->LoadOptions
= StrDuplicate(TokenList
[1]);
548 MergeStrings(&SubEntry
->LoadOptions
, InitrdOption
, L
' ');
549 FreeTokenLine(&TokenList
, &TokenCount
);
550 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_LINUX
;
551 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
554 FreePool(InitrdOption
);
558 } // if Linux options file exists
560 } else if (Entry
->OSType
== 'E') { // entries for ELILO
561 SubEntry
= InitializeLoaderEntry(Entry
);
562 if (SubEntry
!= NULL
) {
563 SubEntry
->me
.Title
= L
"Run ELILO in interactive mode";
564 SubEntry
->LoadOptions
= L
"-p";
565 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
566 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
569 SubEntry
= InitializeLoaderEntry(Entry
);
570 if (SubEntry
!= NULL
) {
571 SubEntry
->me
.Title
= L
"Boot Linux for a 17\" iMac or a 15\" MacBook Pro (*)";
572 SubEntry
->UseGraphicsMode
= TRUE
;
573 SubEntry
->LoadOptions
= L
"-d 0 i17";
574 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
575 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
578 SubEntry
= InitializeLoaderEntry(Entry
);
579 if (SubEntry
!= NULL
) {
580 SubEntry
->me
.Title
= L
"Boot Linux for a 20\" iMac (*)";
581 SubEntry
->UseGraphicsMode
= TRUE
;
582 SubEntry
->LoadOptions
= L
"-d 0 i20";
583 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
584 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
587 SubEntry
= InitializeLoaderEntry(Entry
);
588 if (SubEntry
!= NULL
) {
589 SubEntry
->me
.Title
= L
"Boot Linux for a Mac Mini (*)";
590 SubEntry
->UseGraphicsMode
= TRUE
;
591 SubEntry
->LoadOptions
= L
"-d 0 mini";
592 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
593 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
596 AddMenuInfoLine(SubScreen
, L
"NOTE: This is an example. Entries");
597 AddMenuInfoLine(SubScreen
, L
"marked with (*) may not work.");
599 } else if (Entry
->OSType
== 'X') { // entries for xom.efi
600 // by default, skip the built-in selection and boot from hard disk only
601 Entry
->LoadOptions
= L
"-s -h";
603 SubEntry
= InitializeLoaderEntry(Entry
);
604 if (SubEntry
!= NULL
) {
605 SubEntry
->me
.Title
= L
"Boot Windows from Hard Disk";
606 SubEntry
->LoadOptions
= L
"-s -h";
607 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
608 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
611 SubEntry
= InitializeLoaderEntry(Entry
);
612 if (SubEntry
!= NULL
) {
613 SubEntry
->me
.Title
= L
"Boot Windows from CD-ROM";
614 SubEntry
->LoadOptions
= L
"-s -c";
615 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
616 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
619 SubEntry
= InitializeLoaderEntry(Entry
);
620 if (SubEntry
!= NULL
) {
621 SubEntry
->me
.Title
= L
"Run XOM in text mode";
622 SubEntry
->UseGraphicsMode
= FALSE
;
623 SubEntry
->LoadOptions
= L
"-v";
624 SubEntry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
625 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
627 } // entries for xom.efi
628 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
629 Entry
->me
.SubScreen
= SubScreen
;
630 } // VOID GenerateSubScreen()
632 // Returns options for a Linux kernel. Reads them from an options file in the
633 // kernel's directory; and if present, adds an initrd= option for an initial
634 // RAM disk file with the same version number as the kernel file.
635 static CHAR16
* GetMainLinuxOptions(IN CHAR16
* LoaderPath
, IN REFIT_VOLUME
*Volume
) {
636 CHAR16
*Options
= NULL
, *InitrdName
, *InitrdOption
= NULL
;
638 Options
= GetFirstOptionsFromFile(LoaderPath
, Volume
);
639 InitrdName
= FindInitrd(LoaderPath
, Volume
);
640 if (InitrdName
!= NULL
) {
641 MergeStrings(&InitrdOption
, L
"initrd=", 0);
642 MergeStrings(&InitrdOption
, InitrdName
, 0);
644 MergeStrings(&Options
, InitrdOption
, ' ');
645 if (InitrdOption
!= NULL
)
646 FreePool(InitrdOption
);
647 if (InitrdName
!= NULL
)
648 FreePool(InitrdName
);
650 } // static CHAR16 * GetMainLinuxOptions()
652 // Sets a few defaults for a loader entry -- mainly the icon, but also the OS type
653 // code and shortcut letter. For Linux EFI stub loaders, also sets kernel options
654 // that will (with luck) work fairly automatically.
655 VOID
SetLoaderDefaults(LOADER_ENTRY
*Entry
, CHAR16
*LoaderPath
, IN REFIT_VOLUME
*Volume
) {
656 CHAR16 IconFileName
[256];
657 CHAR16
*FileName
, *PathOnly
, *OSIconName
= NULL
, *Temp
;
658 CHAR16 ShortcutLetter
= 0;
660 FileName
= Basename(LoaderPath
);
661 PathOnly
= FindPath(LoaderPath
);
663 // locate a custom icon for the loader
664 StrCpy(IconFileName
, LoaderPath
);
665 ReplaceEfiExtension(IconFileName
, L
".icns");
666 if (FileExists(Volume
->RootDir
, IconFileName
)) {
667 Entry
->me
.Image
= LoadIcns(Volume
->RootDir
, IconFileName
, 128);
668 } else if ((StrLen(PathOnly
) == 0) && (Volume
->VolIconImage
!= NULL
)) {
669 Entry
->me
.Image
= Volume
->VolIconImage
;
670 } // icon matched to loader or volume
672 Temp
= FindLastDirName(LoaderPath
);
673 MergeStrings(&OSIconName
, Temp
, L
',');
675 if (OSIconName
!= NULL
) {
676 ShortcutLetter
= OSIconName
[0];
679 // detect specific loaders
680 if (StriSubCmp(L
"bzImage", LoaderPath
) || StriSubCmp(L
"vmlinuz", LoaderPath
)) {
681 MergeStrings(&OSIconName
, L
"linux", L
',');
683 if (ShortcutLetter
== 0)
684 ShortcutLetter
= 'L';
685 Entry
->LoadOptions
= GetMainLinuxOptions(LoaderPath
, Volume
);
686 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_LINUX
;
687 } else if (StriSubCmp(L
"refit", LoaderPath
)) {
688 MergeStrings(&OSIconName
, L
"refit", L
',');
690 ShortcutLetter
= 'R';
691 } else if (StriCmp(LoaderPath
, MACOSX_LOADER_PATH
) == 0) {
692 if (Volume
->VolIconImage
!= NULL
) { // custom icon file found
693 Entry
->me
.Image
= Volume
->VolIconImage
;
695 MergeStrings(&OSIconName
, L
"mac", L
',');
697 ShortcutLetter
= 'M';
698 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_OSX
;
699 } else if (StriCmp(FileName
, L
"diags.efi") == 0) {
700 MergeStrings(&OSIconName
, L
"hwtest", L
',');
701 } else if (StriCmp(FileName
, L
"e.efi") == 0 || StriCmp(FileName
, L
"elilo.efi") == 0) {
702 MergeStrings(&OSIconName
, L
"elilo,linux", L
',');
704 if (ShortcutLetter
== 0)
705 ShortcutLetter
= 'L';
706 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_ELILO
;
707 } else if (StriSubCmp(L
"grub", FileName
)) {
709 ShortcutLetter
= 'G';
710 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_GRUB
;
711 } else if (StriCmp(FileName
, L
"cdboot.efi") == 0 ||
712 StriCmp(FileName
, L
"bootmgr.efi") == 0 ||
713 StriCmp(FileName
, L
"Bootmgfw.efi") == 0) {
714 MergeStrings(&OSIconName
, L
"win", L
',');
716 ShortcutLetter
= 'W';
717 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
718 } else if (StriCmp(FileName
, L
"xom.efi") == 0) {
719 MergeStrings(&OSIconName
, L
"xom,win", L
',');
720 Entry
->UseGraphicsMode
= TRUE
;
722 ShortcutLetter
= 'W';
723 Entry
->UseGraphicsMode
= GlobalConfig
.GraphicsFor
& GRAPHICS_FOR_WINDOWS
;
726 if ((ShortcutLetter
>= 'a') && (ShortcutLetter
<= 'z'))
727 ShortcutLetter
= ShortcutLetter
- 'a' + 'A'; // convert lowercase to uppercase
728 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
729 if (Entry
->me
.Image
== NULL
)
730 Entry
->me
.Image
= LoadOSIcon(OSIconName
, L
"unknown", FALSE
);
731 if (PathOnly
!= NULL
)
733 } // VOID SetLoaderDefaults()
735 // Add a specified EFI boot loader to the list, using automatic settings
736 // for icons, options, etc.
737 LOADER_ENTRY
* AddLoaderEntry(IN CHAR16
*LoaderPath
, IN CHAR16
*LoaderTitle
, IN REFIT_VOLUME
*Volume
) {
740 CleanUpPathNameSlashes(LoaderPath
);
741 Entry
= InitializeLoaderEntry(NULL
);
743 Entry
->Title
= StrDuplicate((LoaderTitle
!= NULL
) ? LoaderTitle
: LoaderPath
);
744 Entry
->me
.Title
= AllocateZeroPool(sizeof(CHAR16
) * 256);
745 SPrint(Entry
->me
.Title
, 255, L
"Boot %s from %s", (LoaderTitle
!= NULL
) ? LoaderTitle
: LoaderPath
, Volume
->VolName
);
747 Entry
->me
.BadgeImage
= Volume
->VolBadgeImage
;
748 if ((LoaderPath
!= NULL
) && (LoaderPath
[0] != L
'\\')) {
749 Entry
->LoaderPath
= StrDuplicate(L
"\\");
751 Entry
->LoaderPath
= NULL
;
753 MergeStrings(&(Entry
->LoaderPath
), LoaderPath
, 0);
754 Entry
->VolName
= Volume
->VolName
;
755 Entry
->DevicePath
= FileDevicePath(Volume
->DeviceHandle
, Entry
->LoaderPath
);
756 SetLoaderDefaults(Entry
, LoaderPath
, Volume
);
757 GenerateSubScreen(Entry
, Volume
);
758 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
762 } // LOADER_ENTRY * AddLoaderEntry()
764 // Returns -1 if (Time1 < Time2), +1 if (Time1 > Time2), or 0 if
765 // (Time1 == Time2). Precision is only to the nearest second; since
766 // this is used for sorting boot loader entries, differences smaller
767 // than this are likely to be meaningless (and unlikely!).
768 INTN
TimeComp(EFI_TIME
*Time1
, EFI_TIME
*Time2
) {
769 INT64 Time1InSeconds
, Time2InSeconds
;
771 // Following values are overestimates; I'm assuming 31 days in every month.
772 // This is fine for the purpose of this function, which has a limited
774 Time1InSeconds
= Time1
->Second
+ (Time1
->Minute
* 60) + (Time1
->Hour
* 3600) + (Time1
->Day
* 86400) +
775 (Time1
->Month
* 2678400) + ((Time1
->Year
- 1998) * 32140800);
776 Time2InSeconds
= Time2
->Second
+ (Time2
->Minute
* 60) + (Time2
->Hour
* 3600) + (Time2
->Day
* 86400) +
777 (Time2
->Month
* 2678400) + ((Time2
->Year
- 1998) * 32140800);
778 if (Time1InSeconds
< Time2InSeconds
)
780 else if (Time1InSeconds
> Time2InSeconds
)
786 // Adds a loader list element, keeping it sorted by date. Returns the new
787 // first element (the one with the most recent date).
788 static struct LOADER_LIST
* AddLoaderListEntry(struct LOADER_LIST
*LoaderList
, struct LOADER_LIST
*NewEntry
) {
789 struct LOADER_LIST
*LatestEntry
, *CurrentEntry
, *PrevEntry
= NULL
;
791 LatestEntry
= CurrentEntry
= LoaderList
;
792 if (LoaderList
== NULL
) {
793 LatestEntry
= NewEntry
;
795 while ((CurrentEntry
!= NULL
) && (TimeComp(&(NewEntry
->TimeStamp
), &(CurrentEntry
->TimeStamp
)) < 0)) {
796 PrevEntry
= CurrentEntry
;
797 CurrentEntry
= CurrentEntry
->NextEntry
;
799 NewEntry
->NextEntry
= CurrentEntry
;
800 if (PrevEntry
== NULL
) {
801 LatestEntry
= NewEntry
;
803 PrevEntry
->NextEntry
= NewEntry
;
806 return (LatestEntry
);
807 } // static VOID AddLoaderListEntry()
809 // Delete the LOADER_LIST linked list
810 static VOID
CleanUpLoaderList(struct LOADER_LIST
*LoaderList
) {
811 struct LOADER_LIST
*Temp
;
813 while (LoaderList
!= NULL
) {
815 LoaderList
= LoaderList
->NextEntry
;
816 FreePool(Temp
->FileName
);
819 } // static VOID CleanUpLoaderList()
821 // Scan an individual directory for EFI boot loader files and, if found,
822 // add them to the list. Sorts the entries within the loader directory
823 // so that the most recent one appears first in the list.
824 static VOID
ScanLoaderDir(IN REFIT_VOLUME
*Volume
, IN CHAR16
*Path
, IN CHAR16
*Pattern
)
827 REFIT_DIR_ITER DirIter
;
828 EFI_FILE_INFO
*DirEntry
;
829 CHAR16 FileName
[256], *Extension
;
830 struct LOADER_LIST
*LoaderList
= NULL
, *NewLoader
;
832 if ((!SelfDirPath
|| !Path
|| ((StriCmp(Path
, SelfDirPath
) == 0) && Volume
->DeviceHandle
!= SelfVolume
->DeviceHandle
) ||
833 (StriCmp(Path
, SelfDirPath
) != 0)) && (!IsIn(Path
, GlobalConfig
.DontScan
))) {
834 // look through contents of the directory
835 DirIterOpen(Volume
->RootDir
, Path
, &DirIter
);
836 while (DirIterNext(&DirIter
, 2, Pattern
, &DirEntry
)) {
837 Extension
= FindExtension(DirEntry
->FileName
);
838 if (DirEntry
->FileName
[0] == '.' ||
839 StriCmp(DirEntry
->FileName
, L
"TextMode.efi") == 0 ||
840 StriCmp(DirEntry
->FileName
, L
"ebounce.efi") == 0 ||
841 StriCmp(DirEntry
->FileName
, L
"GraphicsConsole.efi") == 0 ||
842 StriCmp(Extension
, L
".icns") == 0 ||
843 StriSubCmp(L
"shell", DirEntry
->FileName
))
844 continue; // skip this
847 SPrint(FileName
, 255, L
"\\%s\\%s", Path
, DirEntry
->FileName
);
849 SPrint(FileName
, 255, L
"\\%s", DirEntry
->FileName
);
850 CleanUpPathNameSlashes(FileName
);
851 NewLoader
= AllocateZeroPool(sizeof(struct LOADER_LIST
));
852 if (NewLoader
!= NULL
) {
853 NewLoader
->FileName
= StrDuplicate(FileName
);
854 NewLoader
->TimeStamp
= DirEntry
->ModificationTime
;
855 LoaderList
= AddLoaderListEntry(LoaderList
, NewLoader
);
859 NewLoader
= LoaderList
;
860 while (NewLoader
!= NULL
) {
861 AddLoaderEntry(NewLoader
->FileName
, NULL
, Volume
);
862 NewLoader
= NewLoader
->NextEntry
;
864 CleanUpLoaderList(LoaderList
);
865 Status
= DirIterClose(&DirIter
);
866 if (Status
!= EFI_NOT_FOUND
) {
868 SPrint(FileName
, 255, L
"while scanning the %s directory", Path
);
870 StrCpy(FileName
, L
"while scanning the root directory");
871 CheckError(Status
, FileName
);
872 } // if (Status != EFI_NOT_FOUND)
873 } // if not scanning our own directory
874 } /* static VOID ScanLoaderDir() */
876 static VOID
ScanEfiFiles(REFIT_VOLUME
*Volume
) {
878 REFIT_DIR_ITER EfiDirIter
;
879 EFI_FILE_INFO
*EfiDirEntry
;
880 CHAR16 FileName
[256], *Directory
, *MatchPatterns
;
883 MatchPatterns
= StrDuplicate(LOADER_MATCH_PATTERNS
);
884 if (GlobalConfig
.ScanAllLinux
)
885 MergeStrings(&MatchPatterns
, LINUX_MATCH_PATTERNS
, L
',');
887 if ((Volume
->RootDir
!= NULL
) && (Volume
->VolName
!= NULL
)) {
888 // check for Mac OS X boot loader
889 if (!IsIn(L
"System\\Library\\CoreServices", GlobalConfig
.DontScan
)) {
890 StrCpy(FileName
, MACOSX_LOADER_PATH
);
891 if (FileExists(Volume
->RootDir
, FileName
)) {
892 AddLoaderEntry(FileName
, L
"Mac OS X", Volume
);
896 StrCpy(FileName
, L
"System\\Library\\CoreServices\\xom.efi");
897 if (FileExists(Volume
->RootDir
, FileName
)) {
898 AddLoaderEntry(FileName
, L
"Windows XP (XoM)", Volume
);
900 } // if Mac directory not in GlobalConfig.DontScan list
902 // check for Microsoft boot loader/menu
903 StrCpy(FileName
, L
"EFI\\Microsoft\\Boot\\Bootmgfw.efi");
904 if (FileExists(Volume
->RootDir
, FileName
) && !IsIn(L
"EFI\\Microsoft\\Boot", GlobalConfig
.DontScan
)) {
905 AddLoaderEntry(FileName
, L
"Microsoft EFI boot", Volume
);
908 // scan the root directory for EFI executables
909 ScanLoaderDir(Volume
, L
"\\", MatchPatterns
);
911 // scan subdirectories of the EFI directory (as per the standard)
912 DirIterOpen(Volume
->RootDir
, L
"EFI", &EfiDirIter
);
913 while (DirIterNext(&EfiDirIter
, 1, NULL
, &EfiDirEntry
)) {
914 if (StriCmp(EfiDirEntry
->FileName
, L
"tools") == 0 || EfiDirEntry
->FileName
[0] == '.')
915 continue; // skip this, doesn't contain boot loaders
916 SPrint(FileName
, 255, L
"EFI\\%s", EfiDirEntry
->FileName
);
917 ScanLoaderDir(Volume
, FileName
, MatchPatterns
);
919 Status
= DirIterClose(&EfiDirIter
);
920 if (Status
!= EFI_NOT_FOUND
)
921 CheckError(Status
, L
"while scanning the EFI directory");
923 // Scan user-specified (or additional default) directories....
925 while ((Directory
= FindCommaDelimited(GlobalConfig
.AlsoScan
, i
++)) != NULL
) {
926 CleanUpPathNameSlashes(Directory
);
927 Length
= StrLen(Directory
);
929 ScanLoaderDir(Volume
, Directory
, MatchPatterns
);
933 } // static VOID ScanEfiFiles()
935 // Scan internal disks for valid EFI boot loaders....
936 static VOID
ScanInternal(VOID
) {
939 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
940 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_INTERNAL
) {
941 ScanEfiFiles(Volumes
[VolumeIndex
]);
944 } // static VOID ScanInternal()
946 // Scan external disks for valid EFI boot loaders....
947 static VOID
ScanExternal(VOID
) {
950 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
951 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_EXTERNAL
) {
952 ScanEfiFiles(Volumes
[VolumeIndex
]);
955 } // static VOID ScanExternal()
957 // Scan internal disks for valid EFI boot loaders....
958 static VOID
ScanOptical(VOID
) {
961 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
962 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_OPTICAL
) {
963 ScanEfiFiles(Volumes
[VolumeIndex
]);
966 } // static VOID ScanOptical()
969 // legacy boot functions
972 static EFI_STATUS
ActivateMbrPartition(IN EFI_BLOCK_IO
*BlockIO
, IN UINTN PartitionIndex
)
975 UINT8 SectorBuffer
[512];
976 MBR_PARTITION_INFO
*MbrTable
, *EMbrTable
;
977 UINT32 ExtBase
, ExtCurrent
, NextExtCurrent
;
978 UINTN LogicalPartitionIndex
= 4;
980 BOOLEAN HaveBootCode
;
983 Status
= refit_call5_wrapper(BlockIO
->ReadBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, 0, 512, SectorBuffer
);
984 if (EFI_ERROR(Status
))
986 if (*((UINT16
*)(SectorBuffer
+ 510)) != 0xaa55)
987 return EFI_NOT_FOUND
; // safety measure #1
989 // add boot code if necessary
990 HaveBootCode
= FALSE
;
991 for (i
= 0; i
< MBR_BOOTCODE_SIZE
; i
++) {
992 if (SectorBuffer
[i
] != 0) {
998 // no boot code found in the MBR, add the syslinux MBR code
999 SetMem(SectorBuffer
, MBR_BOOTCODE_SIZE
, 0);
1000 CopyMem(SectorBuffer
, syslinux_mbr
, SYSLINUX_MBR_SIZE
);
1003 // set the partition active
1004 MbrTable
= (MBR_PARTITION_INFO
*)(SectorBuffer
+ 446);
1006 for (i
= 0; i
< 4; i
++) {
1007 if (MbrTable
[i
].Flags
!= 0x00 && MbrTable
[i
].Flags
!= 0x80)
1008 return EFI_NOT_FOUND
; // safety measure #2
1009 if (i
== PartitionIndex
)
1010 MbrTable
[i
].Flags
= 0x80;
1011 else if (PartitionIndex
>= 4 && IS_EXTENDED_PART_TYPE(MbrTable
[i
].Type
)) {
1012 MbrTable
[i
].Flags
= 0x80;
1013 ExtBase
= MbrTable
[i
].StartLBA
;
1015 MbrTable
[i
].Flags
= 0x00;
1019 Status
= refit_call5_wrapper(BlockIO
->WriteBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, 0, 512, SectorBuffer
);
1020 if (EFI_ERROR(Status
))
1023 if (PartitionIndex
>= 4) {
1024 // we have to activate a logical partition, so walk the EMBR chain
1026 // NOTE: ExtBase was set above while looking at the MBR table
1027 for (ExtCurrent
= ExtBase
; ExtCurrent
; ExtCurrent
= NextExtCurrent
) {
1028 // read current EMBR
1029 Status
= refit_call5_wrapper(BlockIO
->ReadBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, ExtCurrent
, 512, SectorBuffer
);
1030 if (EFI_ERROR(Status
))
1032 if (*((UINT16
*)(SectorBuffer
+ 510)) != 0xaa55)
1033 return EFI_NOT_FOUND
; // safety measure #3
1035 // scan EMBR, set appropriate partition active
1036 EMbrTable
= (MBR_PARTITION_INFO
*)(SectorBuffer
+ 446);
1038 for (i
= 0; i
< 4; i
++) {
1039 if (EMbrTable
[i
].Flags
!= 0x00 && EMbrTable
[i
].Flags
!= 0x80)
1040 return EFI_NOT_FOUND
; // safety measure #4
1041 if (EMbrTable
[i
].StartLBA
== 0 || EMbrTable
[i
].Size
== 0)
1043 if (IS_EXTENDED_PART_TYPE(EMbrTable
[i
].Type
)) {
1044 // link to next EMBR
1045 NextExtCurrent
= ExtBase
+ EMbrTable
[i
].StartLBA
;
1046 EMbrTable
[i
].Flags
= (PartitionIndex
>= LogicalPartitionIndex
) ? 0x80 : 0x00;
1049 // logical partition
1050 EMbrTable
[i
].Flags
= (PartitionIndex
== LogicalPartitionIndex
) ? 0x80 : 0x00;
1051 LogicalPartitionIndex
++;
1055 // write current EMBR
1056 Status
= refit_call5_wrapper(BlockIO
->WriteBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, ExtCurrent
, 512, SectorBuffer
);
1057 if (EFI_ERROR(Status
))
1060 if (PartitionIndex
< LogicalPartitionIndex
)
1061 break; // stop the loop, no need to touch further EMBRs
1067 } /* static EFI_STATUS ActivateMbrPartition() */
1069 // early 2006 Core Duo / Core Solo models
1070 static UINT8 LegacyLoaderDevicePath1Data
[] = {
1071 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1072 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1073 0xFF, 0xFF, 0xF9, 0xFF, 0x00, 0x00, 0x00, 0x00,
1074 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1075 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1076 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1078 // mid-2006 Mac Pro (and probably other Core 2 models)
1079 static UINT8 LegacyLoaderDevicePath2Data
[] = {
1080 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1081 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1082 0xFF, 0xFF, 0xF7, 0xFF, 0x00, 0x00, 0x00, 0x00,
1083 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1084 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1085 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1087 // mid-2007 MBP ("Santa Rosa" based models)
1088 static UINT8 LegacyLoaderDevicePath3Data
[] = {
1089 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1090 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1091 0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
1092 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1093 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1094 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1097 static UINT8 LegacyLoaderDevicePath4Data
[] = {
1098 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1099 0x00, 0x00, 0xC0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1100 0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
1101 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1102 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1103 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1105 // late-2008 MB/MBP (NVidia chipset)
1106 static UINT8 LegacyLoaderDevicePath5Data
[] = {
1107 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1108 0x00, 0x40, 0xCB, 0xFF, 0x00, 0x00, 0x00, 0x00,
1109 0xFF, 0xBF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
1110 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1111 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1112 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1115 static EFI_DEVICE_PATH
*LegacyLoaderList
[] = {
1116 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath1Data
,
1117 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath2Data
,
1118 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath3Data
,
1119 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath4Data
,
1120 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath5Data
,
1124 #define MAX_DISCOVERED_PATHS (16)
1126 static VOID
StartLegacy(IN LEGACY_ENTRY
*Entry
)
1129 EG_IMAGE
*BootLogoImage
;
1130 UINTN ErrorInStep
= 0;
1131 EFI_DEVICE_PATH
*DiscoveredPathList
[MAX_DISCOVERED_PATHS
];
1133 BeginExternalScreen(TRUE
, L
"Booting Legacy OS");
1135 BootLogoImage
= LoadOSIcon(Entry
->Volume
->OSIconName
, L
"legacy", TRUE
);
1136 if (BootLogoImage
!= NULL
)
1137 BltImageAlpha(BootLogoImage
,
1138 (UGAWidth
- BootLogoImage
->Width
) >> 1,
1139 (UGAHeight
- BootLogoImage
->Height
) >> 1,
1140 &StdBackgroundPixel
);
1142 if (Entry
->Volume
->IsMbrPartition
) {
1143 ActivateMbrPartition(Entry
->Volume
->WholeDiskBlockIO
, Entry
->Volume
->MbrPartitionIndex
);
1146 ExtractLegacyLoaderPaths(DiscoveredPathList
, MAX_DISCOVERED_PATHS
, LegacyLoaderList
);
1148 Status
= StartEFIImageList(DiscoveredPathList
, Entry
->LoadOptions
, NULL
, L
"legacy loader", &ErrorInStep
, TRUE
);
1149 if (Status
== EFI_NOT_FOUND
) {
1150 if (ErrorInStep
== 1) {
1151 Print(L
"\nPlease make sure that you have the latest firmware update installed.\n");
1152 } else if (ErrorInStep
== 3) {
1153 Print(L
"\nThe firmware refused to boot from the selected volume. Note that external\n"
1154 L
"hard drives are not well-supported by Apple's firmware for legacy OS booting.\n");
1157 FinishExternalScreen();
1158 } /* static VOID StartLegacy() */
1160 static LEGACY_ENTRY
* AddLegacyEntry(IN CHAR16
*LoaderTitle
, IN REFIT_VOLUME
*Volume
)
1162 LEGACY_ENTRY
*Entry
, *SubEntry
;
1163 REFIT_MENU_SCREEN
*SubScreen
;
1165 CHAR16 ShortcutLetter
= 0;
1167 if (LoaderTitle
== NULL
) {
1168 if (Volume
->OSName
!= NULL
) {
1169 LoaderTitle
= Volume
->OSName
;
1170 if (LoaderTitle
[0] == 'W' || LoaderTitle
[0] == 'L')
1171 ShortcutLetter
= LoaderTitle
[0];
1173 LoaderTitle
= L
"Legacy OS";
1175 if (Volume
->VolName
!= NULL
)
1176 VolDesc
= Volume
->VolName
;
1178 VolDesc
= (Volume
->DiskKind
== DISK_KIND_OPTICAL
) ? L
"CD" : L
"HD";
1180 // prepare the menu entry
1181 Entry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1182 Entry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1183 SPrint(Entry
->me
.Title
, 255, L
"Boot %s from %s", LoaderTitle
, VolDesc
);
1184 Entry
->me
.Tag
= TAG_LEGACY
;
1186 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1187 Entry
->me
.Image
= LoadOSIcon(Volume
->OSIconName
, L
"legacy", FALSE
);
1188 Entry
->me
.BadgeImage
= Volume
->VolBadgeImage
;
1189 Entry
->Volume
= Volume
;
1190 Entry
->LoadOptions
= (Volume
->DiskKind
== DISK_KIND_OPTICAL
) ? L
"CD" :
1191 ((Volume
->DiskKind
== DISK_KIND_EXTERNAL
) ? L
"USB" : L
"HD");
1192 Entry
->Enabled
= TRUE
;
1194 // create the submenu
1195 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
1196 SubScreen
->Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1197 SPrint(SubScreen
->Title
, 255, L
"Boot Options for %s on %s", LoaderTitle
, VolDesc
);
1198 SubScreen
->TitleImage
= Entry
->me
.Image
;
1201 SubEntry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1202 SubEntry
->me
.Title
= AllocateZeroPool(256 * sizeof(CHAR16
));
1203 SPrint(SubEntry
->me
.Title
, 255, L
"Boot %s", LoaderTitle
);
1204 SubEntry
->me
.Tag
= TAG_LEGACY
;
1205 SubEntry
->Volume
= Entry
->Volume
;
1206 SubEntry
->LoadOptions
= Entry
->LoadOptions
;
1207 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
1209 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
1210 Entry
->me
.SubScreen
= SubScreen
;
1211 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1213 } /* static LEGACY_ENTRY * AddLegacyEntry() */
1215 static VOID
ScanLegacyVolume(REFIT_VOLUME
*Volume
, UINTN VolumeIndex
) {
1217 BOOLEAN ShowVolume
, HideIfOthersFound
;
1220 HideIfOthersFound
= FALSE
;
1221 if (Volume
->IsAppleLegacy
) {
1223 HideIfOthersFound
= TRUE
;
1224 } else if (Volume
->HasBootCode
) {
1226 if (Volume
->BlockIO
== Volume
->WholeDiskBlockIO
&&
1227 Volume
->BlockIOOffset
== 0 &&
1228 Volume
->OSName
== NULL
)
1229 // this is a whole disk (MBR) entry; hide if we have entries for partitions
1230 HideIfOthersFound
= TRUE
;
1232 if (HideIfOthersFound
) {
1233 // check for other bootable entries on the same disk
1234 for (VolumeIndex2
= 0; VolumeIndex2
< VolumesCount
; VolumeIndex2
++) {
1235 if (VolumeIndex2
!= VolumeIndex
&& Volumes
[VolumeIndex2
]->HasBootCode
&&
1236 Volumes
[VolumeIndex2
]->WholeDiskBlockIO
== Volume
->WholeDiskBlockIO
)
1242 AddLegacyEntry(NULL
, Volume
);
1243 } // static VOID ScanLegacyVolume()
1245 // Scan attached optical discs for legacy (BIOS) boot code
1246 // and add anything found to the list....
1247 static VOID
ScanLegacyDisc(VOID
)
1250 REFIT_VOLUME
*Volume
;
1252 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1253 Volume
= Volumes
[VolumeIndex
];
1254 if (Volume
->DiskKind
== DISK_KIND_OPTICAL
)
1255 ScanLegacyVolume(Volume
, VolumeIndex
);
1257 } /* static VOID ScanLegacyDisc() */
1259 // Scan internal hard disks for legacy (BIOS) boot code
1260 // and add anything found to the list....
1261 static VOID
ScanLegacyInternal(VOID
)
1264 REFIT_VOLUME
*Volume
;
1266 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1267 Volume
= Volumes
[VolumeIndex
];
1268 if (Volume
->DiskKind
== DISK_KIND_INTERNAL
)
1269 ScanLegacyVolume(Volume
, VolumeIndex
);
1271 } /* static VOID ScanLegacyInternal() */
1273 // Scan external disks for legacy (BIOS) boot code
1274 // and add anything found to the list....
1275 static VOID
ScanLegacyExternal(VOID
)
1278 REFIT_VOLUME
*Volume
;
1280 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1281 Volume
= Volumes
[VolumeIndex
];
1282 if (Volume
->DiskKind
== DISK_KIND_EXTERNAL
)
1283 ScanLegacyVolume(Volume
, VolumeIndex
);
1285 } /* static VOID ScanLegacyExternal() */
1288 // pre-boot tool functions
1291 static VOID
StartTool(IN LOADER_ENTRY
*Entry
)
1293 BeginExternalScreen(Entry
->UseGraphicsMode
, Entry
->me
.Title
+ 6); // assumes "Start <title>" as assigned below
1294 StartEFIImage(Entry
->DevicePath
, Entry
->LoadOptions
, Basename(Entry
->LoaderPath
),
1295 Basename(Entry
->LoaderPath
), NULL
, TRUE
);
1296 FinishExternalScreen();
1297 } /* static VOID StartTool() */
1299 static LOADER_ENTRY
* AddToolEntry(IN CHAR16
*LoaderPath
, IN CHAR16
*LoaderTitle
, IN EG_IMAGE
*Image
,
1300 IN CHAR16 ShortcutLetter
, IN BOOLEAN UseGraphicsMode
)
1302 LOADER_ENTRY
*Entry
;
1303 CHAR16
*TitleStr
= NULL
;
1305 Entry
= AllocateZeroPool(sizeof(LOADER_ENTRY
));
1307 MergeStrings(&TitleStr
, L
"Start ", 0);
1308 MergeStrings(&TitleStr
, LoaderTitle
, 0);
1309 Entry
->me
.Title
= TitleStr
;
1310 Entry
->me
.Tag
= TAG_TOOL
;
1312 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1313 Entry
->me
.Image
= Image
;
1314 Entry
->LoaderPath
= StrDuplicate(LoaderPath
);
1315 Entry
->DevicePath
= FileDevicePath(SelfLoadedImage
->DeviceHandle
, Entry
->LoaderPath
);
1316 Entry
->UseGraphicsMode
= UseGraphicsMode
;
1318 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1320 } /* static LOADER_ENTRY * AddToolEntry() */
1323 // pre-boot driver functions
1326 static UINTN
ScanDriverDir(IN CHAR16
*Path
)
1329 REFIT_DIR_ITER DirIter
;
1331 EFI_FILE_INFO
*DirEntry
;
1332 CHAR16 FileName
[256];
1334 CleanUpPathNameSlashes(Path
);
1335 // look through contents of the directory
1336 DirIterOpen(SelfRootDir
, Path
, &DirIter
);
1337 while (DirIterNext(&DirIter
, 2, LOADER_MATCH_PATTERNS
, &DirEntry
)) {
1338 if (DirEntry
->FileName
[0] == '.')
1339 continue; // skip this
1341 SPrint(FileName
, 255, L
"%s\\%s", Path
, DirEntry
->FileName
);
1343 Status
= StartEFIImage(FileDevicePath(SelfLoadedImage
->DeviceHandle
, FileName
),
1344 L
"", DirEntry
->FileName
, DirEntry
->FileName
, NULL
, FALSE
);
1346 Status
= DirIterClose(&DirIter
);
1347 if (Status
!= EFI_NOT_FOUND
) {
1348 SPrint(FileName
, 255, L
"while scanning the %s directory", Path
);
1349 CheckError(Status
, FileName
);
1354 #ifdef __MAKEWITH_GNUEFI
1355 static EFI_STATUS
ConnectAllDriversToAllControllers(VOID
)
1358 UINTN AllHandleCount
;
1359 EFI_HANDLE
*AllHandleBuffer
;
1362 EFI_HANDLE
*HandleBuffer
;
1368 Status
= LibLocateHandle(AllHandles
,
1373 if (EFI_ERROR(Status
))
1376 for (Index
= 0; Index
< AllHandleCount
; Index
++) {
1378 // Scan the handle database
1380 Status
= LibScanHandleDatabase(NULL
,
1382 AllHandleBuffer
[Index
],
1387 if (EFI_ERROR (Status
))
1391 if (HandleType
[Index
] & EFI_HANDLE_TYPE_DRIVER_BINDING_HANDLE
)
1393 if (HandleType
[Index
] & EFI_HANDLE_TYPE_IMAGE_HANDLE
)
1398 for (HandleIndex
= 0; HandleIndex
< HandleCount
; HandleIndex
++) {
1399 if (HandleType
[HandleIndex
] & EFI_HANDLE_TYPE_PARENT_HANDLE
)
1404 if (HandleType
[Index
] & EFI_HANDLE_TYPE_DEVICE_HANDLE
) {
1405 Status
= refit_call4_wrapper(BS
->ConnectController
,
1406 AllHandleBuffer
[Index
],
1414 FreePool (HandleBuffer
);
1415 FreePool (HandleType
);
1419 FreePool (AllHandleBuffer
);
1421 } /* EFI_STATUS ConnectAllDriversToAllControllers() */
1423 static EFI_STATUS
ConnectAllDriversToAllControllers(VOID
) {
1424 BdsLibConnectAllDriversToAllControllers();
1429 // Load all EFI drivers from rEFInd's "drivers" subdirectory and from the
1430 // directories specified by the user in the "scan_driver_dirs" configuration
1432 static VOID
LoadDrivers(VOID
)
1434 CHAR16
*Directory
, *SelfDirectory
;
1435 UINTN i
= 0, Length
, NumFound
= 0;
1437 // load drivers from the subdirectories of rEFInd's home directory specified
1438 // in the DRIVER_DIRS constant.
1439 while ((Directory
= FindCommaDelimited(DRIVER_DIRS
, i
++)) != NULL
) {
1440 SelfDirectory
= StrDuplicate(SelfDirPath
);
1441 CleanUpPathNameSlashes(SelfDirectory
);
1442 MergeStrings(&SelfDirectory
, Directory
, L
'\\');
1443 NumFound
+= ScanDriverDir(SelfDirectory
);
1444 FreePool(Directory
);
1445 FreePool(SelfDirectory
);
1448 // Scan additional user-specified driver directories....
1450 while ((Directory
= FindCommaDelimited(GlobalConfig
.DriverDirs
, i
++)) != NULL
) {
1451 CleanUpPathNameSlashes(Directory
);
1452 Length
= StrLen(Directory
);
1454 NumFound
+= ScanDriverDir(Directory
);
1455 FreePool(Directory
);
1458 // connect all devices
1460 ConnectAllDriversToAllControllers();
1461 } /* static VOID LoadDrivers() */
1463 static VOID
ScanForBootloaders(VOID
) {
1468 // scan for loaders and tools, add them to the menu
1469 for (i
= 0; i
< NUM_SCAN_OPTIONS
; i
++) {
1470 switch(GlobalConfig
.ScanFor
[i
]) {
1475 ScanLegacyInternal();
1478 ScanLegacyExternal();
1481 ScanUserConfigured();
1495 // assign shortcut keys
1496 for (i
= 0; i
< MainMenu
.EntryCount
&& MainMenu
.Entries
[i
]->Row
== 0 && i
< 9; i
++)
1497 MainMenu
.Entries
[i
]->ShortcutDigit
= (CHAR16
)('1' + i
);
1499 // wait for user ACK when there were errors
1500 FinishTextScreen(FALSE
);
1501 } // static VOID ScanForBootloaders()
1503 // Add the second-row tags containing built-in and external tools (EFI shell,
1505 static VOID
ScanForTools(VOID
) {
1506 CHAR16
*FileName
= NULL
;
1507 REFIT_MENU_ENTRY
*TempMenuEntry
;
1510 for (i
= 0; i
< NUM_TOOLS
; i
++) {
1511 switch(GlobalConfig
.ShowTools
[i
]) {
1513 TempMenuEntry
= CopyMenuEntry(&MenuEntryShutdown
);
1514 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_SHUTDOWN
);
1515 AddMenuEntry(&MainMenu
, TempMenuEntry
);
1518 TempMenuEntry
= CopyMenuEntry(&MenuEntryReset
);
1519 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_RESET
);
1520 AddMenuEntry(&MainMenu
, TempMenuEntry
);
1523 TempMenuEntry
= CopyMenuEntry(&MenuEntryAbout
);
1524 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT
);
1525 AddMenuEntry(&MainMenu
, TempMenuEntry
);
1528 TempMenuEntry
= CopyMenuEntry(&MenuEntryExit
);
1529 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_EXIT
);
1530 AddMenuEntry(&MainMenu
, TempMenuEntry
);
1534 while ((FileName
= FindCommaDelimited(SHELL_NAMES
, j
++)) != NULL
) {
1535 if (FileExists(SelfRootDir
, FileName
)) {
1536 AddToolEntry(FileName
, L
"EFI Shell", BuiltinIcon(BUILTIN_ICON_TOOL_SHELL
), 'S', FALSE
);
1541 MergeStrings(&FileName
, L
"\\efi\\tools\\gptsync.efi", 0);
1542 if (FileExists(SelfRootDir
, FileName
)) {
1543 AddToolEntry(FileName
, L
"Make Hybrid MBR", BuiltinIcon(BUILTIN_ICON_TOOL_PART
), 'P', FALSE
);
1547 if (FileName
!= NULL
) {
1552 } // static VOID ScanForTools
1554 // Rescan for boot loaders
1555 VOID
RescanAll(VOID
) {
1562 egDisplayMessage(L
"Scanning for new boot loaders; please wait....", &BGColor
);
1563 FreeList((VOID
***) &(MainMenu
.Entries
), &MainMenu
.EntryCount
);
1564 MainMenu
.Entries
= NULL
;
1565 MainMenu
.EntryCount
= 0;
1567 ConnectAllDriversToAllControllers();
1568 ScanForBootloaders();
1571 } // VOID RescanAll()
1573 #ifndef __MAKEWITH_GNUEFI
1575 // Minimal initialization function
1576 static VOID
InitializeLib(IN EFI_HANDLE ImageHandle
, IN EFI_SYSTEM_TABLE
*SystemTable
) {
1578 // gImageHandle = ImageHandle;
1579 gBS
= SystemTable
->BootServices
;
1580 // gRS = SystemTable->RuntimeServices;
1581 EfiGetSystemConfigurationTable (&gEfiDxeServicesTableGuid
, (VOID
**) &gDS
);
1583 InitializeConsoleSim();
1593 efi_main (IN EFI_HANDLE ImageHandle
, IN EFI_SYSTEM_TABLE
*SystemTable
)
1596 BOOLEAN MainLoopRunning
= TRUE
;
1597 REFIT_MENU_ENTRY
*ChosenEntry
;
1602 InitializeLib(ImageHandle
, SystemTable
);
1604 Status
= InitRefitLib(ImageHandle
);
1605 if (EFI_ERROR(Status
))
1608 // read configuration
1609 CopyMem(GlobalConfig
.ScanFor
, "ieo ", NUM_SCAN_OPTIONS
);
1611 MainMenu
.TimeoutSeconds
= GlobalConfig
.Timeout
;
1613 // disable EFI watchdog timer
1614 refit_call4_wrapper(BS
->SetWatchdogTimer
, 0x0000, 0x0000, 0x0000, NULL
);
1616 // further bootstrap (now with config available)
1619 ScanForBootloaders();
1622 Selection
= StrDuplicate(GlobalConfig
.DefaultSelection
);
1623 while (MainLoopRunning
) {
1624 MenuExit
= RunMainMenu(&MainMenu
, Selection
, &ChosenEntry
);
1626 // The Escape key triggers a re-scan operation....
1627 if (MenuExit
== MENU_EXIT_ESCAPE
) {
1632 switch (ChosenEntry
->Tag
) {
1634 case TAG_REBOOT
: // Reboot
1636 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);
1637 MainLoopRunning
= FALSE
; // just in case we get this far
1640 case TAG_SHUTDOWN
: // Shut Down
1642 refit_call4_wrapper(RT
->ResetSystem
, EfiResetShutdown
, EFI_SUCCESS
, 0, NULL
);
1643 MainLoopRunning
= FALSE
; // just in case we get this far
1646 case TAG_ABOUT
: // About rEFInd
1650 case TAG_LOADER
: // Boot OS via .EFI loader
1651 StartLoader((LOADER_ENTRY
*)ChosenEntry
);
1654 case TAG_LEGACY
: // Boot legacy OS
1655 StartLegacy((LEGACY_ENTRY
*)ChosenEntry
);
1658 case TAG_TOOL
: // Start a EFI tool
1659 StartTool((LOADER_ENTRY
*)ChosenEntry
);
1662 case TAG_EXIT
: // Terminate rEFInd
1663 BeginTextScreen(L
" ");
1668 FreePool(Selection
);
1669 Selection
= StrDuplicate(ChosenEntry
->Title
);
1672 // If we end up here, things have gone wrong. Try to reboot, and if that
1673 // fails, go into an endless loop.
1674 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);