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
"Reboot Computer", TAG_REBOOT
, 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
};
70 static REFIT_MENU_ENTRY MenuEntryExit
= { L
"Exit rEFInd", TAG_EXIT
, 1, 0, 0, NULL
, NULL
, NULL
};
72 static REFIT_MENU_SCREEN MainMenu
= { L
"Main Menu", NULL
, 0, NULL
, 0, NULL
, 0, L
"Automatic boot" };
73 static REFIT_MENU_SCREEN AboutMenu
= { L
"About", NULL
, 0, NULL
, 0, NULL
, 0, NULL
};
75 REFIT_CONFIG GlobalConfig
= { FALSE
, 20, 0, 0, NULL
, NULL
, NULL
, NULL
,
76 {TAG_SHELL
, TAG_ABOUT
, TAG_SHUTDOWN
, TAG_REBOOT
, 0, 0, 0, 0, 0 }};
82 static VOID
AboutrEFInd(VOID
)
84 if (AboutMenu
.EntryCount
== 0) {
85 AboutMenu
.TitleImage
= BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT
);
86 AddMenuInfoLine(&AboutMenu
, L
"rEFInd Version 0.2.5");
87 AddMenuInfoLine(&AboutMenu
, L
"");
88 AddMenuInfoLine(&AboutMenu
, L
"Copyright (c) 2006-2010 Christoph Pfisterer");
89 AddMenuInfoLine(&AboutMenu
, L
"Copyright (c) 2012 Roderick W. Smith");
90 AddMenuInfoLine(&AboutMenu
, L
"Portions Copyright (c) Intel Corporation and others");
91 AddMenuInfoLine(&AboutMenu
, L
"Distributed under the terms of the GNU GPLv3 license");
92 AddMenuInfoLine(&AboutMenu
, L
"");
93 AddMenuInfoLine(&AboutMenu
, L
"Running on:");
94 AddMenuInfoLine(&AboutMenu
, PoolPrint(L
" EFI Revision %d.%02d",
95 ST
->Hdr
.Revision
>> 16, ST
->Hdr
.Revision
& ((1 << 16) - 1)));
97 AddMenuInfoLine(&AboutMenu
, L
" Platform: x86 (32 bit)");
99 AddMenuInfoLine(&AboutMenu
, L
" Platform: x86_64 (64 bit)");
101 AddMenuInfoLine(&AboutMenu
, L
" Platform: unknown");
103 AddMenuInfoLine(&AboutMenu
, PoolPrint(L
" Firmware: %s %d.%02d",
104 ST
->FirmwareVendor
, ST
->FirmwareRevision
>> 16, ST
->FirmwareRevision
& ((1 << 16) - 1)));
105 AddMenuInfoLine(&AboutMenu
, PoolPrint(L
" Screen Output: %s", egScreenDescription()));
106 AddMenuInfoLine(&AboutMenu
, L
"");
107 AddMenuInfoLine(&AboutMenu
, L
"For more information, see the rEFInd Web site:");
108 AddMenuInfoLine(&AboutMenu
, L
"http://www.rodsbooks.com/refind/");
109 AddMenuEntry(&AboutMenu
, &MenuEntryReturn
);
112 RunMenu(&AboutMenu
, NULL
);
113 } /* VOID AboutrEFInd() */
115 static EFI_STATUS
StartEFIImageList(IN EFI_DEVICE_PATH
**DevicePaths
,
116 IN CHAR16
*LoadOptions
, IN CHAR16
*LoadOptionsPrefix
,
117 IN CHAR16
*ImageTitle
,
118 OUT UINTN
*ErrorInStep
)
120 EFI_STATUS Status
, ReturnStatus
;
121 EFI_HANDLE ChildImageHandle
;
122 EFI_LOADED_IMAGE
*ChildLoadedImage
;
123 UINTN DevicePathIndex
;
124 CHAR16 ErrorInfo
[256];
125 CHAR16
*FullLoadOptions
= NULL
;
127 Print(L
"Starting %s\n", ImageTitle
);
128 if (ErrorInStep
!= NULL
)
131 // load the image into memory
132 ReturnStatus
= Status
= EFI_NOT_FOUND
; // in case the list is empty
133 for (DevicePathIndex
= 0; DevicePaths
[DevicePathIndex
] != NULL
; DevicePathIndex
++) {
134 ReturnStatus
= Status
= refit_call6_wrapper(BS
->LoadImage
, FALSE
, SelfImageHandle
, DevicePaths
[DevicePathIndex
], NULL
, 0, &ChildImageHandle
);
135 if (ReturnStatus
!= EFI_NOT_FOUND
)
138 SPrint(ErrorInfo
, 255, L
"while loading %s", ImageTitle
);
139 if (CheckError(Status
, ErrorInfo
)) {
140 if (ErrorInStep
!= NULL
)
146 if (LoadOptions
!= NULL
) {
147 ReturnStatus
= Status
= refit_call3_wrapper(BS
->HandleProtocol
, ChildImageHandle
, &LoadedImageProtocol
, (VOID
**) &ChildLoadedImage
);
148 if (CheckError(Status
, L
"while getting a LoadedImageProtocol handle")) {
149 if (ErrorInStep
!= NULL
)
154 if (LoadOptionsPrefix
!= NULL
) {
155 FullLoadOptions
= PoolPrint(L
"%s %s ", LoadOptionsPrefix
, LoadOptions
);
156 // NOTE: That last space is also added by the EFI shell and seems to be significant
157 // when passing options to Apple's boot.efi...
158 LoadOptions
= FullLoadOptions
;
160 // NOTE: We also include the terminating null in the length for safety.
161 ChildLoadedImage
->LoadOptions
= (VOID
*)LoadOptions
;
162 ChildLoadedImage
->LoadOptionsSize
= ((UINT32
)StrLen(LoadOptions
) + 1) * sizeof(CHAR16
);
163 Print(L
"Using load options '%s'\n", LoadOptions
);
166 // close open file handles
169 // turn control over to the image
170 // TODO: (optionally) re-enable the EFI watchdog timer!
171 ReturnStatus
= Status
= refit_call3_wrapper(BS
->StartImage
, ChildImageHandle
, NULL
, NULL
);
172 // control returns here when the child image calls Exit()
173 SPrint(ErrorInfo
, 255, L
"returned from %s", ImageTitle
);
174 if (CheckError(Status
, ErrorInfo
)) {
175 if (ErrorInStep
!= NULL
)
179 // re-open file handles
183 // unload the image, we don't care if it works or not...
184 Status
= refit_call1_wrapper(BS
->UnloadImage
, ChildImageHandle
);
186 if (FullLoadOptions
!= NULL
)
187 FreePool(FullLoadOptions
);
189 } /* static EFI_STATUS StartEFIImageList() */
191 static EFI_STATUS
StartEFIImage(IN EFI_DEVICE_PATH
*DevicePath
,
192 IN CHAR16
*LoadOptions
, IN CHAR16
*LoadOptionsPrefix
,
193 IN CHAR16
*ImageTitle
,
194 OUT UINTN
*ErrorInStep
)
196 EFI_DEVICE_PATH
*DevicePaths
[2];
198 DevicePaths
[0] = DevicePath
;
199 DevicePaths
[1] = NULL
;
200 return StartEFIImageList(DevicePaths
, LoadOptions
, LoadOptionsPrefix
, ImageTitle
, ErrorInStep
);
201 } /* static EFI_STATUS StartEFIImage() */
204 // EFI OS loader functions
207 static VOID
StartLoader(IN LOADER_ENTRY
*Entry
)
209 UINTN ErrorInStep
= 0;
211 BeginExternalScreen(Entry
->UseGraphicsMode
, L
"Booting OS");
212 StartEFIImage(Entry
->DevicePath
, Entry
->LoadOptions
,
213 Basename(Entry
->LoaderPath
), Basename(Entry
->LoaderPath
), &ErrorInStep
);
214 FinishExternalScreen();
217 // Locate an initrd or initramfs file that matches the kernel specified by LoaderPath.
218 // The matching file has a name that begins with "init" and includes the same version
219 // number string as is found in LoaderPath -- but not a longer version number string.
220 // For instance, if LoaderPath is \EFI\kernels\bzImage-3.3.0.efi, and if \EFI\kernels
221 // has a file called initramfs-3.3.0.img, this function will return the string
222 // '\EFI\kernels\initramfs-3.3.0.img'. If the directory ALSO contains the file
223 // initramfs-3.3.0-rc7.img or initramfs-13.3.0.img, those files will NOT match;
224 // however, initmine-3.3.0.img might match. (FindInitrd() returns the first match it
225 // finds). Thus, care should be taken to avoid placing duplicate matching files in
226 // the kernel's directory.
227 // If no matching init file can be found, returns NULL.
228 static CHAR16
* FindInitrd(IN CHAR16
*LoaderPath
, IN REFIT_VOLUME
*Volume
) {
229 CHAR16
*InitrdName
= NULL
, *FileName
, *KernelVersion
, *InitrdVersion
, *Path
;
230 REFIT_DIR_ITER DirIter
;
231 EFI_FILE_INFO
*DirEntry
;
233 FileName
= Basename(LoaderPath
);
234 KernelVersion
= FindNumbers(FileName
);
235 Path
= FindPath(LoaderPath
);
237 MergeStrings(&Path
, L
"\\", 0); // Add trailing backslash; necessary for root directory
238 DirIterOpen(Volume
->RootDir
, Path
, &DirIter
);
239 while ((DirIterNext(&DirIter
, 2, L
"init*", &DirEntry
)) && (InitrdName
== NULL
)) {
240 InitrdVersion
= FindNumbers(DirEntry
->FileName
);
241 if (KernelVersion
!= NULL
) {
242 if (StriCmp(InitrdVersion
, KernelVersion
) == 0)
243 InitrdName
= PoolPrint(L
"%s%s", Path
, DirEntry
->FileName
);
245 if (InitrdVersion
== NULL
)
246 InitrdName
= PoolPrint(L
"%s%s", Path
, DirEntry
->FileName
);
248 if (InitrdVersion
!= NULL
)
249 FreePool(InitrdVersion
);
251 DirIterClose(&DirIter
);
253 // Note: Don't FreePool(FileName), since Basename returns a pointer WITHIN the string it's passed.
254 FreePool(KernelVersion
);
257 } // static CHAR16 * FindInitrd()
259 LOADER_ENTRY
* AddPreparedLoaderEntry(LOADER_ENTRY
*Entry
) {
260 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
263 } // LOADER_ENTRY * AddPreparedLoaderEntry()
265 // Creates a new LOADER_ENTRY data structure and populates it with
266 // default values from the specified Entry, or NULL values if Entry
267 // is unspecified (NULL).
268 // Returns a pointer to the new data structure, or NULL if it
269 // couldn't be allocated
270 LOADER_ENTRY
*InitializeLoaderEntry(IN LOADER_ENTRY
*Entry
) {
271 LOADER_ENTRY
*NewEntry
= NULL
;
273 NewEntry
= AllocateZeroPool(sizeof(LOADER_ENTRY
));
274 if (NewEntry
!= NULL
) {
275 NewEntry
->me
.Title
= NULL
;
276 NewEntry
->me
.Tag
= TAG_LOADER
;
277 NewEntry
->Enabled
= TRUE
;
278 NewEntry
->UseGraphicsMode
= FALSE
;
279 NewEntry
->OSType
= 0;
281 NewEntry
->LoaderPath
= StrDuplicate(Entry
->LoaderPath
);
282 NewEntry
->VolName
= StrDuplicate(Entry
->VolName
);
283 NewEntry
->DevicePath
= Entry
->DevicePath
;
284 NewEntry
->UseGraphicsMode
= Entry
->UseGraphicsMode
;
285 NewEntry
->LoadOptions
= StrDuplicate(Entry
->LoadOptions
);
286 NewEntry
->InitrdPath
= StrDuplicate(Entry
->InitrdPath
);
290 } // LOADER_ENTRY *InitializeLoaderEntry()
292 // Prepare a REFIT_MENU_SCREEN data structure for a subscreen entry. This sets up
293 // the default entry that launches the boot loader using the same options as the
294 // main Entry does. Subsequent options can be added by the calling function.
295 // If a subscreen already exists in the Entry that's passed to this function,
296 // it's left unchanged and a pointer to it is returned.
297 // Returns a pointer to the new subscreen data structure, or NULL if there
298 // were problems allocating memory.
299 REFIT_MENU_SCREEN
*InitializeSubScreen(IN LOADER_ENTRY
*Entry
) {
300 CHAR16
*FileName
, *Temp
;
301 REFIT_MENU_SCREEN
*SubScreen
= NULL
;
302 LOADER_ENTRY
*SubEntry
;
304 FileName
= Basename(Entry
->LoaderPath
);
305 if (Entry
->me
.SubScreen
== NULL
) { // No subscreen yet; initialize default entry....
306 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
307 if (SubScreen
!= NULL
) {
308 SubScreen
->Title
= PoolPrint(L
"Boot Options for %s on %s", (Entry
->Title
!= NULL
) ? Entry
->Title
: FileName
, Entry
->VolName
);
309 SubScreen
->TitleImage
= Entry
->me
.Image
;
311 SubEntry
= InitializeLoaderEntry(Entry
);
312 if (SubEntry
!= NULL
) {
313 SubEntry
->me
.Title
= L
"Boot using default options";
314 if ((SubEntry
->InitrdPath
!= NULL
) && (StrLen(SubEntry
->InitrdPath
) > 0) && (!StriSubCmp(L
"initrd", SubEntry
->LoadOptions
))) {
315 Temp
= PoolPrint(L
"initrd=%s", SubEntry
->InitrdPath
);
316 MergeStrings(&SubEntry
->LoadOptions
, Temp
, L
' ');
319 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
320 } // if (SubEntry != NULL)
321 } // if (SubScreen != NULL)
322 } else { // existing subscreen; less initialization, and just add new entry later....
323 SubScreen
= Entry
->me
.SubScreen
;
326 } // REFIT_MENU_SCREEN *InitializeSubScreen()
328 VOID
GenerateSubScreen(LOADER_ENTRY
*Entry
, IN REFIT_VOLUME
*Volume
) {
329 REFIT_MENU_SCREEN
*SubScreen
;
330 LOADER_ENTRY
*SubEntry
;
331 CHAR16
*FileName
, *InitrdOption
= NULL
, *Temp
;
332 CHAR16 DiagsFileName
[256];
337 FileName
= Basename(Entry
->LoaderPath
);
338 // create the submenu
339 if (StrLen(Entry
->Title
) == 0) {
340 FreePool(Entry
->Title
);
343 SubScreen
= InitializeSubScreen(Entry
);
345 // loader-specific submenu entries
346 if (Entry
->OSType
== 'M') { // entries for Mac OS X
348 SubEntry
= InitializeLoaderEntry(Entry
);
349 if (SubEntry
!= NULL
) {
350 SubEntry
->me
.Title
= L
"Boot Mac OS X with a 64-bit kernel";
351 SubEntry
->LoadOptions
= L
"arch=x86_64";
352 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
355 SubEntry
= InitializeLoaderEntry(Entry
);
356 if (SubEntry
!= NULL
) {
357 SubEntry
->me
.Title
= L
"Boot Mac OS X with a 32-bit kernel";
358 SubEntry
->LoadOptions
= L
"arch=i386";
359 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
363 if (!(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_SINGLEUSER
)) {
364 SubEntry
= InitializeLoaderEntry(Entry
);
365 if (SubEntry
!= NULL
) {
366 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode";
367 SubEntry
->UseGraphicsMode
= FALSE
;
368 SubEntry
->LoadOptions
= L
"-v";
369 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
373 SubEntry
= InitializeLoaderEntry(Entry
);
374 if (SubEntry
!= NULL
) {
375 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode (64-bit)";
376 SubEntry
->UseGraphicsMode
= FALSE
;
377 SubEntry
->LoadOptions
= L
"-v arch=x86_64";
378 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
381 SubEntry
= InitializeLoaderEntry(Entry
);
382 if (SubEntry
!= NULL
) {
383 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode (32-bit)";
384 SubEntry
->UseGraphicsMode
= FALSE
;
385 SubEntry
->LoadOptions
= L
"-v arch=i386";
386 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
390 SubEntry
= InitializeLoaderEntry(Entry
);
391 if (SubEntry
!= NULL
) {
392 SubEntry
->me
.Title
= L
"Boot Mac OS X in single user mode";
393 SubEntry
->UseGraphicsMode
= FALSE
;
394 SubEntry
->LoadOptions
= L
"-v -s";
395 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
399 // check for Apple hardware diagnostics
400 StrCpy(DiagsFileName
, L
"\\System\\Library\\CoreServices\\.diagnostics\\diags.efi");
401 if (FileExists(Volume
->RootDir
, DiagsFileName
) && !(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_HWTEST
)) {
402 SubEntry
= InitializeLoaderEntry(Entry
);
403 if (SubEntry
!= NULL
) {
404 SubEntry
->me
.Title
= L
"Run Apple Hardware Test";
405 FreePool(SubEntry
->LoaderPath
);
406 SubEntry
->LoaderPath
= StrDuplicate(DiagsFileName
);
407 SubEntry
->DevicePath
= FileDevicePath(Volume
->DeviceHandle
, SubEntry
->LoaderPath
);
408 SubEntry
->UseGraphicsMode
= TRUE
;
409 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
411 } // if diagnostics entry found
413 } else if (Entry
->OSType
== 'L') { // entries for Linux kernels with EFI stub loaders
414 File
= ReadLinuxOptionsFile(Entry
->LoaderPath
, Volume
);
416 if ((Temp
= FindInitrd(Entry
->LoaderPath
, Volume
)) != NULL
)
417 InitrdOption
= PoolPrint(L
"initrd=%s", Temp
);
418 TokenCount
= ReadTokenLine(File
, &TokenList
); // read and discard first entry, since it's
419 FreeTokenLine(&TokenList
, &TokenCount
); // set up by InitializeSubScreen(), earlier....
420 while ((TokenCount
= ReadTokenLine(File
, &TokenList
)) > 1) {
421 SubEntry
= InitializeLoaderEntry(Entry
);
422 SubEntry
->me
.Title
= StrDuplicate(TokenList
[0]);
423 if (SubEntry
->LoadOptions
!= NULL
)
424 FreePool(SubEntry
->LoadOptions
);
425 SubEntry
->LoadOptions
= StrDuplicate(TokenList
[1]);
426 MergeStrings(&SubEntry
->LoadOptions
, InitrdOption
, L
' ');
427 FreeTokenLine(&TokenList
, &TokenCount
);
428 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
431 FreePool(InitrdOption
);
435 } // if Linux options file exists
437 } else if (Entry
->OSType
== 'E') { // entries for ELILO
438 SubEntry
= InitializeLoaderEntry(Entry
);
439 if (SubEntry
!= NULL
) {
440 SubEntry
->me
.Title
= PoolPrint(L
"Run %s in interactive mode", FileName
);
441 SubEntry
->LoadOptions
= L
"-p";
442 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
445 SubEntry
= InitializeLoaderEntry(Entry
);
446 if (SubEntry
!= NULL
) {
447 SubEntry
->me
.Title
= L
"Boot Linux for a 17\" iMac or a 15\" MacBook Pro (*)";
448 SubEntry
->UseGraphicsMode
= TRUE
;
449 SubEntry
->LoadOptions
= L
"-d 0 i17";
450 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
453 SubEntry
= InitializeLoaderEntry(Entry
);
454 if (SubEntry
!= NULL
) {
455 SubEntry
->me
.Title
= L
"Boot Linux for a 20\" iMac (*)";
456 SubEntry
->UseGraphicsMode
= TRUE
;
457 SubEntry
->LoadOptions
= L
"-d 0 i20";
458 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
461 SubEntry
= InitializeLoaderEntry(Entry
);
462 if (SubEntry
!= NULL
) {
463 SubEntry
->me
.Title
= L
"Boot Linux for a Mac Mini (*)";
464 SubEntry
->UseGraphicsMode
= TRUE
;
465 SubEntry
->LoadOptions
= L
"-d 0 mini";
466 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
469 AddMenuInfoLine(SubScreen
, L
"NOTE: This is an example. Entries");
470 AddMenuInfoLine(SubScreen
, L
"marked with (*) may not work.");
472 } else if (Entry
->OSType
== 'X') { // entries for xom.efi
473 // by default, skip the built-in selection and boot from hard disk only
474 Entry
->LoadOptions
= L
"-s -h";
476 SubEntry
= InitializeLoaderEntry(Entry
);
477 if (SubEntry
!= NULL
) {
478 SubEntry
->me
.Title
= L
"Boot Windows from Hard Disk";
479 SubEntry
->LoadOptions
= L
"-s -h";
480 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
483 SubEntry
= InitializeLoaderEntry(Entry
);
484 if (SubEntry
!= NULL
) {
485 SubEntry
->me
.Title
= L
"Boot Windows from CD-ROM";
486 SubEntry
->LoadOptions
= L
"-s -c";
487 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
490 SubEntry
= InitializeLoaderEntry(Entry
);
491 if (SubEntry
!= NULL
) {
492 SubEntry
->me
.Title
= PoolPrint(L
"Run %s in text mode", FileName
);
493 SubEntry
->UseGraphicsMode
= FALSE
;
494 SubEntry
->LoadOptions
= L
"-v";
495 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
497 } // entries for xom.efi
498 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
499 Entry
->me
.SubScreen
= SubScreen
;
500 } // VOID GenerateSubScreen()
502 // Returns options for a Linux kernel. Reads them from an options file in the
503 // kernel's directory; and if present, adds an initrd= option for an initial
504 // RAM disk file with the same version number as the kernel file.
505 static CHAR16
* GetMainLinuxOptions(IN CHAR16
* LoaderPath
, IN REFIT_VOLUME
*Volume
) {
506 CHAR16
*Options
= NULL
, *InitrdName
, *InitrdOption
= NULL
;
508 Options
= GetFirstOptionsFromFile(LoaderPath
, Volume
);
509 InitrdName
= FindInitrd(LoaderPath
, Volume
);
510 if (InitrdName
!= NULL
)
511 InitrdOption
= PoolPrint(L
"initrd=%s", InitrdName
);
512 MergeStrings(&Options
, InitrdOption
, ' ');
513 if (InitrdOption
!= NULL
)
514 FreePool(InitrdOption
);
515 if (InitrdName
!= NULL
)
516 FreePool(InitrdName
);
518 } // static CHAR16 * GetMainLinuxOptions()
520 // Sets a few defaults for a loader entry -- mainly the icon, but also the OS type
521 // code and shortcut letter. For Linux EFI stub loaders, also sets kernel options
522 // that will (with luck) work fairly automatically.
523 VOID
SetLoaderDefaults(LOADER_ENTRY
*Entry
, CHAR16
*LoaderPath
, IN REFIT_VOLUME
*Volume
) {
524 CHAR16 IconFileName
[256];
525 CHAR16
*FileName
, *PathOnly
, *OSIconName
= NULL
, *Temp
;
526 CHAR16 ShortcutLetter
= 0;
528 FileName
= Basename(LoaderPath
);
529 PathOnly
= FindPath(LoaderPath
);
531 // locate a custom icon for the loader
532 StrCpy(IconFileName
, LoaderPath
);
533 ReplaceExtension(IconFileName
, L
".icns");
534 if (FileExists(Volume
->RootDir
, IconFileName
)) {
535 Entry
->me
.Image
= LoadIcns(Volume
->RootDir
, IconFileName
, 128);
536 } else if ((StrLen(PathOnly
) == 0) && (Volume
->VolIconImage
!= NULL
)) {
537 Entry
->me
.Image
= Volume
->VolIconImage
;
538 } // icon matched to loader or volume
540 Temp
= FindLastDirName(LoaderPath
);
541 MergeStrings(&OSIconName
, Temp
, L
',');
543 if (OSIconName
!= NULL
) {
544 ShortcutLetter
= OSIconName
[0];
547 // detect specific loaders
548 if (StriSubCmp(L
"bzImage", LoaderPath
) || StriSubCmp(L
"vmlinuz", LoaderPath
)) {
549 MergeStrings(&OSIconName
, L
"linux", L
',');
551 if (ShortcutLetter
== 0)
552 ShortcutLetter
= 'L';
553 Entry
->LoadOptions
= GetMainLinuxOptions(LoaderPath
, Volume
);
554 } else if (StriSubCmp(L
"refit", LoaderPath
)) {
555 MergeStrings(&OSIconName
, L
"refit", L
',');
557 ShortcutLetter
= 'R';
558 } else if (StriCmp(LoaderPath
, MACOSX_LOADER_PATH
) == 0) {
559 if (Volume
->VolIconImage
!= NULL
) { // custom icon file found
560 Entry
->me
.Image
= Volume
->VolIconImage
;
562 MergeStrings(&OSIconName
, L
"mac", L
',');
563 Entry
->UseGraphicsMode
= TRUE
;
565 ShortcutLetter
= 'M';
566 } else if (StriCmp(FileName
, L
"diags.efi") == 0) {
567 MergeStrings(&OSIconName
, L
"hwtest", L
',');
568 } else if (StriCmp(FileName
, L
"e.efi") == 0 || StriCmp(FileName
, L
"elilo.efi") == 0) {
569 MergeStrings(&OSIconName
, L
"elilo,linux", L
',');
571 if (ShortcutLetter
== 0)
572 ShortcutLetter
= 'L';
573 } else if (StriCmp(FileName
, L
"cdboot.efi") == 0 ||
574 StriCmp(FileName
, L
"bootmgr.efi") == 0 ||
575 StriCmp(FileName
, L
"Bootmgfw.efi") == 0) {
576 MergeStrings(&OSIconName
, L
"win", L
',');
578 ShortcutLetter
= 'W';
579 } else if (StriCmp(FileName
, L
"xom.efi") == 0) {
580 MergeStrings(&OSIconName
, L
"xom,win", L
',');
581 Entry
->UseGraphicsMode
= TRUE
;
583 ShortcutLetter
= 'W';
586 if ((ShortcutLetter
>= 'a') && (ShortcutLetter
<= 'z'))
587 ShortcutLetter
= ShortcutLetter
- 'a' + 'A'; // convert lowercase to uppercase
588 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
589 if (Entry
->me
.Image
== NULL
)
590 Entry
->me
.Image
= LoadOSIcon(OSIconName
, L
"unknown", FALSE
);
591 if (PathOnly
!= NULL
)
593 } // VOID SetLoaderDefaults()
595 // Add a specified EFI boot loader to the list, using automatic settings
596 // for icons, options, etc.
597 LOADER_ENTRY
* AddLoaderEntry(IN CHAR16
*LoaderPath
, IN CHAR16
*LoaderTitle
, IN REFIT_VOLUME
*Volume
) {
600 Entry
= InitializeLoaderEntry(NULL
);
602 Entry
->Title
= StrDuplicate(LoaderTitle
);
603 Entry
->me
.Title
= PoolPrint(L
"Boot %s from %s", (LoaderTitle
!= NULL
) ? LoaderTitle
: LoaderPath
+ 1, Volume
->VolName
);
605 Entry
->me
.BadgeImage
= Volume
->VolBadgeImage
;
606 Entry
->LoaderPath
= StrDuplicate(LoaderPath
);
607 Entry
->VolName
= Volume
->VolName
;
608 Entry
->DevicePath
= FileDevicePath(Volume
->DeviceHandle
, Entry
->LoaderPath
);
609 SetLoaderDefaults(Entry
, LoaderPath
, Volume
);
610 GenerateSubScreen(Entry
, Volume
);
611 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
615 } // LOADER_ENTRY * AddLoaderEntry()
617 // Scan an individual directory for EFI boot loader files and, if found,
618 // add them to the list.
619 static VOID
ScanLoaderDir(IN REFIT_VOLUME
*Volume
, IN CHAR16
*Path
)
622 REFIT_DIR_ITER DirIter
;
623 EFI_FILE_INFO
*DirEntry
;
624 CHAR16 FileName
[256];
626 // Note: SelfDirPath includes a leading backslash ('\'), but Path
627 // doesn't, so we rejigger the string to compensate....
628 if (!SelfDirPath
|| !Path
|| ((StriCmp(Path
, &SelfDirPath
[1]) == 0) && Volume
!= SelfVolume
) ||
629 (StriCmp(Path
, &SelfDirPath
[1]) != 0)) {
630 // look through contents of the directory
631 DirIterOpen(Volume
->RootDir
, Path
, &DirIter
);
632 while (DirIterNext(&DirIter
, 2, L
"*.efi", &DirEntry
)) {
633 if (DirEntry
->FileName
[0] == '.' ||
634 StriCmp(DirEntry
->FileName
, L
"TextMode.efi") == 0 ||
635 StriCmp(DirEntry
->FileName
, L
"ebounce.efi") == 0 ||
636 StriCmp(DirEntry
->FileName
, L
"GraphicsConsole.efi") == 0 ||
637 StriSubCmp(L
"shell", DirEntry
->FileName
))
638 continue; // skip this
641 SPrint(FileName
, 255, L
"\\%s\\%s", Path
, DirEntry
->FileName
);
643 SPrint(FileName
, 255, L
"\\%s", DirEntry
->FileName
);
644 AddLoaderEntry(FileName
, NULL
, Volume
);
646 Status
= DirIterClose(&DirIter
);
647 if (Status
!= EFI_NOT_FOUND
) {
649 SPrint(FileName
, 255, L
"while scanning the %s directory", Path
);
651 StrCpy(FileName
, L
"while scanning the root directory");
652 CheckError(Status
, FileName
);
653 } // if (Status != EFI_NOT_FOUND)
654 } // if not scanning our own directory
655 } /* static VOID ScanLoaderDir() */
657 static VOID
ScanEfiFiles(REFIT_VOLUME
*Volume
) {
659 REFIT_DIR_ITER EfiDirIter
;
660 EFI_FILE_INFO
*EfiDirEntry
;
661 CHAR16 FileName
[256];
663 if ((Volume
->RootDir
!= NULL
) && (Volume
->VolName
!= NULL
)) {
664 // check for Mac OS X boot loader
665 StrCpy(FileName
, MACOSX_LOADER_PATH
);
666 if (FileExists(Volume
->RootDir
, FileName
)) {
667 AddLoaderEntry(FileName
, L
"Mac OS X", Volume
);
671 StrCpy(FileName
, L
"\\System\\Library\\CoreServices\\xom.efi");
672 if (FileExists(Volume
->RootDir
, FileName
)) {
673 AddLoaderEntry(FileName
, L
"Windows XP (XoM)", Volume
);
676 // check for Microsoft boot loader/menu
677 StrCpy(FileName
, L
"\\EFI\\Microsoft\\Boot\\Bootmgfw.efi");
678 if (FileExists(Volume
->RootDir
, FileName
)) {
679 AddLoaderEntry(FileName
, L
"Microsoft EFI boot", Volume
);
682 // scan the root directory for EFI executables
683 ScanLoaderDir(Volume
, NULL
);
684 // scan the elilo directory (as used on gimli's first Live CD)
685 ScanLoaderDir(Volume
, L
"elilo");
686 // scan the boot directory
687 ScanLoaderDir(Volume
, L
"boot");
689 // scan subdirectories of the EFI directory (as per the standard)
690 DirIterOpen(Volume
->RootDir
, L
"EFI", &EfiDirIter
);
691 while (DirIterNext(&EfiDirIter
, 1, NULL
, &EfiDirEntry
)) {
692 if (StriCmp(EfiDirEntry
->FileName
, L
"tools") == 0 || EfiDirEntry
->FileName
[0] == '.')
693 continue; // skip this, doesn't contain boot loaders
694 SPrint(FileName
, 255, L
"EFI\\%s", EfiDirEntry
->FileName
);
695 ScanLoaderDir(Volume
, FileName
);
697 Status
= DirIterClose(&EfiDirIter
);
698 if (Status
!= EFI_NOT_FOUND
)
699 CheckError(Status
, L
"while scanning the EFI directory");
701 } // static VOID ScanEfiFiles()
703 // Scan internal disks for valid EFI boot loaders....
704 static VOID
ScanInternal(VOID
) {
707 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
708 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_INTERNAL
) {
709 ScanEfiFiles(Volumes
[VolumeIndex
]);
712 } // static VOID ScanInternal()
714 // Scan external disks for valid EFI boot loaders....
715 static VOID
ScanExternal(VOID
) {
718 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
719 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_EXTERNAL
) {
720 ScanEfiFiles(Volumes
[VolumeIndex
]);
723 } // static VOID ScanExternal()
725 // Scan internal disks for valid EFI boot loaders....
726 static VOID
ScanOptical(VOID
) {
729 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
730 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_OPTICAL
) {
731 ScanEfiFiles(Volumes
[VolumeIndex
]);
734 } // static VOID ScanOptical()
737 // legacy boot functions
740 static EFI_STATUS
ActivateMbrPartition(IN EFI_BLOCK_IO
*BlockIO
, IN UINTN PartitionIndex
)
743 UINT8 SectorBuffer
[512];
744 MBR_PARTITION_INFO
*MbrTable
, *EMbrTable
;
745 UINT32 ExtBase
, ExtCurrent
, NextExtCurrent
;
746 UINTN LogicalPartitionIndex
= 4;
748 BOOLEAN HaveBootCode
;
751 Status
= refit_call5_wrapper(BlockIO
->ReadBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, 0, 512, SectorBuffer
);
752 if (EFI_ERROR(Status
))
754 if (*((UINT16
*)(SectorBuffer
+ 510)) != 0xaa55)
755 return EFI_NOT_FOUND
; // safety measure #1
757 // add boot code if necessary
758 HaveBootCode
= FALSE
;
759 for (i
= 0; i
< MBR_BOOTCODE_SIZE
; i
++) {
760 if (SectorBuffer
[i
] != 0) {
766 // no boot code found in the MBR, add the syslinux MBR code
767 SetMem(SectorBuffer
, MBR_BOOTCODE_SIZE
, 0);
768 CopyMem(SectorBuffer
, syslinux_mbr
, SYSLINUX_MBR_SIZE
);
771 // set the partition active
772 MbrTable
= (MBR_PARTITION_INFO
*)(SectorBuffer
+ 446);
774 for (i
= 0; i
< 4; i
++) {
775 if (MbrTable
[i
].Flags
!= 0x00 && MbrTable
[i
].Flags
!= 0x80)
776 return EFI_NOT_FOUND
; // safety measure #2
777 if (i
== PartitionIndex
)
778 MbrTable
[i
].Flags
= 0x80;
779 else if (PartitionIndex
>= 4 && IS_EXTENDED_PART_TYPE(MbrTable
[i
].Type
)) {
780 MbrTable
[i
].Flags
= 0x80;
781 ExtBase
= MbrTable
[i
].StartLBA
;
783 MbrTable
[i
].Flags
= 0x00;
787 Status
= refit_call5_wrapper(BlockIO
->WriteBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, 0, 512, SectorBuffer
);
788 if (EFI_ERROR(Status
))
791 if (PartitionIndex
>= 4) {
792 // we have to activate a logical partition, so walk the EMBR chain
794 // NOTE: ExtBase was set above while looking at the MBR table
795 for (ExtCurrent
= ExtBase
; ExtCurrent
; ExtCurrent
= NextExtCurrent
) {
797 Status
= refit_call5_wrapper(BlockIO
->ReadBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, ExtCurrent
, 512, SectorBuffer
);
798 if (EFI_ERROR(Status
))
800 if (*((UINT16
*)(SectorBuffer
+ 510)) != 0xaa55)
801 return EFI_NOT_FOUND
; // safety measure #3
803 // scan EMBR, set appropriate partition active
804 EMbrTable
= (MBR_PARTITION_INFO
*)(SectorBuffer
+ 446);
806 for (i
= 0; i
< 4; i
++) {
807 if (EMbrTable
[i
].Flags
!= 0x00 && EMbrTable
[i
].Flags
!= 0x80)
808 return EFI_NOT_FOUND
; // safety measure #4
809 if (EMbrTable
[i
].StartLBA
== 0 || EMbrTable
[i
].Size
== 0)
811 if (IS_EXTENDED_PART_TYPE(EMbrTable
[i
].Type
)) {
813 NextExtCurrent
= ExtBase
+ EMbrTable
[i
].StartLBA
;
814 EMbrTable
[i
].Flags
= (PartitionIndex
>= LogicalPartitionIndex
) ? 0x80 : 0x00;
818 EMbrTable
[i
].Flags
= (PartitionIndex
== LogicalPartitionIndex
) ? 0x80 : 0x00;
819 LogicalPartitionIndex
++;
823 // write current EMBR
824 Status
= refit_call5_wrapper(BlockIO
->WriteBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, ExtCurrent
, 512, SectorBuffer
);
825 if (EFI_ERROR(Status
))
828 if (PartitionIndex
< LogicalPartitionIndex
)
829 break; // stop the loop, no need to touch further EMBRs
835 } /* static EFI_STATUS ActivateMbrPartition() */
837 // early 2006 Core Duo / Core Solo models
838 static UINT8 LegacyLoaderDevicePath1Data
[] = {
839 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
840 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
841 0xFF, 0xFF, 0xF9, 0xFF, 0x00, 0x00, 0x00, 0x00,
842 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
843 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
844 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
846 // mid-2006 Mac Pro (and probably other Core 2 models)
847 static UINT8 LegacyLoaderDevicePath2Data
[] = {
848 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
849 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
850 0xFF, 0xFF, 0xF7, 0xFF, 0x00, 0x00, 0x00, 0x00,
851 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
852 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
853 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
855 // mid-2007 MBP ("Santa Rosa" based models)
856 static UINT8 LegacyLoaderDevicePath3Data
[] = {
857 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
858 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
859 0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
860 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
861 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
862 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
865 static UINT8 LegacyLoaderDevicePath4Data
[] = {
866 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
867 0x00, 0x00, 0xC0, 0xFF, 0x00, 0x00, 0x00, 0x00,
868 0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
869 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
870 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
871 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
873 // late-2008 MB/MBP (NVidia chipset)
874 static UINT8 LegacyLoaderDevicePath5Data
[] = {
875 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
876 0x00, 0x40, 0xCB, 0xFF, 0x00, 0x00, 0x00, 0x00,
877 0xFF, 0xBF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
878 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
879 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
880 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
883 static EFI_DEVICE_PATH
*LegacyLoaderList
[] = {
884 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath1Data
,
885 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath2Data
,
886 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath3Data
,
887 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath4Data
,
888 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath5Data
,
892 #define MAX_DISCOVERED_PATHS (16)
894 static VOID
StartLegacy(IN LEGACY_ENTRY
*Entry
)
897 EG_IMAGE
*BootLogoImage
;
898 UINTN ErrorInStep
= 0;
899 EFI_DEVICE_PATH
*DiscoveredPathList
[MAX_DISCOVERED_PATHS
];
901 BeginExternalScreen(TRUE
, L
"Booting Legacy OS");
903 BootLogoImage
= LoadOSIcon(Entry
->Volume
->OSIconName
, L
"legacy", TRUE
);
904 if (BootLogoImage
!= NULL
)
905 BltImageAlpha(BootLogoImage
,
906 (UGAWidth
- BootLogoImage
->Width
) >> 1,
907 (UGAHeight
- BootLogoImage
->Height
) >> 1,
908 &StdBackgroundPixel
);
910 if (Entry
->Volume
->IsMbrPartition
)
911 ActivateMbrPartition(Entry
->Volume
->WholeDiskBlockIO
, Entry
->Volume
->MbrPartitionIndex
);
913 ExtractLegacyLoaderPaths(DiscoveredPathList
, MAX_DISCOVERED_PATHS
, LegacyLoaderList
);
915 Status
= StartEFIImageList(DiscoveredPathList
, Entry
->LoadOptions
, NULL
, L
"legacy loader", &ErrorInStep
);
916 if (Status
== EFI_NOT_FOUND
) {
917 if (ErrorInStep
== 1) {
918 Print(L
"\nPlease make sure that you have the latest firmware update installed.\n");
919 } else if (ErrorInStep
== 3) {
920 Print(L
"\nThe firmware refused to boot from the selected volume. Note that external\n"
921 L
"hard drives are not well-supported by Apple's firmware for legacy OS booting.\n");
924 FinishExternalScreen();
925 } /* static VOID StartLegacy() */
927 static LEGACY_ENTRY
* AddLegacyEntry(IN CHAR16
*LoaderTitle
, IN REFIT_VOLUME
*Volume
)
929 LEGACY_ENTRY
*Entry
, *SubEntry
;
930 REFIT_MENU_SCREEN
*SubScreen
;
932 CHAR16 ShortcutLetter
= 0;
934 if (LoaderTitle
== NULL
) {
935 if (Volume
->OSName
!= NULL
) {
936 LoaderTitle
= Volume
->OSName
;
937 if (LoaderTitle
[0] == 'W' || LoaderTitle
[0] == 'L')
938 ShortcutLetter
= LoaderTitle
[0];
940 LoaderTitle
= L
"Legacy OS";
942 if (Volume
->VolName
!= NULL
)
943 VolDesc
= Volume
->VolName
;
945 VolDesc
= (Volume
->DiskKind
== DISK_KIND_OPTICAL
) ? L
"CD" : L
"HD";
947 // prepare the menu entry
948 Entry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
949 Entry
->me
.Title
= PoolPrint(L
"Boot %s from %s", LoaderTitle
, VolDesc
);
950 Entry
->me
.Tag
= TAG_LEGACY
;
952 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
953 Entry
->me
.Image
= LoadOSIcon(Volume
->OSIconName
, L
"legacy", FALSE
);
954 Entry
->me
.BadgeImage
= Volume
->VolBadgeImage
;
955 Entry
->Volume
= Volume
;
956 Entry
->LoadOptions
= (Volume
->DiskKind
== DISK_KIND_OPTICAL
) ? L
"CD" :
957 ((Volume
->DiskKind
== DISK_KIND_EXTERNAL
) ? L
"USB" : L
"HD");
958 Entry
->Enabled
= TRUE
;
960 // create the submenu
961 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
962 SubScreen
->Title
= PoolPrint(L
"Boot Options for %s on %s", LoaderTitle
, VolDesc
);
963 SubScreen
->TitleImage
= Entry
->me
.Image
;
966 SubEntry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
967 SubEntry
->me
.Title
= PoolPrint(L
"Boot %s", LoaderTitle
);
968 SubEntry
->me
.Tag
= TAG_LEGACY
;
969 SubEntry
->Volume
= Entry
->Volume
;
970 SubEntry
->LoadOptions
= Entry
->LoadOptions
;
971 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
973 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
974 Entry
->me
.SubScreen
= SubScreen
;
975 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
977 } /* static LEGACY_ENTRY * AddLegacyEntry() */
979 static VOID
ScanLegacyVolume(REFIT_VOLUME
*Volume
, UINTN VolumeIndex
) {
981 BOOLEAN ShowVolume
, HideIfOthersFound
;
984 HideIfOthersFound
= FALSE
;
985 if (Volume
->IsAppleLegacy
) {
987 HideIfOthersFound
= TRUE
;
988 } else if (Volume
->HasBootCode
) {
990 if (Volume
->BlockIO
== Volume
->WholeDiskBlockIO
&&
991 Volume
->BlockIOOffset
== 0 &&
992 Volume
->OSName
== NULL
)
993 // this is a whole disk (MBR) entry; hide if we have entries for partitions
994 HideIfOthersFound
= TRUE
;
996 if (HideIfOthersFound
) {
997 // check for other bootable entries on the same disk
998 for (VolumeIndex2
= 0; VolumeIndex2
< VolumesCount
; VolumeIndex2
++) {
999 if (VolumeIndex2
!= VolumeIndex
&& Volumes
[VolumeIndex2
]->HasBootCode
&&
1000 Volumes
[VolumeIndex2
]->WholeDiskBlockIO
== Volume
->WholeDiskBlockIO
)
1006 AddLegacyEntry(NULL
, Volume
);
1007 } // static VOID ScanLegacyVolume()
1009 // Scan attached optical discs for legacy (BIOS) boot code
1010 // and add anything found to the list....
1011 static VOID
ScanLegacyDisc(VOID
)
1014 REFIT_VOLUME
*Volume
;
1016 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1017 Volume
= Volumes
[VolumeIndex
];
1018 if (Volume
->DiskKind
== DISK_KIND_OPTICAL
)
1019 ScanLegacyVolume(Volume
, VolumeIndex
);
1021 } /* static VOID ScanLegacyDisc() */
1023 // Scan internal hard disks for legacy (BIOS) boot code
1024 // and add anything found to the list....
1025 static VOID
ScanLegacyInternal(VOID
)
1028 REFIT_VOLUME
*Volume
;
1030 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1031 Volume
= Volumes
[VolumeIndex
];
1032 if (Volume
->DiskKind
== DISK_KIND_INTERNAL
)
1033 ScanLegacyVolume(Volume
, VolumeIndex
);
1035 } /* static VOID ScanLegacyInternal() */
1037 // Scan external disks for legacy (BIOS) boot code
1038 // and add anything found to the list....
1039 static VOID
ScanLegacyExternal(VOID
)
1042 REFIT_VOLUME
*Volume
;
1044 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1045 Volume
= Volumes
[VolumeIndex
];
1046 if (Volume
->DiskKind
== DISK_KIND_EXTERNAL
)
1047 ScanLegacyVolume(Volume
, VolumeIndex
);
1049 } /* static VOID ScanLegacyExternal() */
1052 // pre-boot tool functions
1055 static VOID
StartTool(IN LOADER_ENTRY
*Entry
)
1057 BeginExternalScreen(Entry
->UseGraphicsMode
, Entry
->me
.Title
+ 6); // assumes "Start <title>" as assigned below
1058 StartEFIImage(Entry
->DevicePath
, Entry
->LoadOptions
, Basename(Entry
->LoaderPath
),
1059 Basename(Entry
->LoaderPath
), NULL
);
1060 FinishExternalScreen();
1061 } /* static VOID StartTool() */
1063 static LOADER_ENTRY
* AddToolEntry(IN CHAR16
*LoaderPath
, IN CHAR16
*LoaderTitle
, IN EG_IMAGE
*Image
,
1064 IN CHAR16 ShortcutLetter
, IN BOOLEAN UseGraphicsMode
)
1066 LOADER_ENTRY
*Entry
;
1068 Entry
= AllocateZeroPool(sizeof(LOADER_ENTRY
));
1070 Entry
->me
.Title
= PoolPrint(L
"Start %s", LoaderTitle
);
1071 Entry
->me
.Tag
= TAG_TOOL
;
1073 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1074 Entry
->me
.Image
= Image
;
1075 Entry
->LoaderPath
= StrDuplicate(LoaderPath
);
1076 Entry
->DevicePath
= FileDevicePath(SelfLoadedImage
->DeviceHandle
, Entry
->LoaderPath
);
1077 Entry
->UseGraphicsMode
= UseGraphicsMode
;
1079 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1081 } /* static LOADER_ENTRY * AddToolEntry() */
1083 #ifdef DEBIAN_ENABLE_EFI110
1085 // pre-boot driver functions
1088 static VOID
ScanDriverDir(IN CHAR16
*Path
)
1091 REFIT_DIR_ITER DirIter
;
1092 EFI_FILE_INFO
*DirEntry
;
1093 CHAR16 FileName
[256];
1095 // look through contents of the directory
1096 DirIterOpen(SelfRootDir
, Path
, &DirIter
);
1097 while (DirIterNext(&DirIter
, 2, L
"*.EFI", &DirEntry
)) {
1098 if (DirEntry
->FileName
[0] == '.')
1099 continue; // skip this
1101 SPrint(FileName
, 255, L
"%s\\%s", Path
, DirEntry
->FileName
);
1102 Status
= StartEFIImage(FileDevicePath(SelfLoadedImage
->DeviceHandle
, FileName
),
1103 L
"", DirEntry
->FileName
, DirEntry
->FileName
, NULL
);
1105 Status
= DirIterClose(&DirIter
);
1106 if (Status
!= EFI_NOT_FOUND
) {
1107 SPrint(FileName
, 255, L
"while scanning the %s directory", Path
);
1108 CheckError(Status
, FileName
);
1112 LibScanHandleDatabase (
1113 EFI_HANDLE DriverBindingHandle
, OPTIONAL
1114 UINT32
*DriverBindingHandleIndex
, OPTIONAL
1115 EFI_HANDLE ControllerHandle
, OPTIONAL
1116 UINT32
*ControllerHandleIndex
, OPTIONAL
1118 EFI_HANDLE
**HandleBuffer
,
1121 #define EFI_HANDLE_TYPE_UNKNOWN 0x000
1122 #define EFI_HANDLE_TYPE_IMAGE_HANDLE 0x001
1123 #define EFI_HANDLE_TYPE_DRIVER_BINDING_HANDLE 0x002
1124 #define EFI_HANDLE_TYPE_DEVICE_DRIVER 0x004
1125 #define EFI_HANDLE_TYPE_BUS_DRIVER 0x008
1126 #define EFI_HANDLE_TYPE_DRIVER_CONFIGURATION_HANDLE 0x010
1127 #define EFI_HANDLE_TYPE_DRIVER_DIAGNOSTICS_HANDLE 0x020
1128 #define EFI_HANDLE_TYPE_COMPONENT_NAME_HANDLE 0x040
1129 #define EFI_HANDLE_TYPE_DEVICE_HANDLE 0x080
1130 #define EFI_HANDLE_TYPE_PARENT_HANDLE 0x100
1131 #define EFI_HANDLE_TYPE_CONTROLLER_HANDLE 0x200
1132 #define EFI_HANDLE_TYPE_CHILD_HANDLE 0x400
1134 static EFI_STATUS
ConnectAllDriversToAllControllers(VOID
)
1137 UINTN AllHandleCount
;
1138 EFI_HANDLE
*AllHandleBuffer
;
1141 EFI_HANDLE
*HandleBuffer
;
1147 Status
= LibLocateHandle(AllHandles
,
1152 if (EFI_ERROR(Status
))
1155 for (Index
= 0; Index
< AllHandleCount
; Index
++) {
1157 // Scan the handle database
1159 Status
= LibScanHandleDatabase(NULL
,
1161 AllHandleBuffer
[Index
],
1166 if (EFI_ERROR (Status
))
1170 if (HandleType
[Index
] & EFI_HANDLE_TYPE_DRIVER_BINDING_HANDLE
)
1172 if (HandleType
[Index
] & EFI_HANDLE_TYPE_IMAGE_HANDLE
)
1177 for (HandleIndex
= 0; HandleIndex
< HandleCount
; HandleIndex
++) {
1178 if (HandleType
[HandleIndex
] & EFI_HANDLE_TYPE_PARENT_HANDLE
)
1183 if (HandleType
[Index
] & EFI_HANDLE_TYPE_DEVICE_HANDLE
) {
1184 Status
= refit_call4_wrapper(BS
->ConnectController
,
1185 AllHandleBuffer
[Index
],
1193 FreePool (HandleBuffer
);
1194 FreePool (HandleType
);
1198 FreePool (AllHandleBuffer
);
1202 static VOID
LoadDrivers(VOID
)
1204 CHAR16 DirName
[256];
1206 // load drivers from /efi/refind/drivers
1207 SPrint(DirName
, 255, L
"%s\\drivers", SelfDirPath
);
1208 ScanDriverDir(DirName
);
1210 // load drivers from /efi/tools/drivers
1211 ScanDriverDir(L
"\\efi\\tools\\drivers");
1213 // connect all devices
1214 ConnectAllDriversToAllControllers();
1216 #endif /* DEBIAN_ENABLE_EFI110 */
1218 static VOID
ScanForBootloaders(VOID
) {
1222 // Commented-out below: Was part of an attempt to get rEFInd to
1223 // re-scan disk devices on pressing Esc; but doesn't work (yet), so
1225 // MainMenu.Title = StrDuplicate(L"Main Menu 2");
1226 // MainMenu.TitleImage = NULL;
1227 // MainMenu.InfoLineCount = 0;
1228 // MainMenu.InfoLines = NULL;
1229 // MainMenu.EntryCount = 0;
1230 // MainMenu.Entries = NULL;
1231 // MainMenu.TimeoutSeconds = 20;
1232 // MainMenu.TimeoutText = StrDuplicate(L"Automatic boot");
1235 // scan for loaders and tools, add them to the menu
1236 for (i
= 0; i
< NUM_SCAN_OPTIONS
; i
++) {
1237 switch(GlobalConfig
.ScanFor
[i
]) {
1242 ScanLegacyInternal();
1245 ScanLegacyExternal();
1248 ScanUserConfigured();
1262 // assign shortcut keys
1263 for (i
= 0; i
< MainMenu
.EntryCount
&& MainMenu
.Entries
[i
]->Row
== 0 && i
< 9; i
++)
1264 MainMenu
.Entries
[i
]->ShortcutDigit
= (CHAR16
)('1' + i
);
1266 // wait for user ACK when there were errors
1267 FinishTextScreen(FALSE
);
1268 } // static VOID ScanForBootloaders()
1270 // Add the second-row tags containing built-in and external tools (EFI shell,
1272 static VOID
ScanForTools(VOID
) {
1273 CHAR16
*FileName
= NULL
;
1276 for (i
= 0; i
< NUM_TOOLS
; i
++) {
1277 switch(GlobalConfig
.ShowTools
[i
]) {
1279 MenuEntryShutdown
.Image
= BuiltinIcon(BUILTIN_ICON_FUNC_SHUTDOWN
);
1280 AddMenuEntry(&MainMenu
, &MenuEntryShutdown
);
1283 MenuEntryReset
.Image
= BuiltinIcon(BUILTIN_ICON_FUNC_RESET
);
1284 AddMenuEntry(&MainMenu
, &MenuEntryReset
);
1287 MenuEntryAbout
.Image
= BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT
);
1288 AddMenuEntry(&MainMenu
, &MenuEntryAbout
);
1291 MenuEntryExit
.Image
= BuiltinIcon(BUILTIN_ICON_FUNC_EXIT
);
1292 AddMenuEntry(&MainMenu
, &MenuEntryExit
);
1296 while ((FileName
= FindCommaDelimited(SHELL_NAMES
, j
++)) != NULL
) {
1297 if (FileExists(SelfRootDir
, FileName
)) {
1298 AddToolEntry(FileName
, L
"EFI Shell", BuiltinIcon(BUILTIN_ICON_TOOL_SHELL
), 'E', FALSE
);
1303 MergeStrings(&FileName
, L
"\\efi\\tools\\gptsync.efi", 0);
1304 if (FileExists(SelfRootDir
, FileName
)) {
1305 AddToolEntry(FileName
, L
"Make Hybrid MBR", BuiltinIcon(BUILTIN_ICON_TOOL_PART
), 'P', FALSE
);
1309 if (FileName
!= NULL
) {
1314 } // static VOID ScanForTools
1322 efi_main (IN EFI_HANDLE ImageHandle
, IN EFI_SYSTEM_TABLE
*SystemTable
)
1325 BOOLEAN MainLoopRunning
= TRUE
;
1326 REFIT_MENU_ENTRY
*ChosenEntry
;
1330 InitializeLib(ImageHandle
, SystemTable
);
1332 Status
= InitRefitLib(ImageHandle
);
1333 if (EFI_ERROR(Status
))
1336 // read configuration
1337 CopyMem(GlobalConfig
.ScanFor
, "ieo ", NUM_SCAN_OPTIONS
);
1339 MainMenu
.TimeoutSeconds
= GlobalConfig
.Timeout
;
1341 // disable EFI watchdog timer
1342 refit_call4_wrapper(BS
->SetWatchdogTimer
, 0x0000, 0x0000, 0x0000, NULL
);
1344 // further bootstrap (now with config available)
1346 #ifdef DEBIAN_ENABLE_EFI110
1348 #endif /* DEBIAN_ENABLE_EFI110 */
1349 ScanForBootloaders();
1352 while (MainLoopRunning
) {
1353 MenuExit
= RunMainMenu(&MainMenu
, GlobalConfig
.DefaultSelection
, &ChosenEntry
);
1355 // We don't allow exiting the main menu with the Escape key.
1356 if (MenuExit
== MENU_EXIT_ESCAPE
) {
1357 // Commented-out below: Was part of an attempt to get rEFInd to
1358 // re-scan disk devices on pressing Esc; but doesn't work (yet), so
1361 // ScanForBootloaders();
1366 switch (ChosenEntry
->Tag
) {
1368 case TAG_REBOOT
: // Reboot
1370 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);
1371 MainLoopRunning
= FALSE
; // just in case we get this far
1374 case TAG_SHUTDOWN
: // Shut Down
1376 refit_call4_wrapper(RT
->ResetSystem
, EfiResetShutdown
, EFI_SUCCESS
, 0, NULL
);
1377 MainLoopRunning
= FALSE
; // just in case we get this far
1380 case TAG_ABOUT
: // About rEFInd
1384 case TAG_LOADER
: // Boot OS via .EFI loader
1385 StartLoader((LOADER_ENTRY
*)ChosenEntry
);
1388 case TAG_LEGACY
: // Boot legacy OS
1389 StartLegacy((LEGACY_ENTRY
*)ChosenEntry
);
1392 case TAG_TOOL
: // Start a EFI tool
1393 StartTool((LOADER_ENTRY
*)ChosenEntry
);
1396 case TAG_EXIT
: // Terminate rEFInd
1397 BeginTextScreen(L
" ");
1404 // If we end up here, things have gone wrong. Try to reboot, and if that
1405 // fails, go into an endless loop.
1406 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);