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 "refit_call_wrapper.h"
52 #include "driver_support.h"
53 #include "../include/syslinux_mbr.h"
58 #define MACOSX_LOADER_PATH L"System\\Library\\CoreServices\\boot.efi"
60 #define SHELL_NAMES L"\\EFI\\tools\\shell.efi,\\shellx64.efi"
61 #define DRIVER_DIRS L"drivers,drivers_x64"
63 #define SHELL_NAMES L"\\EFI\\tools\\shell.efi,\\shellia32.efi"
64 #define DRIVER_DIRS L"drivers,drivers_ia32"
66 #define SHELL_NAMES L"\\EFI\\tools\\shell.efi"
69 // Filename patterns that identify EFI boot loaders. Note that a single case (either L"*.efi" or
70 // L"*.EFI") is fine for most systems; but Gigabyte's buggy Hybrid EFI does a case-sensitive
71 // comparison when it should do a case-insensitive comparison, so I'm doubling this up. It does
72 // no harm on other computers, AFAIK. In theory, every case variation should be done for
73 // completeness, but that's ridiculous....
74 #define LOADER_MATCH_PATTERNS L"*.efi,*.EFI"
76 // Patterns that identify Linux kernels. Added to the loader match pattern when the
77 // scan_all_linux_kernels option is set in the configuration file. Causes kernels WITHOUT
78 // a ".efi" extension to be found when scanning for boot loaders.
79 #define LINUX_MATCH_PATTERNS L"vmlinuz*,bzImage*"
81 static REFIT_MENU_ENTRY MenuEntryAbout
= { L
"About rEFInd", TAG_ABOUT
, 1, 0, 'A', NULL
, NULL
, NULL
};
82 static REFIT_MENU_ENTRY MenuEntryReset
= { L
"Reboot Computer", TAG_REBOOT
, 1, 0, 'R', NULL
, NULL
, NULL
};
83 static REFIT_MENU_ENTRY MenuEntryShutdown
= { L
"Shut Down Computer", TAG_SHUTDOWN
, 1, 0, 'U', NULL
, NULL
, NULL
};
84 static REFIT_MENU_ENTRY MenuEntryReturn
= { L
"Return to Main Menu", TAG_RETURN
, 0, 0, 0, NULL
, NULL
, NULL
};
85 static REFIT_MENU_ENTRY MenuEntryExit
= { L
"Exit rEFInd", TAG_EXIT
, 1, 0, 0, NULL
, NULL
, NULL
};
87 static REFIT_MENU_SCREEN MainMenu
= { L
"Main Menu", NULL
, 0, NULL
, 0, NULL
, 0, L
"Automatic boot" };
88 static REFIT_MENU_SCREEN AboutMenu
= { L
"About", NULL
, 0, NULL
, 0, NULL
, 0, NULL
};
90 REFIT_CONFIG GlobalConfig
= { FALSE
, FALSE
, 0, 0, 20, 0, 0, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
91 {TAG_SHELL
, TAG_ABOUT
, TAG_SHUTDOWN
, TAG_REBOOT
, 0, 0, 0, 0, 0 }};
93 // Structure used to hold boot loader filenames and time stamps in
94 // a linked list; used to sort entries within a directory.
98 struct LOADER_LIST
*NextEntry
;
105 static VOID
AboutrEFInd(VOID
)
107 if (AboutMenu
.EntryCount
== 0) {
108 AboutMenu
.TitleImage
= BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT
);
109 AddMenuInfoLine(&AboutMenu
, L
"rEFInd Version 0.4.1");
110 AddMenuInfoLine(&AboutMenu
, L
"");
111 AddMenuInfoLine(&AboutMenu
, L
"Copyright (c) 2006-2010 Christoph Pfisterer");
112 AddMenuInfoLine(&AboutMenu
, L
"Copyright (c) 2012 Roderick W. Smith");
113 AddMenuInfoLine(&AboutMenu
, L
"Portions Copyright (c) Intel Corporation and others");
114 AddMenuInfoLine(&AboutMenu
, L
"Distributed under the terms of the GNU GPLv3 license");
115 AddMenuInfoLine(&AboutMenu
, L
"");
116 AddMenuInfoLine(&AboutMenu
, L
"Running on:");
117 AddMenuInfoLine(&AboutMenu
, PoolPrint(L
" EFI Revision %d.%02d",
118 ST
->Hdr
.Revision
>> 16, ST
->Hdr
.Revision
& ((1 << 16) - 1)));
120 AddMenuInfoLine(&AboutMenu
, L
" Platform: x86 (32 bit)");
121 #elif defined(EFIX64)
122 AddMenuInfoLine(&AboutMenu
, L
" Platform: x86_64 (64 bit)");
124 AddMenuInfoLine(&AboutMenu
, L
" Platform: unknown");
126 AddMenuInfoLine(&AboutMenu
, PoolPrint(L
" Firmware: %s %d.%02d",
127 ST
->FirmwareVendor
, ST
->FirmwareRevision
>> 16, ST
->FirmwareRevision
& ((1 << 16) - 1)));
128 AddMenuInfoLine(&AboutMenu
, PoolPrint(L
" Screen Output: %s", egScreenDescription()));
129 AddMenuInfoLine(&AboutMenu
, L
"");
130 AddMenuInfoLine(&AboutMenu
, L
"For more information, see the rEFInd Web site:");
131 AddMenuInfoLine(&AboutMenu
, L
"http://www.rodsbooks.com/refind/");
132 AddMenuEntry(&AboutMenu
, &MenuEntryReturn
);
135 RunMenu(&AboutMenu
, NULL
);
136 } /* VOID AboutrEFInd() */
138 static EFI_STATUS
StartEFIImageList(IN EFI_DEVICE_PATH
**DevicePaths
,
139 IN CHAR16
*LoadOptions
, IN CHAR16
*LoadOptionsPrefix
,
140 IN CHAR16
*ImageTitle
,
141 OUT UINTN
*ErrorInStep
,
144 EFI_STATUS Status
, ReturnStatus
;
145 EFI_HANDLE ChildImageHandle
;
146 EFI_LOADED_IMAGE
*ChildLoadedImage
;
147 UINTN DevicePathIndex
;
148 CHAR16 ErrorInfo
[256];
149 CHAR16
*FullLoadOptions
= NULL
;
152 Print(L
"Starting %s\n", ImageTitle
);
153 if (ErrorInStep
!= NULL
)
156 // load the image into memory
157 ReturnStatus
= Status
= EFI_NOT_FOUND
; // in case the list is empty
158 for (DevicePathIndex
= 0; DevicePaths
[DevicePathIndex
] != NULL
; DevicePathIndex
++) {
159 // Print(L"About to try loading '%s' from '%s'\n", ImageTitle, DevicePathToStr(DevicePaths[DevicePathIndex]));
161 ReturnStatus
= Status
= refit_call6_wrapper(BS
->LoadImage
, FALSE
, SelfImageHandle
, DevicePaths
[DevicePathIndex
], NULL
, 0, &ChildImageHandle
);
162 if (ReturnStatus
!= EFI_NOT_FOUND
)
165 SPrint(ErrorInfo
, 255, L
"while loading %s", ImageTitle
);
166 if (CheckError(Status
, ErrorInfo
)) {
167 if (ErrorInStep
!= NULL
)
173 if (LoadOptions
!= NULL
) {
174 ReturnStatus
= Status
= refit_call3_wrapper(BS
->HandleProtocol
, ChildImageHandle
, &LoadedImageProtocol
, (VOID
**) &ChildLoadedImage
);
175 if (CheckError(Status
, L
"while getting a LoadedImageProtocol handle")) {
176 if (ErrorInStep
!= NULL
)
181 if (LoadOptionsPrefix
!= NULL
) {
182 FullLoadOptions
= PoolPrint(L
"%s %s ", LoadOptionsPrefix
, LoadOptions
);
183 // NOTE: That last space is also added by the EFI shell and seems to be significant
184 // when passing options to Apple's boot.efi...
185 LoadOptions
= FullLoadOptions
;
187 // NOTE: We also include the terminating null in the length for safety.
188 ChildLoadedImage
->LoadOptions
= (VOID
*)LoadOptions
;
189 ChildLoadedImage
->LoadOptionsSize
= ((UINT32
)StrLen(LoadOptions
) + 1) * sizeof(CHAR16
);
191 Print(L
"Using load options '%s'\n", LoadOptions
);
194 // close open file handles
197 // turn control over to the image
198 // TODO: (optionally) re-enable the EFI watchdog timer!
199 ReturnStatus
= Status
= refit_call3_wrapper(BS
->StartImage
, ChildImageHandle
, NULL
, NULL
);
200 // control returns here when the child image calls Exit()
201 SPrint(ErrorInfo
, 255, L
"returned from %s", ImageTitle
);
202 if (CheckError(Status
, ErrorInfo
)) {
203 if (ErrorInStep
!= NULL
)
207 // re-open file handles
211 // unload the image, we don't care if it works or not...
212 Status
= refit_call1_wrapper(BS
->UnloadImage
, ChildImageHandle
);
214 if (FullLoadOptions
!= NULL
)
215 FreePool(FullLoadOptions
);
217 } /* static EFI_STATUS StartEFIImageList() */
219 static EFI_STATUS
StartEFIImage(IN EFI_DEVICE_PATH
*DevicePath
,
220 IN CHAR16
*LoadOptions
, IN CHAR16
*LoadOptionsPrefix
,
221 IN CHAR16
*ImageTitle
,
222 OUT UINTN
*ErrorInStep
,
225 EFI_DEVICE_PATH
*DevicePaths
[2];
227 DevicePaths
[0] = DevicePath
;
228 DevicePaths
[1] = NULL
;
229 return StartEFIImageList(DevicePaths
, LoadOptions
, LoadOptionsPrefix
, ImageTitle
, ErrorInStep
, Verbose
);
230 } /* static EFI_STATUS StartEFIImage() */
233 // EFI OS loader functions
236 static VOID
StartLoader(IN LOADER_ENTRY
*Entry
)
238 UINTN ErrorInStep
= 0;
240 BeginExternalScreen(Entry
->UseGraphicsMode
, L
"Booting OS");
241 StartEFIImage(Entry
->DevicePath
, Entry
->LoadOptions
,
242 Basename(Entry
->LoaderPath
), Basename(Entry
->LoaderPath
), &ErrorInStep
, TRUE
);
243 FinishExternalScreen();
246 // Locate an initrd or initramfs file that matches the kernel specified by LoaderPath.
247 // The matching file has a name that begins with "init" and includes the same version
248 // number string as is found in LoaderPath -- but not a longer version number string.
249 // For instance, if LoaderPath is \EFI\kernels\bzImage-3.3.0.efi, and if \EFI\kernels
250 // has a file called initramfs-3.3.0.img, this function will return the string
251 // '\EFI\kernels\initramfs-3.3.0.img'. If the directory ALSO contains the file
252 // initramfs-3.3.0-rc7.img or initramfs-13.3.0.img, those files will NOT match;
253 // however, initmine-3.3.0.img might match. (FindInitrd() returns the first match it
254 // finds). Thus, care should be taken to avoid placing duplicate matching files in
255 // the kernel's directory.
256 // If no matching init file can be found, returns NULL.
257 static CHAR16
* FindInitrd(IN CHAR16
*LoaderPath
, IN REFIT_VOLUME
*Volume
) {
258 CHAR16
*InitrdName
= NULL
, *FileName
, *KernelVersion
, *InitrdVersion
, *Path
;
259 REFIT_DIR_ITER DirIter
;
260 EFI_FILE_INFO
*DirEntry
;
262 FileName
= Basename(LoaderPath
);
263 KernelVersion
= FindNumbers(FileName
);
264 Path
= FindPath(LoaderPath
);
266 // Add trailing backslash for root directory; necessary on some systems, but must
267 // NOT be added to all directories, since on other systems, a trailing backslash on
268 // anything but the root directory causes them to flake out!
269 if (StrLen(Path
) == 0) {
270 MergeStrings(&Path
, L
"\\", 0);
272 DirIterOpen(Volume
->RootDir
, Path
, &DirIter
);
273 // Now add a trailing backslash if it was NOT added earlier, for consistency in
274 // building the InitrdName later....
275 if ((StrLen(Path
) > 0) && (Path
[StrLen(Path
) - 1] != L
'\\'))
276 MergeStrings(&Path
, L
"\\", 0);
277 while ((DirIterNext(&DirIter
, 2, L
"init*", &DirEntry
)) && (InitrdName
== NULL
)) {
278 InitrdVersion
= FindNumbers(DirEntry
->FileName
);
279 if (KernelVersion
!= NULL
) {
280 if (StriCmp(InitrdVersion
, KernelVersion
) == 0)
281 InitrdName
= PoolPrint(L
"%s%s", Path
, DirEntry
->FileName
);
283 if (InitrdVersion
== NULL
)
284 InitrdName
= PoolPrint(L
"%s%s", Path
, DirEntry
->FileName
);
286 if (InitrdVersion
!= NULL
)
287 FreePool(InitrdVersion
);
289 DirIterClose(&DirIter
);
291 // Note: Don't FreePool(FileName), since Basename returns a pointer WITHIN the string it's passed.
292 FreePool(KernelVersion
);
295 } // static CHAR16 * FindInitrd()
297 LOADER_ENTRY
* AddPreparedLoaderEntry(LOADER_ENTRY
*Entry
) {
298 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
301 } // LOADER_ENTRY * AddPreparedLoaderEntry()
303 // Creates a copy of a menu screen.
304 // Returns a pointer to the copy of the menu screen.
305 static REFIT_MENU_SCREEN
* CopyMenuScreen(REFIT_MENU_SCREEN
*Entry
) {
306 REFIT_MENU_SCREEN
*NewEntry
;
309 NewEntry
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
310 if ((Entry
!= NULL
) && (NewEntry
!= NULL
)) {
311 CopyMem(NewEntry
, Entry
, sizeof(REFIT_MENU_SCREEN
));
312 NewEntry
->Title
= StrDuplicate(Entry
->Title
);
313 NewEntry
->TimeoutText
= StrDuplicate(Entry
->TimeoutText
);
314 if (Entry
->TitleImage
!= NULL
) {
315 NewEntry
->TitleImage
= AllocatePool(sizeof(EG_IMAGE
));
316 if (NewEntry
->TitleImage
!= NULL
)
317 CopyMem(NewEntry
->TitleImage
, Entry
->TitleImage
, sizeof(EG_IMAGE
));
319 NewEntry
->InfoLines
= (CHAR16
**) AllocateZeroPool(Entry
->InfoLineCount
* (sizeof(CHAR16
*)));
320 for (i
= 0; i
< Entry
->InfoLineCount
&& NewEntry
->InfoLines
; i
++) {
321 NewEntry
->InfoLines
[i
] = StrDuplicate(Entry
->InfoLines
[i
]);
323 NewEntry
->Entries
= (REFIT_MENU_ENTRY
**) AllocateZeroPool(Entry
->EntryCount
* (sizeof (REFIT_MENU_ENTRY
*)));
324 for (i
= 0; i
< Entry
->EntryCount
&& NewEntry
->Entries
; i
++) {
325 AddMenuEntry(NewEntry
, Entry
->Entries
[i
]);
329 } // static REFIT_MENU_SCREEN* CopyMenuScreen()
331 // Creates a copy of a menu entry. Intended to enable moving a stack-based
332 // menu entry (such as the ones for the "reboot" and "exit" functions) to
333 // to the heap. This enables easier deletion of the whole set of menu
334 // entries when re-scanning.
335 // Returns a pointer to the copy of the menu entry.
336 static REFIT_MENU_ENTRY
* CopyMenuEntry(REFIT_MENU_ENTRY
*Entry
) {
337 REFIT_MENU_ENTRY
*NewEntry
;
339 NewEntry
= AllocateZeroPool(sizeof(REFIT_MENU_ENTRY
));
340 if ((Entry
!= NULL
) && (NewEntry
!= NULL
)) {
341 CopyMem(NewEntry
, Entry
, sizeof(REFIT_MENU_ENTRY
));
342 NewEntry
->Title
= StrDuplicate(Entry
->Title
);
343 if (Entry
->BadgeImage
!= NULL
) {
344 NewEntry
->BadgeImage
= AllocatePool(sizeof(EG_IMAGE
));
345 if (NewEntry
->BadgeImage
!= NULL
)
346 CopyMem(NewEntry
->BadgeImage
, Entry
->BadgeImage
, sizeof(EG_IMAGE
));
348 if (Entry
->Image
!= NULL
) {
349 NewEntry
->Image
= AllocatePool(sizeof(EG_IMAGE
));
350 if (NewEntry
->Image
!= NULL
)
351 CopyMem(NewEntry
->Image
, Entry
->Image
, sizeof(EG_IMAGE
));
353 if (Entry
->SubScreen
!= NULL
) {
354 NewEntry
->SubScreen
= CopyMenuScreen(Entry
->SubScreen
);
358 } // REFIT_MENU_ENTRY* CopyMenuEntry()
360 // Creates a new LOADER_ENTRY data structure and populates it with
361 // default values from the specified Entry, or NULL values if Entry
362 // is unspecified (NULL).
363 // Returns a pointer to the new data structure, or NULL if it
364 // couldn't be allocated
365 LOADER_ENTRY
*InitializeLoaderEntry(IN LOADER_ENTRY
*Entry
) {
366 LOADER_ENTRY
*NewEntry
= NULL
;
368 NewEntry
= AllocateZeroPool(sizeof(LOADER_ENTRY
));
369 if (NewEntry
!= NULL
) {
370 NewEntry
->me
.Title
= NULL
;
371 NewEntry
->me
.Tag
= TAG_LOADER
;
372 NewEntry
->Enabled
= TRUE
;
373 NewEntry
->UseGraphicsMode
= FALSE
;
374 NewEntry
->OSType
= 0;
376 NewEntry
->LoaderPath
= StrDuplicate(Entry
->LoaderPath
);
377 NewEntry
->VolName
= StrDuplicate(Entry
->VolName
);
378 NewEntry
->DevicePath
= Entry
->DevicePath
;
379 NewEntry
->UseGraphicsMode
= Entry
->UseGraphicsMode
;
380 NewEntry
->LoadOptions
= StrDuplicate(Entry
->LoadOptions
);
381 NewEntry
->InitrdPath
= StrDuplicate(Entry
->InitrdPath
);
385 } // LOADER_ENTRY *InitializeLoaderEntry()
387 // Prepare a REFIT_MENU_SCREEN data structure for a subscreen entry. This sets up
388 // the default entry that launches the boot loader using the same options as the
389 // main Entry does. Subsequent options can be added by the calling function.
390 // If a subscreen already exists in the Entry that's passed to this function,
391 // it's left unchanged and a pointer to it is returned.
392 // Returns a pointer to the new subscreen data structure, or NULL if there
393 // were problems allocating memory.
394 REFIT_MENU_SCREEN
*InitializeSubScreen(IN LOADER_ENTRY
*Entry
) {
395 CHAR16
*FileName
, *Temp
;
396 REFIT_MENU_SCREEN
*SubScreen
= NULL
;
397 LOADER_ENTRY
*SubEntry
;
399 FileName
= Basename(Entry
->LoaderPath
);
400 if (Entry
->me
.SubScreen
== NULL
) { // No subscreen yet; initialize default entry....
401 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
402 if (SubScreen
!= NULL
) {
403 SubScreen
->Title
= PoolPrint(L
"Boot Options for %s on %s", (Entry
->Title
!= NULL
) ? Entry
->Title
: FileName
, Entry
->VolName
);
404 SubScreen
->TitleImage
= Entry
->me
.Image
;
406 SubEntry
= InitializeLoaderEntry(Entry
);
407 if (SubEntry
!= NULL
) {
408 SubEntry
->me
.Title
= L
"Boot using default options";
409 if ((SubEntry
->InitrdPath
!= NULL
) && (StrLen(SubEntry
->InitrdPath
) > 0) && (!StriSubCmp(L
"initrd", SubEntry
->LoadOptions
))) {
410 Temp
= PoolPrint(L
"initrd=%s", SubEntry
->InitrdPath
);
411 MergeStrings(&SubEntry
->LoadOptions
, Temp
, L
' ');
414 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
415 } // if (SubEntry != NULL)
416 } // if (SubScreen != NULL)
417 } else { // existing subscreen; less initialization, and just add new entry later....
418 SubScreen
= Entry
->me
.SubScreen
;
421 } // REFIT_MENU_SCREEN *InitializeSubScreen()
423 VOID
GenerateSubScreen(LOADER_ENTRY
*Entry
, IN REFIT_VOLUME
*Volume
) {
424 REFIT_MENU_SCREEN
*SubScreen
;
425 LOADER_ENTRY
*SubEntry
;
426 CHAR16
*FileName
, *InitrdOption
= NULL
, *Temp
;
427 CHAR16 DiagsFileName
[256];
432 FileName
= Basename(Entry
->LoaderPath
);
433 // create the submenu
434 if (StrLen(Entry
->Title
) == 0) {
435 FreePool(Entry
->Title
);
438 SubScreen
= InitializeSubScreen(Entry
);
440 // loader-specific submenu entries
441 if (Entry
->OSType
== 'M') { // entries for Mac OS X
443 SubEntry
= InitializeLoaderEntry(Entry
);
444 if (SubEntry
!= NULL
) {
445 SubEntry
->me
.Title
= L
"Boot Mac OS X with a 64-bit kernel";
446 SubEntry
->LoadOptions
= L
"arch=x86_64";
447 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
450 SubEntry
= InitializeLoaderEntry(Entry
);
451 if (SubEntry
!= NULL
) {
452 SubEntry
->me
.Title
= L
"Boot Mac OS X with a 32-bit kernel";
453 SubEntry
->LoadOptions
= L
"arch=i386";
454 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
458 if (!(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_SINGLEUSER
)) {
459 SubEntry
= InitializeLoaderEntry(Entry
);
460 if (SubEntry
!= NULL
) {
461 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode";
462 SubEntry
->UseGraphicsMode
= FALSE
;
463 SubEntry
->LoadOptions
= L
"-v";
464 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
468 SubEntry
= InitializeLoaderEntry(Entry
);
469 if (SubEntry
!= NULL
) {
470 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode (64-bit)";
471 SubEntry
->UseGraphicsMode
= FALSE
;
472 SubEntry
->LoadOptions
= L
"-v arch=x86_64";
473 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
476 SubEntry
= InitializeLoaderEntry(Entry
);
477 if (SubEntry
!= NULL
) {
478 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode (32-bit)";
479 SubEntry
->UseGraphicsMode
= FALSE
;
480 SubEntry
->LoadOptions
= L
"-v arch=i386";
481 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
485 SubEntry
= InitializeLoaderEntry(Entry
);
486 if (SubEntry
!= NULL
) {
487 SubEntry
->me
.Title
= L
"Boot Mac OS X in single user mode";
488 SubEntry
->UseGraphicsMode
= FALSE
;
489 SubEntry
->LoadOptions
= L
"-v -s";
490 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
494 // check for Apple hardware diagnostics
495 StrCpy(DiagsFileName
, L
"\\System\\Library\\CoreServices\\.diagnostics\\diags.efi");
496 if (FileExists(Volume
->RootDir
, DiagsFileName
) && !(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_HWTEST
)) {
497 SubEntry
= InitializeLoaderEntry(Entry
);
498 if (SubEntry
!= NULL
) {
499 SubEntry
->me
.Title
= L
"Run Apple Hardware Test";
500 FreePool(SubEntry
->LoaderPath
);
501 SubEntry
->LoaderPath
= StrDuplicate(DiagsFileName
);
502 SubEntry
->DevicePath
= FileDevicePath(Volume
->DeviceHandle
, SubEntry
->LoaderPath
);
503 SubEntry
->UseGraphicsMode
= TRUE
;
504 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
506 } // if diagnostics entry found
508 } else if (Entry
->OSType
== 'L') { // entries for Linux kernels with EFI stub loaders
509 File
= ReadLinuxOptionsFile(Entry
->LoaderPath
, Volume
);
511 if ((Temp
= FindInitrd(Entry
->LoaderPath
, Volume
)) != NULL
)
512 InitrdOption
= PoolPrint(L
"initrd=%s", Temp
);
513 TokenCount
= ReadTokenLine(File
, &TokenList
); // read and discard first entry, since it's
514 FreeTokenLine(&TokenList
, &TokenCount
); // set up by InitializeSubScreen(), earlier....
515 while ((TokenCount
= ReadTokenLine(File
, &TokenList
)) > 1) {
516 SubEntry
= InitializeLoaderEntry(Entry
);
517 SubEntry
->me
.Title
= StrDuplicate(TokenList
[0]);
518 if (SubEntry
->LoadOptions
!= NULL
)
519 FreePool(SubEntry
->LoadOptions
);
520 SubEntry
->LoadOptions
= StrDuplicate(TokenList
[1]);
521 MergeStrings(&SubEntry
->LoadOptions
, InitrdOption
, L
' ');
522 FreeTokenLine(&TokenList
, &TokenCount
);
523 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
526 FreePool(InitrdOption
);
530 } // if Linux options file exists
532 } else if (Entry
->OSType
== 'E') { // entries for ELILO
533 SubEntry
= InitializeLoaderEntry(Entry
);
534 if (SubEntry
!= NULL
) {
535 SubEntry
->me
.Title
= PoolPrint(L
"Run %s in interactive mode", FileName
);
536 SubEntry
->LoadOptions
= L
"-p";
537 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
540 SubEntry
= InitializeLoaderEntry(Entry
);
541 if (SubEntry
!= NULL
) {
542 SubEntry
->me
.Title
= L
"Boot Linux for a 17\" iMac or a 15\" MacBook Pro (*)";
543 SubEntry
->UseGraphicsMode
= TRUE
;
544 SubEntry
->LoadOptions
= L
"-d 0 i17";
545 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
548 SubEntry
= InitializeLoaderEntry(Entry
);
549 if (SubEntry
!= NULL
) {
550 SubEntry
->me
.Title
= L
"Boot Linux for a 20\" iMac (*)";
551 SubEntry
->UseGraphicsMode
= TRUE
;
552 SubEntry
->LoadOptions
= L
"-d 0 i20";
553 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
556 SubEntry
= InitializeLoaderEntry(Entry
);
557 if (SubEntry
!= NULL
) {
558 SubEntry
->me
.Title
= L
"Boot Linux for a Mac Mini (*)";
559 SubEntry
->UseGraphicsMode
= TRUE
;
560 SubEntry
->LoadOptions
= L
"-d 0 mini";
561 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
564 AddMenuInfoLine(SubScreen
, L
"NOTE: This is an example. Entries");
565 AddMenuInfoLine(SubScreen
, L
"marked with (*) may not work.");
567 } else if (Entry
->OSType
== 'X') { // entries for xom.efi
568 // by default, skip the built-in selection and boot from hard disk only
569 Entry
->LoadOptions
= L
"-s -h";
571 SubEntry
= InitializeLoaderEntry(Entry
);
572 if (SubEntry
!= NULL
) {
573 SubEntry
->me
.Title
= L
"Boot Windows from Hard Disk";
574 SubEntry
->LoadOptions
= L
"-s -h";
575 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
578 SubEntry
= InitializeLoaderEntry(Entry
);
579 if (SubEntry
!= NULL
) {
580 SubEntry
->me
.Title
= L
"Boot Windows from CD-ROM";
581 SubEntry
->LoadOptions
= L
"-s -c";
582 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
585 SubEntry
= InitializeLoaderEntry(Entry
);
586 if (SubEntry
!= NULL
) {
587 SubEntry
->me
.Title
= PoolPrint(L
"Run %s in text mode", FileName
);
588 SubEntry
->UseGraphicsMode
= FALSE
;
589 SubEntry
->LoadOptions
= L
"-v";
590 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
592 } // entries for xom.efi
593 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
594 Entry
->me
.SubScreen
= SubScreen
;
595 } // VOID GenerateSubScreen()
597 // Returns options for a Linux kernel. Reads them from an options file in the
598 // kernel's directory; and if present, adds an initrd= option for an initial
599 // RAM disk file with the same version number as the kernel file.
600 static CHAR16
* GetMainLinuxOptions(IN CHAR16
* LoaderPath
, IN REFIT_VOLUME
*Volume
) {
601 CHAR16
*Options
= NULL
, *InitrdName
, *InitrdOption
= NULL
;
603 Options
= GetFirstOptionsFromFile(LoaderPath
, Volume
);
604 InitrdName
= FindInitrd(LoaderPath
, Volume
);
605 if (InitrdName
!= NULL
)
606 InitrdOption
= PoolPrint(L
"initrd=%s", InitrdName
);
607 MergeStrings(&Options
, InitrdOption
, ' ');
608 if (InitrdOption
!= NULL
)
609 FreePool(InitrdOption
);
610 if (InitrdName
!= NULL
)
611 FreePool(InitrdName
);
613 } // static CHAR16 * GetMainLinuxOptions()
615 // Sets a few defaults for a loader entry -- mainly the icon, but also the OS type
616 // code and shortcut letter. For Linux EFI stub loaders, also sets kernel options
617 // that will (with luck) work fairly automatically.
618 VOID
SetLoaderDefaults(LOADER_ENTRY
*Entry
, CHAR16
*LoaderPath
, IN REFIT_VOLUME
*Volume
) {
619 CHAR16 IconFileName
[256];
620 CHAR16
*FileName
, *PathOnly
, *OSIconName
= NULL
, *Temp
;
621 CHAR16 ShortcutLetter
= 0;
623 FileName
= Basename(LoaderPath
);
624 PathOnly
= FindPath(LoaderPath
);
626 // locate a custom icon for the loader
627 StrCpy(IconFileName
, LoaderPath
);
628 ReplaceEfiExtension(IconFileName
, L
".icns");
629 if (FileExists(Volume
->RootDir
, IconFileName
)) {
630 Entry
->me
.Image
= LoadIcns(Volume
->RootDir
, IconFileName
, 128);
631 } else if ((StrLen(PathOnly
) == 0) && (Volume
->VolIconImage
!= NULL
)) {
632 Entry
->me
.Image
= Volume
->VolIconImage
;
633 } // icon matched to loader or volume
635 Temp
= FindLastDirName(LoaderPath
);
636 MergeStrings(&OSIconName
, Temp
, L
',');
638 if (OSIconName
!= NULL
) {
639 ShortcutLetter
= OSIconName
[0];
642 // detect specific loaders
643 if (StriSubCmp(L
"bzImage", LoaderPath
) || StriSubCmp(L
"vmlinuz", LoaderPath
)) {
644 MergeStrings(&OSIconName
, L
"linux", L
',');
646 if (ShortcutLetter
== 0)
647 ShortcutLetter
= 'L';
648 Entry
->LoadOptions
= GetMainLinuxOptions(LoaderPath
, Volume
);
649 } else if (StriSubCmp(L
"refit", LoaderPath
)) {
650 MergeStrings(&OSIconName
, L
"refit", L
',');
652 ShortcutLetter
= 'R';
653 } else if (StriCmp(LoaderPath
, MACOSX_LOADER_PATH
) == 0) {
654 if (Volume
->VolIconImage
!= NULL
) { // custom icon file found
655 Entry
->me
.Image
= Volume
->VolIconImage
;
657 MergeStrings(&OSIconName
, L
"mac", L
',');
658 Entry
->UseGraphicsMode
= TRUE
;
660 ShortcutLetter
= 'M';
661 } else if (StriCmp(FileName
, L
"diags.efi") == 0) {
662 MergeStrings(&OSIconName
, L
"hwtest", L
',');
663 } else if (StriCmp(FileName
, L
"e.efi") == 0 || StriCmp(FileName
, L
"elilo.efi") == 0) {
664 MergeStrings(&OSIconName
, L
"elilo,linux", L
',');
666 if (ShortcutLetter
== 0)
667 ShortcutLetter
= 'L';
668 } else if (StriSubCmp(L
"grub", FileName
)) {
670 ShortcutLetter
= 'G';
671 } else if (StriCmp(FileName
, L
"cdboot.efi") == 0 ||
672 StriCmp(FileName
, L
"bootmgr.efi") == 0 ||
673 StriCmp(FileName
, L
"Bootmgfw.efi") == 0) {
674 MergeStrings(&OSIconName
, L
"win", L
',');
676 ShortcutLetter
= 'W';
677 } else if (StriCmp(FileName
, L
"xom.efi") == 0) {
678 MergeStrings(&OSIconName
, L
"xom,win", L
',');
679 Entry
->UseGraphicsMode
= TRUE
;
681 ShortcutLetter
= 'W';
684 if ((ShortcutLetter
>= 'a') && (ShortcutLetter
<= 'z'))
685 ShortcutLetter
= ShortcutLetter
- 'a' + 'A'; // convert lowercase to uppercase
686 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
687 if (Entry
->me
.Image
== NULL
)
688 Entry
->me
.Image
= LoadOSIcon(OSIconName
, L
"unknown", FALSE
);
689 if (PathOnly
!= NULL
)
691 } // VOID SetLoaderDefaults()
693 // Add a specified EFI boot loader to the list, using automatic settings
694 // for icons, options, etc.
695 LOADER_ENTRY
* AddLoaderEntry(IN CHAR16
*LoaderPath
, IN CHAR16
*LoaderTitle
, IN REFIT_VOLUME
*Volume
) {
698 CleanUpPathNameSlashes(LoaderPath
);
699 Entry
= InitializeLoaderEntry(NULL
);
701 Entry
->Title
= StrDuplicate((LoaderTitle
!= NULL
) ? LoaderTitle
: LoaderPath
);
702 Entry
->me
.Title
= PoolPrint(L
"Boot %s from %s", (LoaderTitle
!= NULL
) ? LoaderTitle
: LoaderPath
, Volume
->VolName
);
704 Entry
->me
.BadgeImage
= Volume
->VolBadgeImage
;
705 if ((LoaderPath
!= NULL
) && (LoaderPath
[0] != L
'\\')) {
706 Entry
->LoaderPath
= StrDuplicate(L
"\\");
708 Entry
->LoaderPath
= NULL
;
710 MergeStrings(&(Entry
->LoaderPath
), LoaderPath
, 0);
711 // Entry->LoaderPath = StrDuplicate(LoaderPath);
712 Entry
->VolName
= Volume
->VolName
;
713 Entry
->DevicePath
= FileDevicePath(Volume
->DeviceHandle
, Entry
->LoaderPath
);
714 SetLoaderDefaults(Entry
, LoaderPath
, Volume
);
715 GenerateSubScreen(Entry
, Volume
);
716 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
720 } // LOADER_ENTRY * AddLoaderEntry()
722 // Returns -1 if (Time1 < Time2), +1 if (Time1 > Time2), or 0 if
723 // (Time1 == Time2). Precision is only to the nearest second; since
724 // this is used for sorting boot loader entries, differences smaller
725 // than this are likely to be meaningless (and unlikely!).
726 INTN
TimeComp(EFI_TIME
*Time1
, EFI_TIME
*Time2
) {
727 INT64 Time1InSeconds
, Time2InSeconds
;
729 // Following values are overestimates; I'm assuming 31 days in every month.
730 // This is fine for the purpose of this function, which has a limited
732 Time1InSeconds
= Time1
->Second
+ (Time1
->Minute
* 60) + (Time1
->Hour
* 3600) + (Time1
->Day
* 86400) +
733 (Time1
->Month
* 2678400) + ((Time1
->Year
- 1998) * 32140800);
734 Time2InSeconds
= Time2
->Second
+ (Time2
->Minute
* 60) + (Time2
->Hour
* 3600) + (Time2
->Day
* 86400) +
735 (Time2
->Month
* 2678400) + ((Time2
->Year
- 1998) * 32140800);
736 if (Time1InSeconds
< Time2InSeconds
)
738 else if (Time1InSeconds
> Time2InSeconds
)
744 // Adds a loader list element, keeping it sorted by date. Returns the new
745 // first element (the one with the most recent date).
746 static struct LOADER_LIST
* AddLoaderListEntry(struct LOADER_LIST
*LoaderList
, struct LOADER_LIST
*NewEntry
) {
747 struct LOADER_LIST
*LatestEntry
, *CurrentEntry
, *PrevEntry
= NULL
;
749 LatestEntry
= CurrentEntry
= LoaderList
;
750 if (LoaderList
== NULL
) {
751 LatestEntry
= NewEntry
;
753 while ((CurrentEntry
!= NULL
) && (TimeComp(&(NewEntry
->TimeStamp
), &(CurrentEntry
->TimeStamp
)) < 0)) {
754 PrevEntry
= CurrentEntry
;
755 CurrentEntry
= CurrentEntry
->NextEntry
;
757 NewEntry
->NextEntry
= CurrentEntry
;
758 if (PrevEntry
== NULL
) {
759 LatestEntry
= NewEntry
;
761 PrevEntry
->NextEntry
= NewEntry
;
764 return (LatestEntry
);
765 } // static VOID AddLoaderListEntry()
767 // Delete the LOADER_LIST linked list
768 static VOID
CleanUpLoaderList(struct LOADER_LIST
*LoaderList
) {
769 struct LOADER_LIST
*Temp
;
771 while (LoaderList
!= NULL
) {
773 LoaderList
= LoaderList
->NextEntry
;
774 FreePool(Temp
->FileName
);
777 } // static VOID CleanUpLoaderList()
779 // Scan an individual directory for EFI boot loader files and, if found,
780 // add them to the list. Sorts the entries within the loader directory
781 // so that the most recent one appears first in the list.
782 static VOID
ScanLoaderDir(IN REFIT_VOLUME
*Volume
, IN CHAR16
*Path
, IN CHAR16
*Pattern
)
785 REFIT_DIR_ITER DirIter
;
786 EFI_FILE_INFO
*DirEntry
;
787 CHAR16 FileName
[256], *Extension
;
788 struct LOADER_LIST
*LoaderList
= NULL
, *NewLoader
;
790 if (!SelfDirPath
|| !Path
|| ((StriCmp(Path
, SelfDirPath
) == 0) && Volume
->DeviceHandle
!= SelfVolume
->DeviceHandle
) ||
791 (StriCmp(Path
, SelfDirPath
) != 0)) {
792 // look through contents of the directory
793 DirIterOpen(Volume
->RootDir
, Path
, &DirIter
);
794 while (DirIterNext(&DirIter
, 2, Pattern
, &DirEntry
)) {
795 Extension
= FindExtension(DirEntry
->FileName
);
796 if (DirEntry
->FileName
[0] == '.' ||
797 StriCmp(DirEntry
->FileName
, L
"TextMode.efi") == 0 ||
798 StriCmp(DirEntry
->FileName
, L
"ebounce.efi") == 0 ||
799 StriCmp(DirEntry
->FileName
, L
"GraphicsConsole.efi") == 0 ||
800 StriCmp(Extension
, L
".icns") == 0 ||
801 StriSubCmp(L
"shell", DirEntry
->FileName
))
802 continue; // skip this
805 SPrint(FileName
, 255, L
"\\%s\\%s", Path
, DirEntry
->FileName
);
807 SPrint(FileName
, 255, L
"\\%s", DirEntry
->FileName
);
808 CleanUpPathNameSlashes(FileName
);
809 NewLoader
= AllocateZeroPool(sizeof(struct LOADER_LIST
));
810 if (NewLoader
!= NULL
) {
811 NewLoader
->FileName
= StrDuplicate(FileName
);
812 NewLoader
->TimeStamp
= DirEntry
->ModificationTime
;
813 LoaderList
= AddLoaderListEntry(LoaderList
, NewLoader
);
817 NewLoader
= LoaderList
;
818 while (NewLoader
!= NULL
) {
819 AddLoaderEntry(NewLoader
->FileName
, NULL
, Volume
);
820 NewLoader
= NewLoader
->NextEntry
;
822 CleanUpLoaderList(LoaderList
);
823 Status
= DirIterClose(&DirIter
);
824 if (Status
!= EFI_NOT_FOUND
) {
826 SPrint(FileName
, 255, L
"while scanning the %s directory", Path
);
828 StrCpy(FileName
, L
"while scanning the root directory");
829 CheckError(Status
, FileName
);
830 } // if (Status != EFI_NOT_FOUND)
831 } // if not scanning our own directory
832 } /* static VOID ScanLoaderDir() */
834 static VOID
ScanEfiFiles(REFIT_VOLUME
*Volume
) {
836 REFIT_DIR_ITER EfiDirIter
;
837 EFI_FILE_INFO
*EfiDirEntry
;
838 CHAR16 FileName
[256], *Directory
, *MatchPatterns
;
841 MatchPatterns
= StrDuplicate(LOADER_MATCH_PATTERNS
);
842 if (GlobalConfig
.ScanAllLinux
)
843 MergeStrings(&MatchPatterns
, LINUX_MATCH_PATTERNS
, L
',');
845 if ((Volume
->RootDir
!= NULL
) && (Volume
->VolName
!= NULL
)) {
846 // check for Mac OS X boot loader
847 StrCpy(FileName
, MACOSX_LOADER_PATH
);
848 if (FileExists(Volume
->RootDir
, FileName
)) {
849 AddLoaderEntry(FileName
, L
"Mac OS X", Volume
);
853 StrCpy(FileName
, L
"System\\Library\\CoreServices\\xom.efi");
854 if (FileExists(Volume
->RootDir
, FileName
)) {
855 AddLoaderEntry(FileName
, L
"Windows XP (XoM)", Volume
);
858 // check for Microsoft boot loader/menu
859 StrCpy(FileName
, L
"EFI\\Microsoft\\Boot\\Bootmgfw.efi");
860 if (FileExists(Volume
->RootDir
, FileName
)) {
861 AddLoaderEntry(FileName
, L
"Microsoft EFI boot", Volume
);
864 // scan the root directory for EFI executables
865 ScanLoaderDir(Volume
, L
"\\", MatchPatterns
);
867 // scan subdirectories of the EFI directory (as per the standard)
868 DirIterOpen(Volume
->RootDir
, L
"EFI", &EfiDirIter
);
869 while (DirIterNext(&EfiDirIter
, 1, NULL
, &EfiDirEntry
)) {
870 if (StriCmp(EfiDirEntry
->FileName
, L
"tools") == 0 || EfiDirEntry
->FileName
[0] == '.')
871 continue; // skip this, doesn't contain boot loaders
872 SPrint(FileName
, 255, L
"EFI\\%s", EfiDirEntry
->FileName
);
873 ScanLoaderDir(Volume
, FileName
, MatchPatterns
);
875 Status
= DirIterClose(&EfiDirIter
);
876 if (Status
!= EFI_NOT_FOUND
)
877 CheckError(Status
, L
"while scanning the EFI directory");
879 // Scan user-specified (or additional default) directories....
881 while ((Directory
= FindCommaDelimited(GlobalConfig
.AlsoScan
, i
++)) != NULL
) {
882 CleanUpPathNameSlashes(Directory
);
883 Length
= StrLen(Directory
);
885 ScanLoaderDir(Volume
, Directory
, MatchPatterns
);
889 } // static VOID ScanEfiFiles()
891 // Scan internal disks for valid EFI boot loaders....
892 static VOID
ScanInternal(VOID
) {
895 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
896 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_INTERNAL
) {
897 ScanEfiFiles(Volumes
[VolumeIndex
]);
900 } // static VOID ScanInternal()
902 // Scan external disks for valid EFI boot loaders....
903 static VOID
ScanExternal(VOID
) {
906 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
907 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_EXTERNAL
) {
908 ScanEfiFiles(Volumes
[VolumeIndex
]);
911 } // static VOID ScanExternal()
913 // Scan internal disks for valid EFI boot loaders....
914 static VOID
ScanOptical(VOID
) {
917 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
918 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_OPTICAL
) {
919 ScanEfiFiles(Volumes
[VolumeIndex
]);
922 } // static VOID ScanOptical()
925 // legacy boot functions
928 static EFI_STATUS
ActivateMbrPartition(IN EFI_BLOCK_IO
*BlockIO
, IN UINTN PartitionIndex
)
931 UINT8 SectorBuffer
[512];
932 MBR_PARTITION_INFO
*MbrTable
, *EMbrTable
;
933 UINT32 ExtBase
, ExtCurrent
, NextExtCurrent
;
934 UINTN LogicalPartitionIndex
= 4;
936 BOOLEAN HaveBootCode
;
939 Status
= refit_call5_wrapper(BlockIO
->ReadBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, 0, 512, SectorBuffer
);
940 if (EFI_ERROR(Status
))
942 if (*((UINT16
*)(SectorBuffer
+ 510)) != 0xaa55)
943 return EFI_NOT_FOUND
; // safety measure #1
945 // add boot code if necessary
946 HaveBootCode
= FALSE
;
947 for (i
= 0; i
< MBR_BOOTCODE_SIZE
; i
++) {
948 if (SectorBuffer
[i
] != 0) {
954 // no boot code found in the MBR, add the syslinux MBR code
955 SetMem(SectorBuffer
, MBR_BOOTCODE_SIZE
, 0);
956 CopyMem(SectorBuffer
, syslinux_mbr
, SYSLINUX_MBR_SIZE
);
959 // set the partition active
960 MbrTable
= (MBR_PARTITION_INFO
*)(SectorBuffer
+ 446);
962 for (i
= 0; i
< 4; i
++) {
963 if (MbrTable
[i
].Flags
!= 0x00 && MbrTable
[i
].Flags
!= 0x80)
964 return EFI_NOT_FOUND
; // safety measure #2
965 if (i
== PartitionIndex
)
966 MbrTable
[i
].Flags
= 0x80;
967 else if (PartitionIndex
>= 4 && IS_EXTENDED_PART_TYPE(MbrTable
[i
].Type
)) {
968 MbrTable
[i
].Flags
= 0x80;
969 ExtBase
= MbrTable
[i
].StartLBA
;
971 MbrTable
[i
].Flags
= 0x00;
975 Status
= refit_call5_wrapper(BlockIO
->WriteBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, 0, 512, SectorBuffer
);
976 if (EFI_ERROR(Status
))
979 if (PartitionIndex
>= 4) {
980 // we have to activate a logical partition, so walk the EMBR chain
982 // NOTE: ExtBase was set above while looking at the MBR table
983 for (ExtCurrent
= ExtBase
; ExtCurrent
; ExtCurrent
= NextExtCurrent
) {
985 Status
= refit_call5_wrapper(BlockIO
->ReadBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, ExtCurrent
, 512, SectorBuffer
);
986 if (EFI_ERROR(Status
))
988 if (*((UINT16
*)(SectorBuffer
+ 510)) != 0xaa55)
989 return EFI_NOT_FOUND
; // safety measure #3
991 // scan EMBR, set appropriate partition active
992 EMbrTable
= (MBR_PARTITION_INFO
*)(SectorBuffer
+ 446);
994 for (i
= 0; i
< 4; i
++) {
995 if (EMbrTable
[i
].Flags
!= 0x00 && EMbrTable
[i
].Flags
!= 0x80)
996 return EFI_NOT_FOUND
; // safety measure #4
997 if (EMbrTable
[i
].StartLBA
== 0 || EMbrTable
[i
].Size
== 0)
999 if (IS_EXTENDED_PART_TYPE(EMbrTable
[i
].Type
)) {
1000 // link to next EMBR
1001 NextExtCurrent
= ExtBase
+ EMbrTable
[i
].StartLBA
;
1002 EMbrTable
[i
].Flags
= (PartitionIndex
>= LogicalPartitionIndex
) ? 0x80 : 0x00;
1005 // logical partition
1006 EMbrTable
[i
].Flags
= (PartitionIndex
== LogicalPartitionIndex
) ? 0x80 : 0x00;
1007 LogicalPartitionIndex
++;
1011 // write current EMBR
1012 Status
= refit_call5_wrapper(BlockIO
->WriteBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, ExtCurrent
, 512, SectorBuffer
);
1013 if (EFI_ERROR(Status
))
1016 if (PartitionIndex
< LogicalPartitionIndex
)
1017 break; // stop the loop, no need to touch further EMBRs
1023 } /* static EFI_STATUS ActivateMbrPartition() */
1025 // early 2006 Core Duo / Core Solo models
1026 static UINT8 LegacyLoaderDevicePath1Data
[] = {
1027 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1028 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1029 0xFF, 0xFF, 0xF9, 0xFF, 0x00, 0x00, 0x00, 0x00,
1030 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1031 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1032 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1034 // mid-2006 Mac Pro (and probably other Core 2 models)
1035 static UINT8 LegacyLoaderDevicePath2Data
[] = {
1036 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1037 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1038 0xFF, 0xFF, 0xF7, 0xFF, 0x00, 0x00, 0x00, 0x00,
1039 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1040 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1041 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1043 // mid-2007 MBP ("Santa Rosa" based models)
1044 static UINT8 LegacyLoaderDevicePath3Data
[] = {
1045 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1046 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1047 0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
1048 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1049 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1050 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1053 static UINT8 LegacyLoaderDevicePath4Data
[] = {
1054 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1055 0x00, 0x00, 0xC0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1056 0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
1057 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1058 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1059 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1061 // late-2008 MB/MBP (NVidia chipset)
1062 static UINT8 LegacyLoaderDevicePath5Data
[] = {
1063 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1064 0x00, 0x40, 0xCB, 0xFF, 0x00, 0x00, 0x00, 0x00,
1065 0xFF, 0xBF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
1066 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1067 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1068 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1071 static EFI_DEVICE_PATH
*LegacyLoaderList
[] = {
1072 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath1Data
,
1073 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath2Data
,
1074 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath3Data
,
1075 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath4Data
,
1076 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath5Data
,
1080 #define MAX_DISCOVERED_PATHS (16)
1082 static VOID
StartLegacy(IN LEGACY_ENTRY
*Entry
)
1085 EG_IMAGE
*BootLogoImage
;
1086 UINTN ErrorInStep
= 0;
1087 EFI_DEVICE_PATH
*DiscoveredPathList
[MAX_DISCOVERED_PATHS
];
1089 BeginExternalScreen(TRUE
, L
"Booting Legacy OS");
1091 BootLogoImage
= LoadOSIcon(Entry
->Volume
->OSIconName
, L
"legacy", TRUE
);
1092 if (BootLogoImage
!= NULL
)
1093 BltImageAlpha(BootLogoImage
,
1094 (UGAWidth
- BootLogoImage
->Width
) >> 1,
1095 (UGAHeight
- BootLogoImage
->Height
) >> 1,
1096 &StdBackgroundPixel
);
1098 if (Entry
->Volume
->IsMbrPartition
)
1099 ActivateMbrPartition(Entry
->Volume
->WholeDiskBlockIO
, Entry
->Volume
->MbrPartitionIndex
);
1101 ExtractLegacyLoaderPaths(DiscoveredPathList
, MAX_DISCOVERED_PATHS
, LegacyLoaderList
);
1103 Status
= StartEFIImageList(DiscoveredPathList
, Entry
->LoadOptions
, NULL
, L
"legacy loader", &ErrorInStep
, TRUE
);
1104 if (Status
== EFI_NOT_FOUND
) {
1105 if (ErrorInStep
== 1) {
1106 Print(L
"\nPlease make sure that you have the latest firmware update installed.\n");
1107 } else if (ErrorInStep
== 3) {
1108 Print(L
"\nThe firmware refused to boot from the selected volume. Note that external\n"
1109 L
"hard drives are not well-supported by Apple's firmware for legacy OS booting.\n");
1112 FinishExternalScreen();
1113 } /* static VOID StartLegacy() */
1115 static LEGACY_ENTRY
* AddLegacyEntry(IN CHAR16
*LoaderTitle
, IN REFIT_VOLUME
*Volume
)
1117 LEGACY_ENTRY
*Entry
, *SubEntry
;
1118 REFIT_MENU_SCREEN
*SubScreen
;
1120 CHAR16 ShortcutLetter
= 0;
1122 if (LoaderTitle
== NULL
) {
1123 if (Volume
->OSName
!= NULL
) {
1124 LoaderTitle
= Volume
->OSName
;
1125 if (LoaderTitle
[0] == 'W' || LoaderTitle
[0] == 'L')
1126 ShortcutLetter
= LoaderTitle
[0];
1128 LoaderTitle
= L
"Legacy OS";
1130 if (Volume
->VolName
!= NULL
)
1131 VolDesc
= Volume
->VolName
;
1133 VolDesc
= (Volume
->DiskKind
== DISK_KIND_OPTICAL
) ? L
"CD" : L
"HD";
1135 // prepare the menu entry
1136 Entry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1137 Entry
->me
.Title
= PoolPrint(L
"Boot %s from %s", LoaderTitle
, VolDesc
);
1138 Entry
->me
.Tag
= TAG_LEGACY
;
1140 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1141 Entry
->me
.Image
= LoadOSIcon(Volume
->OSIconName
, L
"legacy", FALSE
);
1142 Entry
->me
.BadgeImage
= Volume
->VolBadgeImage
;
1143 Entry
->Volume
= Volume
;
1144 Entry
->LoadOptions
= (Volume
->DiskKind
== DISK_KIND_OPTICAL
) ? L
"CD" :
1145 ((Volume
->DiskKind
== DISK_KIND_EXTERNAL
) ? L
"USB" : L
"HD");
1146 Entry
->Enabled
= TRUE
;
1148 // create the submenu
1149 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
1150 SubScreen
->Title
= PoolPrint(L
"Boot Options for %s on %s", LoaderTitle
, VolDesc
);
1151 SubScreen
->TitleImage
= Entry
->me
.Image
;
1154 SubEntry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1155 SubEntry
->me
.Title
= PoolPrint(L
"Boot %s", LoaderTitle
);
1156 SubEntry
->me
.Tag
= TAG_LEGACY
;
1157 SubEntry
->Volume
= Entry
->Volume
;
1158 SubEntry
->LoadOptions
= Entry
->LoadOptions
;
1159 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
1161 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
1162 Entry
->me
.SubScreen
= SubScreen
;
1163 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1165 } /* static LEGACY_ENTRY * AddLegacyEntry() */
1167 static VOID
ScanLegacyVolume(REFIT_VOLUME
*Volume
, UINTN VolumeIndex
) {
1169 BOOLEAN ShowVolume
, HideIfOthersFound
;
1172 HideIfOthersFound
= FALSE
;
1173 if (Volume
->IsAppleLegacy
) {
1175 HideIfOthersFound
= TRUE
;
1176 } else if (Volume
->HasBootCode
) {
1178 if (Volume
->BlockIO
== Volume
->WholeDiskBlockIO
&&
1179 Volume
->BlockIOOffset
== 0 &&
1180 Volume
->OSName
== NULL
)
1181 // this is a whole disk (MBR) entry; hide if we have entries for partitions
1182 HideIfOthersFound
= TRUE
;
1184 if (HideIfOthersFound
) {
1185 // check for other bootable entries on the same disk
1186 for (VolumeIndex2
= 0; VolumeIndex2
< VolumesCount
; VolumeIndex2
++) {
1187 if (VolumeIndex2
!= VolumeIndex
&& Volumes
[VolumeIndex2
]->HasBootCode
&&
1188 Volumes
[VolumeIndex2
]->WholeDiskBlockIO
== Volume
->WholeDiskBlockIO
)
1194 AddLegacyEntry(NULL
, Volume
);
1195 } // static VOID ScanLegacyVolume()
1197 // Scan attached optical discs for legacy (BIOS) boot code
1198 // and add anything found to the list....
1199 static VOID
ScanLegacyDisc(VOID
)
1202 REFIT_VOLUME
*Volume
;
1204 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1205 Volume
= Volumes
[VolumeIndex
];
1206 if (Volume
->DiskKind
== DISK_KIND_OPTICAL
)
1207 ScanLegacyVolume(Volume
, VolumeIndex
);
1209 } /* static VOID ScanLegacyDisc() */
1211 // Scan internal hard disks for legacy (BIOS) boot code
1212 // and add anything found to the list....
1213 static VOID
ScanLegacyInternal(VOID
)
1216 REFIT_VOLUME
*Volume
;
1218 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1219 Volume
= Volumes
[VolumeIndex
];
1220 if (Volume
->DiskKind
== DISK_KIND_INTERNAL
)
1221 ScanLegacyVolume(Volume
, VolumeIndex
);
1223 } /* static VOID ScanLegacyInternal() */
1225 // Scan external disks for legacy (BIOS) boot code
1226 // and add anything found to the list....
1227 static VOID
ScanLegacyExternal(VOID
)
1230 REFIT_VOLUME
*Volume
;
1232 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1233 Volume
= Volumes
[VolumeIndex
];
1234 if (Volume
->DiskKind
== DISK_KIND_EXTERNAL
)
1235 ScanLegacyVolume(Volume
, VolumeIndex
);
1237 } /* static VOID ScanLegacyExternal() */
1240 // pre-boot tool functions
1243 static VOID
StartTool(IN LOADER_ENTRY
*Entry
)
1245 BeginExternalScreen(Entry
->UseGraphicsMode
, Entry
->me
.Title
+ 6); // assumes "Start <title>" as assigned below
1246 StartEFIImage(Entry
->DevicePath
, Entry
->LoadOptions
, Basename(Entry
->LoaderPath
),
1247 Basename(Entry
->LoaderPath
), NULL
, TRUE
);
1248 FinishExternalScreen();
1249 } /* static VOID StartTool() */
1251 static LOADER_ENTRY
* AddToolEntry(IN CHAR16
*LoaderPath
, IN CHAR16
*LoaderTitle
, IN EG_IMAGE
*Image
,
1252 IN CHAR16 ShortcutLetter
, IN BOOLEAN UseGraphicsMode
)
1254 LOADER_ENTRY
*Entry
;
1256 Entry
= AllocateZeroPool(sizeof(LOADER_ENTRY
));
1258 Entry
->me
.Title
= PoolPrint(L
"Start %s", LoaderTitle
);
1259 Entry
->me
.Tag
= TAG_TOOL
;
1261 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1262 Entry
->me
.Image
= Image
;
1263 Entry
->LoaderPath
= StrDuplicate(LoaderPath
);
1264 Entry
->DevicePath
= FileDevicePath(SelfLoadedImage
->DeviceHandle
, Entry
->LoaderPath
);
1265 Entry
->UseGraphicsMode
= UseGraphicsMode
;
1267 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1269 } /* static LOADER_ENTRY * AddToolEntry() */
1272 // pre-boot driver functions
1275 static UINTN
ScanDriverDir(IN CHAR16
*Path
)
1278 REFIT_DIR_ITER DirIter
;
1280 EFI_FILE_INFO
*DirEntry
;
1281 CHAR16 FileName
[256];
1283 CleanUpPathNameSlashes(Path
);
1284 // look through contents of the directory
1285 DirIterOpen(SelfRootDir
, Path
, &DirIter
);
1286 while (DirIterNext(&DirIter
, 2, LOADER_MATCH_PATTERNS
, &DirEntry
)) {
1287 if (DirEntry
->FileName
[0] == '.')
1288 continue; // skip this
1290 SPrint(FileName
, 255, L
"%s\\%s", Path
, DirEntry
->FileName
);
1292 Status
= StartEFIImage(FileDevicePath(SelfLoadedImage
->DeviceHandle
, FileName
),
1293 L
"", DirEntry
->FileName
, DirEntry
->FileName
, NULL
, FALSE
);
1295 Status
= DirIterClose(&DirIter
);
1296 if (Status
!= EFI_NOT_FOUND
) {
1297 SPrint(FileName
, 255, L
"while scanning the %s directory", Path
);
1298 CheckError(Status
, FileName
);
1303 static EFI_STATUS
ConnectAllDriversToAllControllers(VOID
)
1306 UINTN AllHandleCount
;
1307 EFI_HANDLE
*AllHandleBuffer
;
1310 EFI_HANDLE
*HandleBuffer
;
1316 Status
= LibLocateHandle(AllHandles
,
1321 if (EFI_ERROR(Status
))
1324 for (Index
= 0; Index
< AllHandleCount
; Index
++) {
1326 // Scan the handle database
1328 Status
= LibScanHandleDatabase(NULL
,
1330 AllHandleBuffer
[Index
],
1335 if (EFI_ERROR (Status
))
1339 if (HandleType
[Index
] & EFI_HANDLE_TYPE_DRIVER_BINDING_HANDLE
)
1341 if (HandleType
[Index
] & EFI_HANDLE_TYPE_IMAGE_HANDLE
)
1346 for (HandleIndex
= 0; HandleIndex
< HandleCount
; HandleIndex
++) {
1347 if (HandleType
[HandleIndex
] & EFI_HANDLE_TYPE_PARENT_HANDLE
)
1352 if (HandleType
[Index
] & EFI_HANDLE_TYPE_DEVICE_HANDLE
) {
1353 Status
= refit_call4_wrapper(BS
->ConnectController
,
1354 AllHandleBuffer
[Index
],
1362 FreePool (HandleBuffer
);
1363 FreePool (HandleType
);
1367 FreePool (AllHandleBuffer
);
1369 } /* EFI_STATUS ConnectAllDriversToAllControllers() */
1371 // Load all EFI drivers from rEFInd's "drivers" subdirectory and from the
1372 // directories specified by the user in the "scan_driver_dirs" configuration
1374 static VOID
LoadDrivers(VOID
)
1376 CHAR16
*Directory
, *SelfDirectory
;
1377 UINTN i
= 0, Length
, NumFound
= 0;
1379 // load drivers from the subdirectories of rEFInd's home directory specified
1380 // in the DRIVER_DIRS constant.
1381 while ((Directory
= FindCommaDelimited(DRIVER_DIRS
, i
++)) != NULL
) {
1382 SelfDirectory
= StrDuplicate(SelfDirPath
);
1383 CleanUpPathNameSlashes(SelfDirectory
);
1384 MergeStrings(&SelfDirectory
, Directory
, L
'\\');
1385 NumFound
+= ScanDriverDir(SelfDirectory
);
1386 FreePool(Directory
);
1387 FreePool(SelfDirectory
);
1390 // Scan additional user-specified driver directories....
1392 while ((Directory
= FindCommaDelimited(GlobalConfig
.DriverDirs
, i
++)) != NULL
) {
1393 CleanUpPathNameSlashes(Directory
);
1394 Length
= StrLen(Directory
);
1396 NumFound
+= ScanDriverDir(Directory
);
1397 FreePool(Directory
);
1400 // connect all devices
1402 ConnectAllDriversToAllControllers();
1403 } /* static VOID LoadDrivers() */
1405 static VOID
ScanForBootloaders(VOID
) {
1410 // scan for loaders and tools, add them to the menu
1411 for (i
= 0; i
< NUM_SCAN_OPTIONS
; i
++) {
1412 switch(GlobalConfig
.ScanFor
[i
]) {
1417 ScanLegacyInternal();
1420 ScanLegacyExternal();
1423 ScanUserConfigured();
1437 // assign shortcut keys
1438 for (i
= 0; i
< MainMenu
.EntryCount
&& MainMenu
.Entries
[i
]->Row
== 0 && i
< 9; i
++)
1439 MainMenu
.Entries
[i
]->ShortcutDigit
= (CHAR16
)('1' + i
);
1441 // wait for user ACK when there were errors
1442 FinishTextScreen(FALSE
);
1443 } // static VOID ScanForBootloaders()
1445 // Add the second-row tags containing built-in and external tools (EFI shell,
1447 static VOID
ScanForTools(VOID
) {
1448 CHAR16
*FileName
= NULL
;
1449 REFIT_MENU_ENTRY
*TempMenuEntry
;
1452 for (i
= 0; i
< NUM_TOOLS
; i
++) {
1453 switch(GlobalConfig
.ShowTools
[i
]) {
1455 TempMenuEntry
= CopyMenuEntry(&MenuEntryShutdown
);
1456 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_SHUTDOWN
);
1457 AddMenuEntry(&MainMenu
, TempMenuEntry
);
1460 TempMenuEntry
= CopyMenuEntry(&MenuEntryReset
);
1461 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_RESET
);
1462 AddMenuEntry(&MainMenu
, TempMenuEntry
);
1465 TempMenuEntry
= CopyMenuEntry(&MenuEntryAbout
);
1466 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT
);
1467 AddMenuEntry(&MainMenu
, TempMenuEntry
);
1470 TempMenuEntry
= CopyMenuEntry(&MenuEntryExit
);
1471 TempMenuEntry
->Image
= BuiltinIcon(BUILTIN_ICON_FUNC_EXIT
);
1472 AddMenuEntry(&MainMenu
, TempMenuEntry
);
1476 while ((FileName
= FindCommaDelimited(SHELL_NAMES
, j
++)) != NULL
) {
1477 if (FileExists(SelfRootDir
, FileName
)) {
1478 AddToolEntry(FileName
, L
"EFI Shell", BuiltinIcon(BUILTIN_ICON_TOOL_SHELL
), 'S', FALSE
);
1483 MergeStrings(&FileName
, L
"\\efi\\tools\\gptsync.efi", 0);
1484 if (FileExists(SelfRootDir
, FileName
)) {
1485 AddToolEntry(FileName
, L
"Make Hybrid MBR", BuiltinIcon(BUILTIN_ICON_TOOL_PART
), 'P', FALSE
);
1489 if (FileName
!= NULL
) {
1494 } // static VOID ScanForTools
1496 // Rescan for boot loaders
1497 VOID
RescanAll(VOID
) {
1504 egDisplayMessage(L
"Scanning for new boot loaders; please wait....", &BGColor
);
1505 FreeList((VOID
***) &(MainMenu
.Entries
), &MainMenu
.EntryCount
);
1506 MainMenu
.Entries
= NULL
;
1507 MainMenu
.EntryCount
= 0;
1509 ConnectAllDriversToAllControllers();
1510 ScanForBootloaders();
1513 } // VOID RescanAll()
1520 efi_main (IN EFI_HANDLE ImageHandle
, IN EFI_SYSTEM_TABLE
*SystemTable
)
1523 BOOLEAN MainLoopRunning
= TRUE
;
1524 REFIT_MENU_ENTRY
*ChosenEntry
;
1529 InitializeLib(ImageHandle
, SystemTable
);
1531 Status
= InitRefitLib(ImageHandle
);
1532 if (EFI_ERROR(Status
))
1535 // read configuration
1536 CopyMem(GlobalConfig
.ScanFor
, "ieo ", NUM_SCAN_OPTIONS
);
1538 MainMenu
.TimeoutSeconds
= GlobalConfig
.Timeout
;
1540 // disable EFI watchdog timer
1541 refit_call4_wrapper(BS
->SetWatchdogTimer
, 0x0000, 0x0000, 0x0000, NULL
);
1543 // further bootstrap (now with config available)
1546 ScanForBootloaders();
1549 Selection
= StrDuplicate(GlobalConfig
.DefaultSelection
);
1550 while (MainLoopRunning
) {
1551 MenuExit
= RunMainMenu(&MainMenu
, Selection
, &ChosenEntry
);
1553 // The Escape key triggers a re-scan operation....
1554 if (MenuExit
== MENU_EXIT_ESCAPE
) {
1559 switch (ChosenEntry
->Tag
) {
1561 case TAG_REBOOT
: // Reboot
1563 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);
1564 MainLoopRunning
= FALSE
; // just in case we get this far
1567 case TAG_SHUTDOWN
: // Shut Down
1569 refit_call4_wrapper(RT
->ResetSystem
, EfiResetShutdown
, EFI_SUCCESS
, 0, NULL
);
1570 MainLoopRunning
= FALSE
; // just in case we get this far
1573 case TAG_ABOUT
: // About rEFInd
1577 case TAG_LOADER
: // Boot OS via .EFI loader
1578 StartLoader((LOADER_ENTRY
*)ChosenEntry
);
1581 case TAG_LEGACY
: // Boot legacy OS
1582 StartLegacy((LEGACY_ENTRY
*)ChosenEntry
);
1585 case TAG_TOOL
: // Start a EFI tool
1586 StartTool((LOADER_ENTRY
*)ChosenEntry
);
1589 case TAG_EXIT
: // Terminate rEFInd
1590 BeginTextScreen(L
" ");
1595 FreePool(Selection
);
1596 Selection
= StrDuplicate(ChosenEntry
->Title
);
1599 // If we end up here, things have gone wrong. Try to reboot, and if that
1600 // fails, go into an endless loop.
1601 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);