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 static REFIT_MENU_ENTRY MenuEntryAbout
= { L
"About rEFInd", TAG_ABOUT
, 1, 0, 'A', NULL
, NULL
, NULL
};
60 static REFIT_MENU_ENTRY MenuEntryReset
= { L
"Restart Computer", TAG_RESET
, 1, 0, 'R', NULL
, NULL
, NULL
};
61 static REFIT_MENU_ENTRY MenuEntryShutdown
= { L
"Shut Down Computer", TAG_SHUTDOWN
, 1, 0, 'U', NULL
, NULL
, NULL
};
62 static REFIT_MENU_ENTRY MenuEntryReturn
= { L
"Return to Main Menu", TAG_RETURN
, 0, 0, 0, NULL
, NULL
, NULL
};
64 static REFIT_MENU_SCREEN MainMenu
= { L
"Main Menu", NULL
, 0, NULL
, 0, NULL
, 0, L
"Automatic boot" };
65 static REFIT_MENU_SCREEN AboutMenu
= { L
"About", NULL
, 0, NULL
, 0, NULL
, 0, NULL
};
67 REFIT_CONFIG GlobalConfig
= { FALSE
, 20, 0, 0, FALSE
, NULL
, NULL
, NULL
, NULL
};
73 static VOID
AboutrEFInd(VOID
)
75 if (AboutMenu
.EntryCount
== 0) {
76 AboutMenu
.TitleImage
= BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT
);
77 AddMenuInfoLine(&AboutMenu
, L
"rEFInd Version 0.2.2.2");
78 AddMenuInfoLine(&AboutMenu
, L
"");
79 AddMenuInfoLine(&AboutMenu
, L
"Copyright (c) 2006-2010 Christoph Pfisterer");
80 AddMenuInfoLine(&AboutMenu
, L
"Copyright (c) 2012 Roderick W. Smith");
81 AddMenuInfoLine(&AboutMenu
, L
"Portions Copyright (c) Intel Corporation and others");
82 AddMenuInfoLine(&AboutMenu
, L
"");
83 AddMenuInfoLine(&AboutMenu
, L
"Running on:");
84 AddMenuInfoLine(&AboutMenu
, PoolPrint(L
" EFI Revision %d.%02d",
85 ST
->Hdr
.Revision
>> 16, ST
->Hdr
.Revision
& ((1 << 16) - 1)));
87 AddMenuInfoLine(&AboutMenu
, L
" Platform: x86 (32 bit)");
89 AddMenuInfoLine(&AboutMenu
, L
" Platform: x86_64 (64 bit)");
91 AddMenuInfoLine(&AboutMenu
, L
" Platform: unknown");
93 AddMenuInfoLine(&AboutMenu
, PoolPrint(L
" Firmware: %s %d.%02d",
94 ST
->FirmwareVendor
, ST
->FirmwareRevision
>> 16, ST
->FirmwareRevision
& ((1 << 16) - 1)));
95 AddMenuInfoLine(&AboutMenu
, PoolPrint(L
" Screen Output: %s", egScreenDescription()));
96 AddMenuInfoLine(&AboutMenu
, L
"");
97 AddMenuInfoLine(&AboutMenu
, L
"For more information, see the rEFInd Web site:");
98 AddMenuInfoLine(&AboutMenu
, L
"http://www.rodsbooks.com/refind/");
99 AddMenuEntry(&AboutMenu
, &MenuEntryReturn
);
102 RunMenu(&AboutMenu
, NULL
);
103 } /* VOID AboutrEFInd() */
105 static EFI_STATUS
StartEFIImageList(IN EFI_DEVICE_PATH
**DevicePaths
,
106 IN CHAR16
*LoadOptions
, IN CHAR16
*LoadOptionsPrefix
,
107 IN CHAR16
*ImageTitle
,
108 OUT UINTN
*ErrorInStep
)
110 EFI_STATUS Status
, ReturnStatus
;
111 EFI_HANDLE ChildImageHandle
;
112 EFI_LOADED_IMAGE
*ChildLoadedImage
;
113 UINTN DevicePathIndex
;
114 CHAR16 ErrorInfo
[256];
115 CHAR16
*FullLoadOptions
= NULL
;
117 Print(L
"Starting %s\n", ImageTitle
);
118 if (ErrorInStep
!= NULL
)
121 // load the image into memory
122 ReturnStatus
= Status
= EFI_NOT_FOUND
; // in case the list is empty
123 for (DevicePathIndex
= 0; DevicePaths
[DevicePathIndex
] != NULL
; DevicePathIndex
++) {
124 ReturnStatus
= Status
= refit_call6_wrapper(BS
->LoadImage
, FALSE
, SelfImageHandle
, DevicePaths
[DevicePathIndex
], NULL
, 0, &ChildImageHandle
);
125 if (ReturnStatus
!= EFI_NOT_FOUND
)
128 SPrint(ErrorInfo
, 255, L
"while loading %s", ImageTitle
);
129 if (CheckError(Status
, ErrorInfo
)) {
130 if (ErrorInStep
!= NULL
)
136 if (LoadOptions
!= NULL
) {
137 ReturnStatus
= Status
= refit_call3_wrapper(BS
->HandleProtocol
, ChildImageHandle
, &LoadedImageProtocol
, (VOID
**) &ChildLoadedImage
);
138 if (CheckError(Status
, L
"while getting a LoadedImageProtocol handle")) {
139 if (ErrorInStep
!= NULL
)
144 if (LoadOptionsPrefix
!= NULL
) {
145 FullLoadOptions
= PoolPrint(L
"%s %s ", LoadOptionsPrefix
, LoadOptions
);
146 // NOTE: That last space is also added by the EFI shell and seems to be significant
147 // when passing options to Apple's boot.efi...
148 LoadOptions
= FullLoadOptions
;
150 // NOTE: We also include the terminating null in the length for safety.
151 ChildLoadedImage
->LoadOptions
= (VOID
*)LoadOptions
;
152 ChildLoadedImage
->LoadOptionsSize
= ((UINT32
)StrLen(LoadOptions
) + 1) * sizeof(CHAR16
);
153 Print(L
"Using load options '%s'\n", LoadOptions
);
156 // close open file handles
159 // turn control over to the image
160 // TODO: (optionally) re-enable the EFI watchdog timer!
161 ReturnStatus
= Status
= refit_call3_wrapper(BS
->StartImage
, ChildImageHandle
, NULL
, NULL
);
162 // control returns here when the child image calls Exit()
163 SPrint(ErrorInfo
, 255, L
"returned from %s", ImageTitle
);
164 if (CheckError(Status
, ErrorInfo
)) {
165 if (ErrorInStep
!= NULL
)
169 // re-open file handles
173 // unload the image, we don't care if it works or not...
174 Status
= refit_call1_wrapper(BS
->UnloadImage
, ChildImageHandle
);
176 if (FullLoadOptions
!= NULL
)
177 FreePool(FullLoadOptions
);
179 } /* static EFI_STATUS StartEFIImageList() */
181 static EFI_STATUS
StartEFIImage(IN EFI_DEVICE_PATH
*DevicePath
,
182 IN CHAR16
*LoadOptions
, IN CHAR16
*LoadOptionsPrefix
,
183 IN CHAR16
*ImageTitle
,
184 OUT UINTN
*ErrorInStep
)
186 EFI_DEVICE_PATH
*DevicePaths
[2];
188 DevicePaths
[0] = DevicePath
;
189 DevicePaths
[1] = NULL
;
190 return StartEFIImageList(DevicePaths
, LoadOptions
, LoadOptionsPrefix
, ImageTitle
, ErrorInStep
);
191 } /* static EFI_STATUS StartEFIImage() */
194 // EFI OS loader functions
197 static VOID
StartLoader(IN LOADER_ENTRY
*Entry
)
199 UINTN ErrorInStep
= 0;
201 BeginExternalScreen(Entry
->UseGraphicsMode
, L
"Booting OS");
202 StartEFIImage(Entry
->DevicePath
, Entry
->LoadOptions
,
203 Basename(Entry
->LoaderPath
), Basename(Entry
->LoaderPath
), &ErrorInStep
);
204 FinishExternalScreen();
207 // Locate an initrd or initramfs file that matches the kernel specified by LoaderPath.
208 // The matching file has a name that begins with "init" and includes the same version
209 // number string as is found in LoaderPath -- but not a longer version number string.
210 // For instance, if LoaderPath is \EFI\kernels\bzImage-3.3.0.efi, and if \EFI\kernels
211 // has a file called initramfs-3.3.0.img, this function will return the string
212 // '\EFI\kernels\initramfs-3.3.0.img'. If the directory ALSO contains the file
213 // initramfs-3.3.0-rc7.img or initramfs-13.3.0.img, those files will NOT match;
214 // however, initmine-3.3.0.img might match. (FindInitrd() returns the first match it
215 // finds). Thus, care should be taken to avoid placing duplicate matching files in
216 // the kernel's directory.
217 // If no matching init file can be found, returns NULL.
218 static CHAR16
* FindInitrd(IN CHAR16
*LoaderPath
, IN REFIT_VOLUME
*Volume
) {
219 CHAR16
*InitrdName
= NULL
, *FileName
, *KernelVersion
, *InitrdVersion
, *Path
;
220 REFIT_DIR_ITER DirIter
;
221 EFI_FILE_INFO
*DirEntry
;
223 FileName
= Basename(LoaderPath
);
224 KernelVersion
= FindNumbers(FileName
);
225 Path
= FindPath(LoaderPath
);
227 DirIterOpen(Volume
->RootDir
, Path
, &DirIter
);
228 while ((DirIterNext(&DirIter
, 2, L
"init*", &DirEntry
)) && (InitrdName
== NULL
)) {
229 InitrdVersion
= FindNumbers(DirEntry
->FileName
);
230 if (KernelVersion
!= NULL
) {
231 if (StriCmp(InitrdVersion
, KernelVersion
) == 0)
232 InitrdName
= PoolPrint(L
"%s\\%s", Path
, DirEntry
->FileName
);
234 if (InitrdVersion
== NULL
)
235 InitrdName
= PoolPrint(L
"%s\\%s", Path
, DirEntry
->FileName
);
237 if (InitrdVersion
!= NULL
)
238 FreePool(InitrdVersion
);
240 DirIterClose(&DirIter
);
242 // Note: Don't FreePool(FileName), since Basename returns a pointer WITHIN the string it's passed.
243 FreePool(KernelVersion
);
246 } // static CHAR16 * FindInitrd()
248 LOADER_ENTRY
* AddPreparedLoaderEntry(LOADER_ENTRY
*Entry
) {
249 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
252 } // LOADER_ENTRY * AddPreparedLoaderEntry()
254 // Creates a new LOADER_ENTRY data structure and populates it with
255 // default values from the specified Entry, or NULL values if Entry
256 // is unspecified (NULL).
257 // Returns a pointer to the new data structure, or NULL if it
258 // couldn't be allocated
259 LOADER_ENTRY
*InitializeLoaderEntry(IN LOADER_ENTRY
*Entry
) {
260 LOADER_ENTRY
*NewEntry
= NULL
;
262 NewEntry
= AllocateZeroPool(sizeof(LOADER_ENTRY
));
263 if (NewEntry
!= NULL
) {
264 NewEntry
->me
.Title
= NULL
;
265 NewEntry
->me
.Tag
= TAG_LOADER
;
266 NewEntry
->Enabled
= TRUE
;
267 NewEntry
->UseGraphicsMode
= FALSE
;
268 NewEntry
->OSType
= 0;
270 NewEntry
->LoaderPath
= StrDuplicate(Entry
->LoaderPath
);
271 NewEntry
->VolName
= StrDuplicate(Entry
->VolName
);
272 NewEntry
->DevicePath
= Entry
->DevicePath
;
273 NewEntry
->UseGraphicsMode
= Entry
->UseGraphicsMode
;
274 NewEntry
->LoadOptions
= StrDuplicate(Entry
->LoadOptions
);
275 NewEntry
->InitrdPath
= StrDuplicate(Entry
->InitrdPath
);
279 } // LOADER_ENTRY *InitializeLoaderEntry()
281 // Prepare a REFIT_MENU_SCREEN data structure for a subscreen entry. This sets up
282 // the default entry that launches the boot loader using the same options as the
283 // main Entry does. Subsequent options can be added by the calling function.
284 // If a subscreen already exists in the Entry that's passed to this function,
285 // it's left unchanged and a pointer to it is returned.
286 // Returns a pointer to the new subscreen data structure, or NULL if there
287 // were problems allocating memory.
288 REFIT_MENU_SCREEN
*InitializeSubScreen(IN LOADER_ENTRY
*Entry
) {
289 CHAR16
*FileName
, *Temp
;
290 REFIT_MENU_SCREEN
*SubScreen
= NULL
;
291 LOADER_ENTRY
*SubEntry
;
293 FileName
= Basename(Entry
->LoaderPath
);
294 if (Entry
->me
.SubScreen
== NULL
) { // No subscreen yet; initialize default entry....
295 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
296 if (SubScreen
!= NULL
) {
297 SubScreen
->Title
= PoolPrint(L
"Boot Options for %s on %s", (Entry
->Title
!= NULL
) ? Entry
->Title
: FileName
, Entry
->VolName
);
298 SubScreen
->TitleImage
= Entry
->me
.Image
;
300 SubEntry
= InitializeLoaderEntry(Entry
);
301 if (SubEntry
!= NULL
) {
302 SubEntry
->me
.Title
= L
"Boot using default options";
303 if ((SubEntry
->InitrdPath
!= NULL
) && (StrLen(SubEntry
->InitrdPath
) > 0) && (!StriSubCmp(L
"initrd", SubEntry
->LoadOptions
))) {
304 Temp
= PoolPrint(L
"initrd=%s", SubEntry
->InitrdPath
);
305 MergeStrings(&SubEntry
->LoadOptions
, Temp
, L
' ');
308 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
309 } // if (SubEntry != NULL)
310 } // if (SubScreen != NULL)
311 } else { // existing subscreen; less initialization, and just add new entry later....
312 SubScreen
= Entry
->me
.SubScreen
;
315 } // REFIT_MENU_SCREEN *InitializeSubScreen()
317 VOID
GenerateSubScreen(LOADER_ENTRY
*Entry
, IN REFIT_VOLUME
*Volume
) {
318 REFIT_MENU_SCREEN
*SubScreen
;
319 LOADER_ENTRY
*SubEntry
;
320 CHAR16
*FileName
, *InitrdOption
= NULL
, *Temp
;
321 CHAR16 DiagsFileName
[256];
326 FileName
= Basename(Entry
->LoaderPath
);
327 // create the submenu
328 if (StrLen(Entry
->Title
) == 0) {
329 FreePool(Entry
->Title
);
332 SubScreen
= InitializeSubScreen(Entry
);
334 // loader-specific submenu entries
335 if (Entry
->OSType
== 'M') { // entries for Mac OS X
337 SubEntry
= InitializeLoaderEntry(Entry
);
338 if (SubEntry
!= NULL
) {
339 SubEntry
->me
.Title
= L
"Boot Mac OS X with a 64-bit kernel";
340 SubEntry
->LoadOptions
= L
"arch=x86_64";
341 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
344 SubEntry
= InitializeLoaderEntry(Entry
);
345 if (SubEntry
!= NULL
) {
346 SubEntry
->me
.Title
= L
"Boot Mac OS X with a 32-bit kernel";
347 SubEntry
->LoadOptions
= L
"arch=i386";
348 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
352 if (!(GlobalConfig
.DisableFlags
& DISABLE_FLAG_SINGLEUSER
)) {
353 SubEntry
= InitializeLoaderEntry(Entry
);
354 if (SubEntry
!= NULL
) {
355 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode";
356 SubEntry
->UseGraphicsMode
= FALSE
;
357 SubEntry
->LoadOptions
= L
"-v";
358 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
362 SubEntry
= InitializeLoaderEntry(Entry
);
363 if (SubEntry
!= NULL
) {
364 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode (64-bit)";
365 SubEntry
->UseGraphicsMode
= FALSE
;
366 SubEntry
->LoadOptions
= L
"-v arch=x86_64";
367 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
370 SubEntry
= InitializeLoaderEntry(Entry
);
371 if (SubEntry
!= NULL
) {
372 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode (32-bit)";
373 SubEntry
->UseGraphicsMode
= FALSE
;
374 SubEntry
->LoadOptions
= L
"-v arch=i386";
375 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
379 SubEntry
= InitializeLoaderEntry(Entry
);
380 if (SubEntry
!= NULL
) {
381 SubEntry
->me
.Title
= L
"Boot Mac OS X in single user mode";
382 SubEntry
->UseGraphicsMode
= FALSE
;
383 SubEntry
->LoadOptions
= L
"-v -s";
384 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
388 // check for Apple hardware diagnostics
389 StrCpy(DiagsFileName
, L
"\\System\\Library\\CoreServices\\.diagnostics\\diags.efi");
390 if (FileExists(Volume
->RootDir
, DiagsFileName
) && !(GlobalConfig
.DisableFlags
& DISABLE_FLAG_HWTEST
)) {
391 SubEntry
= InitializeLoaderEntry(Entry
);
392 if (SubEntry
!= NULL
) {
393 SubEntry
->me
.Title
= L
"Run Apple Hardware Test";
394 FreePool(SubEntry
->LoaderPath
);
395 SubEntry
->LoaderPath
= StrDuplicate(DiagsFileName
);
396 SubEntry
->DevicePath
= FileDevicePath(Volume
->DeviceHandle
, SubEntry
->LoaderPath
);
397 SubEntry
->UseGraphicsMode
= TRUE
;
398 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
400 } // if diagnostics entry found
402 } else if (Entry
->OSType
== 'L') { // entries for Linux kernels with EFI stub loaders
403 File
= ReadLinuxOptionsFile(Entry
->LoaderPath
, Volume
);
405 if ((Temp
= FindInitrd(Entry
->LoaderPath
, Volume
)) != NULL
)
406 InitrdOption
= PoolPrint(L
"initrd=%s", Temp
);
407 TokenCount
= ReadTokenLine(File
, &TokenList
); // read and discard first entry, since it's
408 FreeTokenLine(&TokenList
, &TokenCount
); // set up by InitializeSubScreen(), earlier....
409 while ((TokenCount
= ReadTokenLine(File
, &TokenList
)) > 1) {
410 SubEntry
= InitializeLoaderEntry(Entry
);
411 SubEntry
->me
.Title
= StrDuplicate(TokenList
[0]);
412 if (SubEntry
->LoadOptions
!= NULL
)
413 FreePool(SubEntry
->LoadOptions
);
414 SubEntry
->LoadOptions
= StrDuplicate(TokenList
[1]);
415 MergeStrings(&SubEntry
->LoadOptions
, InitrdOption
, L
' ');
416 FreeTokenLine(&TokenList
, &TokenCount
);
417 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
420 FreePool(InitrdOption
);
424 } // if Linux options file exists
426 } else if (Entry
->OSType
== 'E') { // entries for ELILO
427 SubEntry
= InitializeLoaderEntry(Entry
);
428 if (SubEntry
!= NULL
) {
429 SubEntry
->me
.Title
= PoolPrint(L
"Run %s in interactive mode", FileName
);
430 SubEntry
->LoadOptions
= L
"-p";
431 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
434 SubEntry
= InitializeLoaderEntry(Entry
);
435 if (SubEntry
!= NULL
) {
436 SubEntry
->me
.Title
= L
"Boot Linux for a 17\" iMac or a 15\" MacBook Pro (*)";
437 SubEntry
->UseGraphicsMode
= TRUE
;
438 SubEntry
->LoadOptions
= L
"-d 0 i17";
439 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
442 SubEntry
= InitializeLoaderEntry(Entry
);
443 if (SubEntry
!= NULL
) {
444 SubEntry
->me
.Title
= L
"Boot Linux for a 20\" iMac (*)";
445 SubEntry
->UseGraphicsMode
= TRUE
;
446 SubEntry
->LoadOptions
= L
"-d 0 i20";
447 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
450 SubEntry
= InitializeLoaderEntry(Entry
);
451 if (SubEntry
!= NULL
) {
452 SubEntry
->me
.Title
= L
"Boot Linux for a Mac Mini (*)";
453 SubEntry
->UseGraphicsMode
= TRUE
;
454 SubEntry
->LoadOptions
= L
"-d 0 mini";
455 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
458 AddMenuInfoLine(SubScreen
, L
"NOTE: This is an example. Entries");
459 AddMenuInfoLine(SubScreen
, L
"marked with (*) may not work.");
461 } else if (Entry
->OSType
== 'X') { // entries for xom.efi
462 // by default, skip the built-in selection and boot from hard disk only
463 Entry
->LoadOptions
= L
"-s -h";
465 SubEntry
= InitializeLoaderEntry(Entry
);
466 if (SubEntry
!= NULL
) {
467 SubEntry
->me
.Title
= L
"Boot Windows from Hard Disk";
468 SubEntry
->LoadOptions
= L
"-s -h";
469 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
472 SubEntry
= InitializeLoaderEntry(Entry
);
473 if (SubEntry
!= NULL
) {
474 SubEntry
->me
.Title
= L
"Boot Windows from CD-ROM";
475 SubEntry
->LoadOptions
= L
"-s -c";
476 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
479 SubEntry
= InitializeLoaderEntry(Entry
);
480 if (SubEntry
!= NULL
) {
481 SubEntry
->me
.Title
= PoolPrint(L
"Run %s in text mode", FileName
);
482 SubEntry
->UseGraphicsMode
= FALSE
;
483 SubEntry
->LoadOptions
= L
"-v";
484 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
486 } // entries for xom.efi
487 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
488 Entry
->me
.SubScreen
= SubScreen
;
489 } // VOID GenerateSubScreen()
491 // Returns options for a Linux kernel. Reads them from an options file in the
492 // kernel's directory; and if present, adds an initrd= option for an initial
493 // RAM disk file with the same version number as the kernel file.
494 static CHAR16
* GetMainLinuxOptions(IN CHAR16
* LoaderPath
, IN REFIT_VOLUME
*Volume
) {
495 CHAR16
*Options
= NULL
, *InitrdName
, *InitrdOption
= NULL
;
497 Options
= GetFirstOptionsFromFile(LoaderPath
, Volume
);
498 InitrdName
= FindInitrd(LoaderPath
, Volume
);
499 if (InitrdName
!= NULL
)
500 InitrdOption
= PoolPrint(L
"initrd=%s", InitrdName
);
501 MergeStrings(&Options
, InitrdOption
, ' ');
502 if (InitrdOption
!= NULL
)
503 FreePool(InitrdOption
);
504 if (InitrdName
!= NULL
)
505 FreePool(InitrdName
);
507 } // static CHAR16 * GetMainLinuxOptions()
509 // Sets a few defaults for a loader entry -- mainly the icon, but also the OS type
510 // code and shortcut letter. For Linux EFI stub loaders, also sets kernel options
511 // that will (with luck) work fairly automatically.
512 VOID
SetLoaderDefaults(LOADER_ENTRY
*Entry
, CHAR16
*LoaderPath
, IN REFIT_VOLUME
*Volume
) {
513 CHAR16 IconFileName
[256];
514 CHAR16
*FileName
, *OSIconName
= NULL
, *Temp
;
515 CHAR16 ShortcutLetter
= 0;
517 FileName
= Basename(LoaderPath
);
519 // locate a custom icon for the loader
520 StrCpy(IconFileName
, LoaderPath
);
521 ReplaceExtension(IconFileName
, L
".icns");
522 if (FileExists(Volume
->RootDir
, IconFileName
)) {
523 Entry
->me
.Image
= LoadIcns(Volume
->RootDir
, IconFileName
, 128);
526 Temp
= FindLastDirName(LoaderPath
);
527 MergeStrings(&OSIconName
, Temp
, L
',');
529 if (OSIconName
!= NULL
) {
530 ShortcutLetter
= OSIconName
[0];
533 // detect specific loaders
534 if (StriSubCmp(L
"bzImage", LoaderPath
) || StriSubCmp(L
"vmlinuz", LoaderPath
)) {
535 MergeStrings(&OSIconName
, L
"linux", L
',');
537 if (ShortcutLetter
== 0)
538 ShortcutLetter
= 'L';
539 Entry
->LoadOptions
= GetMainLinuxOptions(LoaderPath
, Volume
);
540 } else if (StriSubCmp(L
"refit", LoaderPath
)) {
541 MergeStrings(&OSIconName
, L
"refit", L
',');
543 ShortcutLetter
= 'R';
544 } else if (StriCmp(LoaderPath
, MACOSX_LOADER_PATH
) == 0) {
545 MergeStrings(&OSIconName
, L
"mac", L
',');
546 Entry
->UseGraphicsMode
= TRUE
;
548 ShortcutLetter
= 'M';
549 } else if (StriCmp(FileName
, L
"diags.efi") == 0) {
550 MergeStrings(&OSIconName
, L
"hwtest", L
',');
551 } else if (StriCmp(FileName
, L
"e.efi") == 0 || StriCmp(FileName
, L
"elilo.efi") == 0) {
552 MergeStrings(&OSIconName
, L
"elilo,linux", L
',');
554 if (ShortcutLetter
== 0)
555 ShortcutLetter
= 'L';
556 } else if (StriCmp(FileName
, L
"cdboot.efi") == 0 ||
557 StriCmp(FileName
, L
"bootmgr.efi") == 0 ||
558 StriCmp(FileName
, L
"Bootmgfw.efi") == 0) {
559 MergeStrings(&OSIconName
, L
"win", L
',');
561 ShortcutLetter
= 'W';
562 } else if (StriCmp(FileName
, L
"xom.efi") == 0) {
563 MergeStrings(&OSIconName
, L
"xom,win", L
',');
564 Entry
->UseGraphicsMode
= TRUE
;
566 ShortcutLetter
= 'W';
569 if ((ShortcutLetter
>= 'a') && (ShortcutLetter
<= 'z'))
570 ShortcutLetter
= ShortcutLetter
- 'a' + 'A'; // convert lowercase to uppercase
571 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
572 if (Entry
->me
.Image
== NULL
)
573 Entry
->me
.Image
= LoadOSIcon(OSIconName
, L
"unknown", FALSE
);
574 } // VOID SetLoaderDefaults()
576 // Add a specified EFI boot loader to the list, using automatic settings
577 // for icons, options, etc.
578 LOADER_ENTRY
* AddLoaderEntry(IN CHAR16
*LoaderPath
, IN CHAR16
*LoaderTitle
, IN REFIT_VOLUME
*Volume
) {
581 Entry
= InitializeLoaderEntry(NULL
);
583 Entry
->Title
= StrDuplicate(LoaderTitle
);
584 Entry
->me
.Title
= PoolPrint(L
"Boot %s from %s", (LoaderTitle
!= NULL
) ? LoaderTitle
: LoaderPath
+ 1, Volume
->VolName
);
586 Entry
->me
.BadgeImage
= Volume
->VolBadgeImage
;
587 Entry
->LoaderPath
= StrDuplicate(LoaderPath
);
588 Entry
->VolName
= Volume
->VolName
;
589 Entry
->DevicePath
= FileDevicePath(Volume
->DeviceHandle
, Entry
->LoaderPath
);
590 SetLoaderDefaults(Entry
, LoaderPath
, Volume
);
591 GenerateSubScreen(Entry
, Volume
);
592 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
596 } // LOADER_ENTRY * AddLoaderEntry()
598 // Scan an individual directory for EFI boot loader files and, if found,
599 // add them to the list.
600 static VOID
ScanLoaderDir(IN REFIT_VOLUME
*Volume
, IN CHAR16
*Path
)
603 REFIT_DIR_ITER DirIter
;
604 EFI_FILE_INFO
*DirEntry
;
605 CHAR16 FileName
[256];
607 // Note: SelfDirPath includes a leading backslash ('\'), but Path
608 // doesn't, so we rejigger the string to compensate....
609 if (!SelfDirPath
|| !Path
|| ((StriCmp(Path
, &SelfDirPath
[1]) == 0) && Volume
!= SelfVolume
) ||
610 (StriCmp(Path
, &SelfDirPath
[1]) != 0)) {
611 // look through contents of the directory
612 DirIterOpen(Volume
->RootDir
, Path
, &DirIter
);
613 while (DirIterNext(&DirIter
, 2, L
"*.efi", &DirEntry
)) {
614 if (DirEntry
->FileName
[0] == '.' ||
615 StriCmp(DirEntry
->FileName
, L
"TextMode.efi") == 0 ||
616 StriCmp(DirEntry
->FileName
, L
"ebounce.efi") == 0 ||
617 StriCmp(DirEntry
->FileName
, L
"GraphicsConsole.efi") == 0)
618 continue; // skip this
621 SPrint(FileName
, 255, L
"\\%s\\%s", Path
, DirEntry
->FileName
);
623 SPrint(FileName
, 255, L
"\\%s", DirEntry
->FileName
);
624 AddLoaderEntry(FileName
, NULL
, Volume
);
626 Status
= DirIterClose(&DirIter
);
627 if (Status
!= EFI_NOT_FOUND
) {
629 SPrint(FileName
, 255, L
"while scanning the %s directory", Path
);
631 StrCpy(FileName
, L
"while scanning the root directory");
632 CheckError(Status
, FileName
);
633 } // if (Status != EFI_NOT_FOUND)
634 } // if not scanning our own directory
635 } /* static VOID ScanLoaderDir() */
637 static VOID
ScanEfiFiles(REFIT_VOLUME
*Volume
) {
639 REFIT_DIR_ITER EfiDirIter
;
640 EFI_FILE_INFO
*EfiDirEntry
;
641 CHAR16 FileName
[256];
642 // LOADER_ENTRY *Entry;
644 if ((Volume
->RootDir
!= NULL
) && (Volume
->VolName
!= NULL
)) {
645 // check for Mac OS X boot loader
646 StrCpy(FileName
, MACOSX_LOADER_PATH
);
647 if (FileExists(Volume
->RootDir
, FileName
)) {
648 AddLoaderEntry(FileName
, L
"Mac OS X", Volume
);
652 StrCpy(FileName
, L
"\\System\\Library\\CoreServices\\xom.efi");
653 if (FileExists(Volume
->RootDir
, FileName
)) {
654 AddLoaderEntry(FileName
, L
"Windows XP (XoM)", Volume
);
657 // check for Microsoft boot loader/menu
658 StrCpy(FileName
, L
"\\EFI\\Microsoft\\Boot\\Bootmgfw.efi");
659 if (FileExists(Volume
->RootDir
, FileName
)) {
660 AddLoaderEntry(FileName
, L
"Microsoft EFI boot", Volume
);
663 // scan the root directory for EFI executables
664 ScanLoaderDir(Volume
, NULL
);
665 // scan the elilo directory (as used on gimli's first Live CD)
666 ScanLoaderDir(Volume
, L
"elilo");
667 // scan the boot directory
668 ScanLoaderDir(Volume
, L
"boot");
670 // scan subdirectories of the EFI directory (as per the standard)
671 DirIterOpen(Volume
->RootDir
, L
"EFI", &EfiDirIter
);
672 while (DirIterNext(&EfiDirIter
, 1, NULL
, &EfiDirEntry
)) {
673 if (StriCmp(EfiDirEntry
->FileName
, L
"TOOLS") == 0 || EfiDirEntry
->FileName
[0] == '.')
674 continue; // skip this, doesn't contain boot loaders
675 SPrint(FileName
, 255, L
"EFI\\%s", EfiDirEntry
->FileName
);
676 ScanLoaderDir(Volume
, FileName
);
678 Status
= DirIterClose(&EfiDirIter
);
679 if (Status
!= EFI_NOT_FOUND
)
680 CheckError(Status
, L
"while scanning the EFI directory");
682 } // static VOID ScanEfiFiles()
684 // Scan internal disks for valid EFI boot loaders....
685 static VOID
ScanInternal(VOID
) {
688 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
689 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_INTERNAL
) {
690 ScanEfiFiles(Volumes
[VolumeIndex
]);
693 } // static VOID ScanInternal()
695 // Scan external disks for valid EFI boot loaders....
696 static VOID
ScanExternal(VOID
) {
699 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
700 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_EXTERNAL
) {
701 ScanEfiFiles(Volumes
[VolumeIndex
]);
704 } // static VOID ScanExternal()
706 // Scan internal disks for valid EFI boot loaders....
707 static VOID
ScanOptical(VOID
) {
710 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
711 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_OPTICAL
) {
712 ScanEfiFiles(Volumes
[VolumeIndex
]);
715 } // static VOID ScanOptical()
718 // legacy boot functions
721 static EFI_STATUS
ActivateMbrPartition(IN EFI_BLOCK_IO
*BlockIO
, IN UINTN PartitionIndex
)
724 UINT8 SectorBuffer
[512];
725 MBR_PARTITION_INFO
*MbrTable
, *EMbrTable
;
726 UINT32 ExtBase
, ExtCurrent
, NextExtCurrent
;
727 UINTN LogicalPartitionIndex
= 4;
729 BOOLEAN HaveBootCode
;
732 Status
= refit_call5_wrapper(BlockIO
->ReadBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, 0, 512, SectorBuffer
);
733 if (EFI_ERROR(Status
))
735 if (*((UINT16
*)(SectorBuffer
+ 510)) != 0xaa55)
736 return EFI_NOT_FOUND
; // safety measure #1
738 // add boot code if necessary
739 HaveBootCode
= FALSE
;
740 for (i
= 0; i
< MBR_BOOTCODE_SIZE
; i
++) {
741 if (SectorBuffer
[i
] != 0) {
747 // no boot code found in the MBR, add the syslinux MBR code
748 SetMem(SectorBuffer
, MBR_BOOTCODE_SIZE
, 0);
749 CopyMem(SectorBuffer
, syslinux_mbr
, SYSLINUX_MBR_SIZE
);
752 // set the partition active
753 MbrTable
= (MBR_PARTITION_INFO
*)(SectorBuffer
+ 446);
755 for (i
= 0; i
< 4; i
++) {
756 if (MbrTable
[i
].Flags
!= 0x00 && MbrTable
[i
].Flags
!= 0x80)
757 return EFI_NOT_FOUND
; // safety measure #2
758 if (i
== PartitionIndex
)
759 MbrTable
[i
].Flags
= 0x80;
760 else if (PartitionIndex
>= 4 && IS_EXTENDED_PART_TYPE(MbrTable
[i
].Type
)) {
761 MbrTable
[i
].Flags
= 0x80;
762 ExtBase
= MbrTable
[i
].StartLBA
;
764 MbrTable
[i
].Flags
= 0x00;
768 Status
= refit_call5_wrapper(BlockIO
->WriteBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, 0, 512, SectorBuffer
);
769 if (EFI_ERROR(Status
))
772 if (PartitionIndex
>= 4) {
773 // we have to activate a logical partition, so walk the EMBR chain
775 // NOTE: ExtBase was set above while looking at the MBR table
776 for (ExtCurrent
= ExtBase
; ExtCurrent
; ExtCurrent
= NextExtCurrent
) {
778 Status
= refit_call5_wrapper(BlockIO
->ReadBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, ExtCurrent
, 512, SectorBuffer
);
779 if (EFI_ERROR(Status
))
781 if (*((UINT16
*)(SectorBuffer
+ 510)) != 0xaa55)
782 return EFI_NOT_FOUND
; // safety measure #3
784 // scan EMBR, set appropriate partition active
785 EMbrTable
= (MBR_PARTITION_INFO
*)(SectorBuffer
+ 446);
787 for (i
= 0; i
< 4; i
++) {
788 if (EMbrTable
[i
].Flags
!= 0x00 && EMbrTable
[i
].Flags
!= 0x80)
789 return EFI_NOT_FOUND
; // safety measure #4
790 if (EMbrTable
[i
].StartLBA
== 0 || EMbrTable
[i
].Size
== 0)
792 if (IS_EXTENDED_PART_TYPE(EMbrTable
[i
].Type
)) {
794 NextExtCurrent
= ExtBase
+ EMbrTable
[i
].StartLBA
;
795 EMbrTable
[i
].Flags
= (PartitionIndex
>= LogicalPartitionIndex
) ? 0x80 : 0x00;
799 EMbrTable
[i
].Flags
= (PartitionIndex
== LogicalPartitionIndex
) ? 0x80 : 0x00;
800 LogicalPartitionIndex
++;
804 // write current EMBR
805 Status
= refit_call5_wrapper(BlockIO
->WriteBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, ExtCurrent
, 512, SectorBuffer
);
806 if (EFI_ERROR(Status
))
809 if (PartitionIndex
< LogicalPartitionIndex
)
810 break; // stop the loop, no need to touch further EMBRs
816 } /* static EFI_STATUS ActivateMbrPartition() */
818 // early 2006 Core Duo / Core Solo models
819 static UINT8 LegacyLoaderDevicePath1Data
[] = {
820 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
821 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
822 0xFF, 0xFF, 0xF9, 0xFF, 0x00, 0x00, 0x00, 0x00,
823 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
824 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
825 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
827 // mid-2006 Mac Pro (and probably other Core 2 models)
828 static UINT8 LegacyLoaderDevicePath2Data
[] = {
829 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
830 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
831 0xFF, 0xFF, 0xF7, 0xFF, 0x00, 0x00, 0x00, 0x00,
832 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
833 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
834 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
836 // mid-2007 MBP ("Santa Rosa" based models)
837 static UINT8 LegacyLoaderDevicePath3Data
[] = {
838 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
839 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
840 0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
841 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
842 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
843 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
846 static UINT8 LegacyLoaderDevicePath4Data
[] = {
847 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
848 0x00, 0x00, 0xC0, 0xFF, 0x00, 0x00, 0x00, 0x00,
849 0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
850 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
851 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
852 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
854 // late-2008 MB/MBP (NVidia chipset)
855 static UINT8 LegacyLoaderDevicePath5Data
[] = {
856 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
857 0x00, 0x40, 0xCB, 0xFF, 0x00, 0x00, 0x00, 0x00,
858 0xFF, 0xBF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
859 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
860 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
861 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
864 static EFI_DEVICE_PATH
*LegacyLoaderList
[] = {
865 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath1Data
,
866 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath2Data
,
867 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath3Data
,
868 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath4Data
,
869 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath5Data
,
873 #define MAX_DISCOVERED_PATHS (16)
875 static VOID
StartLegacy(IN LEGACY_ENTRY
*Entry
)
878 EG_IMAGE
*BootLogoImage
;
879 UINTN ErrorInStep
= 0;
880 EFI_DEVICE_PATH
*DiscoveredPathList
[MAX_DISCOVERED_PATHS
];
882 BeginExternalScreen(TRUE
, L
"Booting Legacy OS");
884 BootLogoImage
= LoadOSIcon(Entry
->Volume
->OSIconName
, L
"legacy", TRUE
);
885 if (BootLogoImage
!= NULL
)
886 BltImageAlpha(BootLogoImage
,
887 (UGAWidth
- BootLogoImage
->Width
) >> 1,
888 (UGAHeight
- BootLogoImage
->Height
) >> 1,
889 &StdBackgroundPixel
);
891 if (Entry
->Volume
->IsMbrPartition
)
892 ActivateMbrPartition(Entry
->Volume
->WholeDiskBlockIO
, Entry
->Volume
->MbrPartitionIndex
);
894 ExtractLegacyLoaderPaths(DiscoveredPathList
, MAX_DISCOVERED_PATHS
, LegacyLoaderList
);
896 Status
= StartEFIImageList(DiscoveredPathList
, Entry
->LoadOptions
, NULL
, L
"legacy loader", &ErrorInStep
);
897 if (Status
== EFI_NOT_FOUND
) {
898 if (ErrorInStep
== 1) {
899 Print(L
"\nPlease make sure that you have the latest firmware update installed.\n");
900 } else if (ErrorInStep
== 3) {
901 Print(L
"\nThe firmware refused to boot from the selected volume. Note that external\n"
902 L
"hard drives are not well-supported by Apple's firmware for legacy OS booting.\n");
905 FinishExternalScreen();
906 } /* static VOID StartLegacy() */
908 static LEGACY_ENTRY
* AddLegacyEntry(IN CHAR16
*LoaderTitle
, IN REFIT_VOLUME
*Volume
)
910 LEGACY_ENTRY
*Entry
, *SubEntry
;
911 REFIT_MENU_SCREEN
*SubScreen
;
913 CHAR16 ShortcutLetter
= 0;
915 if (LoaderTitle
== NULL
) {
916 if (Volume
->OSName
!= NULL
) {
917 LoaderTitle
= Volume
->OSName
;
918 if (LoaderTitle
[0] == 'W' || LoaderTitle
[0] == 'L')
919 ShortcutLetter
= LoaderTitle
[0];
921 LoaderTitle
= L
"Legacy OS";
923 if (Volume
->VolName
!= NULL
)
924 VolDesc
= Volume
->VolName
;
926 VolDesc
= (Volume
->DiskKind
== DISK_KIND_OPTICAL
) ? L
"CD" : L
"HD";
928 // prepare the menu entry
929 Entry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
930 Entry
->me
.Title
= PoolPrint(L
"Boot %s from %s", LoaderTitle
, VolDesc
);
931 Entry
->me
.Tag
= TAG_LEGACY
;
933 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
934 Entry
->me
.Image
= LoadOSIcon(Volume
->OSIconName
, L
"legacy", FALSE
);
935 Entry
->me
.BadgeImage
= Volume
->VolBadgeImage
;
936 Entry
->Volume
= Volume
;
937 Entry
->LoadOptions
= (Volume
->DiskKind
== DISK_KIND_OPTICAL
) ? L
"CD" :
938 ((Volume
->DiskKind
== DISK_KIND_EXTERNAL
) ? L
"USB" : L
"HD");
939 Entry
->Enabled
= TRUE
;
941 // create the submenu
942 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
943 SubScreen
->Title
= PoolPrint(L
"Boot Options for %s on %s", LoaderTitle
, VolDesc
);
944 SubScreen
->TitleImage
= Entry
->me
.Image
;
947 SubEntry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
948 SubEntry
->me
.Title
= PoolPrint(L
"Boot %s", LoaderTitle
);
949 SubEntry
->me
.Tag
= TAG_LEGACY
;
950 SubEntry
->Volume
= Entry
->Volume
;
951 SubEntry
->LoadOptions
= Entry
->LoadOptions
;
952 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
954 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
955 Entry
->me
.SubScreen
= SubScreen
;
956 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
958 } /* static LEGACY_ENTRY * AddLegacyEntry() */
960 static VOID
ScanLegacyVolume(REFIT_VOLUME
*Volume
, UINTN VolumeIndex
) {
962 BOOLEAN ShowVolume
, HideIfOthersFound
;
965 HideIfOthersFound
= FALSE
;
966 if (Volume
->IsAppleLegacy
) {
968 HideIfOthersFound
= TRUE
;
969 } else if (Volume
->HasBootCode
) {
971 if (Volume
->BlockIO
== Volume
->WholeDiskBlockIO
&&
972 Volume
->BlockIOOffset
== 0 &&
973 Volume
->OSName
== NULL
)
974 // this is a whole disk (MBR) entry; hide if we have entries for partitions
975 HideIfOthersFound
= TRUE
;
977 if (HideIfOthersFound
) {
978 // check for other bootable entries on the same disk
979 for (VolumeIndex2
= 0; VolumeIndex2
< VolumesCount
; VolumeIndex2
++) {
980 if (VolumeIndex2
!= VolumeIndex
&& Volumes
[VolumeIndex2
]->HasBootCode
&&
981 Volumes
[VolumeIndex2
]->WholeDiskBlockIO
== Volume
->WholeDiskBlockIO
)
987 AddLegacyEntry(NULL
, Volume
);
988 } // static VOID ScanLegacyVolume()
990 // Scan attached optical discs for legacy (BIOS) boot code
991 // and add anything found to the list....
992 static VOID
ScanLegacyDisc(VOID
)
995 REFIT_VOLUME
*Volume
;
997 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
998 Volume
= Volumes
[VolumeIndex
];
999 if (Volume
->DiskKind
== DISK_KIND_OPTICAL
)
1000 ScanLegacyVolume(Volume
, VolumeIndex
);
1002 } /* static VOID ScanLegacyDisc() */
1004 // Scan internal hard disks for legacy (BIOS) boot code
1005 // and add anything found to the list....
1006 static VOID
ScanLegacyInternal(VOID
)
1009 REFIT_VOLUME
*Volume
;
1011 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1012 Volume
= Volumes
[VolumeIndex
];
1013 if (Volume
->DiskKind
== DISK_KIND_INTERNAL
)
1014 ScanLegacyVolume(Volume
, VolumeIndex
);
1016 } /* static VOID ScanLegacyInternal() */
1018 // Scan external disks for legacy (BIOS) boot code
1019 // and add anything found to the list....
1020 static VOID
ScanLegacyExternal(VOID
)
1023 REFIT_VOLUME
*Volume
;
1025 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1026 Volume
= Volumes
[VolumeIndex
];
1027 if (Volume
->DiskKind
== DISK_KIND_EXTERNAL
)
1028 ScanLegacyVolume(Volume
, VolumeIndex
);
1030 } /* static VOID ScanLegacyExternal() */
1033 // pre-boot tool functions
1036 static VOID
StartTool(IN LOADER_ENTRY
*Entry
)
1038 BeginExternalScreen(Entry
->UseGraphicsMode
, Entry
->me
.Title
+ 6); // assumes "Start <title>" as assigned below
1039 StartEFIImage(Entry
->DevicePath
, Entry
->LoadOptions
, Basename(Entry
->LoaderPath
),
1040 Basename(Entry
->LoaderPath
), NULL
);
1041 FinishExternalScreen();
1042 } /* static VOID StartTool() */
1044 static LOADER_ENTRY
* AddToolEntry(IN CHAR16
*LoaderPath
, IN CHAR16
*LoaderTitle
, IN EG_IMAGE
*Image
,
1045 IN CHAR16 ShortcutLetter
, IN BOOLEAN UseGraphicsMode
)
1047 LOADER_ENTRY
*Entry
;
1049 Entry
= AllocateZeroPool(sizeof(LOADER_ENTRY
));
1051 Entry
->me
.Title
= PoolPrint(L
"Start %s", LoaderTitle
);
1052 Entry
->me
.Tag
= TAG_TOOL
;
1054 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1055 Entry
->me
.Image
= Image
;
1056 Entry
->LoaderPath
= StrDuplicate(LoaderPath
);
1057 Entry
->DevicePath
= FileDevicePath(SelfLoadedImage
->DeviceHandle
, Entry
->LoaderPath
);
1058 Entry
->UseGraphicsMode
= UseGraphicsMode
;
1060 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1062 } /* static LOADER_ENTRY * AddToolEntry() */
1064 static VOID
ScanTool(VOID
)
1066 CHAR16 FileName
[256];
1067 LOADER_ENTRY
*Entry
;
1069 if (GlobalConfig
.DisableFlags
& DISABLE_FLAG_TOOLS
)
1072 // look for the EFI shell
1073 if (!(GlobalConfig
.DisableFlags
& DISABLE_FLAG_SHELL
)) {
1074 SPrint(FileName
, 255, L
"%s\\apps\\shell.efi", SelfDirPath
);
1075 if (FileExists(SelfRootDir
, FileName
)) {
1076 AddToolEntry(FileName
, L
"EFI Shell", BuiltinIcon(BUILTIN_ICON_TOOL_SHELL
), 'E', FALSE
);
1078 StrCpy(FileName
, L
"\\efi\\tools\\shell.efi");
1079 if (FileExists(SelfRootDir
, FileName
)) {
1080 AddToolEntry(FileName
, L
"EFI Shell", BuiltinIcon(BUILTIN_ICON_TOOL_SHELL
), 'E', FALSE
);
1085 // look for the GPT/MBR sync tool
1086 StrCpy(FileName
, L
"\\efi\\tools\\gptsync.efi");
1087 if (FileExists(SelfRootDir
, FileName
)) {
1088 AddToolEntry(FileName
, L
"Make Hybrid MBR", BuiltinIcon(BUILTIN_ICON_TOOL_PART
), 'P', FALSE
);
1091 // look for rescue Linux
1092 StrCpy(FileName
, L
"\\efi\\rescue\\elilo.efi");
1093 if (SelfVolume
!= NULL
&& FileExists(SelfRootDir
, FileName
)) {
1094 Entry
= AddToolEntry(FileName
, L
"Rescue Linux", BuiltinIcon(BUILTIN_ICON_TOOL_RESCUE
), '0', FALSE
);
1096 if (UGAWidth
== 1440 && UGAHeight
== 900)
1097 Entry
->LoadOptions
= L
"-d 0 i17";
1098 else if (UGAWidth
== 1680 && UGAHeight
== 1050)
1099 Entry
->LoadOptions
= L
"-d 0 i20";
1101 Entry
->LoadOptions
= L
"-d 0 mini";
1106 #ifdef DEBIAN_ENABLE_EFI110
1108 // pre-boot driver functions
1111 static VOID
ScanDriverDir(IN CHAR16
*Path
)
1114 REFIT_DIR_ITER DirIter
;
1115 EFI_FILE_INFO
*DirEntry
;
1116 CHAR16 FileName
[256];
1118 // look through contents of the directory
1119 DirIterOpen(SelfRootDir
, Path
, &DirIter
);
1120 while (DirIterNext(&DirIter
, 2, L
"*.EFI", &DirEntry
)) {
1121 if (DirEntry
->FileName
[0] == '.')
1122 continue; // skip this
1124 SPrint(FileName
, 255, L
"%s\\%s", Path
, DirEntry
->FileName
);
1125 Status
= StartEFIImage(FileDevicePath(SelfLoadedImage
->DeviceHandle
, FileName
),
1126 L
"", DirEntry
->FileName
, DirEntry
->FileName
, NULL
);
1128 Status
= DirIterClose(&DirIter
);
1129 if (Status
!= EFI_NOT_FOUND
) {
1130 SPrint(FileName
, 255, L
"while scanning the %s directory", Path
);
1131 CheckError(Status
, FileName
);
1135 static EFI_STATUS
ConnectAllDriversToAllControllers(VOID
)
1138 UINTN AllHandleCount
;
1139 EFI_HANDLE
*AllHandleBuffer
;
1142 EFI_HANDLE
*HandleBuffer
;
1148 Status
= LibLocateHandle(AllHandles
,
1153 if (EFI_ERROR(Status
))
1156 for (Index
= 0; Index
< AllHandleCount
; Index
++) {
1158 // Scan the handle database
1160 Status
= LibScanHandleDatabase(NULL
,
1162 AllHandleBuffer
[Index
],
1167 if (EFI_ERROR (Status
))
1171 if (HandleType
[Index
] & EFI_HANDLE_TYPE_DRIVER_BINDING_HANDLE
)
1173 if (HandleType
[Index
] & EFI_HANDLE_TYPE_IMAGE_HANDLE
)
1178 for (HandleIndex
= 0; HandleIndex
< HandleCount
; HandleIndex
++) {
1179 if (HandleType
[HandleIndex
] & EFI_HANDLE_TYPE_PARENT_HANDLE
)
1184 if (HandleType
[Index
] & EFI_HANDLE_TYPE_DEVICE_HANDLE
) {
1185 Status
= refit_call4_wrapper(BS
->ConnectController
,
1186 AllHandleBuffer
[Index
],
1194 FreePool (HandleBuffer
);
1195 FreePool (HandleType
);
1199 FreePool (AllHandleBuffer
);
1203 static VOID
LoadDrivers(VOID
)
1205 CHAR16 DirName
[256];
1207 // load drivers from /efi/refind/drivers
1208 SPrint(DirName
, 255, L
"%s\\drivers", SelfDirPath
);
1209 ScanDriverDir(DirName
);
1211 // load drivers from /efi/tools/drivers
1212 ScanDriverDir(L
"\\efi\\tools\\drivers");
1214 // connect all devices
1215 ConnectAllDriversToAllControllers();
1217 #endif /* DEBIAN_ENABLE_EFI110 */
1219 static VOID
ScanForBootloaders(VOID
) {
1223 // Commented-out below: Was part of an attempt to get rEFInd to
1224 // re-scan disk devices on pressing Esc; but doesn't work (yet), so
1226 // MainMenu.Title = StrDuplicate(L"Main Menu 2");
1227 // MainMenu.TitleImage = NULL;
1228 // MainMenu.InfoLineCount = 0;
1229 // MainMenu.InfoLines = NULL;
1230 // MainMenu.EntryCount = 0;
1231 // MainMenu.Entries = NULL;
1232 // MainMenu.TimeoutSeconds = 20;
1233 // MainMenu.TimeoutText = StrDuplicate(L"Automatic boot");
1236 // scan for loaders and tools, add them to the menu
1237 for (i
= 0; i
< NUM_SCAN_OPTIONS
; i
++) {
1238 switch(GlobalConfig
.ScanFor
[i
]) {
1243 ScanLegacyInternal();
1246 ScanLegacyExternal();
1249 ScanUserConfigured();
1264 // fixed other menu entries
1265 if (!(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_FUNCS
)) {
1266 MenuEntryAbout
.Image
= BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT
);
1267 AddMenuEntry(&MainMenu
, &MenuEntryAbout
);
1269 if (!(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_FUNCS
) || MainMenu
.EntryCount
== 0) {
1270 MenuEntryShutdown
.Image
= BuiltinIcon(BUILTIN_ICON_FUNC_SHUTDOWN
);
1271 AddMenuEntry(&MainMenu
, &MenuEntryShutdown
);
1272 MenuEntryReset
.Image
= BuiltinIcon(BUILTIN_ICON_FUNC_RESET
);
1273 AddMenuEntry(&MainMenu
, &MenuEntryReset
);
1276 // assign shortcut keys
1277 for (i
= 0; i
< MainMenu
.EntryCount
&& MainMenu
.Entries
[i
]->Row
== 0 && i
< 9; i
++)
1278 MainMenu
.Entries
[i
]->ShortcutDigit
= (CHAR16
)('1' + i
);
1280 // wait for user ACK when there were errors
1281 FinishTextScreen(FALSE
);
1282 } // static VOID ScanForBootloaders()
1290 efi_main (IN EFI_HANDLE ImageHandle
, IN EFI_SYSTEM_TABLE
*SystemTable
)
1293 BOOLEAN MainLoopRunning
= TRUE
;
1294 REFIT_MENU_ENTRY
*ChosenEntry
;
1298 InitializeLib(ImageHandle
, SystemTable
);
1300 Status
= InitRefitLib(ImageHandle
);
1301 if (EFI_ERROR(Status
))
1304 // read configuration
1305 CopyMem(GlobalConfig
.ScanFor
, "ieo ", NUM_SCAN_OPTIONS
);
1307 MainMenu
.TimeoutSeconds
= GlobalConfig
.Timeout
;
1309 // disable EFI watchdog timer
1310 refit_call4_wrapper(BS
->SetWatchdogTimer
, 0x0000, 0x0000, 0x0000, NULL
);
1312 // further bootstrap (now with config available)
1314 #ifdef DEBIAN_ENABLE_EFI110
1316 #endif /* DEBIAN_ENABLE_EFI110 */
1317 ScanForBootloaders();
1319 while (MainLoopRunning
) {
1320 MenuExit
= RunMainMenu(&MainMenu
, GlobalConfig
.DefaultSelection
, &ChosenEntry
);
1322 // We don't allow exiting the main menu with the Escape key.
1323 if (MenuExit
== MENU_EXIT_ESCAPE
) {
1324 // Commented-out below: Was part of an attempt to get rEFInd to
1325 // re-scan disk devices on pressing Esc; but doesn't work (yet), so
1328 // ScanForBootloaders();
1333 switch (ChosenEntry
->Tag
) {
1335 case TAG_RESET
: // Restart
1337 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);
1338 MainLoopRunning
= FALSE
; // just in case we get this far
1341 case TAG_SHUTDOWN
: // Shut Down
1343 refit_call4_wrapper(RT
->ResetSystem
, EfiResetShutdown
, EFI_SUCCESS
, 0, NULL
);
1344 MainLoopRunning
= FALSE
; // just in case we get this far
1347 case TAG_ABOUT
: // About rEFInd
1351 case TAG_LOADER
: // Boot OS via .EFI loader
1352 StartLoader((LOADER_ENTRY
*)ChosenEntry
);
1355 case TAG_LEGACY
: // Boot legacy OS
1356 StartLegacy((LEGACY_ENTRY
*)ChosenEntry
);
1359 case TAG_TOOL
: // Start a EFI tool
1360 StartTool((LOADER_ENTRY
*)ChosenEntry
);
1366 // If we end up here, things have gone wrong. Try to reboot, and if that
1367 // fails, go into an endless loop.
1368 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);