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 "../include/syslinux_mbr.h"
57 #define MACOSX_LOADER_PATH L"\\System\\Library\\CoreServices\\boot.efi"
59 #define SHELL_NAMES L"\\EFI\\tools\\shell.efi,\\shellx64.efi"
61 #define SHELL_NAMES L"\\EFI\\tools\\shell.efi,\\shellia32.efi"
63 #define SHELL_NAMES L"\\EFI\\tools\\shell.efi"
66 static REFIT_MENU_ENTRY MenuEntryAbout
= { L
"About rEFInd", TAG_ABOUT
, 1, 0, 'A', NULL
, NULL
, NULL
};
67 static REFIT_MENU_ENTRY MenuEntryReset
= { L
"Restart Computer", TAG_RESET
, 1, 0, 'R', NULL
, NULL
, NULL
};
68 static REFIT_MENU_ENTRY MenuEntryShutdown
= { L
"Shut Down Computer", TAG_SHUTDOWN
, 1, 0, 'U', NULL
, NULL
, NULL
};
69 static REFIT_MENU_ENTRY MenuEntryReturn
= { L
"Return to Main Menu", TAG_RETURN
, 0, 0, 0, NULL
, NULL
, NULL
};
71 static REFIT_MENU_SCREEN MainMenu
= { L
"Main Menu", NULL
, 0, NULL
, 0, NULL
, 0, L
"Automatic boot" };
72 static REFIT_MENU_SCREEN AboutMenu
= { L
"About", NULL
, 0, NULL
, 0, NULL
, 0, NULL
};
74 REFIT_CONFIG GlobalConfig
= { FALSE
, 20, 0, 0, 0, NULL
, NULL
, NULL
, NULL
};
80 static VOID
AboutrEFInd(VOID
)
82 if (AboutMenu
.EntryCount
== 0) {
83 AboutMenu
.TitleImage
= BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT
);
84 AddMenuInfoLine(&AboutMenu
, L
"rEFInd Version 0.2.3.3");
85 AddMenuInfoLine(&AboutMenu
, L
"");
86 AddMenuInfoLine(&AboutMenu
, L
"Copyright (c) 2006-2010 Christoph Pfisterer");
87 AddMenuInfoLine(&AboutMenu
, L
"Copyright (c) 2012 Roderick W. Smith");
88 AddMenuInfoLine(&AboutMenu
, L
"Portions Copyright (c) Intel Corporation and others");
89 AddMenuInfoLine(&AboutMenu
, L
"Distributed under the terms of the GNU GPLv3 license");
90 AddMenuInfoLine(&AboutMenu
, L
"");
91 AddMenuInfoLine(&AboutMenu
, L
"Running on:");
92 AddMenuInfoLine(&AboutMenu
, PoolPrint(L
" EFI Revision %d.%02d",
93 ST
->Hdr
.Revision
>> 16, ST
->Hdr
.Revision
& ((1 << 16) - 1)));
95 AddMenuInfoLine(&AboutMenu
, L
" Platform: x86 (32 bit)");
97 AddMenuInfoLine(&AboutMenu
, L
" Platform: x86_64 (64 bit)");
99 AddMenuInfoLine(&AboutMenu
, L
" Platform: unknown");
101 AddMenuInfoLine(&AboutMenu
, PoolPrint(L
" Firmware: %s %d.%02d",
102 ST
->FirmwareVendor
, ST
->FirmwareRevision
>> 16, ST
->FirmwareRevision
& ((1 << 16) - 1)));
103 AddMenuInfoLine(&AboutMenu
, PoolPrint(L
" Screen Output: %s", egScreenDescription()));
104 AddMenuInfoLine(&AboutMenu
, L
"");
105 AddMenuInfoLine(&AboutMenu
, L
"For more information, see the rEFInd Web site:");
106 AddMenuInfoLine(&AboutMenu
, L
"http://www.rodsbooks.com/refind/");
107 AddMenuEntry(&AboutMenu
, &MenuEntryReturn
);
110 RunMenu(&AboutMenu
, NULL
);
111 } /* VOID AboutrEFInd() */
113 static EFI_STATUS
StartEFIImageList(IN EFI_DEVICE_PATH
**DevicePaths
,
114 IN CHAR16
*LoadOptions
, IN CHAR16
*LoadOptionsPrefix
,
115 IN CHAR16
*ImageTitle
,
116 OUT UINTN
*ErrorInStep
)
118 EFI_STATUS Status
, ReturnStatus
;
119 EFI_HANDLE ChildImageHandle
;
120 EFI_LOADED_IMAGE
*ChildLoadedImage
;
121 UINTN DevicePathIndex
;
122 CHAR16 ErrorInfo
[256];
123 CHAR16
*FullLoadOptions
= NULL
;
125 Print(L
"Starting %s\n", ImageTitle
);
126 if (ErrorInStep
!= NULL
)
129 // load the image into memory
130 ReturnStatus
= Status
= EFI_NOT_FOUND
; // in case the list is empty
131 for (DevicePathIndex
= 0; DevicePaths
[DevicePathIndex
] != NULL
; DevicePathIndex
++) {
132 ReturnStatus
= Status
= refit_call6_wrapper(BS
->LoadImage
, FALSE
, SelfImageHandle
, DevicePaths
[DevicePathIndex
], NULL
, 0, &ChildImageHandle
);
133 if (ReturnStatus
!= EFI_NOT_FOUND
)
136 SPrint(ErrorInfo
, 255, L
"while loading %s", ImageTitle
);
137 if (CheckError(Status
, ErrorInfo
)) {
138 if (ErrorInStep
!= NULL
)
144 if (LoadOptions
!= NULL
) {
145 ReturnStatus
= Status
= refit_call3_wrapper(BS
->HandleProtocol
, ChildImageHandle
, &LoadedImageProtocol
, (VOID
**) &ChildLoadedImage
);
146 if (CheckError(Status
, L
"while getting a LoadedImageProtocol handle")) {
147 if (ErrorInStep
!= NULL
)
152 if (LoadOptionsPrefix
!= NULL
) {
153 FullLoadOptions
= PoolPrint(L
"%s %s ", LoadOptionsPrefix
, LoadOptions
);
154 // NOTE: That last space is also added by the EFI shell and seems to be significant
155 // when passing options to Apple's boot.efi...
156 LoadOptions
= FullLoadOptions
;
158 // NOTE: We also include the terminating null in the length for safety.
159 ChildLoadedImage
->LoadOptions
= (VOID
*)LoadOptions
;
160 ChildLoadedImage
->LoadOptionsSize
= ((UINT32
)StrLen(LoadOptions
) + 1) * sizeof(CHAR16
);
161 Print(L
"Using load options '%s'\n", LoadOptions
);
164 // close open file handles
167 // turn control over to the image
168 // TODO: (optionally) re-enable the EFI watchdog timer!
169 ReturnStatus
= Status
= refit_call3_wrapper(BS
->StartImage
, ChildImageHandle
, NULL
, NULL
);
170 // control returns here when the child image calls Exit()
171 SPrint(ErrorInfo
, 255, L
"returned from %s", ImageTitle
);
172 if (CheckError(Status
, ErrorInfo
)) {
173 if (ErrorInStep
!= NULL
)
177 // re-open file handles
181 // unload the image, we don't care if it works or not...
182 Status
= refit_call1_wrapper(BS
->UnloadImage
, ChildImageHandle
);
184 if (FullLoadOptions
!= NULL
)
185 FreePool(FullLoadOptions
);
187 } /* static EFI_STATUS StartEFIImageList() */
189 static EFI_STATUS
StartEFIImage(IN EFI_DEVICE_PATH
*DevicePath
,
190 IN CHAR16
*LoadOptions
, IN CHAR16
*LoadOptionsPrefix
,
191 IN CHAR16
*ImageTitle
,
192 OUT UINTN
*ErrorInStep
)
194 EFI_DEVICE_PATH
*DevicePaths
[2];
196 DevicePaths
[0] = DevicePath
;
197 DevicePaths
[1] = NULL
;
198 return StartEFIImageList(DevicePaths
, LoadOptions
, LoadOptionsPrefix
, ImageTitle
, ErrorInStep
);
199 } /* static EFI_STATUS StartEFIImage() */
202 // EFI OS loader functions
205 static VOID
StartLoader(IN LOADER_ENTRY
*Entry
)
207 UINTN ErrorInStep
= 0;
209 BeginExternalScreen(Entry
->UseGraphicsMode
, L
"Booting OS");
210 StartEFIImage(Entry
->DevicePath
, Entry
->LoadOptions
,
211 Basename(Entry
->LoaderPath
), Basename(Entry
->LoaderPath
), &ErrorInStep
);
212 FinishExternalScreen();
215 // Locate an initrd or initramfs file that matches the kernel specified by LoaderPath.
216 // The matching file has a name that begins with "init" and includes the same version
217 // number string as is found in LoaderPath -- but not a longer version number string.
218 // For instance, if LoaderPath is \EFI\kernels\bzImage-3.3.0.efi, and if \EFI\kernels
219 // has a file called initramfs-3.3.0.img, this function will return the string
220 // '\EFI\kernels\initramfs-3.3.0.img'. If the directory ALSO contains the file
221 // initramfs-3.3.0-rc7.img or initramfs-13.3.0.img, those files will NOT match;
222 // however, initmine-3.3.0.img might match. (FindInitrd() returns the first match it
223 // finds). Thus, care should be taken to avoid placing duplicate matching files in
224 // the kernel's directory.
225 // If no matching init file can be found, returns NULL.
226 static CHAR16
* FindInitrd(IN CHAR16
*LoaderPath
, IN REFIT_VOLUME
*Volume
) {
227 CHAR16
*InitrdName
= NULL
, *FileName
, *KernelVersion
, *InitrdVersion
, *Path
;
228 REFIT_DIR_ITER DirIter
;
229 EFI_FILE_INFO
*DirEntry
;
231 FileName
= Basename(LoaderPath
);
232 KernelVersion
= FindNumbers(FileName
);
233 Path
= FindPath(LoaderPath
);
235 DirIterOpen(Volume
->RootDir
, Path
, &DirIter
);
236 while ((DirIterNext(&DirIter
, 2, L
"init*", &DirEntry
)) && (InitrdName
== NULL
)) {
237 InitrdVersion
= FindNumbers(DirEntry
->FileName
);
238 if (KernelVersion
!= NULL
) {
239 if (StriCmp(InitrdVersion
, KernelVersion
) == 0)
240 InitrdName
= PoolPrint(L
"%s\\%s", Path
, DirEntry
->FileName
);
242 if (InitrdVersion
== NULL
)
243 InitrdName
= PoolPrint(L
"%s\\%s", Path
, DirEntry
->FileName
);
245 if (InitrdVersion
!= NULL
)
246 FreePool(InitrdVersion
);
248 DirIterClose(&DirIter
);
250 // Note: Don't FreePool(FileName), since Basename returns a pointer WITHIN the string it's passed.
251 FreePool(KernelVersion
);
254 } // static CHAR16 * FindInitrd()
256 LOADER_ENTRY
* AddPreparedLoaderEntry(LOADER_ENTRY
*Entry
) {
257 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
260 } // LOADER_ENTRY * AddPreparedLoaderEntry()
262 // Creates a new LOADER_ENTRY data structure and populates it with
263 // default values from the specified Entry, or NULL values if Entry
264 // is unspecified (NULL).
265 // Returns a pointer to the new data structure, or NULL if it
266 // couldn't be allocated
267 LOADER_ENTRY
*InitializeLoaderEntry(IN LOADER_ENTRY
*Entry
) {
268 LOADER_ENTRY
*NewEntry
= NULL
;
270 NewEntry
= AllocateZeroPool(sizeof(LOADER_ENTRY
));
271 if (NewEntry
!= NULL
) {
272 NewEntry
->me
.Title
= NULL
;
273 NewEntry
->me
.Tag
= TAG_LOADER
;
274 NewEntry
->Enabled
= TRUE
;
275 NewEntry
->UseGraphicsMode
= FALSE
;
276 NewEntry
->OSType
= 0;
278 NewEntry
->LoaderPath
= StrDuplicate(Entry
->LoaderPath
);
279 NewEntry
->VolName
= StrDuplicate(Entry
->VolName
);
280 NewEntry
->DevicePath
= Entry
->DevicePath
;
281 NewEntry
->UseGraphicsMode
= Entry
->UseGraphicsMode
;
282 NewEntry
->LoadOptions
= StrDuplicate(Entry
->LoadOptions
);
283 NewEntry
->InitrdPath
= StrDuplicate(Entry
->InitrdPath
);
287 } // LOADER_ENTRY *InitializeLoaderEntry()
289 // Prepare a REFIT_MENU_SCREEN data structure for a subscreen entry. This sets up
290 // the default entry that launches the boot loader using the same options as the
291 // main Entry does. Subsequent options can be added by the calling function.
292 // If a subscreen already exists in the Entry that's passed to this function,
293 // it's left unchanged and a pointer to it is returned.
294 // Returns a pointer to the new subscreen data structure, or NULL if there
295 // were problems allocating memory.
296 REFIT_MENU_SCREEN
*InitializeSubScreen(IN LOADER_ENTRY
*Entry
) {
297 CHAR16
*FileName
, *Temp
;
298 REFIT_MENU_SCREEN
*SubScreen
= NULL
;
299 LOADER_ENTRY
*SubEntry
;
301 FileName
= Basename(Entry
->LoaderPath
);
302 if (Entry
->me
.SubScreen
== NULL
) { // No subscreen yet; initialize default entry....
303 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
304 if (SubScreen
!= NULL
) {
305 SubScreen
->Title
= PoolPrint(L
"Boot Options for %s on %s", (Entry
->Title
!= NULL
) ? Entry
->Title
: FileName
, Entry
->VolName
);
306 SubScreen
->TitleImage
= Entry
->me
.Image
;
308 SubEntry
= InitializeLoaderEntry(Entry
);
309 if (SubEntry
!= NULL
) {
310 SubEntry
->me
.Title
= L
"Boot using default options";
311 if ((SubEntry
->InitrdPath
!= NULL
) && (StrLen(SubEntry
->InitrdPath
) > 0) && (!StriSubCmp(L
"initrd", SubEntry
->LoadOptions
))) {
312 Temp
= PoolPrint(L
"initrd=%s", SubEntry
->InitrdPath
);
313 MergeStrings(&SubEntry
->LoadOptions
, Temp
, L
' ');
316 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
317 } // if (SubEntry != NULL)
318 } // if (SubScreen != NULL)
319 } else { // existing subscreen; less initialization, and just add new entry later....
320 SubScreen
= Entry
->me
.SubScreen
;
323 } // REFIT_MENU_SCREEN *InitializeSubScreen()
325 VOID
GenerateSubScreen(LOADER_ENTRY
*Entry
, IN REFIT_VOLUME
*Volume
) {
326 REFIT_MENU_SCREEN
*SubScreen
;
327 LOADER_ENTRY
*SubEntry
;
328 CHAR16
*FileName
, *InitrdOption
= NULL
, *Temp
;
329 CHAR16 DiagsFileName
[256];
334 FileName
= Basename(Entry
->LoaderPath
);
335 // create the submenu
336 if (StrLen(Entry
->Title
) == 0) {
337 FreePool(Entry
->Title
);
340 SubScreen
= InitializeSubScreen(Entry
);
342 // loader-specific submenu entries
343 if (Entry
->OSType
== 'M') { // entries for Mac OS X
345 SubEntry
= InitializeLoaderEntry(Entry
);
346 if (SubEntry
!= NULL
) {
347 SubEntry
->me
.Title
= L
"Boot Mac OS X with a 64-bit kernel";
348 SubEntry
->LoadOptions
= L
"arch=x86_64";
349 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
352 SubEntry
= InitializeLoaderEntry(Entry
);
353 if (SubEntry
!= NULL
) {
354 SubEntry
->me
.Title
= L
"Boot Mac OS X with a 32-bit kernel";
355 SubEntry
->LoadOptions
= L
"arch=i386";
356 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
360 if (!(GlobalConfig
.DisableFlags
& DISABLE_FLAG_SINGLEUSER
)) {
361 SubEntry
= InitializeLoaderEntry(Entry
);
362 if (SubEntry
!= NULL
) {
363 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode";
364 SubEntry
->UseGraphicsMode
= FALSE
;
365 SubEntry
->LoadOptions
= L
"-v";
366 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
370 SubEntry
= InitializeLoaderEntry(Entry
);
371 if (SubEntry
!= NULL
) {
372 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode (64-bit)";
373 SubEntry
->UseGraphicsMode
= FALSE
;
374 SubEntry
->LoadOptions
= L
"-v arch=x86_64";
375 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
378 SubEntry
= InitializeLoaderEntry(Entry
);
379 if (SubEntry
!= NULL
) {
380 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode (32-bit)";
381 SubEntry
->UseGraphicsMode
= FALSE
;
382 SubEntry
->LoadOptions
= L
"-v arch=i386";
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 single user mode";
390 SubEntry
->UseGraphicsMode
= FALSE
;
391 SubEntry
->LoadOptions
= L
"-v -s";
392 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
396 // check for Apple hardware diagnostics
397 StrCpy(DiagsFileName
, L
"\\System\\Library\\CoreServices\\.diagnostics\\diags.efi");
398 if (FileExists(Volume
->RootDir
, DiagsFileName
) && !(GlobalConfig
.DisableFlags
& DISABLE_FLAG_HWTEST
)) {
399 SubEntry
= InitializeLoaderEntry(Entry
);
400 if (SubEntry
!= NULL
) {
401 SubEntry
->me
.Title
= L
"Run Apple Hardware Test";
402 FreePool(SubEntry
->LoaderPath
);
403 SubEntry
->LoaderPath
= StrDuplicate(DiagsFileName
);
404 SubEntry
->DevicePath
= FileDevicePath(Volume
->DeviceHandle
, SubEntry
->LoaderPath
);
405 SubEntry
->UseGraphicsMode
= TRUE
;
406 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
408 } // if diagnostics entry found
410 } else if (Entry
->OSType
== 'L') { // entries for Linux kernels with EFI stub loaders
411 File
= ReadLinuxOptionsFile(Entry
->LoaderPath
, Volume
);
413 if ((Temp
= FindInitrd(Entry
->LoaderPath
, Volume
)) != NULL
)
414 InitrdOption
= PoolPrint(L
"initrd=%s", Temp
);
415 TokenCount
= ReadTokenLine(File
, &TokenList
); // read and discard first entry, since it's
416 FreeTokenLine(&TokenList
, &TokenCount
); // set up by InitializeSubScreen(), earlier....
417 while ((TokenCount
= ReadTokenLine(File
, &TokenList
)) > 1) {
418 SubEntry
= InitializeLoaderEntry(Entry
);
419 SubEntry
->me
.Title
= StrDuplicate(TokenList
[0]);
420 if (SubEntry
->LoadOptions
!= NULL
)
421 FreePool(SubEntry
->LoadOptions
);
422 SubEntry
->LoadOptions
= StrDuplicate(TokenList
[1]);
423 MergeStrings(&SubEntry
->LoadOptions
, InitrdOption
, L
' ');
424 FreeTokenLine(&TokenList
, &TokenCount
);
425 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
428 FreePool(InitrdOption
);
432 } // if Linux options file exists
434 } else if (Entry
->OSType
== 'E') { // entries for ELILO
435 SubEntry
= InitializeLoaderEntry(Entry
);
436 if (SubEntry
!= NULL
) {
437 SubEntry
->me
.Title
= PoolPrint(L
"Run %s in interactive mode", FileName
);
438 SubEntry
->LoadOptions
= L
"-p";
439 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
442 SubEntry
= InitializeLoaderEntry(Entry
);
443 if (SubEntry
!= NULL
) {
444 SubEntry
->me
.Title
= L
"Boot Linux for a 17\" iMac or a 15\" MacBook Pro (*)";
445 SubEntry
->UseGraphicsMode
= TRUE
;
446 SubEntry
->LoadOptions
= L
"-d 0 i17";
447 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
450 SubEntry
= InitializeLoaderEntry(Entry
);
451 if (SubEntry
!= NULL
) {
452 SubEntry
->me
.Title
= L
"Boot Linux for a 20\" iMac (*)";
453 SubEntry
->UseGraphicsMode
= TRUE
;
454 SubEntry
->LoadOptions
= L
"-d 0 i20";
455 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
458 SubEntry
= InitializeLoaderEntry(Entry
);
459 if (SubEntry
!= NULL
) {
460 SubEntry
->me
.Title
= L
"Boot Linux for a Mac Mini (*)";
461 SubEntry
->UseGraphicsMode
= TRUE
;
462 SubEntry
->LoadOptions
= L
"-d 0 mini";
463 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
466 AddMenuInfoLine(SubScreen
, L
"NOTE: This is an example. Entries");
467 AddMenuInfoLine(SubScreen
, L
"marked with (*) may not work.");
469 } else if (Entry
->OSType
== 'X') { // entries for xom.efi
470 // by default, skip the built-in selection and boot from hard disk only
471 Entry
->LoadOptions
= L
"-s -h";
473 SubEntry
= InitializeLoaderEntry(Entry
);
474 if (SubEntry
!= NULL
) {
475 SubEntry
->me
.Title
= L
"Boot Windows from Hard Disk";
476 SubEntry
->LoadOptions
= L
"-s -h";
477 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
480 SubEntry
= InitializeLoaderEntry(Entry
);
481 if (SubEntry
!= NULL
) {
482 SubEntry
->me
.Title
= L
"Boot Windows from CD-ROM";
483 SubEntry
->LoadOptions
= L
"-s -c";
484 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
487 SubEntry
= InitializeLoaderEntry(Entry
);
488 if (SubEntry
!= NULL
) {
489 SubEntry
->me
.Title
= PoolPrint(L
"Run %s in text mode", FileName
);
490 SubEntry
->UseGraphicsMode
= FALSE
;
491 SubEntry
->LoadOptions
= L
"-v";
492 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
494 } // entries for xom.efi
495 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
496 Entry
->me
.SubScreen
= SubScreen
;
497 } // VOID GenerateSubScreen()
499 // Returns options for a Linux kernel. Reads them from an options file in the
500 // kernel's directory; and if present, adds an initrd= option for an initial
501 // RAM disk file with the same version number as the kernel file.
502 static CHAR16
* GetMainLinuxOptions(IN CHAR16
* LoaderPath
, IN REFIT_VOLUME
*Volume
) {
503 CHAR16
*Options
= NULL
, *InitrdName
, *InitrdOption
= NULL
;
505 Options
= GetFirstOptionsFromFile(LoaderPath
, Volume
);
506 InitrdName
= FindInitrd(LoaderPath
, Volume
);
507 if (InitrdName
!= NULL
)
508 InitrdOption
= PoolPrint(L
"initrd=%s", InitrdName
);
509 MergeStrings(&Options
, InitrdOption
, ' ');
510 if (InitrdOption
!= NULL
)
511 FreePool(InitrdOption
);
512 if (InitrdName
!= NULL
)
513 FreePool(InitrdName
);
515 } // static CHAR16 * GetMainLinuxOptions()
517 // Sets a few defaults for a loader entry -- mainly the icon, but also the OS type
518 // code and shortcut letter. For Linux EFI stub loaders, also sets kernel options
519 // that will (with luck) work fairly automatically.
520 VOID
SetLoaderDefaults(LOADER_ENTRY
*Entry
, CHAR16
*LoaderPath
, IN REFIT_VOLUME
*Volume
) {
521 CHAR16 IconFileName
[256];
522 CHAR16
*FileName
, *OSIconName
= NULL
, *Temp
;
523 CHAR16 ShortcutLetter
= 0;
525 FileName
= Basename(LoaderPath
);
527 // locate a custom icon for the loader
528 StrCpy(IconFileName
, LoaderPath
);
529 ReplaceExtension(IconFileName
, L
".icns");
530 if (FileExists(Volume
->RootDir
, IconFileName
)) {
531 Entry
->me
.Image
= LoadIcns(Volume
->RootDir
, IconFileName
, 128);
534 Temp
= FindLastDirName(LoaderPath
);
535 MergeStrings(&OSIconName
, Temp
, L
',');
537 if (OSIconName
!= NULL
) {
538 ShortcutLetter
= OSIconName
[0];
541 // detect specific loaders
542 if (StriSubCmp(L
"bzImage", LoaderPath
) || StriSubCmp(L
"vmlinuz", LoaderPath
)) {
543 MergeStrings(&OSIconName
, L
"linux", L
',');
545 if (ShortcutLetter
== 0)
546 ShortcutLetter
= 'L';
547 Entry
->LoadOptions
= GetMainLinuxOptions(LoaderPath
, Volume
);
548 } else if (StriSubCmp(L
"refit", LoaderPath
)) {
549 MergeStrings(&OSIconName
, L
"refit", L
',');
551 ShortcutLetter
= 'R';
552 } else if (StriCmp(LoaderPath
, MACOSX_LOADER_PATH
) == 0) {
553 MergeStrings(&OSIconName
, L
"mac", L
',');
554 Entry
->UseGraphicsMode
= TRUE
;
556 ShortcutLetter
= 'M';
557 } else if (StriCmp(FileName
, L
"diags.efi") == 0) {
558 MergeStrings(&OSIconName
, L
"hwtest", L
',');
559 } else if (StriCmp(FileName
, L
"e.efi") == 0 || StriCmp(FileName
, L
"elilo.efi") == 0) {
560 MergeStrings(&OSIconName
, L
"elilo,linux", L
',');
562 if (ShortcutLetter
== 0)
563 ShortcutLetter
= 'L';
564 } else if (StriCmp(FileName
, L
"cdboot.efi") == 0 ||
565 StriCmp(FileName
, L
"bootmgr.efi") == 0 ||
566 StriCmp(FileName
, L
"Bootmgfw.efi") == 0) {
567 MergeStrings(&OSIconName
, L
"win", L
',');
569 ShortcutLetter
= 'W';
570 } else if (StriCmp(FileName
, L
"xom.efi") == 0) {
571 MergeStrings(&OSIconName
, L
"xom,win", L
',');
572 Entry
->UseGraphicsMode
= TRUE
;
574 ShortcutLetter
= 'W';
577 if ((ShortcutLetter
>= 'a') && (ShortcutLetter
<= 'z'))
578 ShortcutLetter
= ShortcutLetter
- 'a' + 'A'; // convert lowercase to uppercase
579 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
580 if (Entry
->me
.Image
== NULL
)
581 Entry
->me
.Image
= LoadOSIcon(OSIconName
, L
"unknown", FALSE
);
582 } // VOID SetLoaderDefaults()
584 // Add a specified EFI boot loader to the list, using automatic settings
585 // for icons, options, etc.
586 LOADER_ENTRY
* AddLoaderEntry(IN CHAR16
*LoaderPath
, IN CHAR16
*LoaderTitle
, IN REFIT_VOLUME
*Volume
) {
589 Entry
= InitializeLoaderEntry(NULL
);
591 Entry
->Title
= StrDuplicate(LoaderTitle
);
592 Entry
->me
.Title
= PoolPrint(L
"Boot %s from %s", (LoaderTitle
!= NULL
) ? LoaderTitle
: LoaderPath
+ 1, Volume
->VolName
);
594 Entry
->me
.BadgeImage
= Volume
->VolBadgeImage
;
595 Entry
->LoaderPath
= StrDuplicate(LoaderPath
);
596 Entry
->VolName
= Volume
->VolName
;
597 Entry
->DevicePath
= FileDevicePath(Volume
->DeviceHandle
, Entry
->LoaderPath
);
598 SetLoaderDefaults(Entry
, LoaderPath
, Volume
);
599 GenerateSubScreen(Entry
, Volume
);
600 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
604 } // LOADER_ENTRY * AddLoaderEntry()
606 // Scan an individual directory for EFI boot loader files and, if found,
607 // add them to the list.
608 static VOID
ScanLoaderDir(IN REFIT_VOLUME
*Volume
, IN CHAR16
*Path
)
611 REFIT_DIR_ITER DirIter
;
612 EFI_FILE_INFO
*DirEntry
;
613 CHAR16 FileName
[256];
615 // Note: SelfDirPath includes a leading backslash ('\'), but Path
616 // doesn't, so we rejigger the string to compensate....
617 if (!SelfDirPath
|| !Path
|| ((StriCmp(Path
, &SelfDirPath
[1]) == 0) && Volume
!= SelfVolume
) ||
618 (StriCmp(Path
, &SelfDirPath
[1]) != 0)) {
619 // look through contents of the directory
620 DirIterOpen(Volume
->RootDir
, Path
, &DirIter
);
621 while (DirIterNext(&DirIter
, 2, L
"*.efi", &DirEntry
)) {
622 if (DirEntry
->FileName
[0] == '.' ||
623 StriCmp(DirEntry
->FileName
, L
"TextMode.efi") == 0 ||
624 StriCmp(DirEntry
->FileName
, L
"ebounce.efi") == 0 ||
625 StriCmp(DirEntry
->FileName
, L
"GraphicsConsole.efi") == 0 ||
626 StriSubCmp(L
"shell", DirEntry
->FileName
))
627 continue; // skip this
630 SPrint(FileName
, 255, L
"\\%s\\%s", Path
, DirEntry
->FileName
);
632 SPrint(FileName
, 255, L
"\\%s", DirEntry
->FileName
);
633 AddLoaderEntry(FileName
, NULL
, Volume
);
635 Status
= DirIterClose(&DirIter
);
636 if (Status
!= EFI_NOT_FOUND
) {
638 SPrint(FileName
, 255, L
"while scanning the %s directory", Path
);
640 StrCpy(FileName
, L
"while scanning the root directory");
641 CheckError(Status
, FileName
);
642 } // if (Status != EFI_NOT_FOUND)
643 } // if not scanning our own directory
644 } /* static VOID ScanLoaderDir() */
646 static VOID
ScanEfiFiles(REFIT_VOLUME
*Volume
) {
648 REFIT_DIR_ITER EfiDirIter
;
649 EFI_FILE_INFO
*EfiDirEntry
;
650 CHAR16 FileName
[256];
652 if ((Volume
->RootDir
!= NULL
) && (Volume
->VolName
!= NULL
)) {
653 // check for Mac OS X boot loader
654 StrCpy(FileName
, MACOSX_LOADER_PATH
);
655 if (FileExists(Volume
->RootDir
, FileName
)) {
656 AddLoaderEntry(FileName
, L
"Mac OS X", Volume
);
660 StrCpy(FileName
, L
"\\System\\Library\\CoreServices\\xom.efi");
661 if (FileExists(Volume
->RootDir
, FileName
)) {
662 AddLoaderEntry(FileName
, L
"Windows XP (XoM)", Volume
);
665 // check for Microsoft boot loader/menu
666 StrCpy(FileName
, L
"\\EFI\\Microsoft\\Boot\\Bootmgfw.efi");
667 if (FileExists(Volume
->RootDir
, FileName
)) {
668 AddLoaderEntry(FileName
, L
"Microsoft EFI boot", Volume
);
671 // scan the root directory for EFI executables
672 ScanLoaderDir(Volume
, NULL
);
673 // scan the elilo directory (as used on gimli's first Live CD)
674 ScanLoaderDir(Volume
, L
"elilo");
675 // scan the boot directory
676 ScanLoaderDir(Volume
, L
"boot");
678 // scan subdirectories of the EFI directory (as per the standard)
679 DirIterOpen(Volume
->RootDir
, L
"EFI", &EfiDirIter
);
680 while (DirIterNext(&EfiDirIter
, 1, NULL
, &EfiDirEntry
)) {
681 if (StriCmp(EfiDirEntry
->FileName
, L
"tools") == 0 || EfiDirEntry
->FileName
[0] == '.')
682 continue; // skip this, doesn't contain boot loaders
683 SPrint(FileName
, 255, L
"EFI\\%s", EfiDirEntry
->FileName
);
684 ScanLoaderDir(Volume
, FileName
);
686 Status
= DirIterClose(&EfiDirIter
);
687 if (Status
!= EFI_NOT_FOUND
)
688 CheckError(Status
, L
"while scanning the EFI directory");
690 } // static VOID ScanEfiFiles()
692 // Scan internal disks for valid EFI boot loaders....
693 static VOID
ScanInternal(VOID
) {
696 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
697 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_INTERNAL
) {
698 ScanEfiFiles(Volumes
[VolumeIndex
]);
701 } // static VOID ScanInternal()
703 // Scan external disks for valid EFI boot loaders....
704 static VOID
ScanExternal(VOID
) {
707 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
708 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_EXTERNAL
) {
709 ScanEfiFiles(Volumes
[VolumeIndex
]);
712 } // static VOID ScanExternal()
714 // Scan internal disks for valid EFI boot loaders....
715 static VOID
ScanOptical(VOID
) {
718 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
719 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_OPTICAL
) {
720 ScanEfiFiles(Volumes
[VolumeIndex
]);
723 } // static VOID ScanOptical()
726 // legacy boot functions
729 static EFI_STATUS
ActivateMbrPartition(IN EFI_BLOCK_IO
*BlockIO
, IN UINTN PartitionIndex
)
732 UINT8 SectorBuffer
[512];
733 MBR_PARTITION_INFO
*MbrTable
, *EMbrTable
;
734 UINT32 ExtBase
, ExtCurrent
, NextExtCurrent
;
735 UINTN LogicalPartitionIndex
= 4;
737 BOOLEAN HaveBootCode
;
740 Status
= refit_call5_wrapper(BlockIO
->ReadBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, 0, 512, SectorBuffer
);
741 if (EFI_ERROR(Status
))
743 if (*((UINT16
*)(SectorBuffer
+ 510)) != 0xaa55)
744 return EFI_NOT_FOUND
; // safety measure #1
746 // add boot code if necessary
747 HaveBootCode
= FALSE
;
748 for (i
= 0; i
< MBR_BOOTCODE_SIZE
; i
++) {
749 if (SectorBuffer
[i
] != 0) {
755 // no boot code found in the MBR, add the syslinux MBR code
756 SetMem(SectorBuffer
, MBR_BOOTCODE_SIZE
, 0);
757 CopyMem(SectorBuffer
, syslinux_mbr
, SYSLINUX_MBR_SIZE
);
760 // set the partition active
761 MbrTable
= (MBR_PARTITION_INFO
*)(SectorBuffer
+ 446);
763 for (i
= 0; i
< 4; i
++) {
764 if (MbrTable
[i
].Flags
!= 0x00 && MbrTable
[i
].Flags
!= 0x80)
765 return EFI_NOT_FOUND
; // safety measure #2
766 if (i
== PartitionIndex
)
767 MbrTable
[i
].Flags
= 0x80;
768 else if (PartitionIndex
>= 4 && IS_EXTENDED_PART_TYPE(MbrTable
[i
].Type
)) {
769 MbrTable
[i
].Flags
= 0x80;
770 ExtBase
= MbrTable
[i
].StartLBA
;
772 MbrTable
[i
].Flags
= 0x00;
776 Status
= refit_call5_wrapper(BlockIO
->WriteBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, 0, 512, SectorBuffer
);
777 if (EFI_ERROR(Status
))
780 if (PartitionIndex
>= 4) {
781 // we have to activate a logical partition, so walk the EMBR chain
783 // NOTE: ExtBase was set above while looking at the MBR table
784 for (ExtCurrent
= ExtBase
; ExtCurrent
; ExtCurrent
= NextExtCurrent
) {
786 Status
= refit_call5_wrapper(BlockIO
->ReadBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, ExtCurrent
, 512, SectorBuffer
);
787 if (EFI_ERROR(Status
))
789 if (*((UINT16
*)(SectorBuffer
+ 510)) != 0xaa55)
790 return EFI_NOT_FOUND
; // safety measure #3
792 // scan EMBR, set appropriate partition active
793 EMbrTable
= (MBR_PARTITION_INFO
*)(SectorBuffer
+ 446);
795 for (i
= 0; i
< 4; i
++) {
796 if (EMbrTable
[i
].Flags
!= 0x00 && EMbrTable
[i
].Flags
!= 0x80)
797 return EFI_NOT_FOUND
; // safety measure #4
798 if (EMbrTable
[i
].StartLBA
== 0 || EMbrTable
[i
].Size
== 0)
800 if (IS_EXTENDED_PART_TYPE(EMbrTable
[i
].Type
)) {
802 NextExtCurrent
= ExtBase
+ EMbrTable
[i
].StartLBA
;
803 EMbrTable
[i
].Flags
= (PartitionIndex
>= LogicalPartitionIndex
) ? 0x80 : 0x00;
807 EMbrTable
[i
].Flags
= (PartitionIndex
== LogicalPartitionIndex
) ? 0x80 : 0x00;
808 LogicalPartitionIndex
++;
812 // write current EMBR
813 Status
= refit_call5_wrapper(BlockIO
->WriteBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, ExtCurrent
, 512, SectorBuffer
);
814 if (EFI_ERROR(Status
))
817 if (PartitionIndex
< LogicalPartitionIndex
)
818 break; // stop the loop, no need to touch further EMBRs
824 } /* static EFI_STATUS ActivateMbrPartition() */
826 // early 2006 Core Duo / Core Solo models
827 static UINT8 LegacyLoaderDevicePath1Data
[] = {
828 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
829 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
830 0xFF, 0xFF, 0xF9, 0xFF, 0x00, 0x00, 0x00, 0x00,
831 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
832 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
833 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
835 // mid-2006 Mac Pro (and probably other Core 2 models)
836 static UINT8 LegacyLoaderDevicePath2Data
[] = {
837 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
838 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
839 0xFF, 0xFF, 0xF7, 0xFF, 0x00, 0x00, 0x00, 0x00,
840 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
841 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
842 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
844 // mid-2007 MBP ("Santa Rosa" based models)
845 static UINT8 LegacyLoaderDevicePath3Data
[] = {
846 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
847 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
848 0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
849 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
850 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
851 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
854 static UINT8 LegacyLoaderDevicePath4Data
[] = {
855 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
856 0x00, 0x00, 0xC0, 0xFF, 0x00, 0x00, 0x00, 0x00,
857 0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
858 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
859 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
860 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
862 // late-2008 MB/MBP (NVidia chipset)
863 static UINT8 LegacyLoaderDevicePath5Data
[] = {
864 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
865 0x00, 0x40, 0xCB, 0xFF, 0x00, 0x00, 0x00, 0x00,
866 0xFF, 0xBF, 0xFF, 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,
872 static EFI_DEVICE_PATH
*LegacyLoaderList
[] = {
873 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath1Data
,
874 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath2Data
,
875 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath3Data
,
876 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath4Data
,
877 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath5Data
,
881 #define MAX_DISCOVERED_PATHS (16)
883 static VOID
StartLegacy(IN LEGACY_ENTRY
*Entry
)
886 EG_IMAGE
*BootLogoImage
;
887 UINTN ErrorInStep
= 0;
888 EFI_DEVICE_PATH
*DiscoveredPathList
[MAX_DISCOVERED_PATHS
];
890 BeginExternalScreen(TRUE
, L
"Booting Legacy OS");
892 BootLogoImage
= LoadOSIcon(Entry
->Volume
->OSIconName
, L
"legacy", TRUE
);
893 if (BootLogoImage
!= NULL
)
894 BltImageAlpha(BootLogoImage
,
895 (UGAWidth
- BootLogoImage
->Width
) >> 1,
896 (UGAHeight
- BootLogoImage
->Height
) >> 1,
897 &StdBackgroundPixel
);
899 if (Entry
->Volume
->IsMbrPartition
)
900 ActivateMbrPartition(Entry
->Volume
->WholeDiskBlockIO
, Entry
->Volume
->MbrPartitionIndex
);
902 ExtractLegacyLoaderPaths(DiscoveredPathList
, MAX_DISCOVERED_PATHS
, LegacyLoaderList
);
904 Status
= StartEFIImageList(DiscoveredPathList
, Entry
->LoadOptions
, NULL
, L
"legacy loader", &ErrorInStep
);
905 if (Status
== EFI_NOT_FOUND
) {
906 if (ErrorInStep
== 1) {
907 Print(L
"\nPlease make sure that you have the latest firmware update installed.\n");
908 } else if (ErrorInStep
== 3) {
909 Print(L
"\nThe firmware refused to boot from the selected volume. Note that external\n"
910 L
"hard drives are not well-supported by Apple's firmware for legacy OS booting.\n");
913 FinishExternalScreen();
914 } /* static VOID StartLegacy() */
916 static LEGACY_ENTRY
* AddLegacyEntry(IN CHAR16
*LoaderTitle
, IN REFIT_VOLUME
*Volume
)
918 LEGACY_ENTRY
*Entry
, *SubEntry
;
919 REFIT_MENU_SCREEN
*SubScreen
;
921 CHAR16 ShortcutLetter
= 0;
923 if (LoaderTitle
== NULL
) {
924 if (Volume
->OSName
!= NULL
) {
925 LoaderTitle
= Volume
->OSName
;
926 if (LoaderTitle
[0] == 'W' || LoaderTitle
[0] == 'L')
927 ShortcutLetter
= LoaderTitle
[0];
929 LoaderTitle
= L
"Legacy OS";
931 if (Volume
->VolName
!= NULL
)
932 VolDesc
= Volume
->VolName
;
934 VolDesc
= (Volume
->DiskKind
== DISK_KIND_OPTICAL
) ? L
"CD" : L
"HD";
936 // prepare the menu entry
937 Entry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
938 Entry
->me
.Title
= PoolPrint(L
"Boot %s from %s", LoaderTitle
, VolDesc
);
939 Entry
->me
.Tag
= TAG_LEGACY
;
941 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
942 Entry
->me
.Image
= LoadOSIcon(Volume
->OSIconName
, L
"legacy", FALSE
);
943 Entry
->me
.BadgeImage
= Volume
->VolBadgeImage
;
944 Entry
->Volume
= Volume
;
945 Entry
->LoadOptions
= (Volume
->DiskKind
== DISK_KIND_OPTICAL
) ? L
"CD" :
946 ((Volume
->DiskKind
== DISK_KIND_EXTERNAL
) ? L
"USB" : L
"HD");
947 Entry
->Enabled
= TRUE
;
949 // create the submenu
950 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
951 SubScreen
->Title
= PoolPrint(L
"Boot Options for %s on %s", LoaderTitle
, VolDesc
);
952 SubScreen
->TitleImage
= Entry
->me
.Image
;
955 SubEntry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
956 SubEntry
->me
.Title
= PoolPrint(L
"Boot %s", LoaderTitle
);
957 SubEntry
->me
.Tag
= TAG_LEGACY
;
958 SubEntry
->Volume
= Entry
->Volume
;
959 SubEntry
->LoadOptions
= Entry
->LoadOptions
;
960 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
962 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
963 Entry
->me
.SubScreen
= SubScreen
;
964 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
966 } /* static LEGACY_ENTRY * AddLegacyEntry() */
968 static VOID
ScanLegacyVolume(REFIT_VOLUME
*Volume
, UINTN VolumeIndex
) {
970 BOOLEAN ShowVolume
, HideIfOthersFound
;
973 HideIfOthersFound
= FALSE
;
974 if (Volume
->IsAppleLegacy
) {
976 HideIfOthersFound
= TRUE
;
977 } else if (Volume
->HasBootCode
) {
979 if (Volume
->BlockIO
== Volume
->WholeDiskBlockIO
&&
980 Volume
->BlockIOOffset
== 0 &&
981 Volume
->OSName
== NULL
)
982 // this is a whole disk (MBR) entry; hide if we have entries for partitions
983 HideIfOthersFound
= TRUE
;
985 if (HideIfOthersFound
) {
986 // check for other bootable entries on the same disk
987 for (VolumeIndex2
= 0; VolumeIndex2
< VolumesCount
; VolumeIndex2
++) {
988 if (VolumeIndex2
!= VolumeIndex
&& Volumes
[VolumeIndex2
]->HasBootCode
&&
989 Volumes
[VolumeIndex2
]->WholeDiskBlockIO
== Volume
->WholeDiskBlockIO
)
995 AddLegacyEntry(NULL
, Volume
);
996 } // static VOID ScanLegacyVolume()
998 // Scan attached optical discs for legacy (BIOS) boot code
999 // and add anything found to the list....
1000 static VOID
ScanLegacyDisc(VOID
)
1003 REFIT_VOLUME
*Volume
;
1005 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1006 Volume
= Volumes
[VolumeIndex
];
1007 if (Volume
->DiskKind
== DISK_KIND_OPTICAL
)
1008 ScanLegacyVolume(Volume
, VolumeIndex
);
1010 } /* static VOID ScanLegacyDisc() */
1012 // Scan internal hard disks for legacy (BIOS) boot code
1013 // and add anything found to the list....
1014 static VOID
ScanLegacyInternal(VOID
)
1017 REFIT_VOLUME
*Volume
;
1019 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1020 Volume
= Volumes
[VolumeIndex
];
1021 if (Volume
->DiskKind
== DISK_KIND_INTERNAL
)
1022 ScanLegacyVolume(Volume
, VolumeIndex
);
1024 } /* static VOID ScanLegacyInternal() */
1026 // Scan external disks for legacy (BIOS) boot code
1027 // and add anything found to the list....
1028 static VOID
ScanLegacyExternal(VOID
)
1031 REFIT_VOLUME
*Volume
;
1033 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1034 Volume
= Volumes
[VolumeIndex
];
1035 if (Volume
->DiskKind
== DISK_KIND_EXTERNAL
)
1036 ScanLegacyVolume(Volume
, VolumeIndex
);
1038 } /* static VOID ScanLegacyExternal() */
1041 // pre-boot tool functions
1044 static VOID
StartTool(IN LOADER_ENTRY
*Entry
)
1046 BeginExternalScreen(Entry
->UseGraphicsMode
, Entry
->me
.Title
+ 6); // assumes "Start <title>" as assigned below
1047 StartEFIImage(Entry
->DevicePath
, Entry
->LoadOptions
, Basename(Entry
->LoaderPath
),
1048 Basename(Entry
->LoaderPath
), NULL
);
1049 FinishExternalScreen();
1050 } /* static VOID StartTool() */
1052 static LOADER_ENTRY
* AddToolEntry(IN CHAR16
*LoaderPath
, IN CHAR16
*LoaderTitle
, IN EG_IMAGE
*Image
,
1053 IN CHAR16 ShortcutLetter
, IN BOOLEAN UseGraphicsMode
)
1055 LOADER_ENTRY
*Entry
;
1057 Entry
= AllocateZeroPool(sizeof(LOADER_ENTRY
));
1059 Entry
->me
.Title
= PoolPrint(L
"Start %s", LoaderTitle
);
1060 Entry
->me
.Tag
= TAG_TOOL
;
1062 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1063 Entry
->me
.Image
= Image
;
1064 Entry
->LoaderPath
= StrDuplicate(LoaderPath
);
1065 Entry
->DevicePath
= FileDevicePath(SelfLoadedImage
->DeviceHandle
, Entry
->LoaderPath
);
1066 Entry
->UseGraphicsMode
= UseGraphicsMode
;
1068 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1070 } /* static LOADER_ENTRY * AddToolEntry() */
1072 // Check the disk for add-on tools -- an EFI shell, the dangerous gptsync.efi, and a rescue Linux
1074 static VOID
ScanTool(VOID
)
1077 LOADER_ENTRY
*Entry
;
1080 if (GlobalConfig
.DisableFlags
& DISABLE_FLAG_TOOLS
)
1083 // look for the EFI shell
1084 while (((FileName
= FindCommaDelimited(SHELL_NAMES
, i
++)) != NULL
) && (!(GlobalConfig
.DisableFlags
& DISABLE_FLAG_SHELL
))) {
1085 if (FileExists(SelfRootDir
, FileName
)) {
1086 AddToolEntry(FileName
, L
"EFI Shell", BuiltinIcon(BUILTIN_ICON_TOOL_SHELL
), 'E', FALSE
);
1092 // look for the GPT/MBR sync tool
1093 MergeStrings(&FileName
, L
"\\efi\\tools\\gptsync.efi", 0);
1094 if (FileExists(SelfRootDir
, FileName
)) {
1095 AddToolEntry(FileName
, L
"Make Hybrid MBR", BuiltinIcon(BUILTIN_ICON_TOOL_PART
), 'P', FALSE
);
1100 // look for rescue Linux
1101 MergeStrings(&FileName
, L
"\\efi\\rescue\\elilo.efi", 0);
1102 if (SelfVolume
!= NULL
&& FileExists(SelfRootDir
, FileName
)) {
1103 Entry
= AddToolEntry(FileName
, L
"Rescue Linux", BuiltinIcon(BUILTIN_ICON_TOOL_RESCUE
), '0', FALSE
);
1105 if (UGAWidth
== 1440 && UGAHeight
== 900)
1106 Entry
->LoadOptions
= L
"-d 0 i17";
1107 else if (UGAWidth
== 1680 && UGAHeight
== 1050)
1108 Entry
->LoadOptions
= L
"-d 0 i20";
1110 Entry
->LoadOptions
= L
"-d 0 mini";
1113 } /* VOID ScanTool() */
1116 #ifdef DEBIAN_ENABLE_EFI110
1118 // pre-boot driver functions
1121 static VOID
ScanDriverDir(IN CHAR16
*Path
)
1124 REFIT_DIR_ITER DirIter
;
1125 EFI_FILE_INFO
*DirEntry
;
1126 CHAR16 FileName
[256];
1128 // look through contents of the directory
1129 DirIterOpen(SelfRootDir
, Path
, &DirIter
);
1130 while (DirIterNext(&DirIter
, 2, L
"*.EFI", &DirEntry
)) {
1131 if (DirEntry
->FileName
[0] == '.')
1132 continue; // skip this
1134 SPrint(FileName
, 255, L
"%s\\%s", Path
, DirEntry
->FileName
);
1135 Status
= StartEFIImage(FileDevicePath(SelfLoadedImage
->DeviceHandle
, FileName
),
1136 L
"", DirEntry
->FileName
, DirEntry
->FileName
, NULL
);
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
);
1145 static EFI_STATUS
ConnectAllDriversToAllControllers(VOID
)
1148 UINTN AllHandleCount
;
1149 EFI_HANDLE
*AllHandleBuffer
;
1152 EFI_HANDLE
*HandleBuffer
;
1158 Status
= LibLocateHandle(AllHandles
,
1163 if (EFI_ERROR(Status
))
1166 for (Index
= 0; Index
< AllHandleCount
; Index
++) {
1168 // Scan the handle database
1170 Status
= LibScanHandleDatabase(NULL
,
1172 AllHandleBuffer
[Index
],
1177 if (EFI_ERROR (Status
))
1181 if (HandleType
[Index
] & EFI_HANDLE_TYPE_DRIVER_BINDING_HANDLE
)
1183 if (HandleType
[Index
] & EFI_HANDLE_TYPE_IMAGE_HANDLE
)
1188 for (HandleIndex
= 0; HandleIndex
< HandleCount
; HandleIndex
++) {
1189 if (HandleType
[HandleIndex
] & EFI_HANDLE_TYPE_PARENT_HANDLE
)
1194 if (HandleType
[Index
] & EFI_HANDLE_TYPE_DEVICE_HANDLE
) {
1195 Status
= refit_call4_wrapper(BS
->ConnectController
,
1196 AllHandleBuffer
[Index
],
1204 FreePool (HandleBuffer
);
1205 FreePool (HandleType
);
1209 FreePool (AllHandleBuffer
);
1213 static VOID
LoadDrivers(VOID
)
1215 CHAR16 DirName
[256];
1217 // load drivers from /efi/refind/drivers
1218 SPrint(DirName
, 255, L
"%s\\drivers", SelfDirPath
);
1219 ScanDriverDir(DirName
);
1221 // load drivers from /efi/tools/drivers
1222 ScanDriverDir(L
"\\efi\\tools\\drivers");
1224 // connect all devices
1225 ConnectAllDriversToAllControllers();
1227 #endif /* DEBIAN_ENABLE_EFI110 */
1229 static VOID
ScanForBootloaders(VOID
) {
1233 // Commented-out below: Was part of an attempt to get rEFInd to
1234 // re-scan disk devices on pressing Esc; but doesn't work (yet), so
1236 // MainMenu.Title = StrDuplicate(L"Main Menu 2");
1237 // MainMenu.TitleImage = NULL;
1238 // MainMenu.InfoLineCount = 0;
1239 // MainMenu.InfoLines = NULL;
1240 // MainMenu.EntryCount = 0;
1241 // MainMenu.Entries = NULL;
1242 // MainMenu.TimeoutSeconds = 20;
1243 // MainMenu.TimeoutText = StrDuplicate(L"Automatic boot");
1246 // scan for loaders and tools, add them to the menu
1247 for (i
= 0; i
< NUM_SCAN_OPTIONS
; i
++) {
1248 switch(GlobalConfig
.ScanFor
[i
]) {
1253 ScanLegacyInternal();
1256 ScanLegacyExternal();
1259 ScanUserConfigured();
1274 // fixed other menu entries
1275 if (!(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_FUNCS
)) {
1276 MenuEntryAbout
.Image
= BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT
);
1277 AddMenuEntry(&MainMenu
, &MenuEntryAbout
);
1279 if (!(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_FUNCS
) || MainMenu
.EntryCount
== 0) {
1280 MenuEntryShutdown
.Image
= BuiltinIcon(BUILTIN_ICON_FUNC_SHUTDOWN
);
1281 AddMenuEntry(&MainMenu
, &MenuEntryShutdown
);
1282 MenuEntryReset
.Image
= BuiltinIcon(BUILTIN_ICON_FUNC_RESET
);
1283 AddMenuEntry(&MainMenu
, &MenuEntryReset
);
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()
1300 efi_main (IN EFI_HANDLE ImageHandle
, IN EFI_SYSTEM_TABLE
*SystemTable
)
1303 BOOLEAN MainLoopRunning
= TRUE
;
1304 REFIT_MENU_ENTRY
*ChosenEntry
;
1308 InitializeLib(ImageHandle
, SystemTable
);
1310 Status
= InitRefitLib(ImageHandle
);
1311 if (EFI_ERROR(Status
))
1314 // read configuration
1315 CopyMem(GlobalConfig
.ScanFor
, "ieo ", NUM_SCAN_OPTIONS
);
1317 MainMenu
.TimeoutSeconds
= GlobalConfig
.Timeout
;
1319 // disable EFI watchdog timer
1320 refit_call4_wrapper(BS
->SetWatchdogTimer
, 0x0000, 0x0000, 0x0000, NULL
);
1322 // further bootstrap (now with config available)
1324 #ifdef DEBIAN_ENABLE_EFI110
1326 #endif /* DEBIAN_ENABLE_EFI110 */
1327 ScanForBootloaders();
1329 while (MainLoopRunning
) {
1330 MenuExit
= RunMainMenu(&MainMenu
, GlobalConfig
.DefaultSelection
, &ChosenEntry
);
1332 // We don't allow exiting the main menu with the Escape key.
1333 if (MenuExit
== MENU_EXIT_ESCAPE
) {
1334 // Commented-out below: Was part of an attempt to get rEFInd to
1335 // re-scan disk devices on pressing Esc; but doesn't work (yet), so
1338 // ScanForBootloaders();
1343 switch (ChosenEntry
->Tag
) {
1345 case TAG_RESET
: // Restart
1347 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);
1348 MainLoopRunning
= FALSE
; // just in case we get this far
1351 case TAG_SHUTDOWN
: // Shut Down
1353 refit_call4_wrapper(RT
->ResetSystem
, EfiResetShutdown
, EFI_SUCCESS
, 0, NULL
);
1354 MainLoopRunning
= FALSE
; // just in case we get this far
1357 case TAG_ABOUT
: // About rEFInd
1361 case TAG_LOADER
: // Boot OS via .EFI loader
1362 StartLoader((LOADER_ENTRY
*)ChosenEntry
);
1365 case TAG_LEGACY
: // Boot legacy OS
1366 StartLegacy((LEGACY_ENTRY
*)ChosenEntry
);
1369 case TAG_TOOL
: // Start a EFI tool
1370 StartTool((LOADER_ENTRY
*)ChosenEntry
);
1376 // If we end up here, things have gone wrong. Try to reboot, and if that
1377 // fails, go into an endless loop.
1378 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);