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"
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.7.1");
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
, 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
;
641 FileName
= AllocateZeroPool(256 * sizeof(CHAR16
));
642 if ((!SelfDirPath
|| !Path
|| ((StriCmp(Path
, SelfDirPath
) == 0) && Volume
!= SelfVolume
) ||
643 (StriCmp(Path
, SelfDirPath
) != 0)) && FileName
) {
644 // look through contents of the directory
645 DirIterOpen(Volume
->RootDir
, Path
, &DirIter
);
646 while (DirIterNext(&DirIter
, 2, L
"*.efi", &DirEntry
)) {
647 if (DirEntry
->FileName
[0] == '.' ||
648 StriCmp(DirEntry
->FileName
, L
"TextMode.efi") == 0 ||
649 StriCmp(DirEntry
->FileName
, L
"ebounce.efi") == 0 ||
650 StriCmp(DirEntry
->FileName
, L
"GraphicsConsole.efi") == 0 ||
651 StriSubCmp(L
"shell", DirEntry
->FileName
))
652 continue; // skip this
655 SPrint(FileName
, 255, L
"\\%s\\%s", Path
, DirEntry
->FileName
);
657 SPrint(FileName
, 255, L
"\\%s", DirEntry
->FileName
);
658 AddLoaderEntry(FileName
, NULL
, Volume
);
660 Status
= DirIterClose(&DirIter
);
661 if (Status
!= EFI_NOT_FOUND
) {
663 SPrint(FileName
, 255, L
"while scanning the %s directory", Path
);
665 StrCpy(FileName
, L
"while scanning the root directory");
666 CheckError(Status
, FileName
);
667 } // if (Status != EFI_NOT_FOUND)
668 } // if not scanning our own directory
669 } /* static VOID ScanLoaderDir() */
671 static VOID
ScanEfiFiles(REFIT_VOLUME
*Volume
) {
673 REFIT_DIR_ITER EfiDirIter
;
674 EFI_FILE_INFO
*EfiDirEntry
;
675 CHAR16 FileName
[256], *Directory
;
678 if ((Volume
->RootDir
!= NULL
) && (Volume
->VolName
!= NULL
)) {
679 // check for Mac OS X boot loader
680 StrCpy(FileName
, MACOSX_LOADER_PATH
);
681 if (FileExists(Volume
->RootDir
, FileName
)) {
682 AddLoaderEntry(FileName
, L
"Mac OS X", Volume
);
686 StrCpy(FileName
, L
"System\\Library\\CoreServices\\xom.efi");
687 if (FileExists(Volume
->RootDir
, FileName
)) {
688 AddLoaderEntry(FileName
, L
"Windows XP (XoM)", Volume
);
691 // check for Microsoft boot loader/menu
692 StrCpy(FileName
, L
"EFI\\Microsoft\\Boot\\Bootmgfw.efi");
693 if (FileExists(Volume
->RootDir
, FileName
)) {
694 AddLoaderEntry(FileName
, L
"Microsoft EFI boot", Volume
);
697 // scan the root directory for EFI executables
698 ScanLoaderDir(Volume
, NULL
);
700 // scan subdirectories of the EFI directory (as per the standard)
701 DirIterOpen(Volume
->RootDir
, L
"EFI", &EfiDirIter
);
702 while (DirIterNext(&EfiDirIter
, 1, NULL
, &EfiDirEntry
)) {
703 if (StriCmp(EfiDirEntry
->FileName
, L
"tools") == 0 || EfiDirEntry
->FileName
[0] == '.')
704 continue; // skip this, doesn't contain boot loaders
705 SPrint(FileName
, 255, L
"EFI\\%s", EfiDirEntry
->FileName
);
706 ScanLoaderDir(Volume
, FileName
);
708 Status
= DirIterClose(&EfiDirIter
);
709 if (Status
!= EFI_NOT_FOUND
)
710 CheckError(Status
, L
"while scanning the EFI directory");
712 // Scan user-specified (or additional default) directories....
714 while ((Directory
= FindCommaDelimited(GlobalConfig
.AlsoScan
, i
++)) != NULL
) {
715 Length
= StrLen(Directory
);
716 // Some EFI implementations won't read a directory if the path ends in
717 // a backslash, so eliminate this character, if it's present....
718 while ((Length
> 0) && (Directory
[Length
- 1] == L
'\\')) {
719 Directory
[--Length
] = 0;
722 ScanLoaderDir(Volume
, Directory
);
726 } // static VOID ScanEfiFiles()
728 // Scan internal disks for valid EFI boot loaders....
729 static VOID
ScanInternal(VOID
) {
732 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
733 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_INTERNAL
) {
734 ScanEfiFiles(Volumes
[VolumeIndex
]);
737 } // static VOID ScanInternal()
739 // Scan external disks for valid EFI boot loaders....
740 static VOID
ScanExternal(VOID
) {
743 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
744 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_EXTERNAL
) {
745 ScanEfiFiles(Volumes
[VolumeIndex
]);
748 } // static VOID ScanExternal()
750 // Scan internal disks for valid EFI boot loaders....
751 static VOID
ScanOptical(VOID
) {
754 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
755 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_OPTICAL
) {
756 ScanEfiFiles(Volumes
[VolumeIndex
]);
759 } // static VOID ScanOptical()
762 // legacy boot functions
765 static EFI_STATUS
ActivateMbrPartition(IN EFI_BLOCK_IO
*BlockIO
, IN UINTN PartitionIndex
)
768 UINT8 SectorBuffer
[512];
769 MBR_PARTITION_INFO
*MbrTable
, *EMbrTable
;
770 UINT32 ExtBase
, ExtCurrent
, NextExtCurrent
;
771 UINTN LogicalPartitionIndex
= 4;
773 BOOLEAN HaveBootCode
;
776 Status
= refit_call5_wrapper(BlockIO
->ReadBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, 0, 512, SectorBuffer
);
777 if (EFI_ERROR(Status
))
779 if (*((UINT16
*)(SectorBuffer
+ 510)) != 0xaa55)
780 return EFI_NOT_FOUND
; // safety measure #1
782 // add boot code if necessary
783 HaveBootCode
= FALSE
;
784 for (i
= 0; i
< MBR_BOOTCODE_SIZE
; i
++) {
785 if (SectorBuffer
[i
] != 0) {
791 // no boot code found in the MBR, add the syslinux MBR code
792 SetMem(SectorBuffer
, MBR_BOOTCODE_SIZE
, 0);
793 CopyMem(SectorBuffer
, syslinux_mbr
, SYSLINUX_MBR_SIZE
);
796 // set the partition active
797 MbrTable
= (MBR_PARTITION_INFO
*)(SectorBuffer
+ 446);
799 for (i
= 0; i
< 4; i
++) {
800 if (MbrTable
[i
].Flags
!= 0x00 && MbrTable
[i
].Flags
!= 0x80)
801 return EFI_NOT_FOUND
; // safety measure #2
802 if (i
== PartitionIndex
)
803 MbrTable
[i
].Flags
= 0x80;
804 else if (PartitionIndex
>= 4 && IS_EXTENDED_PART_TYPE(MbrTable
[i
].Type
)) {
805 MbrTable
[i
].Flags
= 0x80;
806 ExtBase
= MbrTable
[i
].StartLBA
;
808 MbrTable
[i
].Flags
= 0x00;
812 Status
= refit_call5_wrapper(BlockIO
->WriteBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, 0, 512, SectorBuffer
);
813 if (EFI_ERROR(Status
))
816 if (PartitionIndex
>= 4) {
817 // we have to activate a logical partition, so walk the EMBR chain
819 // NOTE: ExtBase was set above while looking at the MBR table
820 for (ExtCurrent
= ExtBase
; ExtCurrent
; ExtCurrent
= NextExtCurrent
) {
822 Status
= refit_call5_wrapper(BlockIO
->ReadBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, ExtCurrent
, 512, SectorBuffer
);
823 if (EFI_ERROR(Status
))
825 if (*((UINT16
*)(SectorBuffer
+ 510)) != 0xaa55)
826 return EFI_NOT_FOUND
; // safety measure #3
828 // scan EMBR, set appropriate partition active
829 EMbrTable
= (MBR_PARTITION_INFO
*)(SectorBuffer
+ 446);
831 for (i
= 0; i
< 4; i
++) {
832 if (EMbrTable
[i
].Flags
!= 0x00 && EMbrTable
[i
].Flags
!= 0x80)
833 return EFI_NOT_FOUND
; // safety measure #4
834 if (EMbrTable
[i
].StartLBA
== 0 || EMbrTable
[i
].Size
== 0)
836 if (IS_EXTENDED_PART_TYPE(EMbrTable
[i
].Type
)) {
838 NextExtCurrent
= ExtBase
+ EMbrTable
[i
].StartLBA
;
839 EMbrTable
[i
].Flags
= (PartitionIndex
>= LogicalPartitionIndex
) ? 0x80 : 0x00;
843 EMbrTable
[i
].Flags
= (PartitionIndex
== LogicalPartitionIndex
) ? 0x80 : 0x00;
844 LogicalPartitionIndex
++;
848 // write current EMBR
849 Status
= refit_call5_wrapper(BlockIO
->WriteBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, ExtCurrent
, 512, SectorBuffer
);
850 if (EFI_ERROR(Status
))
853 if (PartitionIndex
< LogicalPartitionIndex
)
854 break; // stop the loop, no need to touch further EMBRs
860 } /* static EFI_STATUS ActivateMbrPartition() */
862 // early 2006 Core Duo / Core Solo models
863 static UINT8 LegacyLoaderDevicePath1Data
[] = {
864 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
865 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
866 0xFF, 0xFF, 0xF9, 0xFF, 0x00, 0x00, 0x00, 0x00,
867 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
868 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
869 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
871 // mid-2006 Mac Pro (and probably other Core 2 models)
872 static UINT8 LegacyLoaderDevicePath2Data
[] = {
873 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
874 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
875 0xFF, 0xFF, 0xF7, 0xFF, 0x00, 0x00, 0x00, 0x00,
876 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
877 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
878 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
880 // mid-2007 MBP ("Santa Rosa" based models)
881 static UINT8 LegacyLoaderDevicePath3Data
[] = {
882 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
883 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
884 0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
885 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
886 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
887 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
890 static UINT8 LegacyLoaderDevicePath4Data
[] = {
891 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
892 0x00, 0x00, 0xC0, 0xFF, 0x00, 0x00, 0x00, 0x00,
893 0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
894 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
895 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
896 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
898 // late-2008 MB/MBP (NVidia chipset)
899 static UINT8 LegacyLoaderDevicePath5Data
[] = {
900 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
901 0x00, 0x40, 0xCB, 0xFF, 0x00, 0x00, 0x00, 0x00,
902 0xFF, 0xBF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
903 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
904 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
905 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
908 static EFI_DEVICE_PATH
*LegacyLoaderList
[] = {
909 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath1Data
,
910 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath2Data
,
911 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath3Data
,
912 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath4Data
,
913 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath5Data
,
917 #define MAX_DISCOVERED_PATHS (16)
919 static VOID
StartLegacy(IN LEGACY_ENTRY
*Entry
)
922 EG_IMAGE
*BootLogoImage
;
923 UINTN ErrorInStep
= 0;
924 EFI_DEVICE_PATH
*DiscoveredPathList
[MAX_DISCOVERED_PATHS
];
926 BeginExternalScreen(TRUE
, L
"Booting Legacy OS");
928 BootLogoImage
= LoadOSIcon(Entry
->Volume
->OSIconName
, L
"legacy", TRUE
);
929 if (BootLogoImage
!= NULL
)
930 BltImageAlpha(BootLogoImage
,
931 (UGAWidth
- BootLogoImage
->Width
) >> 1,
932 (UGAHeight
- BootLogoImage
->Height
) >> 1,
933 &StdBackgroundPixel
);
935 if (Entry
->Volume
->IsMbrPartition
)
936 ActivateMbrPartition(Entry
->Volume
->WholeDiskBlockIO
, Entry
->Volume
->MbrPartitionIndex
);
938 ExtractLegacyLoaderPaths(DiscoveredPathList
, MAX_DISCOVERED_PATHS
, LegacyLoaderList
);
940 Status
= StartEFIImageList(DiscoveredPathList
, Entry
->LoadOptions
, NULL
, L
"legacy loader", &ErrorInStep
, TRUE
);
941 if (Status
== EFI_NOT_FOUND
) {
942 if (ErrorInStep
== 1) {
943 Print(L
"\nPlease make sure that you have the latest firmware update installed.\n");
944 } else if (ErrorInStep
== 3) {
945 Print(L
"\nThe firmware refused to boot from the selected volume. Note that external\n"
946 L
"hard drives are not well-supported by Apple's firmware for legacy OS booting.\n");
949 FinishExternalScreen();
950 } /* static VOID StartLegacy() */
952 static LEGACY_ENTRY
* AddLegacyEntry(IN CHAR16
*LoaderTitle
, IN REFIT_VOLUME
*Volume
)
954 LEGACY_ENTRY
*Entry
, *SubEntry
;
955 REFIT_MENU_SCREEN
*SubScreen
;
957 CHAR16 ShortcutLetter
= 0;
959 if (LoaderTitle
== NULL
) {
960 if (Volume
->OSName
!= NULL
) {
961 LoaderTitle
= Volume
->OSName
;
962 if (LoaderTitle
[0] == 'W' || LoaderTitle
[0] == 'L')
963 ShortcutLetter
= LoaderTitle
[0];
965 LoaderTitle
= L
"Legacy OS";
967 if (Volume
->VolName
!= NULL
)
968 VolDesc
= Volume
->VolName
;
970 VolDesc
= (Volume
->DiskKind
== DISK_KIND_OPTICAL
) ? L
"CD" : L
"HD";
972 // prepare the menu entry
973 Entry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
974 Entry
->me
.Title
= PoolPrint(L
"Boot %s from %s", LoaderTitle
, VolDesc
);
975 Entry
->me
.Tag
= TAG_LEGACY
;
977 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
978 Entry
->me
.Image
= LoadOSIcon(Volume
->OSIconName
, L
"legacy", FALSE
);
979 Entry
->me
.BadgeImage
= Volume
->VolBadgeImage
;
980 Entry
->Volume
= Volume
;
981 Entry
->LoadOptions
= (Volume
->DiskKind
== DISK_KIND_OPTICAL
) ? L
"CD" :
982 ((Volume
->DiskKind
== DISK_KIND_EXTERNAL
) ? L
"USB" : L
"HD");
983 Entry
->Enabled
= TRUE
;
985 // create the submenu
986 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
987 SubScreen
->Title
= PoolPrint(L
"Boot Options for %s on %s", LoaderTitle
, VolDesc
);
988 SubScreen
->TitleImage
= Entry
->me
.Image
;
991 SubEntry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
992 SubEntry
->me
.Title
= PoolPrint(L
"Boot %s", LoaderTitle
);
993 SubEntry
->me
.Tag
= TAG_LEGACY
;
994 SubEntry
->Volume
= Entry
->Volume
;
995 SubEntry
->LoadOptions
= Entry
->LoadOptions
;
996 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
998 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
999 Entry
->me
.SubScreen
= SubScreen
;
1000 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1002 } /* static LEGACY_ENTRY * AddLegacyEntry() */
1004 static VOID
ScanLegacyVolume(REFIT_VOLUME
*Volume
, UINTN VolumeIndex
) {
1006 BOOLEAN ShowVolume
, HideIfOthersFound
;
1009 HideIfOthersFound
= FALSE
;
1010 if (Volume
->IsAppleLegacy
) {
1012 HideIfOthersFound
= TRUE
;
1013 } else if (Volume
->HasBootCode
) {
1015 if (Volume
->BlockIO
== Volume
->WholeDiskBlockIO
&&
1016 Volume
->BlockIOOffset
== 0 &&
1017 Volume
->OSName
== NULL
)
1018 // this is a whole disk (MBR) entry; hide if we have entries for partitions
1019 HideIfOthersFound
= TRUE
;
1021 if (HideIfOthersFound
) {
1022 // check for other bootable entries on the same disk
1023 for (VolumeIndex2
= 0; VolumeIndex2
< VolumesCount
; VolumeIndex2
++) {
1024 if (VolumeIndex2
!= VolumeIndex
&& Volumes
[VolumeIndex2
]->HasBootCode
&&
1025 Volumes
[VolumeIndex2
]->WholeDiskBlockIO
== Volume
->WholeDiskBlockIO
)
1031 AddLegacyEntry(NULL
, Volume
);
1032 } // static VOID ScanLegacyVolume()
1034 // Scan attached optical discs for legacy (BIOS) boot code
1035 // and add anything found to the list....
1036 static VOID
ScanLegacyDisc(VOID
)
1039 REFIT_VOLUME
*Volume
;
1041 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1042 Volume
= Volumes
[VolumeIndex
];
1043 if (Volume
->DiskKind
== DISK_KIND_OPTICAL
)
1044 ScanLegacyVolume(Volume
, VolumeIndex
);
1046 } /* static VOID ScanLegacyDisc() */
1048 // Scan internal hard disks for legacy (BIOS) boot code
1049 // and add anything found to the list....
1050 static VOID
ScanLegacyInternal(VOID
)
1053 REFIT_VOLUME
*Volume
;
1055 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1056 Volume
= Volumes
[VolumeIndex
];
1057 if (Volume
->DiskKind
== DISK_KIND_INTERNAL
)
1058 ScanLegacyVolume(Volume
, VolumeIndex
);
1060 } /* static VOID ScanLegacyInternal() */
1062 // Scan external disks for legacy (BIOS) boot code
1063 // and add anything found to the list....
1064 static VOID
ScanLegacyExternal(VOID
)
1067 REFIT_VOLUME
*Volume
;
1069 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1070 Volume
= Volumes
[VolumeIndex
];
1071 if (Volume
->DiskKind
== DISK_KIND_EXTERNAL
)
1072 ScanLegacyVolume(Volume
, VolumeIndex
);
1074 } /* static VOID ScanLegacyExternal() */
1077 // pre-boot tool functions
1080 static VOID
StartTool(IN LOADER_ENTRY
*Entry
)
1082 BeginExternalScreen(Entry
->UseGraphicsMode
, Entry
->me
.Title
+ 6); // assumes "Start <title>" as assigned below
1083 StartEFIImage(Entry
->DevicePath
, Entry
->LoadOptions
, Basename(Entry
->LoaderPath
),
1084 Basename(Entry
->LoaderPath
), NULL
, TRUE
);
1085 FinishExternalScreen();
1086 } /* static VOID StartTool() */
1088 static LOADER_ENTRY
* AddToolEntry(IN CHAR16
*LoaderPath
, IN CHAR16
*LoaderTitle
, IN EG_IMAGE
*Image
,
1089 IN CHAR16 ShortcutLetter
, IN BOOLEAN UseGraphicsMode
)
1091 LOADER_ENTRY
*Entry
;
1093 Entry
= AllocateZeroPool(sizeof(LOADER_ENTRY
));
1095 Entry
->me
.Title
= PoolPrint(L
"Start %s", LoaderTitle
);
1096 Entry
->me
.Tag
= TAG_TOOL
;
1098 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1099 Entry
->me
.Image
= Image
;
1100 Entry
->LoaderPath
= StrDuplicate(LoaderPath
);
1101 Entry
->DevicePath
= FileDevicePath(SelfLoadedImage
->DeviceHandle
, Entry
->LoaderPath
);
1102 Entry
->UseGraphicsMode
= UseGraphicsMode
;
1104 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1106 } /* static LOADER_ENTRY * AddToolEntry() */
1109 // pre-boot driver functions
1112 static UINTN
ScanDriverDir(IN CHAR16
*Path
)
1115 REFIT_DIR_ITER DirIter
;
1117 EFI_FILE_INFO
*DirEntry
;
1118 CHAR16 FileName
[256];
1120 // look through contents of the directory
1121 DirIterOpen(SelfRootDir
, Path
, &DirIter
);
1122 while (DirIterNext(&DirIter
, 2, L
"*.efi", &DirEntry
)) {
1123 if (DirEntry
->FileName
[0] == '.')
1124 continue; // skip this
1126 SPrint(FileName
, 255, L
"%s\\%s", Path
, DirEntry
->FileName
);
1128 Status
= StartEFIImage(FileDevicePath(SelfLoadedImage
->DeviceHandle
, FileName
),
1129 L
"", DirEntry
->FileName
, DirEntry
->FileName
, NULL
, FALSE
);
1131 Status
= DirIterClose(&DirIter
);
1132 if (Status
!= EFI_NOT_FOUND
) {
1133 SPrint(FileName
, 255, L
"while scanning the %s directory", Path
);
1134 CheckError(Status
, FileName
);
1139 static EFI_STATUS
ConnectAllDriversToAllControllers(VOID
)
1142 UINTN AllHandleCount
;
1143 EFI_HANDLE
*AllHandleBuffer
;
1146 EFI_HANDLE
*HandleBuffer
;
1152 // GNU EFI's EFI_BOOT_SERVICES data structure is truncated, but all the
1153 // items are in memory, so point a more complete data structure to it
1154 // so that we can use items not in GNU EFI's implementation....
1155 // gBS = (MY_BOOT_SERVICES*) BS;
1157 Status
= LibLocateHandle(AllHandles
,
1162 if (EFI_ERROR(Status
))
1165 for (Index
= 0; Index
< AllHandleCount
; Index
++) {
1167 // Scan the handle database
1169 Status
= LibScanHandleDatabase(NULL
,
1171 AllHandleBuffer
[Index
],
1176 if (EFI_ERROR (Status
))
1180 if (HandleType
[Index
] & EFI_HANDLE_TYPE_DRIVER_BINDING_HANDLE
)
1182 if (HandleType
[Index
] & EFI_HANDLE_TYPE_IMAGE_HANDLE
)
1187 for (HandleIndex
= 0; HandleIndex
< HandleCount
; HandleIndex
++) {
1188 if (HandleType
[HandleIndex
] & EFI_HANDLE_TYPE_PARENT_HANDLE
)
1193 if (HandleType
[Index
] & EFI_HANDLE_TYPE_DEVICE_HANDLE
) {
1194 Status
= refit_call4_wrapper(BS
->ConnectController
,
1195 AllHandleBuffer
[Index
],
1203 FreePool (HandleBuffer
);
1204 FreePool (HandleType
);
1208 FreePool (AllHandleBuffer
);
1210 } /* EFI_STATUS ConnectAllDriversToAllControllers() */
1212 static VOID
LoadDrivers(VOID
)
1215 UINTN i
= 0, Length
, NumFound
= 0;
1217 // load drivers from the "drivers" subdirectory of rEFInd's home directory
1218 Directory
= StrDuplicate(SelfDirPath
);
1219 MergeStrings(&Directory
, L
"drivers", L
'\\');
1220 NumFound
+= ScanDriverDir(Directory
);
1222 // Scan additional user-specified driver directories....
1223 while ((Directory
= FindCommaDelimited(GlobalConfig
.DriverDirs
, i
++)) != NULL
) {
1224 Length
= StrLen(Directory
);
1225 // Some EFI implementations won't read a directory if the path ends in
1226 // a backslash, so eliminate this character, if it's present....
1227 while ((Length
> 0) && (Directory
[Length
- 1] == L
'\\')) {
1228 Directory
[--Length
] = 0;
1231 NumFound
+= ScanDriverDir(Directory
);
1232 FreePool(Directory
);
1235 // connect all devices
1237 ConnectAllDriversToAllControllers();
1240 static VOID
ScanForBootloaders(VOID
) {
1244 // Commented-out below: Was part of an attempt to get rEFInd to
1245 // re-scan disk devices on pressing Esc; but doesn't work (yet), so
1247 // MainMenu.Title = StrDuplicate(L"Main Menu 2");
1248 // MainMenu.TitleImage = NULL;
1249 // MainMenu.InfoLineCount = 0;
1250 // MainMenu.InfoLines = NULL;
1251 // MainMenu.EntryCount = 0;
1252 // MainMenu.Entries = NULL;
1253 // MainMenu.TimeoutSeconds = 20;
1254 // MainMenu.TimeoutText = StrDuplicate(L"Automatic boot");
1257 // scan for loaders and tools, add them to the menu
1258 for (i
= 0; i
< NUM_SCAN_OPTIONS
; i
++) {
1259 switch(GlobalConfig
.ScanFor
[i
]) {
1264 ScanLegacyInternal();
1267 ScanLegacyExternal();
1270 ScanUserConfigured();
1284 // assign shortcut keys
1285 for (i
= 0; i
< MainMenu
.EntryCount
&& MainMenu
.Entries
[i
]->Row
== 0 && i
< 9; i
++)
1286 MainMenu
.Entries
[i
]->ShortcutDigit
= (CHAR16
)('1' + i
);
1288 // wait for user ACK when there were errors
1289 FinishTextScreen(FALSE
);
1290 } // static VOID ScanForBootloaders()
1292 // Add the second-row tags containing built-in and external tools (EFI shell,
1294 static VOID
ScanForTools(VOID
) {
1295 CHAR16
*FileName
= NULL
;
1298 for (i
= 0; i
< NUM_TOOLS
; i
++) {
1299 switch(GlobalConfig
.ShowTools
[i
]) {
1301 MenuEntryShutdown
.Image
= BuiltinIcon(BUILTIN_ICON_FUNC_SHUTDOWN
);
1302 AddMenuEntry(&MainMenu
, &MenuEntryShutdown
);
1305 MenuEntryReset
.Image
= BuiltinIcon(BUILTIN_ICON_FUNC_RESET
);
1306 AddMenuEntry(&MainMenu
, &MenuEntryReset
);
1309 MenuEntryAbout
.Image
= BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT
);
1310 AddMenuEntry(&MainMenu
, &MenuEntryAbout
);
1313 MenuEntryExit
.Image
= BuiltinIcon(BUILTIN_ICON_FUNC_EXIT
);
1314 AddMenuEntry(&MainMenu
, &MenuEntryExit
);
1318 while ((FileName
= FindCommaDelimited(SHELL_NAMES
, j
++)) != NULL
) {
1319 if (FileExists(SelfRootDir
, FileName
)) {
1320 AddToolEntry(FileName
, L
"EFI Shell", BuiltinIcon(BUILTIN_ICON_TOOL_SHELL
), 'E', FALSE
);
1325 MergeStrings(&FileName
, L
"\\efi\\tools\\gptsync.efi", 0);
1326 if (FileExists(SelfRootDir
, FileName
)) {
1327 AddToolEntry(FileName
, L
"Make Hybrid MBR", BuiltinIcon(BUILTIN_ICON_TOOL_PART
), 'P', FALSE
);
1331 if (FileName
!= NULL
) {
1336 } // static VOID ScanForTools
1344 efi_main (IN EFI_HANDLE ImageHandle
, IN EFI_SYSTEM_TABLE
*SystemTable
)
1347 BOOLEAN MainLoopRunning
= TRUE
;
1348 REFIT_MENU_ENTRY
*ChosenEntry
;
1352 InitializeLib(ImageHandle
, SystemTable
);
1354 Status
= InitRefitLib(ImageHandle
);
1355 if (EFI_ERROR(Status
))
1358 // read configuration
1359 CopyMem(GlobalConfig
.ScanFor
, "ieo ", NUM_SCAN_OPTIONS
);
1361 MainMenu
.TimeoutSeconds
= GlobalConfig
.Timeout
;
1363 // disable EFI watchdog timer
1364 refit_call4_wrapper(BS
->SetWatchdogTimer
, 0x0000, 0x0000, 0x0000, NULL
);
1366 // further bootstrap (now with config available)
1369 ScanForBootloaders();
1372 while (MainLoopRunning
) {
1373 MenuExit
= RunMainMenu(&MainMenu
, GlobalConfig
.DefaultSelection
, &ChosenEntry
);
1375 // We don't allow exiting the main menu with the Escape key.
1376 if (MenuExit
== MENU_EXIT_ESCAPE
) {
1377 // Commented-out below: Was part of an attempt to get rEFInd to
1378 // re-scan disk devices on pressing Esc; but doesn't work (yet), so
1381 // ScanForBootloaders();
1386 switch (ChosenEntry
->Tag
) {
1388 case TAG_REBOOT
: // Reboot
1390 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);
1391 MainLoopRunning
= FALSE
; // just in case we get this far
1394 case TAG_SHUTDOWN
: // Shut Down
1396 refit_call4_wrapper(RT
->ResetSystem
, EfiResetShutdown
, EFI_SUCCESS
, 0, NULL
);
1397 MainLoopRunning
= FALSE
; // just in case we get this far
1400 case TAG_ABOUT
: // About rEFInd
1404 case TAG_LOADER
: // Boot OS via .EFI loader
1405 StartLoader((LOADER_ENTRY
*)ChosenEntry
);
1408 case TAG_LEGACY
: // Boot legacy OS
1409 StartLegacy((LEGACY_ENTRY
*)ChosenEntry
);
1412 case TAG_TOOL
: // Start a EFI tool
1413 StartTool((LOADER_ENTRY
*)ChosenEntry
);
1416 case TAG_EXIT
: // Terminate rEFInd
1417 BeginTextScreen(L
" ");
1424 // If we end up here, things have gone wrong. Try to reboot, and if that
1425 // fails, go into an endless loop.
1426 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);