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.2.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(255 * 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(255 * 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(255 * 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
, *TitleStr
;
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 TitleStr
= AllocateZeroPool(sizeof(CHAR16
) * 256);
423 SPrint(TitleStr
, 255, L
"Boot Options for %s on %s", (Entry
->Title
!= NULL
) ? Entry
->Title
: FileName
, Entry
->VolName
);
424 SubScreen
->Title
= TitleStr
;
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
) {
739 CleanUpPathNameSlashes(LoaderPath
);
740 Entry
= InitializeLoaderEntry(NULL
);
742 Entry
->Title
= StrDuplicate((LoaderTitle
!= NULL
) ? LoaderTitle
: LoaderPath
);
743 PoolStr
= AllocateZeroPool(sizeof(CHAR16
) * 256);
744 SPrint(PoolStr
, 255, L
"Boot %s from %s", (LoaderTitle
!= NULL
) ? LoaderTitle
: LoaderPath
, Volume
->VolName
);
745 Entry
->me
.Title
= PoolStr
;
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
);
1145 ExtractLegacyLoaderPaths(DiscoveredPathList
, MAX_DISCOVERED_PATHS
, LegacyLoaderList
);
1147 Status
= StartEFIImageList(DiscoveredPathList
, Entry
->LoadOptions
, NULL
, L
"legacy loader", &ErrorInStep
, TRUE
);
1148 if (Status
== EFI_NOT_FOUND
) {
1149 if (ErrorInStep
== 1) {
1150 Print(L
"\nPlease make sure that you have the latest firmware update installed.\n");
1151 } else if (ErrorInStep
== 3) {
1152 Print(L
"\nThe firmware refused to boot from the selected volume. Note that external\n"
1153 L
"hard drives are not well-supported by Apple's firmware for legacy OS booting.\n");
1156 FinishExternalScreen();
1157 } /* static VOID StartLegacy() */
1159 static LEGACY_ENTRY
* AddLegacyEntry(IN CHAR16
*LoaderTitle
, IN REFIT_VOLUME
*Volume
)
1161 LEGACY_ENTRY
*Entry
, *SubEntry
;
1162 REFIT_MENU_SCREEN
*SubScreen
;
1164 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 PoolStr
= AllocateZeroPool(256 * sizeof(CHAR16
));
1183 SPrint(PoolStr
, 255, L
"Boot %s from %s", LoaderTitle
, VolDesc
);
1184 Entry
->me
.Title
= PoolStr
;
1185 Entry
->me
.Tag
= TAG_LEGACY
;
1187 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1188 Entry
->me
.Image
= LoadOSIcon(Volume
->OSIconName
, L
"legacy", FALSE
);
1189 Entry
->me
.BadgeImage
= Volume
->VolBadgeImage
;
1190 Entry
->Volume
= Volume
;
1191 Entry
->LoadOptions
= (Volume
->DiskKind
== DISK_KIND_OPTICAL
) ? L
"CD" :
1192 ((Volume
->DiskKind
== DISK_KIND_EXTERNAL
) ? L
"USB" : L
"HD");
1193 Entry
->Enabled
= TRUE
;
1195 // create the submenu
1196 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
1197 PoolStr
= AllocateZeroPool(256 * sizeof(CHAR16
));
1198 SPrint(PoolStr
, 255, L
"Boot Options for %s on %s", LoaderTitle
, VolDesc
);
1199 SubScreen
->Title
= PoolStr
;
1200 SubScreen
->TitleImage
= Entry
->me
.Image
;
1203 SubEntry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1204 PoolStr
= AllocateZeroPool(256 * sizeof(CHAR16
));
1205 SPrint(PoolStr
, 255, L
"Boot %s", LoaderTitle
);
1206 SubEntry
->me
.Title
= PoolStr
;
1207 SubEntry
->me
.Tag
= TAG_LEGACY
;
1208 SubEntry
->Volume
= Entry
->Volume
;
1209 SubEntry
->LoadOptions
= Entry
->LoadOptions
;
1210 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
1212 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
1213 Entry
->me
.SubScreen
= SubScreen
;
1214 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1216 } /* static LEGACY_ENTRY * AddLegacyEntry() */
1218 static VOID
ScanLegacyVolume(REFIT_VOLUME
*Volume
, UINTN VolumeIndex
) {
1220 BOOLEAN ShowVolume
, HideIfOthersFound
;
1223 HideIfOthersFound
= FALSE
;
1224 if (Volume
->IsAppleLegacy
) {
1226 HideIfOthersFound
= TRUE
;
1227 } else if (Volume
->HasBootCode
) {
1229 if (Volume
->BlockIO
== Volume
->WholeDiskBlockIO
&&
1230 Volume
->BlockIOOffset
== 0 &&
1231 Volume
->OSName
== NULL
)
1232 // this is a whole disk (MBR) entry; hide if we have entries for partitions
1233 HideIfOthersFound
= TRUE
;
1235 if (HideIfOthersFound
) {
1236 // check for other bootable entries on the same disk
1237 for (VolumeIndex2
= 0; VolumeIndex2
< VolumesCount
; VolumeIndex2
++) {
1238 if (VolumeIndex2
!= VolumeIndex
&& Volumes
[VolumeIndex2
]->HasBootCode
&&
1239 Volumes
[VolumeIndex2
]->WholeDiskBlockIO
== Volume
->WholeDiskBlockIO
)
1245 AddLegacyEntry(NULL
, Volume
);
1246 } // static VOID ScanLegacyVolume()
1248 // Scan attached optical discs for legacy (BIOS) boot code
1249 // and add anything found to the list....
1250 static VOID
ScanLegacyDisc(VOID
)
1253 REFIT_VOLUME
*Volume
;
1255 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1256 Volume
= Volumes
[VolumeIndex
];
1257 if (Volume
->DiskKind
== DISK_KIND_OPTICAL
)
1258 ScanLegacyVolume(Volume
, VolumeIndex
);
1260 } /* static VOID ScanLegacyDisc() */
1262 // Scan internal hard disks for legacy (BIOS) boot code
1263 // and add anything found to the list....
1264 static VOID
ScanLegacyInternal(VOID
)
1267 REFIT_VOLUME
*Volume
;
1269 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1270 Volume
= Volumes
[VolumeIndex
];
1271 if (Volume
->DiskKind
== DISK_KIND_INTERNAL
)
1272 ScanLegacyVolume(Volume
, VolumeIndex
);
1274 } /* static VOID ScanLegacyInternal() */
1276 // Scan external disks for legacy (BIOS) boot code
1277 // and add anything found to the list....
1278 static VOID
ScanLegacyExternal(VOID
)
1281 REFIT_VOLUME
*Volume
;
1283 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1284 Volume
= Volumes
[VolumeIndex
];
1285 if (Volume
->DiskKind
== DISK_KIND_EXTERNAL
)
1286 ScanLegacyVolume(Volume
, VolumeIndex
);
1288 } /* static VOID ScanLegacyExternal() */
1291 // pre-boot tool functions
1294 static VOID
StartTool(IN LOADER_ENTRY
*Entry
)
1296 BeginExternalScreen(Entry
->UseGraphicsMode
, Entry
->me
.Title
+ 6); // assumes "Start <title>" as assigned below
1297 StartEFIImage(Entry
->DevicePath
, Entry
->LoadOptions
, Basename(Entry
->LoaderPath
),
1298 Basename(Entry
->LoaderPath
), NULL
, TRUE
);
1299 FinishExternalScreen();
1300 } /* static VOID StartTool() */
1302 static LOADER_ENTRY
* AddToolEntry(IN CHAR16
*LoaderPath
, IN CHAR16
*LoaderTitle
, IN EG_IMAGE
*Image
,
1303 IN CHAR16 ShortcutLetter
, IN BOOLEAN UseGraphicsMode
)
1305 LOADER_ENTRY
*Entry
;
1306 CHAR16
*TitleStr
= NULL
;
1308 Entry
= AllocateZeroPool(sizeof(LOADER_ENTRY
));
1310 MergeStrings(&TitleStr
, L
"Start ", 0);
1311 MergeStrings(&TitleStr
, LoaderTitle
, 0);
1312 Entry
->me
.Title
= TitleStr
;
1313 Entry
->me
.Tag
= TAG_TOOL
;
1315 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1316 Entry
->me
.Image
= Image
;
1317 Entry
->LoaderPath
= StrDuplicate(LoaderPath
);
1318 Entry
->DevicePath
= FileDevicePath(SelfLoadedImage
->DeviceHandle
, Entry
->LoaderPath
);
1319 Entry
->UseGraphicsMode
= UseGraphicsMode
;
1321 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1323 } /* static LOADER_ENTRY * AddToolEntry() */
1326 // pre-boot driver functions
1329 static UINTN
ScanDriverDir(IN CHAR16
*Path
)
1332 REFIT_DIR_ITER DirIter
;
1334 EFI_FILE_INFO
*DirEntry
;
1335 CHAR16 FileName
[256];
1337 CleanUpPathNameSlashes(Path
);
1338 // look through contents of the directory
1339 DirIterOpen(SelfRootDir
, Path
, &DirIter
);
1340 while (DirIterNext(&DirIter
, 2, LOADER_MATCH_PATTERNS
, &DirEntry
)) {
1341 if (DirEntry
->FileName
[0] == '.')
1342 continue; // skip this
1344 SPrint(FileName
, 255, L
"%s\\%s", Path
, DirEntry
->FileName
);
1346 Status
= StartEFIImage(FileDevicePath(SelfLoadedImage
->DeviceHandle
, FileName
),
1347 L
"", DirEntry
->FileName
, DirEntry
->FileName
, NULL
, FALSE
);
1349 Status
= DirIterClose(&DirIter
);
1350 if (Status
!= EFI_NOT_FOUND
) {
1351 SPrint(FileName
, 255, L
"while scanning the %s directory", Path
);
1352 CheckError(Status
, FileName
);
1357 #ifdef __MAKEWITH_GNUEFI
1358 static EFI_STATUS
ConnectAllDriversToAllControllers(VOID
)
1361 UINTN AllHandleCount
;
1362 EFI_HANDLE
*AllHandleBuffer
;
1365 EFI_HANDLE
*HandleBuffer
;
1371 Status
= LibLocateHandle(AllHandles
,
1376 if (EFI_ERROR(Status
))
1379 for (Index
= 0; Index
< AllHandleCount
; Index
++) {
1381 // Scan the handle database
1383 Status
= LibScanHandleDatabase(NULL
,
1385 AllHandleBuffer
[Index
],
1390 if (EFI_ERROR (Status
))
1394 if (HandleType
[Index
] & EFI_HANDLE_TYPE_DRIVER_BINDING_HANDLE
)
1396 if (HandleType
[Index
] & EFI_HANDLE_TYPE_IMAGE_HANDLE
)
1401 for (HandleIndex
= 0; HandleIndex
< HandleCount
; HandleIndex
++) {
1402 if (HandleType
[HandleIndex
] & EFI_HANDLE_TYPE_PARENT_HANDLE
)
1407 if (HandleType
[Index
] & EFI_HANDLE_TYPE_DEVICE_HANDLE
) {
1408 Status
= refit_call4_wrapper(BS
->ConnectController
,
1409 AllHandleBuffer
[Index
],
1417 FreePool (HandleBuffer
);
1418 FreePool (HandleType
);
1422 FreePool (AllHandleBuffer
);
1424 } /* EFI_STATUS ConnectAllDriversToAllControllers() */
1426 static EFI_STATUS
ConnectAllDriversToAllControllers(VOID
) {
1427 BdsLibConnectAllDriversToAllControllers();
1432 // Load all EFI drivers from rEFInd's "drivers" subdirectory and from the
1433 // directories specified by the user in the "scan_driver_dirs" configuration
1435 static VOID
LoadDrivers(VOID
)
1437 CHAR16
*Directory
, *SelfDirectory
;
1438 UINTN i
= 0, Length
, NumFound
= 0;
1440 // load drivers from the subdirectories of rEFInd's home directory specified
1441 // in the DRIVER_DIRS constant.
1442 while ((Directory
= FindCommaDelimited(DRIVER_DIRS
, i
++)) != NULL
) {
1443 SelfDirectory
= StrDuplicate(SelfDirPath
);
1444 CleanUpPathNameSlashes(SelfDirectory
);
1445 MergeStrings(&SelfDirectory
, Directory
, L
'\\');
1446 NumFound
+= ScanDriverDir(SelfDirectory
);
1447 FreePool(Directory
);
1448 FreePool(SelfDirectory
);
1451 // Scan additional user-specified driver directories....
1453 while ((Directory
= FindCommaDelimited(GlobalConfig
.DriverDirs
, i
++)) != NULL
) {
1454 CleanUpPathNameSlashes(Directory
);
1455 Length
= StrLen(Directory
);
1457 NumFound
+= ScanDriverDir(Directory
);
1458 FreePool(Directory
);
1461 // connect all devices
1463 ConnectAllDriversToAllControllers();
1464 } /* static VOID LoadDrivers() */
1466 static VOID
ScanForBootloaders(VOID
) {
1471 // scan for loaders and tools, add them to the menu
1472 for (i
= 0; i
< NUM_SCAN_OPTIONS
; i
++) {
1473 switch(GlobalConfig
.ScanFor
[i
]) {
1478 ScanLegacyInternal();
1481 ScanLegacyExternal();
1484 ScanUserConfigured();
1498 // assign shortcut keys
1499 for (i
= 0; i
< MainMenu
.EntryCount
&& MainMenu
.Entries
[i
]->Row
== 0 && i
< 9; i
++)
1500 MainMenu
.Entries
[i
]->ShortcutDigit
= (CHAR16
)('1' + i
);
1502 // wait for user ACK when there were errors
1503 FinishTextScreen(FALSE
);
1504 } // static VOID ScanForBootloaders()
1506 // Add the second-row tags containing built-in and external tools (EFI shell,
1508 static VOID
ScanForTools(VOID
) {
1509 CHAR16
*FileName
= NULL
;
1510 REFIT_MENU_ENTRY
*TempMenuEntry
;
1513 for (i
= 0; i
< NUM_TOOLS
; i
++) {
1514 switch(GlobalConfig
.ShowTools
[i
]) {
1516 TempMenuEntry
= CopyMenuEntry(&MenuEntryShutdown
);
1517 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_SHUTDOWN
);
1518 AddMenuEntry(&MainMenu
, TempMenuEntry
);
1521 TempMenuEntry
= CopyMenuEntry(&MenuEntryReset
);
1522 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_RESET
);
1523 AddMenuEntry(&MainMenu
, TempMenuEntry
);
1526 TempMenuEntry
= CopyMenuEntry(&MenuEntryAbout
);
1527 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT
);
1528 AddMenuEntry(&MainMenu
, TempMenuEntry
);
1531 TempMenuEntry
= CopyMenuEntry(&MenuEntryExit
);
1532 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_EXIT
);
1533 AddMenuEntry(&MainMenu
, TempMenuEntry
);
1537 while ((FileName
= FindCommaDelimited(SHELL_NAMES
, j
++)) != NULL
) {
1538 if (FileExists(SelfRootDir
, FileName
)) {
1539 AddToolEntry(FileName
, L
"EFI Shell", BuiltinIcon(BUILTIN_ICON_TOOL_SHELL
), 'S', FALSE
);
1544 MergeStrings(&FileName
, L
"\\efi\\tools\\gptsync.efi", 0);
1545 if (FileExists(SelfRootDir
, FileName
)) {
1546 AddToolEntry(FileName
, L
"Make Hybrid MBR", BuiltinIcon(BUILTIN_ICON_TOOL_PART
), 'P', FALSE
);
1550 if (FileName
!= NULL
) {
1555 } // static VOID ScanForTools
1557 // Rescan for boot loaders
1558 VOID
RescanAll(VOID
) {
1565 egDisplayMessage(L
"Scanning for new boot loaders; please wait....", &BGColor
);
1566 FreeList((VOID
***) &(MainMenu
.Entries
), &MainMenu
.EntryCount
);
1567 MainMenu
.Entries
= NULL
;
1568 MainMenu
.EntryCount
= 0;
1570 ConnectAllDriversToAllControllers();
1571 ScanForBootloaders();
1574 } // VOID RescanAll()
1576 #ifndef __MAKEWITH_GNUEFI
1578 // Minimal initialization function
1579 static VOID
InitializeLib(IN EFI_HANDLE ImageHandle
, IN EFI_SYSTEM_TABLE
*SystemTable
) {
1581 // gImageHandle = ImageHandle;
1582 gBS
= SystemTable
->BootServices
;
1583 // gRS = SystemTable->RuntimeServices;
1584 EfiGetSystemConfigurationTable (&gEfiDxeServicesTableGuid
, (VOID
**) &gDS
);
1586 InitializeConsoleSim();
1596 efi_main (IN EFI_HANDLE ImageHandle
, IN EFI_SYSTEM_TABLE
*SystemTable
)
1599 BOOLEAN MainLoopRunning
= TRUE
;
1600 REFIT_MENU_ENTRY
*ChosenEntry
;
1605 InitializeLib(ImageHandle
, SystemTable
);
1607 Status
= InitRefitLib(ImageHandle
);
1608 if (EFI_ERROR(Status
))
1611 // read configuration
1612 CopyMem(GlobalConfig
.ScanFor
, "ieo ", NUM_SCAN_OPTIONS
);
1614 MainMenu
.TimeoutSeconds
= GlobalConfig
.Timeout
;
1616 // disable EFI watchdog timer
1617 refit_call4_wrapper(BS
->SetWatchdogTimer
, 0x0000, 0x0000, 0x0000, NULL
);
1619 // further bootstrap (now with config available)
1622 ScanForBootloaders();
1625 Selection
= StrDuplicate(GlobalConfig
.DefaultSelection
);
1626 while (MainLoopRunning
) {
1627 MenuExit
= RunMainMenu(&MainMenu
, Selection
, &ChosenEntry
);
1629 // The Escape key triggers a re-scan operation....
1630 if (MenuExit
== MENU_EXIT_ESCAPE
) {
1635 switch (ChosenEntry
->Tag
) {
1637 case TAG_REBOOT
: // Reboot
1639 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);
1640 MainLoopRunning
= FALSE
; // just in case we get this far
1643 case TAG_SHUTDOWN
: // Shut Down
1645 refit_call4_wrapper(RT
->ResetSystem
, EfiResetShutdown
, EFI_SUCCESS
, 0, NULL
);
1646 MainLoopRunning
= FALSE
; // just in case we get this far
1649 case TAG_ABOUT
: // About rEFInd
1653 case TAG_LOADER
: // Boot OS via .EFI loader
1654 StartLoader((LOADER_ENTRY
*)ChosenEntry
);
1657 case TAG_LEGACY
: // Boot legacy OS
1658 StartLegacy((LEGACY_ENTRY
*)ChosenEntry
);
1661 case TAG_TOOL
: // Start a EFI tool
1662 StartTool((LOADER_ENTRY
*)ChosenEntry
);
1665 case TAG_EXIT
: // Terminate rEFInd
1666 BeginTextScreen(L
" ");
1671 FreePool(Selection
);
1672 Selection
= StrDuplicate(ChosenEntry
->Title
);
1675 // If we end up here, things have gone wrong. Try to reboot, and if that
1676 // fails, go into an endless loop.
1677 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);