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");
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 BeginExternalScreen(Entry
->UseGraphicsMode
, L
"Booting OS");
200 StartEFIImage(Entry
->DevicePath
, Entry
->LoadOptions
,
201 Basename(Entry
->LoaderPath
), Basename(Entry
->LoaderPath
), NULL
);
202 FinishExternalScreen();
205 // Locate an initrd or initramfs file that matches the kernel specified by LoaderPath.
206 // The matching file has a name that begins with "init" and includes the same version
207 // number string as is found in LoaderPath -- but not a longer version number string.
208 // For instance, if LoaderPath is \EFI\kernels\bzImage-3.3.0.efi, and if \EFI\kernels
209 // has a file called initramfs-3.3.0.img, this function will return the string
210 // '\EFI\kernels\initramfs-3.3.0.img'. If the directory ALSO contains the file
211 // initramfs-3.3.0-rc7.img or initramfs-13.3.0.img, those files will NOT match;
212 // however, initmine-3.3.0.img might match. (FindInitrd() returns the first match it
213 // finds). Thus, care should be taken to avoid placing duplicate matching files in
214 // the kernel's directory.
215 // If no matching init file can be found, returns NULL.
216 static CHAR16
* FindInitrd(IN CHAR16
*LoaderPath
, IN REFIT_VOLUME
*Volume
) {
217 CHAR16
*InitrdName
= NULL
, *FileName
, *KernelVersion
, *InitrdVersion
, *Path
;
218 REFIT_DIR_ITER DirIter
;
219 EFI_FILE_INFO
*DirEntry
;
221 FileName
= Basename(LoaderPath
);
222 KernelVersion
= FindNumbers(FileName
);
223 Path
= FindPath(LoaderPath
);
225 DirIterOpen(Volume
->RootDir
, Path
, &DirIter
);
226 while ((DirIterNext(&DirIter
, 2, L
"init*", &DirEntry
)) && (InitrdName
== NULL
)) {
227 InitrdVersion
= FindNumbers(DirEntry
->FileName
);
228 if (KernelVersion
!= NULL
) {
229 // if (StriSubCmp(KernelVersion, DirEntry->FileName)) {
230 if (StriCmp(InitrdVersion
, KernelVersion
) == 0)
231 InitrdName
= PoolPrint(L
"%s\\%s", Path
, DirEntry
->FileName
);
232 // } // if match found
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 // SubEntry->me.Title = (Entry->OSType == 'M') ? L"Boot Mac OS X" : PoolPrint(L"Run %s", FileName);
304 if ((SubEntry
->InitrdPath
!= NULL
) && (StrLen(SubEntry
->InitrdPath
) > 0) && (!StriSubCmp(L
"initrd", SubEntry
->LoadOptions
))) {
305 Temp
= PoolPrint(L
"initrd=%s", SubEntry
->InitrdPath
);
306 MergeStrings(&SubEntry
->LoadOptions
, Temp
, L
' ');
309 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
310 } // if (SubEntry != NULL)
311 } // if (SubScreen != NULL)
312 } else { // existing subscreen; less initialization, and just add new entry later....
313 SubScreen
= Entry
->me
.SubScreen
;
316 } // REFIT_MENU_SCREEN *InitializeSubScreen()
318 VOID
GenerateSubScreen(LOADER_ENTRY
*Entry
, IN REFIT_VOLUME
*Volume
) {
319 REFIT_MENU_SCREEN
*SubScreen
;
320 LOADER_ENTRY
*SubEntry
;
321 CHAR16
*FileName
, *InitrdOption
= NULL
, *Temp
;
322 CHAR16 DiagsFileName
[256];
327 FileName
= Basename(Entry
->LoaderPath
);
328 // create the submenu
329 if (StrLen(Entry
->Title
) == 0) {
330 FreePool(Entry
->Title
);
333 SubScreen
= InitializeSubScreen(Entry
);
335 // loader-specific submenu entries
336 if (Entry
->OSType
== 'M') { // entries for Mac OS X
338 SubEntry
= InitializeLoaderEntry(Entry
);
339 if (SubEntry
!= NULL
) {
340 SubEntry
->me
.Title
= L
"Boot Mac OS X with a 64-bit kernel";
341 SubEntry
->LoadOptions
= L
"arch=x86_64";
342 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
345 SubEntry
= InitializeLoaderEntry(Entry
);
346 if (SubEntry
!= NULL
) {
347 SubEntry
->me
.Title
= L
"Boot Mac OS X with a 32-bit kernel";
348 SubEntry
->LoadOptions
= L
"arch=i386";
349 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
353 if (!(GlobalConfig
.DisableFlags
& DISABLE_FLAG_SINGLEUSER
)) {
354 SubEntry
= InitializeLoaderEntry(Entry
);
355 if (SubEntry
!= NULL
) {
356 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode";
357 SubEntry
->UseGraphicsMode
= FALSE
;
358 SubEntry
->LoadOptions
= L
"-v";
359 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
363 SubEntry
= InitializeLoaderEntry(Entry
);
364 if (SubEntry
!= NULL
) {
365 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode (64-bit)";
366 SubEntry
->UseGraphicsMode
= FALSE
;
367 SubEntry
->LoadOptions
= L
"-v arch=x86_64";
368 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
371 SubEntry
= InitializeLoaderEntry(Entry
);
372 if (SubEntry
!= NULL
) {
373 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode (32-bit)";
374 SubEntry
->UseGraphicsMode
= FALSE
;
375 SubEntry
->LoadOptions
= L
"-v arch=i386";
376 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
380 SubEntry
= InitializeLoaderEntry(Entry
);
381 if (SubEntry
!= NULL
) {
382 SubEntry
->me
.Title
= L
"Boot Mac OS X in single user mode";
383 SubEntry
->UseGraphicsMode
= FALSE
;
384 SubEntry
->LoadOptions
= L
"-v -s";
385 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
389 // check for Apple hardware diagnostics
390 StrCpy(DiagsFileName
, L
"\\System\\Library\\CoreServices\\.diagnostics\\diags.efi");
391 if (FileExists(Volume
->RootDir
, DiagsFileName
) && !(GlobalConfig
.DisableFlags
& DISABLE_FLAG_HWTEST
)) {
392 SubEntry
= InitializeLoaderEntry(Entry
);
393 if (SubEntry
!= NULL
) {
394 SubEntry
->me
.Title
= L
"Run Apple Hardware Test";
395 FreePool(SubEntry
->LoaderPath
);
396 SubEntry
->LoaderPath
= StrDuplicate(DiagsFileName
);
397 SubEntry
->DevicePath
= FileDevicePath(Volume
->DeviceHandle
, SubEntry
->LoaderPath
);
398 SubEntry
->UseGraphicsMode
= TRUE
;
399 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
401 } // if diagnostics entry found
403 } else if (Entry
->OSType
== 'L') { // entries for Linux kernels with EFI stub loaders
404 File
= ReadLinuxOptionsFile(Entry
->LoaderPath
, Volume
);
406 if ((Temp
= FindInitrd(Entry
->LoaderPath
, Volume
)) != NULL
)
407 InitrdOption
= PoolPrint(L
"initrd=%s", Temp
);
408 TokenCount
= ReadTokenLine(File
, &TokenList
); // read and discard first entry, since it's
409 FreeTokenLine(&TokenList
, &TokenCount
); // set up by InitializeSubScreen(), earlier....
410 while ((TokenCount
= ReadTokenLine(File
, &TokenList
)) > 1) {
411 SubEntry
= InitializeLoaderEntry(Entry
);
412 SubEntry
->me
.Title
= StrDuplicate(TokenList
[0]);
413 if (SubEntry
->LoadOptions
!= NULL
)
414 FreePool(SubEntry
->LoadOptions
);
415 SubEntry
->LoadOptions
= StrDuplicate(TokenList
[1]);
416 MergeStrings(&SubEntry
->LoadOptions
, InitrdOption
, L
' ');
417 FreeTokenLine(&TokenList
, &TokenCount
);
418 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
421 FreePool(InitrdOption
);
425 } // if Linux options file exists
427 } else if (Entry
->OSType
== 'E') { // entries for ELILO
428 SubEntry
= InitializeLoaderEntry(Entry
);
429 if (SubEntry
!= NULL
) {
430 SubEntry
->me
.Title
= PoolPrint(L
"Run %s in interactive mode", FileName
);
431 SubEntry
->LoadOptions
= L
"-p";
432 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
435 SubEntry
= InitializeLoaderEntry(Entry
);
436 if (SubEntry
!= NULL
) {
437 SubEntry
->me
.Title
= L
"Boot Linux for a 17\" iMac or a 15\" MacBook Pro (*)";
438 SubEntry
->UseGraphicsMode
= TRUE
;
439 SubEntry
->LoadOptions
= L
"-d 0 i17";
440 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
443 SubEntry
= InitializeLoaderEntry(Entry
);
444 if (SubEntry
!= NULL
) {
445 SubEntry
->me
.Title
= L
"Boot Linux for a 20\" iMac (*)";
446 SubEntry
->UseGraphicsMode
= TRUE
;
447 SubEntry
->LoadOptions
= L
"-d 0 i20";
448 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
451 SubEntry
= InitializeLoaderEntry(Entry
);
452 if (SubEntry
!= NULL
) {
453 SubEntry
->me
.Title
= L
"Boot Linux for a Mac Mini (*)";
454 SubEntry
->UseGraphicsMode
= TRUE
;
455 SubEntry
->LoadOptions
= L
"-d 0 mini";
456 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
459 AddMenuInfoLine(SubScreen
, L
"NOTE: This is an example. Entries");
460 AddMenuInfoLine(SubScreen
, L
"marked with (*) may not work.");
462 } else if (Entry
->OSType
== 'X') { // entries for xom.efi
463 // by default, skip the built-in selection and boot from hard disk only
464 Entry
->LoadOptions
= L
"-s -h";
466 SubEntry
= InitializeLoaderEntry(Entry
);
467 if (SubEntry
!= NULL
) {
468 SubEntry
->me
.Title
= L
"Boot Windows from Hard Disk";
469 SubEntry
->LoadOptions
= L
"-s -h";
470 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
473 SubEntry
= InitializeLoaderEntry(Entry
);
474 if (SubEntry
!= NULL
) {
475 SubEntry
->me
.Title
= L
"Boot Windows from CD-ROM";
476 SubEntry
->LoadOptions
= L
"-s -c";
477 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
480 SubEntry
= InitializeLoaderEntry(Entry
);
481 if (SubEntry
!= NULL
) {
482 SubEntry
->me
.Title
= PoolPrint(L
"Run %s in text mode", FileName
);
483 SubEntry
->UseGraphicsMode
= FALSE
;
484 SubEntry
->LoadOptions
= L
"-v";
485 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
487 } // entries for xom.efi
488 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
489 Entry
->me
.SubScreen
= SubScreen
;
490 } // VOID GenerateSubScreen()
492 // Returns options for a Linux kernel. Reads them from an options file in the
493 // kernel's directory; and if present, adds an initrd= option for an initial
494 // RAM disk file with the same version number as the kernel file.
495 static CHAR16
* GetMainLinuxOptions(IN CHAR16
* LoaderPath
, IN REFIT_VOLUME
*Volume
) {
496 CHAR16
*Options
= NULL
, *InitrdName
, *InitrdOption
= NULL
;
498 Options
= GetFirstOptionsFromFile(LoaderPath
, Volume
);
499 InitrdName
= FindInitrd(LoaderPath
, Volume
);
500 if (InitrdName
!= NULL
)
501 InitrdOption
= PoolPrint(L
"initrd=%s", InitrdName
);
502 MergeStrings(&Options
, InitrdOption
, ' ');
503 if (InitrdOption
!= NULL
)
504 FreePool(InitrdOption
);
505 if (InitrdName
!= NULL
)
506 FreePool(InitrdName
);
508 } // static CHAR16 * GetMainLinuxOptions()
510 // Sets a few defaults for a loader entry -- mainly the icon, but also the OS type
511 // code and shortcut letter. For Linux EFI stub loaders, also sets kernel options
512 // that will (with luck) work fairly automatically.
513 VOID
SetLoaderDefaults(LOADER_ENTRY
*Entry
, CHAR16
*LoaderPath
, IN REFIT_VOLUME
*Volume
) {
514 CHAR16 IconFileName
[256];
515 CHAR16
*FileName
, *OSIconName
= NULL
, *Temp
;
516 CHAR16 ShortcutLetter
= 0;
518 FileName
= Basename(LoaderPath
);
520 // locate a custom icon for the loader
521 StrCpy(IconFileName
, LoaderPath
);
522 ReplaceExtension(IconFileName
, L
".icns");
523 if (FileExists(Volume
->RootDir
, IconFileName
)) {
524 Entry
->me
.Image
= LoadIcns(Volume
->RootDir
, IconFileName
, 128);
527 Temp
= FindLastDirName(LoaderPath
);
528 MergeStrings(&OSIconName
, Temp
, L
',');
530 if (OSIconName
!= NULL
) {
531 ShortcutLetter
= OSIconName
[0];
534 // detect specific loaders
535 if (StriSubCmp(L
"bzImage", LoaderPath
) || StriSubCmp(L
"vmlinuz", LoaderPath
)) {
536 MergeStrings(&OSIconName
, L
"linux", L
',');
538 if (ShortcutLetter
== 0)
539 ShortcutLetter
= 'L';
540 Entry
->LoadOptions
= GetMainLinuxOptions(LoaderPath
, Volume
);
541 } else if (StriSubCmp(L
"refit", LoaderPath
)) {
542 MergeStrings(&OSIconName
, L
"refit", L
',');
544 ShortcutLetter
= 'R';
545 } else if (StriCmp(LoaderPath
, MACOSX_LOADER_PATH
) == 0) {
546 MergeStrings(&OSIconName
, L
"mac", L
',');
547 Entry
->UseGraphicsMode
= TRUE
;
549 ShortcutLetter
= 'M';
550 } else if (StriCmp(FileName
, L
"diags.efi") == 0) {
551 MergeStrings(&OSIconName
, L
"hwtest", L
',');
552 } else if (StriCmp(FileName
, L
"e.efi") == 0 || StriCmp(FileName
, L
"elilo.efi") == 0) {
553 MergeStrings(&OSIconName
, L
"elilo,linux", L
',');
555 if (ShortcutLetter
== 0)
556 ShortcutLetter
= 'L';
557 } else if (StriCmp(FileName
, L
"cdboot.efi") == 0 ||
558 StriCmp(FileName
, L
"bootmgr.efi") == 0 ||
559 StriCmp(FileName
, L
"Bootmgfw.efi") == 0) {
560 MergeStrings(&OSIconName
, L
"win", L
',');
562 ShortcutLetter
= 'W';
563 } else if (StriCmp(FileName
, L
"xom.efi") == 0) {
564 MergeStrings(&OSIconName
, L
"xom,win", L
',');
565 Entry
->UseGraphicsMode
= TRUE
;
567 ShortcutLetter
= 'W';
570 if ((ShortcutLetter
>= 'a') && (ShortcutLetter
<= 'z'))
571 ShortcutLetter
= ShortcutLetter
- 'a' + 'A'; // convert lowercase to uppercase
572 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
573 if (Entry
->me
.Image
== NULL
)
574 Entry
->me
.Image
= LoadOSIcon(OSIconName
, L
"unknown", FALSE
);
575 } // VOID SetLoaderDefaults()
577 // Add a specified EFI boot loader to the list, using automatic settings
578 // for icons, options, etc.
579 LOADER_ENTRY
* AddLoaderEntry(IN CHAR16
*LoaderPath
, IN CHAR16
*LoaderTitle
, IN REFIT_VOLUME
*Volume
) {
582 Entry
= InitializeLoaderEntry(NULL
);
584 Entry
->Title
= StrDuplicate(LoaderTitle
);
585 Entry
->me
.Title
= PoolPrint(L
"Boot %s from %s", (LoaderTitle
!= NULL
) ? LoaderTitle
: LoaderPath
+ 1, Volume
->VolName
);
587 Entry
->me
.BadgeImage
= Volume
->VolBadgeImage
;
588 Entry
->LoaderPath
= StrDuplicate(LoaderPath
);
589 Entry
->VolName
= Volume
->VolName
;
590 Entry
->DevicePath
= FileDevicePath(Volume
->DeviceHandle
, Entry
->LoaderPath
);
591 SetLoaderDefaults(Entry
, LoaderPath
, Volume
);
592 GenerateSubScreen(Entry
, Volume
);
593 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
597 } // LOADER_ENTRY * AddLoaderEntry()
599 // Scan an individual directory for EFI boot loader files and, if found,
600 // add them to the list.
601 static VOID
ScanLoaderDir(IN REFIT_VOLUME
*Volume
, IN CHAR16
*Path
)
604 REFIT_DIR_ITER DirIter
;
605 EFI_FILE_INFO
*DirEntry
;
606 CHAR16 FileName
[256];
608 // Note: SelfDirPath includes a leading backslash ('\'), but Path
609 // doesn't, so we rejigger the string to compensate....
610 if (!SelfDirPath
|| !Path
|| ((StriCmp(Path
, &SelfDirPath
[1]) == 0) && Volume
!= SelfVolume
) ||
611 (StriCmp(Path
, &SelfDirPath
[1]) != 0)) {
612 // look through contents of the directory
613 DirIterOpen(Volume
->RootDir
, Path
, &DirIter
);
614 while (DirIterNext(&DirIter
, 2, L
"*.efi", &DirEntry
)) {
615 if (DirEntry
->FileName
[0] == '.' ||
616 StriCmp(DirEntry
->FileName
, L
"TextMode.efi") == 0 ||
617 StriCmp(DirEntry
->FileName
, L
"ebounce.efi") == 0 ||
618 StriCmp(DirEntry
->FileName
, L
"GraphicsConsole.efi") == 0)
619 continue; // skip this
622 SPrint(FileName
, 255, L
"\\%s\\%s", Path
, DirEntry
->FileName
);
624 SPrint(FileName
, 255, L
"\\%s", DirEntry
->FileName
);
625 AddLoaderEntry(FileName
, NULL
, Volume
);
627 Status
= DirIterClose(&DirIter
);
628 if (Status
!= EFI_NOT_FOUND
) {
630 SPrint(FileName
, 255, L
"while scanning the %s directory", Path
);
632 StrCpy(FileName
, L
"while scanning the root directory");
633 CheckError(Status
, FileName
);
634 } // if (Status != EFI_NOT_FOUND)
635 } // if not scanning our own directory
636 } /* static VOID ScanLoaderDir() */
638 static VOID
ScanEfiFiles(REFIT_VOLUME
*Volume
) {
640 REFIT_DIR_ITER EfiDirIter
;
641 EFI_FILE_INFO
*EfiDirEntry
;
642 CHAR16 FileName
[256];
643 // LOADER_ENTRY *Entry;
645 if ((Volume
->RootDir
!= NULL
) && (Volume
->VolName
!= NULL
)) {
646 // check for Mac OS X boot loader
647 StrCpy(FileName
, MACOSX_LOADER_PATH
);
648 if (FileExists(Volume
->RootDir
, FileName
)) {
649 AddLoaderEntry(FileName
, L
"Mac OS X", Volume
);
653 StrCpy(FileName
, L
"\\System\\Library\\CoreServices\\xom.efi");
654 if (FileExists(Volume
->RootDir
, FileName
)) {
655 AddLoaderEntry(FileName
, L
"Windows XP (XoM)", Volume
);
658 // check for Microsoft boot loader/menu
659 StrCpy(FileName
, L
"\\EFI\\Microsoft\\Boot\\Bootmgfw.efi");
660 if (FileExists(Volume
->RootDir
, FileName
)) {
661 AddLoaderEntry(FileName
, L
"Microsoft EFI boot", Volume
);
664 // scan the root directory for EFI executables
665 ScanLoaderDir(Volume
, NULL
);
666 // scan the elilo directory (as used on gimli's first Live CD)
667 ScanLoaderDir(Volume
, L
"elilo");
668 // scan the boot directory
669 ScanLoaderDir(Volume
, L
"boot");
671 // scan subdirectories of the EFI directory (as per the standard)
672 DirIterOpen(Volume
->RootDir
, L
"EFI", &EfiDirIter
);
673 while (DirIterNext(&EfiDirIter
, 1, NULL
, &EfiDirEntry
)) {
674 if (StriCmp(EfiDirEntry
->FileName
, L
"TOOLS") == 0 || EfiDirEntry
->FileName
[0] == '.')
675 continue; // skip this, doesn't contain boot loaders
676 SPrint(FileName
, 255, L
"EFI\\%s", EfiDirEntry
->FileName
);
677 ScanLoaderDir(Volume
, FileName
);
679 Status
= DirIterClose(&EfiDirIter
);
680 if (Status
!= EFI_NOT_FOUND
)
681 CheckError(Status
, L
"while scanning the EFI directory");
683 } // static VOID ScanEfiFiles()
685 // Scan internal disks for valid EFI boot loaders....
686 static VOID
ScanInternal(VOID
) {
689 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
690 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_INTERNAL
) {
691 ScanEfiFiles(Volumes
[VolumeIndex
]);
694 } // static VOID ScanInternal()
696 // Scan external disks for valid EFI boot loaders....
697 static VOID
ScanExternal(VOID
) {
700 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
701 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_EXTERNAL
) {
702 ScanEfiFiles(Volumes
[VolumeIndex
]);
705 } // static VOID ScanExternal()
707 // Scan internal disks for valid EFI boot loaders....
708 static VOID
ScanOptical(VOID
) {
711 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
712 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_OPTICAL
) {
713 ScanEfiFiles(Volumes
[VolumeIndex
]);
716 } // static VOID ScanOptical()
719 // legacy boot functions
722 static EFI_STATUS
ActivateMbrPartition(IN EFI_BLOCK_IO
*BlockIO
, IN UINTN PartitionIndex
)
725 UINT8 SectorBuffer
[512];
726 MBR_PARTITION_INFO
*MbrTable
, *EMbrTable
;
727 UINT32 ExtBase
, ExtCurrent
, NextExtCurrent
;
728 UINTN LogicalPartitionIndex
= 4;
730 BOOLEAN HaveBootCode
;
733 Status
= refit_call5_wrapper(BlockIO
->ReadBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, 0, 512, SectorBuffer
);
734 if (EFI_ERROR(Status
))
736 if (*((UINT16
*)(SectorBuffer
+ 510)) != 0xaa55)
737 return EFI_NOT_FOUND
; // safety measure #1
739 // add boot code if necessary
740 HaveBootCode
= FALSE
;
741 for (i
= 0; i
< MBR_BOOTCODE_SIZE
; i
++) {
742 if (SectorBuffer
[i
] != 0) {
748 // no boot code found in the MBR, add the syslinux MBR code
749 SetMem(SectorBuffer
, MBR_BOOTCODE_SIZE
, 0);
750 CopyMem(SectorBuffer
, syslinux_mbr
, SYSLINUX_MBR_SIZE
);
753 // set the partition active
754 MbrTable
= (MBR_PARTITION_INFO
*)(SectorBuffer
+ 446);
756 for (i
= 0; i
< 4; i
++) {
757 if (MbrTable
[i
].Flags
!= 0x00 && MbrTable
[i
].Flags
!= 0x80)
758 return EFI_NOT_FOUND
; // safety measure #2
759 if (i
== PartitionIndex
)
760 MbrTable
[i
].Flags
= 0x80;
761 else if (PartitionIndex
>= 4 && IS_EXTENDED_PART_TYPE(MbrTable
[i
].Type
)) {
762 MbrTable
[i
].Flags
= 0x80;
763 ExtBase
= MbrTable
[i
].StartLBA
;
765 MbrTable
[i
].Flags
= 0x00;
769 Status
= refit_call5_wrapper(BlockIO
->WriteBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, 0, 512, SectorBuffer
);
770 if (EFI_ERROR(Status
))
773 if (PartitionIndex
>= 4) {
774 // we have to activate a logical partition, so walk the EMBR chain
776 // NOTE: ExtBase was set above while looking at the MBR table
777 for (ExtCurrent
= ExtBase
; ExtCurrent
; ExtCurrent
= NextExtCurrent
) {
779 Status
= refit_call5_wrapper(BlockIO
->ReadBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, ExtCurrent
, 512, SectorBuffer
);
780 if (EFI_ERROR(Status
))
782 if (*((UINT16
*)(SectorBuffer
+ 510)) != 0xaa55)
783 return EFI_NOT_FOUND
; // safety measure #3
785 // scan EMBR, set appropriate partition active
786 EMbrTable
= (MBR_PARTITION_INFO
*)(SectorBuffer
+ 446);
788 for (i
= 0; i
< 4; i
++) {
789 if (EMbrTable
[i
].Flags
!= 0x00 && EMbrTable
[i
].Flags
!= 0x80)
790 return EFI_NOT_FOUND
; // safety measure #4
791 if (EMbrTable
[i
].StartLBA
== 0 || EMbrTable
[i
].Size
== 0)
793 if (IS_EXTENDED_PART_TYPE(EMbrTable
[i
].Type
)) {
795 NextExtCurrent
= ExtBase
+ EMbrTable
[i
].StartLBA
;
796 EMbrTable
[i
].Flags
= (PartitionIndex
>= LogicalPartitionIndex
) ? 0x80 : 0x00;
800 EMbrTable
[i
].Flags
= (PartitionIndex
== LogicalPartitionIndex
) ? 0x80 : 0x00;
801 LogicalPartitionIndex
++;
805 // write current EMBR
806 Status
= refit_call5_wrapper(BlockIO
->WriteBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, ExtCurrent
, 512, SectorBuffer
);
807 if (EFI_ERROR(Status
))
810 if (PartitionIndex
< LogicalPartitionIndex
)
811 break; // stop the loop, no need to touch further EMBRs
817 } /* static EFI_STATUS ActivateMbrPartition() */
819 // early 2006 Core Duo / Core Solo models
820 static UINT8 LegacyLoaderDevicePath1Data
[] = {
821 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
822 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
823 0xFF, 0xFF, 0xF9, 0xFF, 0x00, 0x00, 0x00, 0x00,
824 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
825 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
826 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
828 // mid-2006 Mac Pro (and probably other Core 2 models)
829 static UINT8 LegacyLoaderDevicePath2Data
[] = {
830 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
831 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
832 0xFF, 0xFF, 0xF7, 0xFF, 0x00, 0x00, 0x00, 0x00,
833 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
834 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
835 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
837 // mid-2007 MBP ("Santa Rosa" based models)
838 static UINT8 LegacyLoaderDevicePath3Data
[] = {
839 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
840 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
841 0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
842 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
843 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
844 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
847 static UINT8 LegacyLoaderDevicePath4Data
[] = {
848 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
849 0x00, 0x00, 0xC0, 0xFF, 0x00, 0x00, 0x00, 0x00,
850 0xFF, 0xFF, 0xF8, 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 // late-2008 MB/MBP (NVidia chipset)
856 static UINT8 LegacyLoaderDevicePath5Data
[] = {
857 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
858 0x00, 0x40, 0xCB, 0xFF, 0x00, 0x00, 0x00, 0x00,
859 0xFF, 0xBF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
860 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
861 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
862 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
865 static EFI_DEVICE_PATH
*LegacyLoaderList
[] = {
866 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath1Data
,
867 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath2Data
,
868 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath3Data
,
869 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath4Data
,
870 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath5Data
,
874 #define MAX_DISCOVERED_PATHS (16)
876 static VOID
StartLegacy(IN LEGACY_ENTRY
*Entry
)
879 EG_IMAGE
*BootLogoImage
;
880 UINTN ErrorInStep
= 0;
881 EFI_DEVICE_PATH
*DiscoveredPathList
[MAX_DISCOVERED_PATHS
];
883 BeginExternalScreen(TRUE
, L
"Booting Legacy OS");
885 BootLogoImage
= LoadOSIcon(Entry
->Volume
->OSIconName
, L
"legacy", TRUE
);
886 if (BootLogoImage
!= NULL
)
887 BltImageAlpha(BootLogoImage
,
888 (UGAWidth
- BootLogoImage
->Width
) >> 1,
889 (UGAHeight
- BootLogoImage
->Height
) >> 1,
890 &StdBackgroundPixel
);
892 if (Entry
->Volume
->IsMbrPartition
)
893 ActivateMbrPartition(Entry
->Volume
->WholeDiskBlockIO
, Entry
->Volume
->MbrPartitionIndex
);
895 ExtractLegacyLoaderPaths(DiscoveredPathList
, MAX_DISCOVERED_PATHS
, LegacyLoaderList
);
897 Status
= StartEFIImageList(DiscoveredPathList
, Entry
->LoadOptions
, NULL
, L
"legacy loader", &ErrorInStep
);
898 if (Status
== EFI_NOT_FOUND
) {
899 if (ErrorInStep
== 1) {
900 Print(L
"\nPlease make sure that you have the latest firmware update installed.\n");
901 } else if (ErrorInStep
== 3) {
902 Print(L
"\nThe firmware refused to boot from the selected volume. Note that external\n"
903 L
"hard drives are not well-supported by Apple's firmware for legacy OS booting.\n");
906 FinishExternalScreen();
907 } /* static VOID StartLegacy() */
909 static LEGACY_ENTRY
* AddLegacyEntry(IN CHAR16
*LoaderTitle
, IN REFIT_VOLUME
*Volume
)
911 LEGACY_ENTRY
*Entry
, *SubEntry
;
912 REFIT_MENU_SCREEN
*SubScreen
;
914 CHAR16 ShortcutLetter
= 0;
916 if (LoaderTitle
== NULL
) {
917 if (Volume
->OSName
!= NULL
) {
918 LoaderTitle
= Volume
->OSName
;
919 if (LoaderTitle
[0] == 'W' || LoaderTitle
[0] == 'L')
920 ShortcutLetter
= LoaderTitle
[0];
922 LoaderTitle
= L
"Legacy OS";
924 if (Volume
->VolName
!= NULL
)
925 VolDesc
= Volume
->VolName
;
927 VolDesc
= (Volume
->DiskKind
== DISK_KIND_OPTICAL
) ? L
"CD" : L
"HD";
929 // prepare the menu entry
930 Entry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
931 Entry
->me
.Title
= PoolPrint(L
"Boot %s from %s", LoaderTitle
, VolDesc
);
932 Entry
->me
.Tag
= TAG_LEGACY
;
934 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
935 Entry
->me
.Image
= LoadOSIcon(Volume
->OSIconName
, L
"legacy", FALSE
);
936 Entry
->me
.BadgeImage
= Volume
->VolBadgeImage
;
937 Entry
->Volume
= Volume
;
938 Entry
->LoadOptions
= (Volume
->DiskKind
== DISK_KIND_OPTICAL
) ? L
"CD" :
939 ((Volume
->DiskKind
== DISK_KIND_EXTERNAL
) ? L
"USB" : L
"HD");
940 Entry
->Enabled
= TRUE
;
942 // create the submenu
943 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
944 SubScreen
->Title
= PoolPrint(L
"Boot Options for %s on %s", LoaderTitle
, VolDesc
);
945 SubScreen
->TitleImage
= Entry
->me
.Image
;
948 SubEntry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
949 SubEntry
->me
.Title
= PoolPrint(L
"Boot %s", LoaderTitle
);
950 SubEntry
->me
.Tag
= TAG_LEGACY
;
951 SubEntry
->Volume
= Entry
->Volume
;
952 SubEntry
->LoadOptions
= Entry
->LoadOptions
;
953 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
955 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
956 Entry
->me
.SubScreen
= SubScreen
;
957 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
959 } /* static LEGACY_ENTRY * AddLegacyEntry() */
961 static VOID
ScanLegacyVolume(REFIT_VOLUME
*Volume
, UINTN VolumeIndex
) {
963 BOOLEAN ShowVolume
, HideIfOthersFound
;
966 HideIfOthersFound
= FALSE
;
967 if (Volume
->IsAppleLegacy
) {
969 HideIfOthersFound
= TRUE
;
970 } else if (Volume
->HasBootCode
) {
972 if (Volume
->BlockIO
== Volume
->WholeDiskBlockIO
&&
973 Volume
->BlockIOOffset
== 0 &&
974 Volume
->OSName
== NULL
)
975 // this is a whole disk (MBR) entry; hide if we have entries for partitions
976 HideIfOthersFound
= TRUE
;
978 if (HideIfOthersFound
) {
979 // check for other bootable entries on the same disk
980 for (VolumeIndex2
= 0; VolumeIndex2
< VolumesCount
; VolumeIndex2
++) {
981 if (VolumeIndex2
!= VolumeIndex
&& Volumes
[VolumeIndex2
]->HasBootCode
&&
982 Volumes
[VolumeIndex2
]->WholeDiskBlockIO
== Volume
->WholeDiskBlockIO
)
988 AddLegacyEntry(NULL
, Volume
);
989 } // static VOID ScanLegacyVolume()
991 // Scan attached optical discs for legacy (BIOS) boot code
992 // and add anything found to the list....
993 static VOID
ScanLegacyDisc(VOID
)
996 REFIT_VOLUME
*Volume
;
998 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
999 Volume
= Volumes
[VolumeIndex
];
1000 if (Volume
->DiskKind
== DISK_KIND_OPTICAL
)
1001 ScanLegacyVolume(Volume
, VolumeIndex
);
1003 } /* static VOID ScanLegacyDisc() */
1005 // Scan internal hard disks for legacy (BIOS) boot code
1006 // and add anything found to the list....
1007 static VOID
ScanLegacyInternal(VOID
)
1010 REFIT_VOLUME
*Volume
;
1012 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1013 Volume
= Volumes
[VolumeIndex
];
1014 if (Volume
->DiskKind
== DISK_KIND_INTERNAL
)
1015 ScanLegacyVolume(Volume
, VolumeIndex
);
1017 } /* static VOID ScanLegacyInternal() */
1019 // Scan external disks for legacy (BIOS) boot code
1020 // and add anything found to the list....
1021 static VOID
ScanLegacyExternal(VOID
)
1024 REFIT_VOLUME
*Volume
;
1026 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1027 Volume
= Volumes
[VolumeIndex
];
1028 if (Volume
->DiskKind
== DISK_KIND_EXTERNAL
)
1029 ScanLegacyVolume(Volume
, VolumeIndex
);
1031 } /* static VOID ScanLegacyExternal() */
1034 // pre-boot tool functions
1037 static VOID
StartTool(IN LOADER_ENTRY
*Entry
)
1039 BeginExternalScreen(Entry
->UseGraphicsMode
, Entry
->me
.Title
+ 6); // assumes "Start <title>" as assigned below
1040 StartEFIImage(Entry
->DevicePath
, Entry
->LoadOptions
, Basename(Entry
->LoaderPath
),
1041 Basename(Entry
->LoaderPath
), NULL
);
1042 FinishExternalScreen();
1043 } /* static VOID StartTool() */
1045 static LOADER_ENTRY
* AddToolEntry(IN CHAR16
*LoaderPath
, IN CHAR16
*LoaderTitle
, IN EG_IMAGE
*Image
,
1046 IN CHAR16 ShortcutLetter
, IN BOOLEAN UseGraphicsMode
)
1048 LOADER_ENTRY
*Entry
;
1050 Entry
= AllocateZeroPool(sizeof(LOADER_ENTRY
));
1052 Entry
->me
.Title
= PoolPrint(L
"Start %s", LoaderTitle
);
1053 Entry
->me
.Tag
= TAG_TOOL
;
1055 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1056 Entry
->me
.Image
= Image
;
1057 Entry
->LoaderPath
= StrDuplicate(LoaderPath
);
1058 Entry
->DevicePath
= FileDevicePath(SelfLoadedImage
->DeviceHandle
, Entry
->LoaderPath
);
1059 Entry
->UseGraphicsMode
= UseGraphicsMode
;
1061 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1063 } /* static LOADER_ENTRY * AddToolEntry() */
1065 static VOID
ScanTool(VOID
)
1067 CHAR16 FileName
[256];
1068 LOADER_ENTRY
*Entry
;
1070 if (GlobalConfig
.DisableFlags
& DISABLE_FLAG_TOOLS
)
1073 // look for the EFI shell
1074 if (!(GlobalConfig
.DisableFlags
& DISABLE_FLAG_SHELL
)) {
1075 SPrint(FileName
, 255, L
"%s\\apps\\shell.efi", SelfDirPath
);
1076 if (FileExists(SelfRootDir
, FileName
)) {
1077 AddToolEntry(FileName
, L
"EFI Shell", BuiltinIcon(BUILTIN_ICON_TOOL_SHELL
), 'E', FALSE
);
1079 StrCpy(FileName
, L
"\\efi\\tools\\shell.efi");
1080 if (FileExists(SelfRootDir
, FileName
)) {
1081 AddToolEntry(FileName
, L
"EFI Shell", BuiltinIcon(BUILTIN_ICON_TOOL_SHELL
), 'E', FALSE
);
1086 // look for the GPT/MBR sync tool
1087 StrCpy(FileName
, L
"\\efi\\tools\\gptsync.efi");
1088 if (FileExists(SelfRootDir
, FileName
)) {
1089 AddToolEntry(FileName
, L
"Make Hybrid MBR", BuiltinIcon(BUILTIN_ICON_TOOL_PART
), 'P', FALSE
);
1092 // look for rescue Linux
1093 StrCpy(FileName
, L
"\\efi\\rescue\\elilo.efi");
1094 if (SelfVolume
!= NULL
&& FileExists(SelfRootDir
, FileName
)) {
1095 Entry
= AddToolEntry(FileName
, L
"Rescue Linux", BuiltinIcon(BUILTIN_ICON_TOOL_RESCUE
), '0', FALSE
);
1097 if (UGAWidth
== 1440 && UGAHeight
== 900)
1098 Entry
->LoadOptions
= L
"-d 0 i17";
1099 else if (UGAWidth
== 1680 && UGAHeight
== 1050)
1100 Entry
->LoadOptions
= L
"-d 0 i20";
1102 Entry
->LoadOptions
= L
"-d 0 mini";
1107 #ifdef DEBIAN_ENABLE_EFI110
1109 // pre-boot driver functions
1112 static VOID
ScanDriverDir(IN CHAR16
*Path
)
1115 REFIT_DIR_ITER DirIter
;
1116 EFI_FILE_INFO
*DirEntry
;
1117 CHAR16 FileName
[256];
1119 // look through contents of the directory
1120 DirIterOpen(SelfRootDir
, Path
, &DirIter
);
1121 while (DirIterNext(&DirIter
, 2, L
"*.EFI", &DirEntry
)) {
1122 if (DirEntry
->FileName
[0] == '.')
1123 continue; // skip this
1125 SPrint(FileName
, 255, L
"%s\\%s", Path
, DirEntry
->FileName
);
1126 Status
= StartEFIImage(FileDevicePath(SelfLoadedImage
->DeviceHandle
, FileName
),
1127 L
"", DirEntry
->FileName
, DirEntry
->FileName
, NULL
);
1129 Status
= DirIterClose(&DirIter
);
1130 if (Status
!= EFI_NOT_FOUND
) {
1131 SPrint(FileName
, 255, L
"while scanning the %s directory", Path
);
1132 CheckError(Status
, FileName
);
1136 static EFI_STATUS
ConnectAllDriversToAllControllers(VOID
)
1139 UINTN AllHandleCount
;
1140 EFI_HANDLE
*AllHandleBuffer
;
1143 EFI_HANDLE
*HandleBuffer
;
1149 Status
= LibLocateHandle(AllHandles
,
1154 if (EFI_ERROR(Status
))
1157 for (Index
= 0; Index
< AllHandleCount
; Index
++) {
1159 // Scan the handle database
1161 Status
= LibScanHandleDatabase(NULL
,
1163 AllHandleBuffer
[Index
],
1168 if (EFI_ERROR (Status
))
1172 if (HandleType
[Index
] & EFI_HANDLE_TYPE_DRIVER_BINDING_HANDLE
)
1174 if (HandleType
[Index
] & EFI_HANDLE_TYPE_IMAGE_HANDLE
)
1179 for (HandleIndex
= 0; HandleIndex
< HandleCount
; HandleIndex
++) {
1180 if (HandleType
[HandleIndex
] & EFI_HANDLE_TYPE_PARENT_HANDLE
)
1185 if (HandleType
[Index
] & EFI_HANDLE_TYPE_DEVICE_HANDLE
) {
1186 Status
= refit_call4_wrapper(BS
->ConnectController
,
1187 AllHandleBuffer
[Index
],
1195 FreePool (HandleBuffer
);
1196 FreePool (HandleType
);
1200 FreePool (AllHandleBuffer
);
1204 static VOID
LoadDrivers(VOID
)
1206 CHAR16 DirName
[256];
1208 // load drivers from /efi/refind/drivers
1209 SPrint(DirName
, 255, L
"%s\\drivers", SelfDirPath
);
1210 ScanDriverDir(DirName
);
1212 // load drivers from /efi/tools/drivers
1213 ScanDriverDir(L
"\\efi\\tools\\drivers");
1215 // connect all devices
1216 ConnectAllDriversToAllControllers();
1218 #endif /* DEBIAN_ENABLE_EFI110 */
1220 static VOID
ScanForBootloaders(VOID
) {
1224 // Commented-out below: Was part of an attempt to get rEFInd to
1225 // re-scan disk devices on pressing Esc; but doesn't work (yet), so
1227 // MainMenu.Title = StrDuplicate(L"Main Menu 2");
1228 // MainMenu.TitleImage = NULL;
1229 // MainMenu.InfoLineCount = 0;
1230 // MainMenu.InfoLines = NULL;
1231 // MainMenu.EntryCount = 0;
1232 // MainMenu.Entries = NULL;
1233 // MainMenu.TimeoutSeconds = 20;
1234 // MainMenu.TimeoutText = StrDuplicate(L"Automatic boot");
1237 // scan for loaders and tools, add them to the menu
1238 for (i
= 0; i
< NUM_SCAN_OPTIONS
; i
++) {
1239 switch(GlobalConfig
.ScanFor
[i
]) {
1244 ScanLegacyInternal();
1247 ScanLegacyExternal();
1250 ScanUserConfigured();
1265 // fixed other menu entries
1266 if (!(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_FUNCS
)) {
1267 MenuEntryAbout
.Image
= BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT
);
1268 AddMenuEntry(&MainMenu
, &MenuEntryAbout
);
1270 if (!(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_FUNCS
) || MainMenu
.EntryCount
== 0) {
1271 MenuEntryShutdown
.Image
= BuiltinIcon(BUILTIN_ICON_FUNC_SHUTDOWN
);
1272 AddMenuEntry(&MainMenu
, &MenuEntryShutdown
);
1273 MenuEntryReset
.Image
= BuiltinIcon(BUILTIN_ICON_FUNC_RESET
);
1274 AddMenuEntry(&MainMenu
, &MenuEntryReset
);
1277 // assign shortcut keys
1278 for (i
= 0; i
< MainMenu
.EntryCount
&& MainMenu
.Entries
[i
]->Row
== 0 && i
< 9; i
++)
1279 MainMenu
.Entries
[i
]->ShortcutDigit
= (CHAR16
)('1' + i
);
1281 // wait for user ACK when there were errors
1282 FinishTextScreen(FALSE
);
1283 } // static VOID ScanForBootloaders()
1291 efi_main (IN EFI_HANDLE ImageHandle
, IN EFI_SYSTEM_TABLE
*SystemTable
)
1294 BOOLEAN MainLoopRunning
= TRUE
;
1295 REFIT_MENU_ENTRY
*ChosenEntry
;
1299 InitializeLib(ImageHandle
, SystemTable
);
1301 Status
= InitRefitLib(ImageHandle
);
1302 if (EFI_ERROR(Status
))
1305 // read configuration
1306 CopyMem(GlobalConfig
.ScanFor
, "ieo ", NUM_SCAN_OPTIONS
);
1308 MainMenu
.TimeoutSeconds
= GlobalConfig
.Timeout
;
1310 // disable EFI watchdog timer
1311 refit_call4_wrapper(BS
->SetWatchdogTimer
, 0x0000, 0x0000, 0x0000, NULL
);
1313 // further bootstrap (now with config available)
1315 #ifdef DEBIAN_ENABLE_EFI110
1317 #endif /* DEBIAN_ENABLE_EFI110 */
1318 ScanForBootloaders();
1320 while (MainLoopRunning
) {
1321 MenuExit
= RunMainMenu(&MainMenu
, GlobalConfig
.DefaultSelection
, &ChosenEntry
);
1323 // We don't allow exiting the main menu with the Escape key.
1324 if (MenuExit
== MENU_EXIT_ESCAPE
) {
1325 // Commented-out below: Was part of an attempt to get rEFInd to
1326 // re-scan disk devices on pressing Esc; but doesn't work (yet), so
1329 // ScanForBootloaders();
1334 switch (ChosenEntry
->Tag
) {
1336 case TAG_RESET
: // Restart
1338 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);
1339 MainLoopRunning
= FALSE
; // just in case we get this far
1342 case TAG_SHUTDOWN
: // Shut Down
1344 refit_call4_wrapper(RT
->ResetSystem
, EfiResetShutdown
, EFI_SUCCESS
, 0, NULL
);
1345 MainLoopRunning
= FALSE
; // just in case we get this far
1348 case TAG_ABOUT
: // About rEFInd
1352 case TAG_LOADER
: // Boot OS via .EFI loader
1353 StartLoader((LOADER_ENTRY
*)ChosenEntry
);
1356 case TAG_LEGACY
: // Boot legacy OS
1357 StartLegacy((LEGACY_ENTRY
*)ChosenEntry
);
1360 case TAG_TOOL
: // Start a EFI tool
1361 StartTool((LOADER_ENTRY
*)ChosenEntry
);
1367 // If we end up here, things have gone wrong. Try to reboot, and if that
1368 // fails, go into an endless loop.
1369 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);