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.6");
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 // Add trailing backslash for root directory; necessary on some systems, but must
238 // NOT be added to all directories, since on other systems, a trailing backslash on
239 // anything but the root directory causes them to flake out!
240 if (StrLen(Path
) == 0) {
241 MergeStrings(&Path
, L
"\\", 0);
243 DirIterOpen(Volume
->RootDir
, Path
, &DirIter
);
244 // Now add a trailing backslash if it was NOT added earlier, for consistency in
245 // building the InitrdName later....
246 if ((StrLen(Path
) > 0) && (Path
[StrLen(Path
) - 1] != L
'\\'))
247 MergeStrings(&Path
, L
"\\", 0);
248 while ((DirIterNext(&DirIter
, 2, L
"init*", &DirEntry
)) && (InitrdName
== NULL
)) {
249 InitrdVersion
= FindNumbers(DirEntry
->FileName
);
250 if (KernelVersion
!= NULL
) {
251 if (StriCmp(InitrdVersion
, KernelVersion
) == 0)
252 InitrdName
= PoolPrint(L
"%s%s", Path
, DirEntry
->FileName
);
254 if (InitrdVersion
== NULL
)
255 InitrdName
= PoolPrint(L
"%s%s", Path
, DirEntry
->FileName
);
257 if (InitrdVersion
!= NULL
)
258 FreePool(InitrdVersion
);
260 DirIterClose(&DirIter
);
262 // Note: Don't FreePool(FileName), since Basename returns a pointer WITHIN the string it's passed.
263 FreePool(KernelVersion
);
266 } // static CHAR16 * FindInitrd()
268 LOADER_ENTRY
* AddPreparedLoaderEntry(LOADER_ENTRY
*Entry
) {
269 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
272 } // LOADER_ENTRY * AddPreparedLoaderEntry()
274 // Creates a new LOADER_ENTRY data structure and populates it with
275 // default values from the specified Entry, or NULL values if Entry
276 // is unspecified (NULL).
277 // Returns a pointer to the new data structure, or NULL if it
278 // couldn't be allocated
279 LOADER_ENTRY
*InitializeLoaderEntry(IN LOADER_ENTRY
*Entry
) {
280 LOADER_ENTRY
*NewEntry
= NULL
;
282 NewEntry
= AllocateZeroPool(sizeof(LOADER_ENTRY
));
283 if (NewEntry
!= NULL
) {
284 NewEntry
->me
.Title
= NULL
;
285 NewEntry
->me
.Tag
= TAG_LOADER
;
286 NewEntry
->Enabled
= TRUE
;
287 NewEntry
->UseGraphicsMode
= FALSE
;
288 NewEntry
->OSType
= 0;
290 NewEntry
->LoaderPath
= StrDuplicate(Entry
->LoaderPath
);
291 NewEntry
->VolName
= StrDuplicate(Entry
->VolName
);
292 NewEntry
->DevicePath
= Entry
->DevicePath
;
293 NewEntry
->UseGraphicsMode
= Entry
->UseGraphicsMode
;
294 NewEntry
->LoadOptions
= StrDuplicate(Entry
->LoadOptions
);
295 NewEntry
->InitrdPath
= StrDuplicate(Entry
->InitrdPath
);
299 } // LOADER_ENTRY *InitializeLoaderEntry()
301 // Prepare a REFIT_MENU_SCREEN data structure for a subscreen entry. This sets up
302 // the default entry that launches the boot loader using the same options as the
303 // main Entry does. Subsequent options can be added by the calling function.
304 // If a subscreen already exists in the Entry that's passed to this function,
305 // it's left unchanged and a pointer to it is returned.
306 // Returns a pointer to the new subscreen data structure, or NULL if there
307 // were problems allocating memory.
308 REFIT_MENU_SCREEN
*InitializeSubScreen(IN LOADER_ENTRY
*Entry
) {
309 CHAR16
*FileName
, *Temp
;
310 REFIT_MENU_SCREEN
*SubScreen
= NULL
;
311 LOADER_ENTRY
*SubEntry
;
313 FileName
= Basename(Entry
->LoaderPath
);
314 if (Entry
->me
.SubScreen
== NULL
) { // No subscreen yet; initialize default entry....
315 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
316 if (SubScreen
!= NULL
) {
317 SubScreen
->Title
= PoolPrint(L
"Boot Options for %s on %s", (Entry
->Title
!= NULL
) ? Entry
->Title
: FileName
, Entry
->VolName
);
318 SubScreen
->TitleImage
= Entry
->me
.Image
;
320 SubEntry
= InitializeLoaderEntry(Entry
);
321 if (SubEntry
!= NULL
) {
322 SubEntry
->me
.Title
= L
"Boot using default options";
323 if ((SubEntry
->InitrdPath
!= NULL
) && (StrLen(SubEntry
->InitrdPath
) > 0) && (!StriSubCmp(L
"initrd", SubEntry
->LoadOptions
))) {
324 Temp
= PoolPrint(L
"initrd=%s", SubEntry
->InitrdPath
);
325 MergeStrings(&SubEntry
->LoadOptions
, Temp
, L
' ');
328 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
329 } // if (SubEntry != NULL)
330 } // if (SubScreen != NULL)
331 } else { // existing subscreen; less initialization, and just add new entry later....
332 SubScreen
= Entry
->me
.SubScreen
;
335 } // REFIT_MENU_SCREEN *InitializeSubScreen()
337 VOID
GenerateSubScreen(LOADER_ENTRY
*Entry
, IN REFIT_VOLUME
*Volume
) {
338 REFIT_MENU_SCREEN
*SubScreen
;
339 LOADER_ENTRY
*SubEntry
;
340 CHAR16
*FileName
, *InitrdOption
= NULL
, *Temp
;
341 CHAR16 DiagsFileName
[256];
346 FileName
= Basename(Entry
->LoaderPath
);
347 // create the submenu
348 if (StrLen(Entry
->Title
) == 0) {
349 FreePool(Entry
->Title
);
352 SubScreen
= InitializeSubScreen(Entry
);
354 // loader-specific submenu entries
355 if (Entry
->OSType
== 'M') { // entries for Mac OS X
357 SubEntry
= InitializeLoaderEntry(Entry
);
358 if (SubEntry
!= NULL
) {
359 SubEntry
->me
.Title
= L
"Boot Mac OS X with a 64-bit kernel";
360 SubEntry
->LoadOptions
= L
"arch=x86_64";
361 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
364 SubEntry
= InitializeLoaderEntry(Entry
);
365 if (SubEntry
!= NULL
) {
366 SubEntry
->me
.Title
= L
"Boot Mac OS X with a 32-bit kernel";
367 SubEntry
->LoadOptions
= L
"arch=i386";
368 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
372 if (!(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_SINGLEUSER
)) {
373 SubEntry
= InitializeLoaderEntry(Entry
);
374 if (SubEntry
!= NULL
) {
375 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode";
376 SubEntry
->UseGraphicsMode
= FALSE
;
377 SubEntry
->LoadOptions
= L
"-v";
378 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
382 SubEntry
= InitializeLoaderEntry(Entry
);
383 if (SubEntry
!= NULL
) {
384 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode (64-bit)";
385 SubEntry
->UseGraphicsMode
= FALSE
;
386 SubEntry
->LoadOptions
= L
"-v arch=x86_64";
387 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
390 SubEntry
= InitializeLoaderEntry(Entry
);
391 if (SubEntry
!= NULL
) {
392 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode (32-bit)";
393 SubEntry
->UseGraphicsMode
= FALSE
;
394 SubEntry
->LoadOptions
= L
"-v arch=i386";
395 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
399 SubEntry
= InitializeLoaderEntry(Entry
);
400 if (SubEntry
!= NULL
) {
401 SubEntry
->me
.Title
= L
"Boot Mac OS X in single user mode";
402 SubEntry
->UseGraphicsMode
= FALSE
;
403 SubEntry
->LoadOptions
= L
"-v -s";
404 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
408 // check for Apple hardware diagnostics
409 StrCpy(DiagsFileName
, L
"\\System\\Library\\CoreServices\\.diagnostics\\diags.efi");
410 if (FileExists(Volume
->RootDir
, DiagsFileName
) && !(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_HWTEST
)) {
411 SubEntry
= InitializeLoaderEntry(Entry
);
412 if (SubEntry
!= NULL
) {
413 SubEntry
->me
.Title
= L
"Run Apple Hardware Test";
414 FreePool(SubEntry
->LoaderPath
);
415 SubEntry
->LoaderPath
= StrDuplicate(DiagsFileName
);
416 SubEntry
->DevicePath
= FileDevicePath(Volume
->DeviceHandle
, SubEntry
->LoaderPath
);
417 SubEntry
->UseGraphicsMode
= TRUE
;
418 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
420 } // if diagnostics entry found
422 } else if (Entry
->OSType
== 'L') { // entries for Linux kernels with EFI stub loaders
423 File
= ReadLinuxOptionsFile(Entry
->LoaderPath
, Volume
);
425 if ((Temp
= FindInitrd(Entry
->LoaderPath
, Volume
)) != NULL
)
426 InitrdOption
= PoolPrint(L
"initrd=%s", Temp
);
427 TokenCount
= ReadTokenLine(File
, &TokenList
); // read and discard first entry, since it's
428 FreeTokenLine(&TokenList
, &TokenCount
); // set up by InitializeSubScreen(), earlier....
429 while ((TokenCount
= ReadTokenLine(File
, &TokenList
)) > 1) {
430 SubEntry
= InitializeLoaderEntry(Entry
);
431 SubEntry
->me
.Title
= StrDuplicate(TokenList
[0]);
432 if (SubEntry
->LoadOptions
!= NULL
)
433 FreePool(SubEntry
->LoadOptions
);
434 SubEntry
->LoadOptions
= StrDuplicate(TokenList
[1]);
435 MergeStrings(&SubEntry
->LoadOptions
, InitrdOption
, L
' ');
436 FreeTokenLine(&TokenList
, &TokenCount
);
437 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
440 FreePool(InitrdOption
);
444 } // if Linux options file exists
446 } else if (Entry
->OSType
== 'E') { // entries for ELILO
447 SubEntry
= InitializeLoaderEntry(Entry
);
448 if (SubEntry
!= NULL
) {
449 SubEntry
->me
.Title
= PoolPrint(L
"Run %s in interactive mode", FileName
);
450 SubEntry
->LoadOptions
= L
"-p";
451 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
454 SubEntry
= InitializeLoaderEntry(Entry
);
455 if (SubEntry
!= NULL
) {
456 SubEntry
->me
.Title
= L
"Boot Linux for a 17\" iMac or a 15\" MacBook Pro (*)";
457 SubEntry
->UseGraphicsMode
= TRUE
;
458 SubEntry
->LoadOptions
= L
"-d 0 i17";
459 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
462 SubEntry
= InitializeLoaderEntry(Entry
);
463 if (SubEntry
!= NULL
) {
464 SubEntry
->me
.Title
= L
"Boot Linux for a 20\" iMac (*)";
465 SubEntry
->UseGraphicsMode
= TRUE
;
466 SubEntry
->LoadOptions
= L
"-d 0 i20";
467 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
470 SubEntry
= InitializeLoaderEntry(Entry
);
471 if (SubEntry
!= NULL
) {
472 SubEntry
->me
.Title
= L
"Boot Linux for a Mac Mini (*)";
473 SubEntry
->UseGraphicsMode
= TRUE
;
474 SubEntry
->LoadOptions
= L
"-d 0 mini";
475 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
478 AddMenuInfoLine(SubScreen
, L
"NOTE: This is an example. Entries");
479 AddMenuInfoLine(SubScreen
, L
"marked with (*) may not work.");
481 } else if (Entry
->OSType
== 'X') { // entries for xom.efi
482 // by default, skip the built-in selection and boot from hard disk only
483 Entry
->LoadOptions
= L
"-s -h";
485 SubEntry
= InitializeLoaderEntry(Entry
);
486 if (SubEntry
!= NULL
) {
487 SubEntry
->me
.Title
= L
"Boot Windows from Hard Disk";
488 SubEntry
->LoadOptions
= L
"-s -h";
489 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
492 SubEntry
= InitializeLoaderEntry(Entry
);
493 if (SubEntry
!= NULL
) {
494 SubEntry
->me
.Title
= L
"Boot Windows from CD-ROM";
495 SubEntry
->LoadOptions
= L
"-s -c";
496 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
499 SubEntry
= InitializeLoaderEntry(Entry
);
500 if (SubEntry
!= NULL
) {
501 SubEntry
->me
.Title
= PoolPrint(L
"Run %s in text mode", FileName
);
502 SubEntry
->UseGraphicsMode
= FALSE
;
503 SubEntry
->LoadOptions
= L
"-v";
504 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
506 } // entries for xom.efi
507 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
508 Entry
->me
.SubScreen
= SubScreen
;
509 } // VOID GenerateSubScreen()
511 // Returns options for a Linux kernel. Reads them from an options file in the
512 // kernel's directory; and if present, adds an initrd= option for an initial
513 // RAM disk file with the same version number as the kernel file.
514 static CHAR16
* GetMainLinuxOptions(IN CHAR16
* LoaderPath
, IN REFIT_VOLUME
*Volume
) {
515 CHAR16
*Options
= NULL
, *InitrdName
, *InitrdOption
= NULL
;
517 Options
= GetFirstOptionsFromFile(LoaderPath
, Volume
);
518 InitrdName
= FindInitrd(LoaderPath
, Volume
);
519 if (InitrdName
!= NULL
)
520 InitrdOption
= PoolPrint(L
"initrd=%s", InitrdName
);
521 MergeStrings(&Options
, InitrdOption
, ' ');
522 if (InitrdOption
!= NULL
)
523 FreePool(InitrdOption
);
524 if (InitrdName
!= NULL
)
525 FreePool(InitrdName
);
527 } // static CHAR16 * GetMainLinuxOptions()
529 // Sets a few defaults for a loader entry -- mainly the icon, but also the OS type
530 // code and shortcut letter. For Linux EFI stub loaders, also sets kernel options
531 // that will (with luck) work fairly automatically.
532 VOID
SetLoaderDefaults(LOADER_ENTRY
*Entry
, CHAR16
*LoaderPath
, IN REFIT_VOLUME
*Volume
) {
533 CHAR16 IconFileName
[256];
534 CHAR16
*FileName
, *PathOnly
, *OSIconName
= NULL
, *Temp
;
535 CHAR16 ShortcutLetter
= 0;
537 FileName
= Basename(LoaderPath
);
538 PathOnly
= FindPath(LoaderPath
);
540 // locate a custom icon for the loader
541 StrCpy(IconFileName
, LoaderPath
);
542 ReplaceExtension(IconFileName
, L
".icns");
543 if (FileExists(Volume
->RootDir
, IconFileName
)) {
544 Entry
->me
.Image
= LoadIcns(Volume
->RootDir
, IconFileName
, 128);
545 } else if ((StrLen(PathOnly
) == 0) && (Volume
->VolIconImage
!= NULL
)) {
546 Entry
->me
.Image
= Volume
->VolIconImage
;
547 } // icon matched to loader or volume
549 Temp
= FindLastDirName(LoaderPath
);
550 MergeStrings(&OSIconName
, Temp
, L
',');
552 if (OSIconName
!= NULL
) {
553 ShortcutLetter
= OSIconName
[0];
556 // detect specific loaders
557 if (StriSubCmp(L
"bzImage", LoaderPath
) || StriSubCmp(L
"vmlinuz", LoaderPath
)) {
558 MergeStrings(&OSIconName
, L
"linux", L
',');
560 if (ShortcutLetter
== 0)
561 ShortcutLetter
= 'L';
562 Entry
->LoadOptions
= GetMainLinuxOptions(LoaderPath
, Volume
);
563 } else if (StriSubCmp(L
"refit", LoaderPath
)) {
564 MergeStrings(&OSIconName
, L
"refit", L
',');
566 ShortcutLetter
= 'R';
567 } else if (StriCmp(LoaderPath
, MACOSX_LOADER_PATH
) == 0) {
568 if (Volume
->VolIconImage
!= NULL
) { // custom icon file found
569 Entry
->me
.Image
= Volume
->VolIconImage
;
571 MergeStrings(&OSIconName
, L
"mac", L
',');
572 Entry
->UseGraphicsMode
= TRUE
;
574 ShortcutLetter
= 'M';
575 } else if (StriCmp(FileName
, L
"diags.efi") == 0) {
576 MergeStrings(&OSIconName
, L
"hwtest", L
',');
577 } else if (StriCmp(FileName
, L
"e.efi") == 0 || StriCmp(FileName
, L
"elilo.efi") == 0) {
578 MergeStrings(&OSIconName
, L
"elilo,linux", L
',');
580 if (ShortcutLetter
== 0)
581 ShortcutLetter
= 'L';
582 } else if (StriCmp(FileName
, L
"cdboot.efi") == 0 ||
583 StriCmp(FileName
, L
"bootmgr.efi") == 0 ||
584 StriCmp(FileName
, L
"Bootmgfw.efi") == 0) {
585 MergeStrings(&OSIconName
, L
"win", L
',');
587 ShortcutLetter
= 'W';
588 } else if (StriCmp(FileName
, L
"xom.efi") == 0) {
589 MergeStrings(&OSIconName
, L
"xom,win", L
',');
590 Entry
->UseGraphicsMode
= TRUE
;
592 ShortcutLetter
= 'W';
595 if ((ShortcutLetter
>= 'a') && (ShortcutLetter
<= 'z'))
596 ShortcutLetter
= ShortcutLetter
- 'a' + 'A'; // convert lowercase to uppercase
597 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
598 if (Entry
->me
.Image
== NULL
)
599 Entry
->me
.Image
= LoadOSIcon(OSIconName
, L
"unknown", FALSE
);
600 if (PathOnly
!= NULL
)
602 } // VOID SetLoaderDefaults()
604 // Add a specified EFI boot loader to the list, using automatic settings
605 // for icons, options, etc.
606 LOADER_ENTRY
* AddLoaderEntry(IN CHAR16
*LoaderPath
, IN CHAR16
*LoaderTitle
, IN REFIT_VOLUME
*Volume
) {
609 Entry
= InitializeLoaderEntry(NULL
);
611 Entry
->Title
= StrDuplicate(LoaderTitle
);
612 Entry
->me
.Title
= PoolPrint(L
"Boot %s from %s", (LoaderTitle
!= NULL
) ? LoaderTitle
: LoaderPath
+ 1, Volume
->VolName
);
614 Entry
->me
.BadgeImage
= Volume
->VolBadgeImage
;
615 Entry
->LoaderPath
= StrDuplicate(LoaderPath
);
616 Entry
->VolName
= Volume
->VolName
;
617 Entry
->DevicePath
= FileDevicePath(Volume
->DeviceHandle
, Entry
->LoaderPath
);
618 SetLoaderDefaults(Entry
, LoaderPath
, Volume
);
619 GenerateSubScreen(Entry
, Volume
);
620 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
624 } // LOADER_ENTRY * AddLoaderEntry()
626 // Scan an individual directory for EFI boot loader files and, if found,
627 // add them to the list.
628 static VOID
ScanLoaderDir(IN REFIT_VOLUME
*Volume
, IN CHAR16
*Path
)
631 REFIT_DIR_ITER DirIter
;
632 EFI_FILE_INFO
*DirEntry
;
633 CHAR16 FileName
[256];
635 // Note: SelfDirPath includes a leading backslash ('\'), but Path
636 // doesn't, so we rejigger the string to compensate....
637 if (!SelfDirPath
|| !Path
|| ((StriCmp(Path
, &SelfDirPath
[1]) == 0) && Volume
!= SelfVolume
) ||
638 (StriCmp(Path
, &SelfDirPath
[1]) != 0)) {
639 // look through contents of the directory
640 DirIterOpen(Volume
->RootDir
, Path
, &DirIter
);
641 while (DirIterNext(&DirIter
, 2, L
"*.efi", &DirEntry
)) {
642 if (DirEntry
->FileName
[0] == '.' ||
643 StriCmp(DirEntry
->FileName
, L
"TextMode.efi") == 0 ||
644 StriCmp(DirEntry
->FileName
, L
"ebounce.efi") == 0 ||
645 StriCmp(DirEntry
->FileName
, L
"GraphicsConsole.efi") == 0 ||
646 StriSubCmp(L
"shell", DirEntry
->FileName
))
647 continue; // skip this
650 SPrint(FileName
, 255, L
"\\%s\\%s", Path
, DirEntry
->FileName
);
652 SPrint(FileName
, 255, L
"\\%s", DirEntry
->FileName
);
653 AddLoaderEntry(FileName
, NULL
, Volume
);
655 Status
= DirIterClose(&DirIter
);
656 if (Status
!= EFI_NOT_FOUND
) {
658 SPrint(FileName
, 255, L
"while scanning the %s directory", Path
);
660 StrCpy(FileName
, L
"while scanning the root directory");
661 CheckError(Status
, FileName
);
662 } // if (Status != EFI_NOT_FOUND)
663 } // if not scanning our own directory
664 } /* static VOID ScanLoaderDir() */
666 static VOID
ScanEfiFiles(REFIT_VOLUME
*Volume
) {
668 REFIT_DIR_ITER EfiDirIter
;
669 EFI_FILE_INFO
*EfiDirEntry
;
670 CHAR16 FileName
[256];
672 if ((Volume
->RootDir
!= NULL
) && (Volume
->VolName
!= NULL
)) {
673 // check for Mac OS X boot loader
674 StrCpy(FileName
, MACOSX_LOADER_PATH
);
675 if (FileExists(Volume
->RootDir
, FileName
)) {
676 AddLoaderEntry(FileName
, L
"Mac OS X", Volume
);
680 StrCpy(FileName
, L
"\\System\\Library\\CoreServices\\xom.efi");
681 if (FileExists(Volume
->RootDir
, FileName
)) {
682 AddLoaderEntry(FileName
, L
"Windows XP (XoM)", Volume
);
685 // check for Microsoft boot loader/menu
686 StrCpy(FileName
, L
"\\EFI\\Microsoft\\Boot\\Bootmgfw.efi");
687 if (FileExists(Volume
->RootDir
, FileName
)) {
688 AddLoaderEntry(FileName
, L
"Microsoft EFI boot", Volume
);
691 // scan the root directory for EFI executables
692 ScanLoaderDir(Volume
, NULL
);
693 // scan the elilo directory (as used on gimli's first Live CD)
694 ScanLoaderDir(Volume
, L
"elilo");
695 // scan the boot directory
696 ScanLoaderDir(Volume
, L
"boot");
698 // scan subdirectories of the EFI directory (as per the standard)
699 DirIterOpen(Volume
->RootDir
, L
"EFI", &EfiDirIter
);
700 while (DirIterNext(&EfiDirIter
, 1, NULL
, &EfiDirEntry
)) {
701 if (StriCmp(EfiDirEntry
->FileName
, L
"tools") == 0 || EfiDirEntry
->FileName
[0] == '.')
702 continue; // skip this, doesn't contain boot loaders
703 SPrint(FileName
, 255, L
"EFI\\%s", EfiDirEntry
->FileName
);
704 ScanLoaderDir(Volume
, FileName
);
706 Status
= DirIterClose(&EfiDirIter
);
707 if (Status
!= EFI_NOT_FOUND
)
708 CheckError(Status
, L
"while scanning the EFI directory");
710 } // static VOID ScanEfiFiles()
712 // Scan internal disks for valid EFI boot loaders....
713 static VOID
ScanInternal(VOID
) {
716 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
717 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_INTERNAL
) {
718 ScanEfiFiles(Volumes
[VolumeIndex
]);
721 } // static VOID ScanInternal()
723 // Scan external disks for valid EFI boot loaders....
724 static VOID
ScanExternal(VOID
) {
727 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
728 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_EXTERNAL
) {
729 ScanEfiFiles(Volumes
[VolumeIndex
]);
732 } // static VOID ScanExternal()
734 // Scan internal disks for valid EFI boot loaders....
735 static VOID
ScanOptical(VOID
) {
738 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
739 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_OPTICAL
) {
740 ScanEfiFiles(Volumes
[VolumeIndex
]);
743 } // static VOID ScanOptical()
746 // legacy boot functions
749 static EFI_STATUS
ActivateMbrPartition(IN EFI_BLOCK_IO
*BlockIO
, IN UINTN PartitionIndex
)
752 UINT8 SectorBuffer
[512];
753 MBR_PARTITION_INFO
*MbrTable
, *EMbrTable
;
754 UINT32 ExtBase
, ExtCurrent
, NextExtCurrent
;
755 UINTN LogicalPartitionIndex
= 4;
757 BOOLEAN HaveBootCode
;
760 Status
= refit_call5_wrapper(BlockIO
->ReadBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, 0, 512, SectorBuffer
);
761 if (EFI_ERROR(Status
))
763 if (*((UINT16
*)(SectorBuffer
+ 510)) != 0xaa55)
764 return EFI_NOT_FOUND
; // safety measure #1
766 // add boot code if necessary
767 HaveBootCode
= FALSE
;
768 for (i
= 0; i
< MBR_BOOTCODE_SIZE
; i
++) {
769 if (SectorBuffer
[i
] != 0) {
775 // no boot code found in the MBR, add the syslinux MBR code
776 SetMem(SectorBuffer
, MBR_BOOTCODE_SIZE
, 0);
777 CopyMem(SectorBuffer
, syslinux_mbr
, SYSLINUX_MBR_SIZE
);
780 // set the partition active
781 MbrTable
= (MBR_PARTITION_INFO
*)(SectorBuffer
+ 446);
783 for (i
= 0; i
< 4; i
++) {
784 if (MbrTable
[i
].Flags
!= 0x00 && MbrTable
[i
].Flags
!= 0x80)
785 return EFI_NOT_FOUND
; // safety measure #2
786 if (i
== PartitionIndex
)
787 MbrTable
[i
].Flags
= 0x80;
788 else if (PartitionIndex
>= 4 && IS_EXTENDED_PART_TYPE(MbrTable
[i
].Type
)) {
789 MbrTable
[i
].Flags
= 0x80;
790 ExtBase
= MbrTable
[i
].StartLBA
;
792 MbrTable
[i
].Flags
= 0x00;
796 Status
= refit_call5_wrapper(BlockIO
->WriteBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, 0, 512, SectorBuffer
);
797 if (EFI_ERROR(Status
))
800 if (PartitionIndex
>= 4) {
801 // we have to activate a logical partition, so walk the EMBR chain
803 // NOTE: ExtBase was set above while looking at the MBR table
804 for (ExtCurrent
= ExtBase
; ExtCurrent
; ExtCurrent
= NextExtCurrent
) {
806 Status
= refit_call5_wrapper(BlockIO
->ReadBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, ExtCurrent
, 512, SectorBuffer
);
807 if (EFI_ERROR(Status
))
809 if (*((UINT16
*)(SectorBuffer
+ 510)) != 0xaa55)
810 return EFI_NOT_FOUND
; // safety measure #3
812 // scan EMBR, set appropriate partition active
813 EMbrTable
= (MBR_PARTITION_INFO
*)(SectorBuffer
+ 446);
815 for (i
= 0; i
< 4; i
++) {
816 if (EMbrTable
[i
].Flags
!= 0x00 && EMbrTable
[i
].Flags
!= 0x80)
817 return EFI_NOT_FOUND
; // safety measure #4
818 if (EMbrTable
[i
].StartLBA
== 0 || EMbrTable
[i
].Size
== 0)
820 if (IS_EXTENDED_PART_TYPE(EMbrTable
[i
].Type
)) {
822 NextExtCurrent
= ExtBase
+ EMbrTable
[i
].StartLBA
;
823 EMbrTable
[i
].Flags
= (PartitionIndex
>= LogicalPartitionIndex
) ? 0x80 : 0x00;
827 EMbrTable
[i
].Flags
= (PartitionIndex
== LogicalPartitionIndex
) ? 0x80 : 0x00;
828 LogicalPartitionIndex
++;
832 // write current EMBR
833 Status
= refit_call5_wrapper(BlockIO
->WriteBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, ExtCurrent
, 512, SectorBuffer
);
834 if (EFI_ERROR(Status
))
837 if (PartitionIndex
< LogicalPartitionIndex
)
838 break; // stop the loop, no need to touch further EMBRs
844 } /* static EFI_STATUS ActivateMbrPartition() */
846 // early 2006 Core Duo / Core Solo models
847 static UINT8 LegacyLoaderDevicePath1Data
[] = {
848 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
849 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
850 0xFF, 0xFF, 0xF9, 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-2006 Mac Pro (and probably other Core 2 models)
856 static UINT8 LegacyLoaderDevicePath2Data
[] = {
857 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
858 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
859 0xFF, 0xFF, 0xF7, 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,
864 // mid-2007 MBP ("Santa Rosa" based models)
865 static UINT8 LegacyLoaderDevicePath3Data
[] = {
866 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
867 0x00, 0x00, 0xE0, 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,
874 static UINT8 LegacyLoaderDevicePath4Data
[] = {
875 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
876 0x00, 0x00, 0xC0, 0xFF, 0x00, 0x00, 0x00, 0x00,
877 0xFF, 0xFF, 0xF8, 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,
882 // late-2008 MB/MBP (NVidia chipset)
883 static UINT8 LegacyLoaderDevicePath5Data
[] = {
884 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
885 0x00, 0x40, 0xCB, 0xFF, 0x00, 0x00, 0x00, 0x00,
886 0xFF, 0xBF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
887 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
888 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
889 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
892 static EFI_DEVICE_PATH
*LegacyLoaderList
[] = {
893 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath1Data
,
894 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath2Data
,
895 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath3Data
,
896 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath4Data
,
897 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath5Data
,
901 #define MAX_DISCOVERED_PATHS (16)
903 static VOID
StartLegacy(IN LEGACY_ENTRY
*Entry
)
906 EG_IMAGE
*BootLogoImage
;
907 UINTN ErrorInStep
= 0;
908 EFI_DEVICE_PATH
*DiscoveredPathList
[MAX_DISCOVERED_PATHS
];
910 BeginExternalScreen(TRUE
, L
"Booting Legacy OS");
912 BootLogoImage
= LoadOSIcon(Entry
->Volume
->OSIconName
, L
"legacy", TRUE
);
913 if (BootLogoImage
!= NULL
)
914 BltImageAlpha(BootLogoImage
,
915 (UGAWidth
- BootLogoImage
->Width
) >> 1,
916 (UGAHeight
- BootLogoImage
->Height
) >> 1,
917 &StdBackgroundPixel
);
919 if (Entry
->Volume
->IsMbrPartition
)
920 ActivateMbrPartition(Entry
->Volume
->WholeDiskBlockIO
, Entry
->Volume
->MbrPartitionIndex
);
922 ExtractLegacyLoaderPaths(DiscoveredPathList
, MAX_DISCOVERED_PATHS
, LegacyLoaderList
);
924 Status
= StartEFIImageList(DiscoveredPathList
, Entry
->LoadOptions
, NULL
, L
"legacy loader", &ErrorInStep
);
925 if (Status
== EFI_NOT_FOUND
) {
926 if (ErrorInStep
== 1) {
927 Print(L
"\nPlease make sure that you have the latest firmware update installed.\n");
928 } else if (ErrorInStep
== 3) {
929 Print(L
"\nThe firmware refused to boot from the selected volume. Note that external\n"
930 L
"hard drives are not well-supported by Apple's firmware for legacy OS booting.\n");
933 FinishExternalScreen();
934 } /* static VOID StartLegacy() */
936 static LEGACY_ENTRY
* AddLegacyEntry(IN CHAR16
*LoaderTitle
, IN REFIT_VOLUME
*Volume
)
938 LEGACY_ENTRY
*Entry
, *SubEntry
;
939 REFIT_MENU_SCREEN
*SubScreen
;
941 CHAR16 ShortcutLetter
= 0;
943 if (LoaderTitle
== NULL
) {
944 if (Volume
->OSName
!= NULL
) {
945 LoaderTitle
= Volume
->OSName
;
946 if (LoaderTitle
[0] == 'W' || LoaderTitle
[0] == 'L')
947 ShortcutLetter
= LoaderTitle
[0];
949 LoaderTitle
= L
"Legacy OS";
951 if (Volume
->VolName
!= NULL
)
952 VolDesc
= Volume
->VolName
;
954 VolDesc
= (Volume
->DiskKind
== DISK_KIND_OPTICAL
) ? L
"CD" : L
"HD";
956 // prepare the menu entry
957 Entry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
958 Entry
->me
.Title
= PoolPrint(L
"Boot %s from %s", LoaderTitle
, VolDesc
);
959 Entry
->me
.Tag
= TAG_LEGACY
;
961 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
962 Entry
->me
.Image
= LoadOSIcon(Volume
->OSIconName
, L
"legacy", FALSE
);
963 Entry
->me
.BadgeImage
= Volume
->VolBadgeImage
;
964 Entry
->Volume
= Volume
;
965 Entry
->LoadOptions
= (Volume
->DiskKind
== DISK_KIND_OPTICAL
) ? L
"CD" :
966 ((Volume
->DiskKind
== DISK_KIND_EXTERNAL
) ? L
"USB" : L
"HD");
967 Entry
->Enabled
= TRUE
;
969 // create the submenu
970 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
971 SubScreen
->Title
= PoolPrint(L
"Boot Options for %s on %s", LoaderTitle
, VolDesc
);
972 SubScreen
->TitleImage
= Entry
->me
.Image
;
975 SubEntry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
976 SubEntry
->me
.Title
= PoolPrint(L
"Boot %s", LoaderTitle
);
977 SubEntry
->me
.Tag
= TAG_LEGACY
;
978 SubEntry
->Volume
= Entry
->Volume
;
979 SubEntry
->LoadOptions
= Entry
->LoadOptions
;
980 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
982 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
983 Entry
->me
.SubScreen
= SubScreen
;
984 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
986 } /* static LEGACY_ENTRY * AddLegacyEntry() */
988 static VOID
ScanLegacyVolume(REFIT_VOLUME
*Volume
, UINTN VolumeIndex
) {
990 BOOLEAN ShowVolume
, HideIfOthersFound
;
993 HideIfOthersFound
= FALSE
;
994 if (Volume
->IsAppleLegacy
) {
996 HideIfOthersFound
= TRUE
;
997 } else if (Volume
->HasBootCode
) {
999 if (Volume
->BlockIO
== Volume
->WholeDiskBlockIO
&&
1000 Volume
->BlockIOOffset
== 0 &&
1001 Volume
->OSName
== NULL
)
1002 // this is a whole disk (MBR) entry; hide if we have entries for partitions
1003 HideIfOthersFound
= TRUE
;
1005 if (HideIfOthersFound
) {
1006 // check for other bootable entries on the same disk
1007 for (VolumeIndex2
= 0; VolumeIndex2
< VolumesCount
; VolumeIndex2
++) {
1008 if (VolumeIndex2
!= VolumeIndex
&& Volumes
[VolumeIndex2
]->HasBootCode
&&
1009 Volumes
[VolumeIndex2
]->WholeDiskBlockIO
== Volume
->WholeDiskBlockIO
)
1015 AddLegacyEntry(NULL
, Volume
);
1016 } // static VOID ScanLegacyVolume()
1018 // Scan attached optical discs for legacy (BIOS) boot code
1019 // and add anything found to the list....
1020 static VOID
ScanLegacyDisc(VOID
)
1023 REFIT_VOLUME
*Volume
;
1025 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1026 Volume
= Volumes
[VolumeIndex
];
1027 if (Volume
->DiskKind
== DISK_KIND_OPTICAL
)
1028 ScanLegacyVolume(Volume
, VolumeIndex
);
1030 } /* static VOID ScanLegacyDisc() */
1032 // Scan internal hard disks for legacy (BIOS) boot code
1033 // and add anything found to the list....
1034 static VOID
ScanLegacyInternal(VOID
)
1037 REFIT_VOLUME
*Volume
;
1039 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1040 Volume
= Volumes
[VolumeIndex
];
1041 if (Volume
->DiskKind
== DISK_KIND_INTERNAL
)
1042 ScanLegacyVolume(Volume
, VolumeIndex
);
1044 } /* static VOID ScanLegacyInternal() */
1046 // Scan external disks for legacy (BIOS) boot code
1047 // and add anything found to the list....
1048 static VOID
ScanLegacyExternal(VOID
)
1051 REFIT_VOLUME
*Volume
;
1053 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1054 Volume
= Volumes
[VolumeIndex
];
1055 if (Volume
->DiskKind
== DISK_KIND_EXTERNAL
)
1056 ScanLegacyVolume(Volume
, VolumeIndex
);
1058 } /* static VOID ScanLegacyExternal() */
1061 // pre-boot tool functions
1064 static VOID
StartTool(IN LOADER_ENTRY
*Entry
)
1066 BeginExternalScreen(Entry
->UseGraphicsMode
, Entry
->me
.Title
+ 6); // assumes "Start <title>" as assigned below
1067 StartEFIImage(Entry
->DevicePath
, Entry
->LoadOptions
, Basename(Entry
->LoaderPath
),
1068 Basename(Entry
->LoaderPath
), NULL
);
1069 FinishExternalScreen();
1070 } /* static VOID StartTool() */
1072 static LOADER_ENTRY
* AddToolEntry(IN CHAR16
*LoaderPath
, IN CHAR16
*LoaderTitle
, IN EG_IMAGE
*Image
,
1073 IN CHAR16 ShortcutLetter
, IN BOOLEAN UseGraphicsMode
)
1075 LOADER_ENTRY
*Entry
;
1077 Entry
= AllocateZeroPool(sizeof(LOADER_ENTRY
));
1079 Entry
->me
.Title
= PoolPrint(L
"Start %s", LoaderTitle
);
1080 Entry
->me
.Tag
= TAG_TOOL
;
1082 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1083 Entry
->me
.Image
= Image
;
1084 Entry
->LoaderPath
= StrDuplicate(LoaderPath
);
1085 Entry
->DevicePath
= FileDevicePath(SelfLoadedImage
->DeviceHandle
, Entry
->LoaderPath
);
1086 Entry
->UseGraphicsMode
= UseGraphicsMode
;
1088 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1090 } /* static LOADER_ENTRY * AddToolEntry() */
1092 #ifdef DEBIAN_ENABLE_EFI110
1094 // pre-boot driver functions
1097 static VOID
ScanDriverDir(IN CHAR16
*Path
)
1100 REFIT_DIR_ITER DirIter
;
1101 EFI_FILE_INFO
*DirEntry
;
1102 CHAR16 FileName
[256];
1104 // look through contents of the directory
1105 DirIterOpen(SelfRootDir
, Path
, &DirIter
);
1106 while (DirIterNext(&DirIter
, 2, L
"*.EFI", &DirEntry
)) {
1107 if (DirEntry
->FileName
[0] == '.')
1108 continue; // skip this
1110 SPrint(FileName
, 255, L
"%s\\%s", Path
, DirEntry
->FileName
);
1111 Status
= StartEFIImage(FileDevicePath(SelfLoadedImage
->DeviceHandle
, FileName
),
1112 L
"", DirEntry
->FileName
, DirEntry
->FileName
, NULL
);
1114 Status
= DirIterClose(&DirIter
);
1115 if (Status
!= EFI_NOT_FOUND
) {
1116 SPrint(FileName
, 255, L
"while scanning the %s directory", Path
);
1117 CheckError(Status
, FileName
);
1121 LibScanHandleDatabase (
1122 EFI_HANDLE DriverBindingHandle, OPTIONAL
1123 UINT32 *DriverBindingHandleIndex, OPTIONAL
1124 EFI_HANDLE ControllerHandle, OPTIONAL
1125 UINT32 *ControllerHandleIndex, OPTIONAL
1127 EFI_HANDLE **HandleBuffer,
1130 #define EFI_HANDLE_TYPE_UNKNOWN 0x000
1131 #define EFI_HANDLE_TYPE_IMAGE_HANDLE 0x001
1132 #define EFI_HANDLE_TYPE_DRIVER_BINDING_HANDLE 0x002
1133 #define EFI_HANDLE_TYPE_DEVICE_DRIVER 0x004
1134 #define EFI_HANDLE_TYPE_BUS_DRIVER 0x008
1135 #define EFI_HANDLE_TYPE_DRIVER_CONFIGURATION_HANDLE 0x010
1136 #define EFI_HANDLE_TYPE_DRIVER_DIAGNOSTICS_HANDLE 0x020
1137 #define EFI_HANDLE_TYPE_COMPONENT_NAME_HANDLE 0x040
1138 #define EFI_HANDLE_TYPE_DEVICE_HANDLE 0x080
1139 #define EFI_HANDLE_TYPE_PARENT_HANDLE 0x100
1140 #define EFI_HANDLE_TYPE_CONTROLLER_HANDLE 0x200
1141 #define EFI_HANDLE_TYPE_CHILD_HANDLE 0x400 */
1143 static EFI_STATUS
ConnectAllDriversToAllControllers(VOID
)
1146 UINTN AllHandleCount
;
1147 EFI_HANDLE
*AllHandleBuffer
;
1150 EFI_HANDLE
*HandleBuffer
;
1156 Status
= LibLocateHandle(AllHandles
,
1161 if (EFI_ERROR(Status
))
1164 for (Index
= 0; Index
< AllHandleCount
; Index
++) {
1166 // Scan the handle database
1168 Status
= LibScanHandleDatabase(NULL
,
1170 AllHandleBuffer
[Index
],
1175 if (EFI_ERROR (Status
))
1179 if (HandleType
[Index
] & EFI_HANDLE_TYPE_DRIVER_BINDING_HANDLE
)
1181 if (HandleType
[Index
] & EFI_HANDLE_TYPE_IMAGE_HANDLE
)
1186 for (HandleIndex
= 0; HandleIndex
< HandleCount
; HandleIndex
++) {
1187 if (HandleType
[HandleIndex
] & EFI_HANDLE_TYPE_PARENT_HANDLE
)
1192 if (HandleType
[Index
] & EFI_HANDLE_TYPE_DEVICE_HANDLE
) {
1193 Status
= refit_call4_wrapper(BS
->ConnectController
,
1194 AllHandleBuffer
[Index
],
1202 FreePool (HandleBuffer
);
1203 FreePool (HandleType
);
1207 FreePool (AllHandleBuffer
);
1211 static VOID
LoadDrivers(VOID
)
1213 CHAR16 DirName
[256];
1215 // load drivers from /efi/refind/drivers
1216 SPrint(DirName
, 255, L
"%s\\drivers", SelfDirPath
);
1217 ScanDriverDir(DirName
);
1219 // load drivers from /efi/tools/drivers
1220 ScanDriverDir(L
"\\efi\\tools\\drivers");
1222 // connect all devices
1223 ConnectAllDriversToAllControllers();
1225 #endif /* DEBIAN_ENABLE_EFI110 */
1227 static VOID
ScanForBootloaders(VOID
) {
1231 // Commented-out below: Was part of an attempt to get rEFInd to
1232 // re-scan disk devices on pressing Esc; but doesn't work (yet), so
1234 // MainMenu.Title = StrDuplicate(L"Main Menu 2");
1235 // MainMenu.TitleImage = NULL;
1236 // MainMenu.InfoLineCount = 0;
1237 // MainMenu.InfoLines = NULL;
1238 // MainMenu.EntryCount = 0;
1239 // MainMenu.Entries = NULL;
1240 // MainMenu.TimeoutSeconds = 20;
1241 // MainMenu.TimeoutText = StrDuplicate(L"Automatic boot");
1244 // scan for loaders and tools, add them to the menu
1245 for (i
= 0; i
< NUM_SCAN_OPTIONS
; i
++) {
1246 switch(GlobalConfig
.ScanFor
[i
]) {
1251 ScanLegacyInternal();
1254 ScanLegacyExternal();
1257 ScanUserConfigured();
1271 // assign shortcut keys
1272 for (i
= 0; i
< MainMenu
.EntryCount
&& MainMenu
.Entries
[i
]->Row
== 0 && i
< 9; i
++)
1273 MainMenu
.Entries
[i
]->ShortcutDigit
= (CHAR16
)('1' + i
);
1275 // wait for user ACK when there were errors
1276 FinishTextScreen(FALSE
);
1277 } // static VOID ScanForBootloaders()
1279 // Add the second-row tags containing built-in and external tools (EFI shell,
1281 static VOID
ScanForTools(VOID
) {
1282 CHAR16
*FileName
= NULL
;
1285 for (i
= 0; i
< NUM_TOOLS
; i
++) {
1286 switch(GlobalConfig
.ShowTools
[i
]) {
1288 MenuEntryShutdown
.Image
= BuiltinIcon(BUILTIN_ICON_FUNC_SHUTDOWN
);
1289 AddMenuEntry(&MainMenu
, &MenuEntryShutdown
);
1292 MenuEntryReset
.Image
= BuiltinIcon(BUILTIN_ICON_FUNC_RESET
);
1293 AddMenuEntry(&MainMenu
, &MenuEntryReset
);
1296 MenuEntryAbout
.Image
= BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT
);
1297 AddMenuEntry(&MainMenu
, &MenuEntryAbout
);
1300 MenuEntryExit
.Image
= BuiltinIcon(BUILTIN_ICON_FUNC_EXIT
);
1301 AddMenuEntry(&MainMenu
, &MenuEntryExit
);
1305 while ((FileName
= FindCommaDelimited(SHELL_NAMES
, j
++)) != NULL
) {
1306 if (FileExists(SelfRootDir
, FileName
)) {
1307 AddToolEntry(FileName
, L
"EFI Shell", BuiltinIcon(BUILTIN_ICON_TOOL_SHELL
), 'E', FALSE
);
1312 MergeStrings(&FileName
, L
"\\efi\\tools\\gptsync.efi", 0);
1313 if (FileExists(SelfRootDir
, FileName
)) {
1314 AddToolEntry(FileName
, L
"Make Hybrid MBR", BuiltinIcon(BUILTIN_ICON_TOOL_PART
), 'P', FALSE
);
1318 if (FileName
!= NULL
) {
1323 } // static VOID ScanForTools
1331 efi_main (IN EFI_HANDLE ImageHandle
, IN EFI_SYSTEM_TABLE
*SystemTable
)
1334 BOOLEAN MainLoopRunning
= TRUE
;
1335 REFIT_MENU_ENTRY
*ChosenEntry
;
1339 InitializeLib(ImageHandle
, SystemTable
);
1341 Status
= InitRefitLib(ImageHandle
);
1342 if (EFI_ERROR(Status
))
1345 // read configuration
1346 CopyMem(GlobalConfig
.ScanFor
, "ieo ", NUM_SCAN_OPTIONS
);
1348 MainMenu
.TimeoutSeconds
= GlobalConfig
.Timeout
;
1350 // disable EFI watchdog timer
1351 refit_call4_wrapper(BS
->SetWatchdogTimer
, 0x0000, 0x0000, 0x0000, NULL
);
1353 // further bootstrap (now with config available)
1355 #ifdef DEBIAN_ENABLE_EFI110
1357 #endif /* DEBIAN_ENABLE_EFI110 */
1358 ScanForBootloaders();
1361 while (MainLoopRunning
) {
1362 MenuExit
= RunMainMenu(&MainMenu
, GlobalConfig
.DefaultSelection
, &ChosenEntry
);
1364 // We don't allow exiting the main menu with the Escape key.
1365 if (MenuExit
== MENU_EXIT_ESCAPE
) {
1366 // Commented-out below: Was part of an attempt to get rEFInd to
1367 // re-scan disk devices on pressing Esc; but doesn't work (yet), so
1370 // ScanForBootloaders();
1375 switch (ChosenEntry
->Tag
) {
1377 case TAG_REBOOT
: // Reboot
1379 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);
1380 MainLoopRunning
= FALSE
; // just in case we get this far
1383 case TAG_SHUTDOWN
: // Shut Down
1385 refit_call4_wrapper(RT
->ResetSystem
, EfiResetShutdown
, EFI_SUCCESS
, 0, NULL
);
1386 MainLoopRunning
= FALSE
; // just in case we get this far
1389 case TAG_ABOUT
: // About rEFInd
1393 case TAG_LOADER
: // Boot OS via .EFI loader
1394 StartLoader((LOADER_ENTRY
*)ChosenEntry
);
1397 case TAG_LEGACY
: // Boot legacy OS
1398 StartLegacy((LEGACY_ENTRY
*)ChosenEntry
);
1401 case TAG_TOOL
: // Start a EFI tool
1402 StartTool((LOADER_ENTRY
*)ChosenEntry
);
1405 case TAG_EXIT
: // Terminate rEFInd
1406 BeginTextScreen(L
" ");
1413 // If we end up here, things have gone wrong. Try to reboot, and if that
1414 // fails, go into an endless loop.
1415 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);