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"
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"
62 #define SHELL_NAMES L"\\EFI\\tools\\shell.efi,\\shellia32.efi"
64 #define SHELL_NAMES L"\\EFI\\tools\\shell.efi"
67 static REFIT_MENU_ENTRY MenuEntryAbout
= { L
"About rEFInd", TAG_ABOUT
, 1, 0, 'A', NULL
, NULL
, NULL
};
68 static REFIT_MENU_ENTRY MenuEntryReset
= { L
"Reboot Computer", TAG_REBOOT
, 1, 0, 'R', NULL
, NULL
, NULL
};
69 static REFIT_MENU_ENTRY MenuEntryShutdown
= { L
"Shut Down Computer", TAG_SHUTDOWN
, 1, 0, 'U', NULL
, NULL
, NULL
};
70 static REFIT_MENU_ENTRY MenuEntryReturn
= { L
"Return to Main Menu", TAG_RETURN
, 0, 0, 0, NULL
, NULL
, NULL
};
71 static REFIT_MENU_ENTRY MenuEntryExit
= { L
"Exit rEFInd", TAG_EXIT
, 1, 0, 0, NULL
, NULL
, NULL
};
73 static REFIT_MENU_SCREEN MainMenu
= { L
"Main Menu", NULL
, 0, NULL
, 0, NULL
, 0, L
"Automatic boot" };
74 static REFIT_MENU_SCREEN AboutMenu
= { L
"About", NULL
, 0, NULL
, 0, NULL
, 0, NULL
};
76 REFIT_CONFIG GlobalConfig
= { FALSE
, 20, 0, 0, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
77 {TAG_SHELL
, TAG_ABOUT
, TAG_SHUTDOWN
, TAG_REBOOT
, 0, 0, 0, 0, 0 }};
83 static VOID
AboutrEFInd(VOID
)
85 if (AboutMenu
.EntryCount
== 0) {
86 AboutMenu
.TitleImage
= BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT
);
87 AddMenuInfoLine(&AboutMenu
, L
"rEFInd Version 0.2.6.2");
88 AddMenuInfoLine(&AboutMenu
, L
"");
89 AddMenuInfoLine(&AboutMenu
, L
"Copyright (c) 2006-2010 Christoph Pfisterer");
90 AddMenuInfoLine(&AboutMenu
, L
"Copyright (c) 2012 Roderick W. Smith");
91 AddMenuInfoLine(&AboutMenu
, L
"Portions Copyright (c) Intel Corporation and others");
92 AddMenuInfoLine(&AboutMenu
, L
"Distributed under the terms of the GNU GPLv3 license");
93 AddMenuInfoLine(&AboutMenu
, L
"");
94 AddMenuInfoLine(&AboutMenu
, L
"Running on:");
95 AddMenuInfoLine(&AboutMenu
, PoolPrint(L
" EFI Revision %d.%02d",
96 ST
->Hdr
.Revision
>> 16, ST
->Hdr
.Revision
& ((1 << 16) - 1)));
98 AddMenuInfoLine(&AboutMenu
, L
" Platform: x86 (32 bit)");
100 AddMenuInfoLine(&AboutMenu
, L
" Platform: x86_64 (64 bit)");
102 AddMenuInfoLine(&AboutMenu
, L
" Platform: unknown");
104 AddMenuInfoLine(&AboutMenu
, PoolPrint(L
" Firmware: %s %d.%02d",
105 ST
->FirmwareVendor
, ST
->FirmwareRevision
>> 16, ST
->FirmwareRevision
& ((1 << 16) - 1)));
106 AddMenuInfoLine(&AboutMenu
, PoolPrint(L
" Screen Output: %s", egScreenDescription()));
107 AddMenuInfoLine(&AboutMenu
, L
"");
108 AddMenuInfoLine(&AboutMenu
, L
"For more information, see the rEFInd Web site:");
109 AddMenuInfoLine(&AboutMenu
, L
"http://www.rodsbooks.com/refind/");
110 AddMenuEntry(&AboutMenu
, &MenuEntryReturn
);
113 RunMenu(&AboutMenu
, NULL
);
114 } /* VOID AboutrEFInd() */
116 static EFI_STATUS
StartEFIImageList(IN EFI_DEVICE_PATH
**DevicePaths
,
117 IN CHAR16
*LoadOptions
, IN CHAR16
*LoadOptionsPrefix
,
118 IN CHAR16
*ImageTitle
,
119 OUT UINTN
*ErrorInStep
,
122 EFI_STATUS Status
, ReturnStatus
;
123 EFI_HANDLE ChildImageHandle
;
124 EFI_LOADED_IMAGE
*ChildLoadedImage
;
125 UINTN DevicePathIndex
;
126 CHAR16 ErrorInfo
[256];
127 CHAR16
*FullLoadOptions
= NULL
;
130 Print(L
"Starting %s\n", ImageTitle
);
131 if (ErrorInStep
!= NULL
)
134 // load the image into memory
135 ReturnStatus
= Status
= EFI_NOT_FOUND
; // in case the list is empty
136 for (DevicePathIndex
= 0; DevicePaths
[DevicePathIndex
] != NULL
; DevicePathIndex
++) {
137 ReturnStatus
= Status
= refit_call6_wrapper(BS
->LoadImage
, FALSE
, SelfImageHandle
, DevicePaths
[DevicePathIndex
], NULL
, 0, &ChildImageHandle
);
138 if (ReturnStatus
!= EFI_NOT_FOUND
)
141 SPrint(ErrorInfo
, 255, L
"while loading %s", ImageTitle
);
142 if (CheckError(Status
, ErrorInfo
)) {
143 if (ErrorInStep
!= NULL
)
149 if (LoadOptions
!= NULL
) {
150 ReturnStatus
= Status
= refit_call3_wrapper(BS
->HandleProtocol
, ChildImageHandle
, &LoadedImageProtocol
, (VOID
**) &ChildLoadedImage
);
151 if (CheckError(Status
, L
"while getting a LoadedImageProtocol handle")) {
152 if (ErrorInStep
!= NULL
)
157 if (LoadOptionsPrefix
!= NULL
) {
158 FullLoadOptions
= PoolPrint(L
"%s %s ", LoadOptionsPrefix
, LoadOptions
);
159 // NOTE: That last space is also added by the EFI shell and seems to be significant
160 // when passing options to Apple's boot.efi...
161 LoadOptions
= FullLoadOptions
;
163 // NOTE: We also include the terminating null in the length for safety.
164 ChildLoadedImage
->LoadOptions
= (VOID
*)LoadOptions
;
165 ChildLoadedImage
->LoadOptionsSize
= ((UINT32
)StrLen(LoadOptions
) + 1) * sizeof(CHAR16
);
167 Print(L
"Using load options '%s'\n", LoadOptions
);
170 // close open file handles
173 // turn control over to the image
174 // TODO: (optionally) re-enable the EFI watchdog timer!
175 ReturnStatus
= Status
= refit_call3_wrapper(BS
->StartImage
, ChildImageHandle
, NULL
, NULL
);
176 // control returns here when the child image calls Exit()
177 SPrint(ErrorInfo
, 255, L
"returned from %s", ImageTitle
);
178 if (CheckError(Status
, ErrorInfo
)) {
179 if (ErrorInStep
!= NULL
)
183 // re-open file handles
187 // unload the image, we don't care if it works or not...
188 Status
= refit_call1_wrapper(BS
->UnloadImage
, ChildImageHandle
);
190 if (FullLoadOptions
!= NULL
)
191 FreePool(FullLoadOptions
);
193 } /* static EFI_STATUS StartEFIImageList() */
195 static EFI_STATUS
StartEFIImage(IN EFI_DEVICE_PATH
*DevicePath
,
196 IN CHAR16
*LoadOptions
, IN CHAR16
*LoadOptionsPrefix
,
197 IN CHAR16
*ImageTitle
,
198 OUT UINTN
*ErrorInStep
,
201 EFI_DEVICE_PATH
*DevicePaths
[2];
203 DevicePaths
[0] = DevicePath
;
204 DevicePaths
[1] = NULL
;
205 return StartEFIImageList(DevicePaths
, LoadOptions
, LoadOptionsPrefix
, ImageTitle
, ErrorInStep
, Verbose
);
206 } /* static EFI_STATUS StartEFIImage() */
209 // EFI OS loader functions
212 static VOID
StartLoader(IN LOADER_ENTRY
*Entry
)
214 UINTN ErrorInStep
= 0;
216 BeginExternalScreen(Entry
->UseGraphicsMode
, L
"Booting OS");
217 StartEFIImage(Entry
->DevicePath
, Entry
->LoadOptions
,
218 Basename(Entry
->LoaderPath
), Basename(Entry
->LoaderPath
), &ErrorInStep
, TRUE
);
219 FinishExternalScreen();
222 // Locate an initrd or initramfs file that matches the kernel specified by LoaderPath.
223 // The matching file has a name that begins with "init" and includes the same version
224 // number string as is found in LoaderPath -- but not a longer version number string.
225 // For instance, if LoaderPath is \EFI\kernels\bzImage-3.3.0.efi, and if \EFI\kernels
226 // has a file called initramfs-3.3.0.img, this function will return the string
227 // '\EFI\kernels\initramfs-3.3.0.img'. If the directory ALSO contains the file
228 // initramfs-3.3.0-rc7.img or initramfs-13.3.0.img, those files will NOT match;
229 // however, initmine-3.3.0.img might match. (FindInitrd() returns the first match it
230 // finds). Thus, care should be taken to avoid placing duplicate matching files in
231 // the kernel's directory.
232 // If no matching init file can be found, returns NULL.
233 static CHAR16
* FindInitrd(IN CHAR16
*LoaderPath
, IN REFIT_VOLUME
*Volume
) {
234 CHAR16
*InitrdName
= NULL
, *FileName
, *KernelVersion
, *InitrdVersion
, *Path
;
235 REFIT_DIR_ITER DirIter
;
236 EFI_FILE_INFO
*DirEntry
;
238 FileName
= Basename(LoaderPath
);
239 KernelVersion
= FindNumbers(FileName
);
240 Path
= FindPath(LoaderPath
);
242 // Add trailing backslash for root directory; necessary on some systems, but must
243 // NOT be added to all directories, since on other systems, a trailing backslash on
244 // anything but the root directory causes them to flake out!
245 if (StrLen(Path
) == 0) {
246 MergeStrings(&Path
, L
"\\", 0);
248 DirIterOpen(Volume
->RootDir
, Path
, &DirIter
);
249 // Now add a trailing backslash if it was NOT added earlier, for consistency in
250 // building the InitrdName later....
251 if ((StrLen(Path
) > 0) && (Path
[StrLen(Path
) - 1] != L
'\\'))
252 MergeStrings(&Path
, L
"\\", 0);
253 while ((DirIterNext(&DirIter
, 2, L
"init*", &DirEntry
)) && (InitrdName
== NULL
)) {
254 InitrdVersion
= FindNumbers(DirEntry
->FileName
);
255 if (KernelVersion
!= NULL
) {
256 if (StriCmp(InitrdVersion
, KernelVersion
) == 0)
257 InitrdName
= PoolPrint(L
"%s%s", Path
, DirEntry
->FileName
);
259 if (InitrdVersion
== NULL
)
260 InitrdName
= PoolPrint(L
"%s%s", Path
, DirEntry
->FileName
);
262 if (InitrdVersion
!= NULL
)
263 FreePool(InitrdVersion
);
265 DirIterClose(&DirIter
);
267 // Note: Don't FreePool(FileName), since Basename returns a pointer WITHIN the string it's passed.
268 FreePool(KernelVersion
);
271 } // static CHAR16 * FindInitrd()
273 LOADER_ENTRY
* AddPreparedLoaderEntry(LOADER_ENTRY
*Entry
) {
274 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
277 } // LOADER_ENTRY * AddPreparedLoaderEntry()
279 // Creates a new LOADER_ENTRY data structure and populates it with
280 // default values from the specified Entry, or NULL values if Entry
281 // is unspecified (NULL).
282 // Returns a pointer to the new data structure, or NULL if it
283 // couldn't be allocated
284 LOADER_ENTRY
*InitializeLoaderEntry(IN LOADER_ENTRY
*Entry
) {
285 LOADER_ENTRY
*NewEntry
= NULL
;
287 NewEntry
= AllocateZeroPool(sizeof(LOADER_ENTRY
));
288 if (NewEntry
!= NULL
) {
289 NewEntry
->me
.Title
= NULL
;
290 NewEntry
->me
.Tag
= TAG_LOADER
;
291 NewEntry
->Enabled
= TRUE
;
292 NewEntry
->UseGraphicsMode
= FALSE
;
293 NewEntry
->OSType
= 0;
295 NewEntry
->LoaderPath
= StrDuplicate(Entry
->LoaderPath
);
296 NewEntry
->VolName
= StrDuplicate(Entry
->VolName
);
297 NewEntry
->DevicePath
= Entry
->DevicePath
;
298 NewEntry
->UseGraphicsMode
= Entry
->UseGraphicsMode
;
299 NewEntry
->LoadOptions
= StrDuplicate(Entry
->LoadOptions
);
300 NewEntry
->InitrdPath
= StrDuplicate(Entry
->InitrdPath
);
304 } // LOADER_ENTRY *InitializeLoaderEntry()
306 // Prepare a REFIT_MENU_SCREEN data structure for a subscreen entry. This sets up
307 // the default entry that launches the boot loader using the same options as the
308 // main Entry does. Subsequent options can be added by the calling function.
309 // If a subscreen already exists in the Entry that's passed to this function,
310 // it's left unchanged and a pointer to it is returned.
311 // Returns a pointer to the new subscreen data structure, or NULL if there
312 // were problems allocating memory.
313 REFIT_MENU_SCREEN
*InitializeSubScreen(IN LOADER_ENTRY
*Entry
) {
314 CHAR16
*FileName
, *Temp
;
315 REFIT_MENU_SCREEN
*SubScreen
= NULL
;
316 LOADER_ENTRY
*SubEntry
;
318 FileName
= Basename(Entry
->LoaderPath
);
319 if (Entry
->me
.SubScreen
== NULL
) { // No subscreen yet; initialize default entry....
320 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
321 if (SubScreen
!= NULL
) {
322 SubScreen
->Title
= PoolPrint(L
"Boot Options for %s on %s", (Entry
->Title
!= NULL
) ? Entry
->Title
: FileName
, Entry
->VolName
);
323 SubScreen
->TitleImage
= Entry
->me
.Image
;
325 SubEntry
= InitializeLoaderEntry(Entry
);
326 if (SubEntry
!= NULL
) {
327 SubEntry
->me
.Title
= L
"Boot using default options";
328 if ((SubEntry
->InitrdPath
!= NULL
) && (StrLen(SubEntry
->InitrdPath
) > 0) && (!StriSubCmp(L
"initrd", SubEntry
->LoadOptions
))) {
329 Temp
= PoolPrint(L
"initrd=%s", SubEntry
->InitrdPath
);
330 MergeStrings(&SubEntry
->LoadOptions
, Temp
, L
' ');
333 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
334 } // if (SubEntry != NULL)
335 } // if (SubScreen != NULL)
336 } else { // existing subscreen; less initialization, and just add new entry later....
337 SubScreen
= Entry
->me
.SubScreen
;
340 } // REFIT_MENU_SCREEN *InitializeSubScreen()
342 VOID
GenerateSubScreen(LOADER_ENTRY
*Entry
, IN REFIT_VOLUME
*Volume
) {
343 REFIT_MENU_SCREEN
*SubScreen
;
344 LOADER_ENTRY
*SubEntry
;
345 CHAR16
*FileName
, *InitrdOption
= NULL
, *Temp
;
346 CHAR16 DiagsFileName
[256];
351 FileName
= Basename(Entry
->LoaderPath
);
352 // create the submenu
353 if (StrLen(Entry
->Title
) == 0) {
354 FreePool(Entry
->Title
);
357 SubScreen
= InitializeSubScreen(Entry
);
359 // loader-specific submenu entries
360 if (Entry
->OSType
== 'M') { // entries for Mac OS X
362 SubEntry
= InitializeLoaderEntry(Entry
);
363 if (SubEntry
!= NULL
) {
364 SubEntry
->me
.Title
= L
"Boot Mac OS X with a 64-bit kernel";
365 SubEntry
->LoadOptions
= L
"arch=x86_64";
366 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
369 SubEntry
= InitializeLoaderEntry(Entry
);
370 if (SubEntry
!= NULL
) {
371 SubEntry
->me
.Title
= L
"Boot Mac OS X with a 32-bit kernel";
372 SubEntry
->LoadOptions
= L
"arch=i386";
373 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
377 if (!(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_SINGLEUSER
)) {
378 SubEntry
= InitializeLoaderEntry(Entry
);
379 if (SubEntry
!= NULL
) {
380 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode";
381 SubEntry
->UseGraphicsMode
= FALSE
;
382 SubEntry
->LoadOptions
= L
"-v";
383 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
387 SubEntry
= InitializeLoaderEntry(Entry
);
388 if (SubEntry
!= NULL
) {
389 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode (64-bit)";
390 SubEntry
->UseGraphicsMode
= FALSE
;
391 SubEntry
->LoadOptions
= L
"-v arch=x86_64";
392 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
395 SubEntry
= InitializeLoaderEntry(Entry
);
396 if (SubEntry
!= NULL
) {
397 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode (32-bit)";
398 SubEntry
->UseGraphicsMode
= FALSE
;
399 SubEntry
->LoadOptions
= L
"-v arch=i386";
400 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
404 SubEntry
= InitializeLoaderEntry(Entry
);
405 if (SubEntry
!= NULL
) {
406 SubEntry
->me
.Title
= L
"Boot Mac OS X in single user mode";
407 SubEntry
->UseGraphicsMode
= FALSE
;
408 SubEntry
->LoadOptions
= L
"-v -s";
409 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
413 // check for Apple hardware diagnostics
414 StrCpy(DiagsFileName
, L
"\\System\\Library\\CoreServices\\.diagnostics\\diags.efi");
415 if (FileExists(Volume
->RootDir
, DiagsFileName
) && !(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_HWTEST
)) {
416 SubEntry
= InitializeLoaderEntry(Entry
);
417 if (SubEntry
!= NULL
) {
418 SubEntry
->me
.Title
= L
"Run Apple Hardware Test";
419 FreePool(SubEntry
->LoaderPath
);
420 SubEntry
->LoaderPath
= StrDuplicate(DiagsFileName
);
421 SubEntry
->DevicePath
= FileDevicePath(Volume
->DeviceHandle
, SubEntry
->LoaderPath
);
422 SubEntry
->UseGraphicsMode
= TRUE
;
423 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
425 } // if diagnostics entry found
427 } else if (Entry
->OSType
== 'L') { // entries for Linux kernels with EFI stub loaders
428 File
= ReadLinuxOptionsFile(Entry
->LoaderPath
, Volume
);
430 if ((Temp
= FindInitrd(Entry
->LoaderPath
, Volume
)) != NULL
)
431 InitrdOption
= PoolPrint(L
"initrd=%s", Temp
);
432 TokenCount
= ReadTokenLine(File
, &TokenList
); // read and discard first entry, since it's
433 FreeTokenLine(&TokenList
, &TokenCount
); // set up by InitializeSubScreen(), earlier....
434 while ((TokenCount
= ReadTokenLine(File
, &TokenList
)) > 1) {
435 SubEntry
= InitializeLoaderEntry(Entry
);
436 SubEntry
->me
.Title
= StrDuplicate(TokenList
[0]);
437 if (SubEntry
->LoadOptions
!= NULL
)
438 FreePool(SubEntry
->LoadOptions
);
439 SubEntry
->LoadOptions
= StrDuplicate(TokenList
[1]);
440 MergeStrings(&SubEntry
->LoadOptions
, InitrdOption
, L
' ');
441 FreeTokenLine(&TokenList
, &TokenCount
);
442 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
445 FreePool(InitrdOption
);
449 } // if Linux options file exists
451 } else if (Entry
->OSType
== 'E') { // entries for ELILO
452 SubEntry
= InitializeLoaderEntry(Entry
);
453 if (SubEntry
!= NULL
) {
454 SubEntry
->me
.Title
= PoolPrint(L
"Run %s in interactive mode", FileName
);
455 SubEntry
->LoadOptions
= L
"-p";
456 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
459 SubEntry
= InitializeLoaderEntry(Entry
);
460 if (SubEntry
!= NULL
) {
461 SubEntry
->me
.Title
= L
"Boot Linux for a 17\" iMac or a 15\" MacBook Pro (*)";
462 SubEntry
->UseGraphicsMode
= TRUE
;
463 SubEntry
->LoadOptions
= L
"-d 0 i17";
464 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
467 SubEntry
= InitializeLoaderEntry(Entry
);
468 if (SubEntry
!= NULL
) {
469 SubEntry
->me
.Title
= L
"Boot Linux for a 20\" iMac (*)";
470 SubEntry
->UseGraphicsMode
= TRUE
;
471 SubEntry
->LoadOptions
= L
"-d 0 i20";
472 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
475 SubEntry
= InitializeLoaderEntry(Entry
);
476 if (SubEntry
!= NULL
) {
477 SubEntry
->me
.Title
= L
"Boot Linux for a Mac Mini (*)";
478 SubEntry
->UseGraphicsMode
= TRUE
;
479 SubEntry
->LoadOptions
= L
"-d 0 mini";
480 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
483 AddMenuInfoLine(SubScreen
, L
"NOTE: This is an example. Entries");
484 AddMenuInfoLine(SubScreen
, L
"marked with (*) may not work.");
486 } else if (Entry
->OSType
== 'X') { // entries for xom.efi
487 // by default, skip the built-in selection and boot from hard disk only
488 Entry
->LoadOptions
= L
"-s -h";
490 SubEntry
= InitializeLoaderEntry(Entry
);
491 if (SubEntry
!= NULL
) {
492 SubEntry
->me
.Title
= L
"Boot Windows from Hard Disk";
493 SubEntry
->LoadOptions
= L
"-s -h";
494 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
497 SubEntry
= InitializeLoaderEntry(Entry
);
498 if (SubEntry
!= NULL
) {
499 SubEntry
->me
.Title
= L
"Boot Windows from CD-ROM";
500 SubEntry
->LoadOptions
= L
"-s -c";
501 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
504 SubEntry
= InitializeLoaderEntry(Entry
);
505 if (SubEntry
!= NULL
) {
506 SubEntry
->me
.Title
= PoolPrint(L
"Run %s in text mode", FileName
);
507 SubEntry
->UseGraphicsMode
= FALSE
;
508 SubEntry
->LoadOptions
= L
"-v";
509 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
511 } // entries for xom.efi
512 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
513 Entry
->me
.SubScreen
= SubScreen
;
514 } // VOID GenerateSubScreen()
516 // Returns options for a Linux kernel. Reads them from an options file in the
517 // kernel's directory; and if present, adds an initrd= option for an initial
518 // RAM disk file with the same version number as the kernel file.
519 static CHAR16
* GetMainLinuxOptions(IN CHAR16
* LoaderPath
, IN REFIT_VOLUME
*Volume
) {
520 CHAR16
*Options
= NULL
, *InitrdName
, *InitrdOption
= NULL
;
522 Options
= GetFirstOptionsFromFile(LoaderPath
, Volume
);
523 InitrdName
= FindInitrd(LoaderPath
, Volume
);
524 if (InitrdName
!= NULL
)
525 InitrdOption
= PoolPrint(L
"initrd=%s", InitrdName
);
526 MergeStrings(&Options
, InitrdOption
, ' ');
527 if (InitrdOption
!= NULL
)
528 FreePool(InitrdOption
);
529 if (InitrdName
!= NULL
)
530 FreePool(InitrdName
);
532 } // static CHAR16 * GetMainLinuxOptions()
534 // Sets a few defaults for a loader entry -- mainly the icon, but also the OS type
535 // code and shortcut letter. For Linux EFI stub loaders, also sets kernel options
536 // that will (with luck) work fairly automatically.
537 VOID
SetLoaderDefaults(LOADER_ENTRY
*Entry
, CHAR16
*LoaderPath
, IN REFIT_VOLUME
*Volume
) {
538 CHAR16 IconFileName
[256];
539 CHAR16
*FileName
, *PathOnly
, *OSIconName
= NULL
, *Temp
;
540 CHAR16 ShortcutLetter
= 0;
542 FileName
= Basename(LoaderPath
);
543 PathOnly
= FindPath(LoaderPath
);
545 // locate a custom icon for the loader
546 StrCpy(IconFileName
, LoaderPath
);
547 ReplaceExtension(IconFileName
, L
".icns");
548 if (FileExists(Volume
->RootDir
, IconFileName
)) {
549 Entry
->me
.Image
= LoadIcns(Volume
->RootDir
, IconFileName
, 128);
550 } else if ((StrLen(PathOnly
) == 0) && (Volume
->VolIconImage
!= NULL
)) {
551 Entry
->me
.Image
= Volume
->VolIconImage
;
552 } // icon matched to loader or volume
554 Temp
= FindLastDirName(LoaderPath
);
555 MergeStrings(&OSIconName
, Temp
, L
',');
557 if (OSIconName
!= NULL
) {
558 ShortcutLetter
= OSIconName
[0];
561 // detect specific loaders
562 if (StriSubCmp(L
"bzImage", LoaderPath
) || StriSubCmp(L
"vmlinuz", LoaderPath
)) {
563 MergeStrings(&OSIconName
, L
"linux", L
',');
565 if (ShortcutLetter
== 0)
566 ShortcutLetter
= 'L';
567 Entry
->LoadOptions
= GetMainLinuxOptions(LoaderPath
, Volume
);
568 } else if (StriSubCmp(L
"refit", LoaderPath
)) {
569 MergeStrings(&OSIconName
, L
"refit", L
',');
571 ShortcutLetter
= 'R';
572 } else if (StriCmp(LoaderPath
, MACOSX_LOADER_PATH
) == 0) {
573 if (Volume
->VolIconImage
!= NULL
) { // custom icon file found
574 Entry
->me
.Image
= Volume
->VolIconImage
;
576 MergeStrings(&OSIconName
, L
"mac", L
',');
577 Entry
->UseGraphicsMode
= TRUE
;
579 ShortcutLetter
= 'M';
580 } else if (StriCmp(FileName
, L
"diags.efi") == 0) {
581 MergeStrings(&OSIconName
, L
"hwtest", L
',');
582 } else if (StriCmp(FileName
, L
"e.efi") == 0 || StriCmp(FileName
, L
"elilo.efi") == 0) {
583 MergeStrings(&OSIconName
, L
"elilo,linux", L
',');
585 if (ShortcutLetter
== 0)
586 ShortcutLetter
= 'L';
587 } else if (StriCmp(FileName
, L
"cdboot.efi") == 0 ||
588 StriCmp(FileName
, L
"bootmgr.efi") == 0 ||
589 StriCmp(FileName
, L
"Bootmgfw.efi") == 0) {
590 MergeStrings(&OSIconName
, L
"win", L
',');
592 ShortcutLetter
= 'W';
593 } else if (StriCmp(FileName
, L
"xom.efi") == 0) {
594 MergeStrings(&OSIconName
, L
"xom,win", L
',');
595 Entry
->UseGraphicsMode
= TRUE
;
597 ShortcutLetter
= 'W';
600 if ((ShortcutLetter
>= 'a') && (ShortcutLetter
<= 'z'))
601 ShortcutLetter
= ShortcutLetter
- 'a' + 'A'; // convert lowercase to uppercase
602 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
603 if (Entry
->me
.Image
== NULL
)
604 Entry
->me
.Image
= LoadOSIcon(OSIconName
, L
"unknown", FALSE
);
605 if (PathOnly
!= NULL
)
607 } // VOID SetLoaderDefaults()
609 // Add a specified EFI boot loader to the list, using automatic settings
610 // for icons, options, etc.
611 LOADER_ENTRY
* AddLoaderEntry(IN CHAR16
*LoaderPath
, IN CHAR16
*LoaderTitle
, IN REFIT_VOLUME
*Volume
) {
614 CleanUpPathNameSlashes(LoaderPath
);
615 Entry
= InitializeLoaderEntry(NULL
);
617 Entry
->Title
= StrDuplicate(LoaderTitle
);
618 Entry
->me
.Title
= PoolPrint(L
"Boot %s from %s", (LoaderTitle
!= NULL
) ? LoaderTitle
: LoaderPath
+ 1, Volume
->VolName
);
620 Entry
->me
.BadgeImage
= Volume
->VolBadgeImage
;
621 Entry
->LoaderPath
= StrDuplicate(LoaderPath
);
622 Entry
->VolName
= Volume
->VolName
;
623 Entry
->DevicePath
= FileDevicePath(Volume
->DeviceHandle
, Entry
->LoaderPath
);
624 SetLoaderDefaults(Entry
, LoaderPath
, Volume
);
625 GenerateSubScreen(Entry
, Volume
);
626 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
630 } // LOADER_ENTRY * AddLoaderEntry()
632 // Scan an individual directory for EFI boot loader files and, if found,
633 // add them to the list.
634 static VOID
ScanLoaderDir(IN REFIT_VOLUME
*Volume
, IN CHAR16
*Path
)
637 REFIT_DIR_ITER DirIter
;
638 EFI_FILE_INFO
*DirEntry
;
639 CHAR16 FileName
[256], *SelfPath
;
642 // Skip past leading slashes, which are sometimes (but not always) included
643 // in SelfDirPath, to get a path that's known to never include this feature.
644 while ((SelfDirPath
!= NULL
) && (SelfDirPath
[i
] == L
'\\')) {
647 SelfPath
= &SelfDirPath
[i
]; // NOTE: *DO NOT* call FreePool() on SelfPath!!!
649 if (!SelfPath
|| !Path
|| ((StriCmp(Path
, SelfPath
) == 0) && Volume
!= SelfVolume
) ||
650 (StriCmp(Path
, SelfPath
) != 0)) {
651 // look through contents of the directory
652 DirIterOpen(Volume
->RootDir
, Path
, &DirIter
);
653 while (DirIterNext(&DirIter
, 2, L
"*.efi", &DirEntry
)) {
654 if (DirEntry
->FileName
[0] == '.' ||
655 StriCmp(DirEntry
->FileName
, L
"TextMode.efi") == 0 ||
656 StriCmp(DirEntry
->FileName
, L
"ebounce.efi") == 0 ||
657 StriCmp(DirEntry
->FileName
, L
"GraphicsConsole.efi") == 0 ||
658 StriSubCmp(L
"shell", DirEntry
->FileName
))
659 continue; // skip this
662 SPrint(FileName
, 255, L
"\\%s\\%s", Path
, DirEntry
->FileName
);
664 SPrint(FileName
, 255, L
"\\%s", DirEntry
->FileName
);
665 AddLoaderEntry(FileName
, NULL
, Volume
);
667 Status
= DirIterClose(&DirIter
);
668 if (Status
!= EFI_NOT_FOUND
) {
670 SPrint(FileName
, 255, L
"while scanning the %s directory", Path
);
672 StrCpy(FileName
, L
"while scanning the root directory");
673 CheckError(Status
, FileName
);
674 } // if (Status != EFI_NOT_FOUND)
675 } // if not scanning our own directory
676 } /* static VOID ScanLoaderDir() */
678 static VOID
ScanEfiFiles(REFIT_VOLUME
*Volume
) {
680 REFIT_DIR_ITER EfiDirIter
;
681 EFI_FILE_INFO
*EfiDirEntry
;
682 CHAR16 FileName
[256], *Directory
;
685 if ((Volume
->RootDir
!= NULL
) && (Volume
->VolName
!= NULL
)) {
686 // check for Mac OS X boot loader
687 StrCpy(FileName
, MACOSX_LOADER_PATH
);
688 if (FileExists(Volume
->RootDir
, FileName
)) {
689 AddLoaderEntry(FileName
, L
"Mac OS X", Volume
);
693 StrCpy(FileName
, L
"\\System\\Library\\CoreServices\\xom.efi");
694 if (FileExists(Volume
->RootDir
, FileName
)) {
695 AddLoaderEntry(FileName
, L
"Windows XP (XoM)", Volume
);
698 // check for Microsoft boot loader/menu
699 StrCpy(FileName
, L
"\\EFI\\Microsoft\\Boot\\Bootmgfw.efi");
700 if (FileExists(Volume
->RootDir
, FileName
)) {
701 AddLoaderEntry(FileName
, L
"Microsoft EFI boot", Volume
);
704 // scan the root directory for EFI executables
705 ScanLoaderDir(Volume
, NULL
);
707 // scan subdirectories of the EFI directory (as per the standard)
708 DirIterOpen(Volume
->RootDir
, L
"EFI", &EfiDirIter
);
709 while (DirIterNext(&EfiDirIter
, 1, NULL
, &EfiDirEntry
)) {
710 if (StriCmp(EfiDirEntry
->FileName
, L
"tools") == 0 || EfiDirEntry
->FileName
[0] == '.')
711 continue; // skip this, doesn't contain boot loaders
712 SPrint(FileName
, 255, L
"EFI\\%s", EfiDirEntry
->FileName
);
713 ScanLoaderDir(Volume
, FileName
);
715 Status
= DirIterClose(&EfiDirIter
);
716 if (Status
!= EFI_NOT_FOUND
)
717 CheckError(Status
, L
"while scanning the EFI directory");
719 // Scan user-specified (or additional default) directories....
721 while ((Directory
= FindCommaDelimited(GlobalConfig
.AlsoScan
, i
++)) != NULL
) {
722 Length
= StrLen(Directory
);
723 // Some EFI implementations won't read a directory if the path ends in
724 // a backslash, so eliminate this character, if it's present....
725 while ((Length
> 0) && (Directory
[Length
- 1] == L
'\\')) {
726 Directory
[--Length
] = 0;
729 ScanLoaderDir(Volume
, Directory
);
733 } // static VOID ScanEfiFiles()
735 // Scan internal disks for valid EFI boot loaders....
736 static VOID
ScanInternal(VOID
) {
739 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
740 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_INTERNAL
) {
741 ScanEfiFiles(Volumes
[VolumeIndex
]);
744 } // static VOID ScanInternal()
746 // Scan external disks for valid EFI boot loaders....
747 static VOID
ScanExternal(VOID
) {
750 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
751 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_EXTERNAL
) {
752 ScanEfiFiles(Volumes
[VolumeIndex
]);
755 } // static VOID ScanExternal()
757 // Scan internal disks for valid EFI boot loaders....
758 static VOID
ScanOptical(VOID
) {
761 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
762 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_OPTICAL
) {
763 ScanEfiFiles(Volumes
[VolumeIndex
]);
766 } // static VOID ScanOptical()
769 // legacy boot functions
772 static EFI_STATUS
ActivateMbrPartition(IN EFI_BLOCK_IO
*BlockIO
, IN UINTN PartitionIndex
)
775 UINT8 SectorBuffer
[512];
776 MBR_PARTITION_INFO
*MbrTable
, *EMbrTable
;
777 UINT32 ExtBase
, ExtCurrent
, NextExtCurrent
;
778 UINTN LogicalPartitionIndex
= 4;
780 BOOLEAN HaveBootCode
;
783 Status
= refit_call5_wrapper(BlockIO
->ReadBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, 0, 512, SectorBuffer
);
784 if (EFI_ERROR(Status
))
786 if (*((UINT16
*)(SectorBuffer
+ 510)) != 0xaa55)
787 return EFI_NOT_FOUND
; // safety measure #1
789 // add boot code if necessary
790 HaveBootCode
= FALSE
;
791 for (i
= 0; i
< MBR_BOOTCODE_SIZE
; i
++) {
792 if (SectorBuffer
[i
] != 0) {
798 // no boot code found in the MBR, add the syslinux MBR code
799 SetMem(SectorBuffer
, MBR_BOOTCODE_SIZE
, 0);
800 CopyMem(SectorBuffer
, syslinux_mbr
, SYSLINUX_MBR_SIZE
);
803 // set the partition active
804 MbrTable
= (MBR_PARTITION_INFO
*)(SectorBuffer
+ 446);
806 for (i
= 0; i
< 4; i
++) {
807 if (MbrTable
[i
].Flags
!= 0x00 && MbrTable
[i
].Flags
!= 0x80)
808 return EFI_NOT_FOUND
; // safety measure #2
809 if (i
== PartitionIndex
)
810 MbrTable
[i
].Flags
= 0x80;
811 else if (PartitionIndex
>= 4 && IS_EXTENDED_PART_TYPE(MbrTable
[i
].Type
)) {
812 MbrTable
[i
].Flags
= 0x80;
813 ExtBase
= MbrTable
[i
].StartLBA
;
815 MbrTable
[i
].Flags
= 0x00;
819 Status
= refit_call5_wrapper(BlockIO
->WriteBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, 0, 512, SectorBuffer
);
820 if (EFI_ERROR(Status
))
823 if (PartitionIndex
>= 4) {
824 // we have to activate a logical partition, so walk the EMBR chain
826 // NOTE: ExtBase was set above while looking at the MBR table
827 for (ExtCurrent
= ExtBase
; ExtCurrent
; ExtCurrent
= NextExtCurrent
) {
829 Status
= refit_call5_wrapper(BlockIO
->ReadBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, ExtCurrent
, 512, SectorBuffer
);
830 if (EFI_ERROR(Status
))
832 if (*((UINT16
*)(SectorBuffer
+ 510)) != 0xaa55)
833 return EFI_NOT_FOUND
; // safety measure #3
835 // scan EMBR, set appropriate partition active
836 EMbrTable
= (MBR_PARTITION_INFO
*)(SectorBuffer
+ 446);
838 for (i
= 0; i
< 4; i
++) {
839 if (EMbrTable
[i
].Flags
!= 0x00 && EMbrTable
[i
].Flags
!= 0x80)
840 return EFI_NOT_FOUND
; // safety measure #4
841 if (EMbrTable
[i
].StartLBA
== 0 || EMbrTable
[i
].Size
== 0)
843 if (IS_EXTENDED_PART_TYPE(EMbrTable
[i
].Type
)) {
845 NextExtCurrent
= ExtBase
+ EMbrTable
[i
].StartLBA
;
846 EMbrTable
[i
].Flags
= (PartitionIndex
>= LogicalPartitionIndex
) ? 0x80 : 0x00;
850 EMbrTable
[i
].Flags
= (PartitionIndex
== LogicalPartitionIndex
) ? 0x80 : 0x00;
851 LogicalPartitionIndex
++;
855 // write current EMBR
856 Status
= refit_call5_wrapper(BlockIO
->WriteBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, ExtCurrent
, 512, SectorBuffer
);
857 if (EFI_ERROR(Status
))
860 if (PartitionIndex
< LogicalPartitionIndex
)
861 break; // stop the loop, no need to touch further EMBRs
867 } /* static EFI_STATUS ActivateMbrPartition() */
869 // early 2006 Core Duo / Core Solo models
870 static UINT8 LegacyLoaderDevicePath1Data
[] = {
871 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
872 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
873 0xFF, 0xFF, 0xF9, 0xFF, 0x00, 0x00, 0x00, 0x00,
874 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
875 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
876 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
878 // mid-2006 Mac Pro (and probably other Core 2 models)
879 static UINT8 LegacyLoaderDevicePath2Data
[] = {
880 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
881 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
882 0xFF, 0xFF, 0xF7, 0xFF, 0x00, 0x00, 0x00, 0x00,
883 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
884 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
885 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
887 // mid-2007 MBP ("Santa Rosa" based models)
888 static UINT8 LegacyLoaderDevicePath3Data
[] = {
889 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
890 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
891 0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
892 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
893 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
894 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
897 static UINT8 LegacyLoaderDevicePath4Data
[] = {
898 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
899 0x00, 0x00, 0xC0, 0xFF, 0x00, 0x00, 0x00, 0x00,
900 0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
901 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
902 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
903 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
905 // late-2008 MB/MBP (NVidia chipset)
906 static UINT8 LegacyLoaderDevicePath5Data
[] = {
907 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
908 0x00, 0x40, 0xCB, 0xFF, 0x00, 0x00, 0x00, 0x00,
909 0xFF, 0xBF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
910 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
911 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
912 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
915 static EFI_DEVICE_PATH
*LegacyLoaderList
[] = {
916 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath1Data
,
917 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath2Data
,
918 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath3Data
,
919 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath4Data
,
920 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath5Data
,
924 #define MAX_DISCOVERED_PATHS (16)
926 static VOID
StartLegacy(IN LEGACY_ENTRY
*Entry
)
929 EG_IMAGE
*BootLogoImage
;
930 UINTN ErrorInStep
= 0;
931 EFI_DEVICE_PATH
*DiscoveredPathList
[MAX_DISCOVERED_PATHS
];
933 BeginExternalScreen(TRUE
, L
"Booting Legacy OS");
935 BootLogoImage
= LoadOSIcon(Entry
->Volume
->OSIconName
, L
"legacy", TRUE
);
936 if (BootLogoImage
!= NULL
)
937 BltImageAlpha(BootLogoImage
,
938 (UGAWidth
- BootLogoImage
->Width
) >> 1,
939 (UGAHeight
- BootLogoImage
->Height
) >> 1,
940 &StdBackgroundPixel
);
942 if (Entry
->Volume
->IsMbrPartition
)
943 ActivateMbrPartition(Entry
->Volume
->WholeDiskBlockIO
, Entry
->Volume
->MbrPartitionIndex
);
945 ExtractLegacyLoaderPaths(DiscoveredPathList
, MAX_DISCOVERED_PATHS
, LegacyLoaderList
);
947 Status
= StartEFIImageList(DiscoveredPathList
, Entry
->LoadOptions
, NULL
, L
"legacy loader", &ErrorInStep
, TRUE
);
948 if (Status
== EFI_NOT_FOUND
) {
949 if (ErrorInStep
== 1) {
950 Print(L
"\nPlease make sure that you have the latest firmware update installed.\n");
951 } else if (ErrorInStep
== 3) {
952 Print(L
"\nThe firmware refused to boot from the selected volume. Note that external\n"
953 L
"hard drives are not well-supported by Apple's firmware for legacy OS booting.\n");
956 FinishExternalScreen();
957 } /* static VOID StartLegacy() */
959 static LEGACY_ENTRY
* AddLegacyEntry(IN CHAR16
*LoaderTitle
, IN REFIT_VOLUME
*Volume
)
961 LEGACY_ENTRY
*Entry
, *SubEntry
;
962 REFIT_MENU_SCREEN
*SubScreen
;
964 CHAR16 ShortcutLetter
= 0;
966 if (LoaderTitle
== NULL
) {
967 if (Volume
->OSName
!= NULL
) {
968 LoaderTitle
= Volume
->OSName
;
969 if (LoaderTitle
[0] == 'W' || LoaderTitle
[0] == 'L')
970 ShortcutLetter
= LoaderTitle
[0];
972 LoaderTitle
= L
"Legacy OS";
974 if (Volume
->VolName
!= NULL
)
975 VolDesc
= Volume
->VolName
;
977 VolDesc
= (Volume
->DiskKind
== DISK_KIND_OPTICAL
) ? L
"CD" : L
"HD";
979 // prepare the menu entry
980 Entry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
981 Entry
->me
.Title
= PoolPrint(L
"Boot %s from %s", LoaderTitle
, VolDesc
);
982 Entry
->me
.Tag
= TAG_LEGACY
;
984 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
985 Entry
->me
.Image
= LoadOSIcon(Volume
->OSIconName
, L
"legacy", FALSE
);
986 Entry
->me
.BadgeImage
= Volume
->VolBadgeImage
;
987 Entry
->Volume
= Volume
;
988 Entry
->LoadOptions
= (Volume
->DiskKind
== DISK_KIND_OPTICAL
) ? L
"CD" :
989 ((Volume
->DiskKind
== DISK_KIND_EXTERNAL
) ? L
"USB" : L
"HD");
990 Entry
->Enabled
= TRUE
;
992 // create the submenu
993 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
994 SubScreen
->Title
= PoolPrint(L
"Boot Options for %s on %s", LoaderTitle
, VolDesc
);
995 SubScreen
->TitleImage
= Entry
->me
.Image
;
998 SubEntry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
999 SubEntry
->me
.Title
= PoolPrint(L
"Boot %s", LoaderTitle
);
1000 SubEntry
->me
.Tag
= TAG_LEGACY
;
1001 SubEntry
->Volume
= Entry
->Volume
;
1002 SubEntry
->LoadOptions
= Entry
->LoadOptions
;
1003 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
1005 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
1006 Entry
->me
.SubScreen
= SubScreen
;
1007 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1009 } /* static LEGACY_ENTRY * AddLegacyEntry() */
1011 static VOID
ScanLegacyVolume(REFIT_VOLUME
*Volume
, UINTN VolumeIndex
) {
1013 BOOLEAN ShowVolume
, HideIfOthersFound
;
1016 HideIfOthersFound
= FALSE
;
1017 if (Volume
->IsAppleLegacy
) {
1019 HideIfOthersFound
= TRUE
;
1020 } else if (Volume
->HasBootCode
) {
1022 if (Volume
->BlockIO
== Volume
->WholeDiskBlockIO
&&
1023 Volume
->BlockIOOffset
== 0 &&
1024 Volume
->OSName
== NULL
)
1025 // this is a whole disk (MBR) entry; hide if we have entries for partitions
1026 HideIfOthersFound
= TRUE
;
1028 if (HideIfOthersFound
) {
1029 // check for other bootable entries on the same disk
1030 for (VolumeIndex2
= 0; VolumeIndex2
< VolumesCount
; VolumeIndex2
++) {
1031 if (VolumeIndex2
!= VolumeIndex
&& Volumes
[VolumeIndex2
]->HasBootCode
&&
1032 Volumes
[VolumeIndex2
]->WholeDiskBlockIO
== Volume
->WholeDiskBlockIO
)
1038 AddLegacyEntry(NULL
, Volume
);
1039 } // static VOID ScanLegacyVolume()
1041 // Scan attached optical discs for legacy (BIOS) boot code
1042 // and add anything found to the list....
1043 static VOID
ScanLegacyDisc(VOID
)
1046 REFIT_VOLUME
*Volume
;
1048 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1049 Volume
= Volumes
[VolumeIndex
];
1050 if (Volume
->DiskKind
== DISK_KIND_OPTICAL
)
1051 ScanLegacyVolume(Volume
, VolumeIndex
);
1053 } /* static VOID ScanLegacyDisc() */
1055 // Scan internal hard disks for legacy (BIOS) boot code
1056 // and add anything found to the list....
1057 static VOID
ScanLegacyInternal(VOID
)
1060 REFIT_VOLUME
*Volume
;
1062 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1063 Volume
= Volumes
[VolumeIndex
];
1064 if (Volume
->DiskKind
== DISK_KIND_INTERNAL
)
1065 ScanLegacyVolume(Volume
, VolumeIndex
);
1067 } /* static VOID ScanLegacyInternal() */
1069 // Scan external disks for legacy (BIOS) boot code
1070 // and add anything found to the list....
1071 static VOID
ScanLegacyExternal(VOID
)
1074 REFIT_VOLUME
*Volume
;
1076 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1077 Volume
= Volumes
[VolumeIndex
];
1078 if (Volume
->DiskKind
== DISK_KIND_EXTERNAL
)
1079 ScanLegacyVolume(Volume
, VolumeIndex
);
1081 } /* static VOID ScanLegacyExternal() */
1084 // pre-boot tool functions
1087 static VOID
StartTool(IN LOADER_ENTRY
*Entry
)
1089 BeginExternalScreen(Entry
->UseGraphicsMode
, Entry
->me
.Title
+ 6); // assumes "Start <title>" as assigned below
1090 StartEFIImage(Entry
->DevicePath
, Entry
->LoadOptions
, Basename(Entry
->LoaderPath
),
1091 Basename(Entry
->LoaderPath
), NULL
, TRUE
);
1092 FinishExternalScreen();
1093 } /* static VOID StartTool() */
1095 static LOADER_ENTRY
* AddToolEntry(IN CHAR16
*LoaderPath
, IN CHAR16
*LoaderTitle
, IN EG_IMAGE
*Image
,
1096 IN CHAR16 ShortcutLetter
, IN BOOLEAN UseGraphicsMode
)
1098 LOADER_ENTRY
*Entry
;
1100 Entry
= AllocateZeroPool(sizeof(LOADER_ENTRY
));
1102 Entry
->me
.Title
= PoolPrint(L
"Start %s", LoaderTitle
);
1103 Entry
->me
.Tag
= TAG_TOOL
;
1105 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1106 Entry
->me
.Image
= Image
;
1107 Entry
->LoaderPath
= StrDuplicate(LoaderPath
);
1108 Entry
->DevicePath
= FileDevicePath(SelfLoadedImage
->DeviceHandle
, Entry
->LoaderPath
);
1109 Entry
->UseGraphicsMode
= UseGraphicsMode
;
1111 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1113 } /* static LOADER_ENTRY * AddToolEntry() */
1116 // pre-boot driver functions
1119 static UINTN
ScanDriverDir(IN CHAR16
*Path
)
1122 REFIT_DIR_ITER DirIter
;
1124 EFI_FILE_INFO
*DirEntry
;
1125 CHAR16 FileName
[256];
1127 // look through contents of the directory
1128 DirIterOpen(SelfRootDir
, Path
, &DirIter
);
1129 while (DirIterNext(&DirIter
, 2, L
"*.efi", &DirEntry
)) {
1130 if (DirEntry
->FileName
[0] == '.')
1131 continue; // skip this
1133 SPrint(FileName
, 255, L
"%s\\%s", Path
, DirEntry
->FileName
);
1135 Status
= StartEFIImage(FileDevicePath(SelfLoadedImage
->DeviceHandle
, FileName
),
1136 L
"", DirEntry
->FileName
, DirEntry
->FileName
, NULL
, FALSE
);
1138 Status
= DirIterClose(&DirIter
);
1139 if (Status
!= EFI_NOT_FOUND
) {
1140 SPrint(FileName
, 255, L
"while scanning the %s directory", Path
);
1141 CheckError(Status
, FileName
);
1146 static EFI_STATUS
ConnectAllDriversToAllControllers(VOID
)
1149 UINTN AllHandleCount
;
1150 EFI_HANDLE
*AllHandleBuffer
;
1153 EFI_HANDLE
*HandleBuffer
;
1159 // GNU EFI's EFI_BOOT_SERVICES data structure is truncated, but all the
1160 // items are in memory, so point a more complete data structure to it
1161 // so that we can use items not in GNU EFI's implementation....
1162 gBS
= (MY_BOOT_SERVICES
*) BS
;
1164 Status
= LibLocateHandle(AllHandles
,
1169 if (EFI_ERROR(Status
))
1172 for (Index
= 0; Index
< AllHandleCount
; Index
++) {
1174 // Scan the handle database
1176 Status
= LibScanHandleDatabase(NULL
,
1178 AllHandleBuffer
[Index
],
1183 if (EFI_ERROR (Status
))
1187 if (HandleType
[Index
] & EFI_HANDLE_TYPE_DRIVER_BINDING_HANDLE
)
1189 if (HandleType
[Index
] & EFI_HANDLE_TYPE_IMAGE_HANDLE
)
1194 for (HandleIndex
= 0; HandleIndex
< HandleCount
; HandleIndex
++) {
1195 if (HandleType
[HandleIndex
] & EFI_HANDLE_TYPE_PARENT_HANDLE
)
1200 if (HandleType
[Index
] & EFI_HANDLE_TYPE_DEVICE_HANDLE
) {
1201 Status
= refit_call4_wrapper(gBS
->ConnectController
,
1202 AllHandleBuffer
[Index
],
1210 FreePool (HandleBuffer
);
1211 FreePool (HandleType
);
1215 FreePool (AllHandleBuffer
);
1217 } /* EFI_STATUS ConnectAllDriversToAllControllers() */
1219 static VOID
LoadDrivers(VOID
)
1222 UINTN i
= 0, Length
, NumFound
= 0;
1224 // Scan user-specified driver directories....
1225 while ((Directory
= FindCommaDelimited(GlobalConfig
.DriverDirs
, i
++)) != NULL
) {
1226 Length
= StrLen(Directory
);
1227 // Some EFI implementations won't read a directory if the path ends in
1228 // a backslash, so eliminate this character, if it's present....
1229 while ((Length
> 0) && (Directory
[Length
- 1] == L
'\\')) {
1230 Directory
[--Length
] = 0;
1233 NumFound
+= ScanDriverDir(Directory
);
1234 FreePool(Directory
);
1237 // connect all devices
1239 ConnectAllDriversToAllControllers();
1242 static VOID
ScanForBootloaders(VOID
) {
1246 // Commented-out below: Was part of an attempt to get rEFInd to
1247 // re-scan disk devices on pressing Esc; but doesn't work (yet), so
1249 // MainMenu.Title = StrDuplicate(L"Main Menu 2");
1250 // MainMenu.TitleImage = NULL;
1251 // MainMenu.InfoLineCount = 0;
1252 // MainMenu.InfoLines = NULL;
1253 // MainMenu.EntryCount = 0;
1254 // MainMenu.Entries = NULL;
1255 // MainMenu.TimeoutSeconds = 20;
1256 // MainMenu.TimeoutText = StrDuplicate(L"Automatic boot");
1259 // scan for loaders and tools, add them to the menu
1260 for (i
= 0; i
< NUM_SCAN_OPTIONS
; i
++) {
1261 switch(GlobalConfig
.ScanFor
[i
]) {
1266 ScanLegacyInternal();
1269 ScanLegacyExternal();
1272 ScanUserConfigured();
1286 // assign shortcut keys
1287 for (i
= 0; i
< MainMenu
.EntryCount
&& MainMenu
.Entries
[i
]->Row
== 0 && i
< 9; i
++)
1288 MainMenu
.Entries
[i
]->ShortcutDigit
= (CHAR16
)('1' + i
);
1290 // wait for user ACK when there were errors
1291 FinishTextScreen(FALSE
);
1292 } // static VOID ScanForBootloaders()
1294 // Add the second-row tags containing built-in and external tools (EFI shell,
1296 static VOID
ScanForTools(VOID
) {
1297 CHAR16
*FileName
= NULL
;
1300 for (i
= 0; i
< NUM_TOOLS
; i
++) {
1301 switch(GlobalConfig
.ShowTools
[i
]) {
1303 MenuEntryShutdown
.Image
= BuiltinIcon(BUILTIN_ICON_FUNC_SHUTDOWN
);
1304 AddMenuEntry(&MainMenu
, &MenuEntryShutdown
);
1307 MenuEntryReset
.Image
= BuiltinIcon(BUILTIN_ICON_FUNC_RESET
);
1308 AddMenuEntry(&MainMenu
, &MenuEntryReset
);
1311 MenuEntryAbout
.Image
= BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT
);
1312 AddMenuEntry(&MainMenu
, &MenuEntryAbout
);
1315 MenuEntryExit
.Image
= BuiltinIcon(BUILTIN_ICON_FUNC_EXIT
);
1316 AddMenuEntry(&MainMenu
, &MenuEntryExit
);
1320 while ((FileName
= FindCommaDelimited(SHELL_NAMES
, j
++)) != NULL
) {
1321 if (FileExists(SelfRootDir
, FileName
)) {
1322 AddToolEntry(FileName
, L
"EFI Shell", BuiltinIcon(BUILTIN_ICON_TOOL_SHELL
), 'E', FALSE
);
1327 MergeStrings(&FileName
, L
"\\efi\\tools\\gptsync.efi", 0);
1328 if (FileExists(SelfRootDir
, FileName
)) {
1329 AddToolEntry(FileName
, L
"Make Hybrid MBR", BuiltinIcon(BUILTIN_ICON_TOOL_PART
), 'P', FALSE
);
1333 if (FileName
!= NULL
) {
1338 } // static VOID ScanForTools
1346 efi_main (IN EFI_HANDLE ImageHandle
, IN EFI_SYSTEM_TABLE
*SystemTable
)
1349 BOOLEAN MainLoopRunning
= TRUE
;
1350 REFIT_MENU_ENTRY
*ChosenEntry
;
1354 InitializeLib(ImageHandle
, SystemTable
);
1356 Status
= InitRefitLib(ImageHandle
);
1357 if (EFI_ERROR(Status
))
1360 // read configuration
1361 CopyMem(GlobalConfig
.ScanFor
, "ieo ", NUM_SCAN_OPTIONS
);
1363 MainMenu
.TimeoutSeconds
= GlobalConfig
.Timeout
;
1365 // disable EFI watchdog timer
1366 refit_call4_wrapper(BS
->SetWatchdogTimer
, 0x0000, 0x0000, 0x0000, NULL
);
1368 // further bootstrap (now with config available)
1371 ScanForBootloaders();
1374 while (MainLoopRunning
) {
1375 MenuExit
= RunMainMenu(&MainMenu
, GlobalConfig
.DefaultSelection
, &ChosenEntry
);
1377 // We don't allow exiting the main menu with the Escape key.
1378 if (MenuExit
== MENU_EXIT_ESCAPE
) {
1379 // Commented-out below: Was part of an attempt to get rEFInd to
1380 // re-scan disk devices on pressing Esc; but doesn't work (yet), so
1383 // ScanForBootloaders();
1388 switch (ChosenEntry
->Tag
) {
1390 case TAG_REBOOT
: // Reboot
1392 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);
1393 MainLoopRunning
= FALSE
; // just in case we get this far
1396 case TAG_SHUTDOWN
: // Shut Down
1398 refit_call4_wrapper(RT
->ResetSystem
, EfiResetShutdown
, EFI_SUCCESS
, 0, NULL
);
1399 MainLoopRunning
= FALSE
; // just in case we get this far
1402 case TAG_ABOUT
: // About rEFInd
1406 case TAG_LOADER
: // Boot OS via .EFI loader
1407 StartLoader((LOADER_ENTRY
*)ChosenEntry
);
1410 case TAG_LEGACY
: // Boot legacy OS
1411 StartLegacy((LEGACY_ENTRY
*)ChosenEntry
);
1414 case TAG_TOOL
: // Start a EFI tool
1415 StartTool((LOADER_ENTRY
*)ChosenEntry
);
1418 case TAG_EXIT
: // Terminate rEFInd
1419 BeginTextScreen(L
" ");
1426 // If we end up here, things have gone wrong. Try to reboot, and if that
1427 // fails, go into an endless loop.
1428 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);