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 "driver_support.h"
53 #include "../include/syslinux_mbr.h"
58 #define MACOSX_LOADER_PATH L"System\\Library\\CoreServices\\boot.efi"
60 #define SHELL_NAMES L"\\EFI\\tools\\shell.efi,\\shellx64.efi"
62 #define SHELL_NAMES L"\\EFI\\tools\\shell.efi,\\shellia32.efi"
64 #define SHELL_NAMES L"\\EFI\\tools\\shell.efi"
67 // Filename patterns that identify EFI boot loaders. Note that a single case (either L"*.efi" or
68 // L"*.EFI") is fine for most systems; but Gigabyte's buggy Hybrid EFI does a case-sensitive
69 // comparison when it should do a case-insensitive comparison, so I'm doubling this up. It does
70 // no harm on other computers, AFAIK. In theory, every case variation should be done for
71 // completeness, but that's ridiculous....
72 #define LOADER_MATCH_PATTERNS L"*.efi,*.EFI"
74 // Patterns that identify Linux kernels. Added to the loader match pattern when the
75 // scan_all_linux_kernels option is set in the configuration file. Causes kernels WITHOUT
76 // a ".efi" extension to be found when scanning for boot loaders.
77 #define LINUX_MATCH_PATTERNS L"vmlinuz*,bzImage*"
79 static REFIT_MENU_ENTRY MenuEntryAbout
= { L
"About rEFInd", TAG_ABOUT
, 1, 0, 'A', NULL
, NULL
, NULL
};
80 static REFIT_MENU_ENTRY MenuEntryReset
= { L
"Reboot Computer", TAG_REBOOT
, 1, 0, 'R', NULL
, NULL
, NULL
};
81 static REFIT_MENU_ENTRY MenuEntryShutdown
= { L
"Shut Down Computer", TAG_SHUTDOWN
, 1, 0, 'U', NULL
, NULL
, NULL
};
82 static REFIT_MENU_ENTRY MenuEntryReturn
= { L
"Return to Main Menu", TAG_RETURN
, 0, 0, 0, NULL
, NULL
, NULL
};
83 static REFIT_MENU_ENTRY MenuEntryExit
= { L
"Exit rEFInd", TAG_EXIT
, 1, 0, 0, NULL
, NULL
, NULL
};
85 static REFIT_MENU_SCREEN MainMenu
= { L
"Main Menu", NULL
, 0, NULL
, 0, NULL
, 0, L
"Automatic boot" };
86 static REFIT_MENU_SCREEN AboutMenu
= { L
"About", NULL
, 0, NULL
, 0, NULL
, 0, NULL
};
88 REFIT_CONFIG GlobalConfig
= { FALSE
, FALSE
, 20, 0, 0, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
89 {TAG_SHELL
, TAG_ABOUT
, TAG_SHUTDOWN
, TAG_REBOOT
, 0, 0, 0, 0, 0 }};
95 static VOID
AboutrEFInd(VOID
)
97 if (AboutMenu
.EntryCount
== 0) {
98 AboutMenu
.TitleImage
= BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT
);
99 AddMenuInfoLine(&AboutMenu
, L
"rEFInd Version 0.2.7.3");
100 AddMenuInfoLine(&AboutMenu
, L
"");
101 AddMenuInfoLine(&AboutMenu
, L
"Copyright (c) 2006-2010 Christoph Pfisterer");
102 AddMenuInfoLine(&AboutMenu
, L
"Copyright (c) 2012 Roderick W. Smith");
103 AddMenuInfoLine(&AboutMenu
, L
"Portions Copyright (c) Intel Corporation and others");
104 AddMenuInfoLine(&AboutMenu
, L
"Distributed under the terms of the GNU GPLv3 license");
105 AddMenuInfoLine(&AboutMenu
, L
"");
106 AddMenuInfoLine(&AboutMenu
, L
"Running on:");
107 AddMenuInfoLine(&AboutMenu
, PoolPrint(L
" EFI Revision %d.%02d",
108 ST
->Hdr
.Revision
>> 16, ST
->Hdr
.Revision
& ((1 << 16) - 1)));
110 AddMenuInfoLine(&AboutMenu
, L
" Platform: x86 (32 bit)");
111 #elif defined(EFIX64)
112 AddMenuInfoLine(&AboutMenu
, L
" Platform: x86_64 (64 bit)");
114 AddMenuInfoLine(&AboutMenu
, L
" Platform: unknown");
116 AddMenuInfoLine(&AboutMenu
, PoolPrint(L
" Firmware: %s %d.%02d",
117 ST
->FirmwareVendor
, ST
->FirmwareRevision
>> 16, ST
->FirmwareRevision
& ((1 << 16) - 1)));
118 AddMenuInfoLine(&AboutMenu
, PoolPrint(L
" Screen Output: %s", egScreenDescription()));
119 AddMenuInfoLine(&AboutMenu
, L
"");
120 AddMenuInfoLine(&AboutMenu
, L
"For more information, see the rEFInd Web site:");
121 AddMenuInfoLine(&AboutMenu
, L
"http://www.rodsbooks.com/refind/");
122 AddMenuEntry(&AboutMenu
, &MenuEntryReturn
);
125 RunMenu(&AboutMenu
, NULL
);
126 } /* VOID AboutrEFInd() */
128 static EFI_STATUS
StartEFIImageList(IN EFI_DEVICE_PATH
**DevicePaths
,
129 IN CHAR16
*LoadOptions
, IN CHAR16
*LoadOptionsPrefix
,
130 IN CHAR16
*ImageTitle
,
131 OUT UINTN
*ErrorInStep
,
134 EFI_STATUS Status
, ReturnStatus
;
135 EFI_HANDLE ChildImageHandle
;
136 EFI_LOADED_IMAGE
*ChildLoadedImage
;
137 UINTN DevicePathIndex
;
138 CHAR16 ErrorInfo
[256];
139 CHAR16
*FullLoadOptions
= NULL
;
142 Print(L
"Starting %s\n", ImageTitle
);
143 if (ErrorInStep
!= NULL
)
146 // load the image into memory
147 ReturnStatus
= Status
= EFI_NOT_FOUND
; // in case the list is empty
148 for (DevicePathIndex
= 0; DevicePaths
[DevicePathIndex
] != NULL
; DevicePathIndex
++) {
149 ReturnStatus
= Status
= refit_call6_wrapper(BS
->LoadImage
, FALSE
, SelfImageHandle
, DevicePaths
[DevicePathIndex
], NULL
, 0, &ChildImageHandle
);
150 if (ReturnStatus
!= EFI_NOT_FOUND
)
153 SPrint(ErrorInfo
, 255, L
"while loading %s", ImageTitle
);
154 if (CheckError(Status
, ErrorInfo
)) {
155 if (ErrorInStep
!= NULL
)
161 if (LoadOptions
!= NULL
) {
162 ReturnStatus
= Status
= refit_call3_wrapper(BS
->HandleProtocol
, ChildImageHandle
, &LoadedImageProtocol
, (VOID
**) &ChildLoadedImage
);
163 if (CheckError(Status
, L
"while getting a LoadedImageProtocol handle")) {
164 if (ErrorInStep
!= NULL
)
169 if (LoadOptionsPrefix
!= NULL
) {
170 FullLoadOptions
= PoolPrint(L
"%s %s ", LoadOptionsPrefix
, LoadOptions
);
171 // NOTE: That last space is also added by the EFI shell and seems to be significant
172 // when passing options to Apple's boot.efi...
173 LoadOptions
= FullLoadOptions
;
175 // NOTE: We also include the terminating null in the length for safety.
176 ChildLoadedImage
->LoadOptions
= (VOID
*)LoadOptions
;
177 ChildLoadedImage
->LoadOptionsSize
= ((UINT32
)StrLen(LoadOptions
) + 1) * sizeof(CHAR16
);
179 Print(L
"Using load options '%s'\n", LoadOptions
);
182 // close open file handles
185 // turn control over to the image
186 // TODO: (optionally) re-enable the EFI watchdog timer!
187 ReturnStatus
= Status
= refit_call3_wrapper(BS
->StartImage
, ChildImageHandle
, NULL
, NULL
);
188 // control returns here when the child image calls Exit()
189 SPrint(ErrorInfo
, 255, L
"returned from %s", ImageTitle
);
190 if (CheckError(Status
, ErrorInfo
)) {
191 if (ErrorInStep
!= NULL
)
195 // re-open file handles
199 // unload the image, we don't care if it works or not...
200 Status
= refit_call1_wrapper(BS
->UnloadImage
, ChildImageHandle
);
202 if (FullLoadOptions
!= NULL
)
203 FreePool(FullLoadOptions
);
205 } /* static EFI_STATUS StartEFIImageList() */
207 static EFI_STATUS
StartEFIImage(IN EFI_DEVICE_PATH
*DevicePath
,
208 IN CHAR16
*LoadOptions
, IN CHAR16
*LoadOptionsPrefix
,
209 IN CHAR16
*ImageTitle
,
210 OUT UINTN
*ErrorInStep
,
213 EFI_DEVICE_PATH
*DevicePaths
[2];
215 DevicePaths
[0] = DevicePath
;
216 DevicePaths
[1] = NULL
;
217 return StartEFIImageList(DevicePaths
, LoadOptions
, LoadOptionsPrefix
, ImageTitle
, ErrorInStep
, Verbose
);
218 } /* static EFI_STATUS StartEFIImage() */
221 // EFI OS loader functions
224 static VOID
StartLoader(IN LOADER_ENTRY
*Entry
)
226 UINTN ErrorInStep
= 0;
228 BeginExternalScreen(Entry
->UseGraphicsMode
, L
"Booting OS");
229 StartEFIImage(Entry
->DevicePath
, Entry
->LoadOptions
,
230 Basename(Entry
->LoaderPath
), Basename(Entry
->LoaderPath
), &ErrorInStep
, TRUE
);
231 FinishExternalScreen();
234 // Locate an initrd or initramfs file that matches the kernel specified by LoaderPath.
235 // The matching file has a name that begins with "init" and includes the same version
236 // number string as is found in LoaderPath -- but not a longer version number string.
237 // For instance, if LoaderPath is \EFI\kernels\bzImage-3.3.0.efi, and if \EFI\kernels
238 // has a file called initramfs-3.3.0.img, this function will return the string
239 // '\EFI\kernels\initramfs-3.3.0.img'. If the directory ALSO contains the file
240 // initramfs-3.3.0-rc7.img or initramfs-13.3.0.img, those files will NOT match;
241 // however, initmine-3.3.0.img might match. (FindInitrd() returns the first match it
242 // finds). Thus, care should be taken to avoid placing duplicate matching files in
243 // the kernel's directory.
244 // If no matching init file can be found, returns NULL.
245 static CHAR16
* FindInitrd(IN CHAR16
*LoaderPath
, IN REFIT_VOLUME
*Volume
) {
246 CHAR16
*InitrdName
= NULL
, *FileName
, *KernelVersion
, *InitrdVersion
, *Path
;
247 REFIT_DIR_ITER DirIter
;
248 EFI_FILE_INFO
*DirEntry
;
250 FileName
= Basename(LoaderPath
);
251 KernelVersion
= FindNumbers(FileName
);
252 Path
= FindPath(LoaderPath
);
254 // Add trailing backslash for root directory; necessary on some systems, but must
255 // NOT be added to all directories, since on other systems, a trailing backslash on
256 // anything but the root directory causes them to flake out!
257 if (StrLen(Path
) == 0) {
258 MergeStrings(&Path
, L
"\\", 0);
260 DirIterOpen(Volume
->RootDir
, Path
, &DirIter
);
261 // Now add a trailing backslash if it was NOT added earlier, for consistency in
262 // building the InitrdName later....
263 if ((StrLen(Path
) > 0) && (Path
[StrLen(Path
) - 1] != L
'\\'))
264 MergeStrings(&Path
, L
"\\", 0);
265 while ((DirIterNext(&DirIter
, 2, L
"init*", &DirEntry
)) && (InitrdName
== NULL
)) {
266 InitrdVersion
= FindNumbers(DirEntry
->FileName
);
267 if (KernelVersion
!= NULL
) {
268 if (StriCmp(InitrdVersion
, KernelVersion
) == 0)
269 InitrdName
= PoolPrint(L
"%s%s", Path
, DirEntry
->FileName
);
271 if (InitrdVersion
== NULL
)
272 InitrdName
= PoolPrint(L
"%s%s", Path
, DirEntry
->FileName
);
274 if (InitrdVersion
!= NULL
)
275 FreePool(InitrdVersion
);
277 DirIterClose(&DirIter
);
279 // Note: Don't FreePool(FileName), since Basename returns a pointer WITHIN the string it's passed.
280 FreePool(KernelVersion
);
283 } // static CHAR16 * FindInitrd()
285 LOADER_ENTRY
* AddPreparedLoaderEntry(LOADER_ENTRY
*Entry
) {
286 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
289 } // LOADER_ENTRY * AddPreparedLoaderEntry()
291 // Creates a new LOADER_ENTRY data structure and populates it with
292 // default values from the specified Entry, or NULL values if Entry
293 // is unspecified (NULL).
294 // Returns a pointer to the new data structure, or NULL if it
295 // couldn't be allocated
296 LOADER_ENTRY
*InitializeLoaderEntry(IN LOADER_ENTRY
*Entry
) {
297 LOADER_ENTRY
*NewEntry
= NULL
;
299 NewEntry
= AllocateZeroPool(sizeof(LOADER_ENTRY
));
300 if (NewEntry
!= NULL
) {
301 NewEntry
->me
.Title
= NULL
;
302 NewEntry
->me
.Tag
= TAG_LOADER
;
303 NewEntry
->Enabled
= TRUE
;
304 NewEntry
->UseGraphicsMode
= FALSE
;
305 NewEntry
->OSType
= 0;
307 NewEntry
->LoaderPath
= StrDuplicate(Entry
->LoaderPath
);
308 NewEntry
->VolName
= StrDuplicate(Entry
->VolName
);
309 NewEntry
->DevicePath
= Entry
->DevicePath
;
310 NewEntry
->UseGraphicsMode
= Entry
->UseGraphicsMode
;
311 NewEntry
->LoadOptions
= StrDuplicate(Entry
->LoadOptions
);
312 NewEntry
->InitrdPath
= StrDuplicate(Entry
->InitrdPath
);
316 } // LOADER_ENTRY *InitializeLoaderEntry()
318 // Prepare a REFIT_MENU_SCREEN data structure for a subscreen entry. This sets up
319 // the default entry that launches the boot loader using the same options as the
320 // main Entry does. Subsequent options can be added by the calling function.
321 // If a subscreen already exists in the Entry that's passed to this function,
322 // it's left unchanged and a pointer to it is returned.
323 // Returns a pointer to the new subscreen data structure, or NULL if there
324 // were problems allocating memory.
325 REFIT_MENU_SCREEN
*InitializeSubScreen(IN LOADER_ENTRY
*Entry
) {
326 CHAR16
*FileName
, *Temp
;
327 REFIT_MENU_SCREEN
*SubScreen
= NULL
;
328 LOADER_ENTRY
*SubEntry
;
330 FileName
= Basename(Entry
->LoaderPath
);
331 if (Entry
->me
.SubScreen
== NULL
) { // No subscreen yet; initialize default entry....
332 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
333 if (SubScreen
!= NULL
) {
334 SubScreen
->Title
= PoolPrint(L
"Boot Options for %s on %s", (Entry
->Title
!= NULL
) ? Entry
->Title
: FileName
, Entry
->VolName
);
335 SubScreen
->TitleImage
= Entry
->me
.Image
;
337 SubEntry
= InitializeLoaderEntry(Entry
);
338 if (SubEntry
!= NULL
) {
339 SubEntry
->me
.Title
= L
"Boot using default options";
340 if ((SubEntry
->InitrdPath
!= NULL
) && (StrLen(SubEntry
->InitrdPath
) > 0) && (!StriSubCmp(L
"initrd", SubEntry
->LoadOptions
))) {
341 Temp
= PoolPrint(L
"initrd=%s", SubEntry
->InitrdPath
);
342 MergeStrings(&SubEntry
->LoadOptions
, Temp
, L
' ');
345 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
346 } // if (SubEntry != NULL)
347 } // if (SubScreen != NULL)
348 } else { // existing subscreen; less initialization, and just add new entry later....
349 SubScreen
= Entry
->me
.SubScreen
;
352 } // REFIT_MENU_SCREEN *InitializeSubScreen()
354 VOID
GenerateSubScreen(LOADER_ENTRY
*Entry
, IN REFIT_VOLUME
*Volume
) {
355 REFIT_MENU_SCREEN
*SubScreen
;
356 LOADER_ENTRY
*SubEntry
;
357 CHAR16
*FileName
, *InitrdOption
= NULL
, *Temp
;
358 CHAR16 DiagsFileName
[256];
363 FileName
= Basename(Entry
->LoaderPath
);
364 // create the submenu
365 if (StrLen(Entry
->Title
) == 0) {
366 FreePool(Entry
->Title
);
369 SubScreen
= InitializeSubScreen(Entry
);
371 // loader-specific submenu entries
372 if (Entry
->OSType
== 'M') { // entries for Mac OS X
374 SubEntry
= InitializeLoaderEntry(Entry
);
375 if (SubEntry
!= NULL
) {
376 SubEntry
->me
.Title
= L
"Boot Mac OS X with a 64-bit kernel";
377 SubEntry
->LoadOptions
= L
"arch=x86_64";
378 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
381 SubEntry
= InitializeLoaderEntry(Entry
);
382 if (SubEntry
!= NULL
) {
383 SubEntry
->me
.Title
= L
"Boot Mac OS X with a 32-bit kernel";
384 SubEntry
->LoadOptions
= L
"arch=i386";
385 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
389 if (!(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_SINGLEUSER
)) {
390 SubEntry
= InitializeLoaderEntry(Entry
);
391 if (SubEntry
!= NULL
) {
392 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode";
393 SubEntry
->UseGraphicsMode
= FALSE
;
394 SubEntry
->LoadOptions
= L
"-v";
395 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
399 SubEntry
= InitializeLoaderEntry(Entry
);
400 if (SubEntry
!= NULL
) {
401 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode (64-bit)";
402 SubEntry
->UseGraphicsMode
= FALSE
;
403 SubEntry
->LoadOptions
= L
"-v arch=x86_64";
404 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
407 SubEntry
= InitializeLoaderEntry(Entry
);
408 if (SubEntry
!= NULL
) {
409 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode (32-bit)";
410 SubEntry
->UseGraphicsMode
= FALSE
;
411 SubEntry
->LoadOptions
= L
"-v arch=i386";
412 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
416 SubEntry
= InitializeLoaderEntry(Entry
);
417 if (SubEntry
!= NULL
) {
418 SubEntry
->me
.Title
= L
"Boot Mac OS X in single user mode";
419 SubEntry
->UseGraphicsMode
= FALSE
;
420 SubEntry
->LoadOptions
= L
"-v -s";
421 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
425 // check for Apple hardware diagnostics
426 StrCpy(DiagsFileName
, L
"\\System\\Library\\CoreServices\\.diagnostics\\diags.efi");
427 if (FileExists(Volume
->RootDir
, DiagsFileName
) && !(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_HWTEST
)) {
428 SubEntry
= InitializeLoaderEntry(Entry
);
429 if (SubEntry
!= NULL
) {
430 SubEntry
->me
.Title
= L
"Run Apple Hardware Test";
431 FreePool(SubEntry
->LoaderPath
);
432 SubEntry
->LoaderPath
= StrDuplicate(DiagsFileName
);
433 SubEntry
->DevicePath
= FileDevicePath(Volume
->DeviceHandle
, SubEntry
->LoaderPath
);
434 SubEntry
->UseGraphicsMode
= TRUE
;
435 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
437 } // if diagnostics entry found
439 } else if (Entry
->OSType
== 'L') { // entries for Linux kernels with EFI stub loaders
440 File
= ReadLinuxOptionsFile(Entry
->LoaderPath
, Volume
);
442 if ((Temp
= FindInitrd(Entry
->LoaderPath
, Volume
)) != NULL
)
443 InitrdOption
= PoolPrint(L
"initrd=%s", Temp
);
444 TokenCount
= ReadTokenLine(File
, &TokenList
); // read and discard first entry, since it's
445 FreeTokenLine(&TokenList
, &TokenCount
); // set up by InitializeSubScreen(), earlier....
446 while ((TokenCount
= ReadTokenLine(File
, &TokenList
)) > 1) {
447 SubEntry
= InitializeLoaderEntry(Entry
);
448 SubEntry
->me
.Title
= StrDuplicate(TokenList
[0]);
449 if (SubEntry
->LoadOptions
!= NULL
)
450 FreePool(SubEntry
->LoadOptions
);
451 SubEntry
->LoadOptions
= StrDuplicate(TokenList
[1]);
452 MergeStrings(&SubEntry
->LoadOptions
, InitrdOption
, L
' ');
453 FreeTokenLine(&TokenList
, &TokenCount
);
454 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
457 FreePool(InitrdOption
);
461 } // if Linux options file exists
463 } else if (Entry
->OSType
== 'E') { // entries for ELILO
464 SubEntry
= InitializeLoaderEntry(Entry
);
465 if (SubEntry
!= NULL
) {
466 SubEntry
->me
.Title
= PoolPrint(L
"Run %s in interactive mode", FileName
);
467 SubEntry
->LoadOptions
= L
"-p";
468 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
471 SubEntry
= InitializeLoaderEntry(Entry
);
472 if (SubEntry
!= NULL
) {
473 SubEntry
->me
.Title
= L
"Boot Linux for a 17\" iMac or a 15\" MacBook Pro (*)";
474 SubEntry
->UseGraphicsMode
= TRUE
;
475 SubEntry
->LoadOptions
= L
"-d 0 i17";
476 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
479 SubEntry
= InitializeLoaderEntry(Entry
);
480 if (SubEntry
!= NULL
) {
481 SubEntry
->me
.Title
= L
"Boot Linux for a 20\" iMac (*)";
482 SubEntry
->UseGraphicsMode
= TRUE
;
483 SubEntry
->LoadOptions
= L
"-d 0 i20";
484 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
487 SubEntry
= InitializeLoaderEntry(Entry
);
488 if (SubEntry
!= NULL
) {
489 SubEntry
->me
.Title
= L
"Boot Linux for a Mac Mini (*)";
490 SubEntry
->UseGraphicsMode
= TRUE
;
491 SubEntry
->LoadOptions
= L
"-d 0 mini";
492 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
495 AddMenuInfoLine(SubScreen
, L
"NOTE: This is an example. Entries");
496 AddMenuInfoLine(SubScreen
, L
"marked with (*) may not work.");
498 } else if (Entry
->OSType
== 'X') { // entries for xom.efi
499 // by default, skip the built-in selection and boot from hard disk only
500 Entry
->LoadOptions
= L
"-s -h";
502 SubEntry
= InitializeLoaderEntry(Entry
);
503 if (SubEntry
!= NULL
) {
504 SubEntry
->me
.Title
= L
"Boot Windows from Hard Disk";
505 SubEntry
->LoadOptions
= L
"-s -h";
506 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
509 SubEntry
= InitializeLoaderEntry(Entry
);
510 if (SubEntry
!= NULL
) {
511 SubEntry
->me
.Title
= L
"Boot Windows from CD-ROM";
512 SubEntry
->LoadOptions
= L
"-s -c";
513 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
516 SubEntry
= InitializeLoaderEntry(Entry
);
517 if (SubEntry
!= NULL
) {
518 SubEntry
->me
.Title
= PoolPrint(L
"Run %s in text mode", FileName
);
519 SubEntry
->UseGraphicsMode
= FALSE
;
520 SubEntry
->LoadOptions
= L
"-v";
521 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
523 } // entries for xom.efi
524 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
525 Entry
->me
.SubScreen
= SubScreen
;
526 } // VOID GenerateSubScreen()
528 // Returns options for a Linux kernel. Reads them from an options file in the
529 // kernel's directory; and if present, adds an initrd= option for an initial
530 // RAM disk file with the same version number as the kernel file.
531 static CHAR16
* GetMainLinuxOptions(IN CHAR16
* LoaderPath
, IN REFIT_VOLUME
*Volume
) {
532 CHAR16
*Options
= NULL
, *InitrdName
, *InitrdOption
= NULL
;
534 Options
= GetFirstOptionsFromFile(LoaderPath
, Volume
);
535 InitrdName
= FindInitrd(LoaderPath
, Volume
);
536 if (InitrdName
!= NULL
)
537 InitrdOption
= PoolPrint(L
"initrd=%s", InitrdName
);
538 MergeStrings(&Options
, InitrdOption
, ' ');
539 if (InitrdOption
!= NULL
)
540 FreePool(InitrdOption
);
541 if (InitrdName
!= NULL
)
542 FreePool(InitrdName
);
544 } // static CHAR16 * GetMainLinuxOptions()
546 // Sets a few defaults for a loader entry -- mainly the icon, but also the OS type
547 // code and shortcut letter. For Linux EFI stub loaders, also sets kernel options
548 // that will (with luck) work fairly automatically.
549 VOID
SetLoaderDefaults(LOADER_ENTRY
*Entry
, CHAR16
*LoaderPath
, IN REFIT_VOLUME
*Volume
) {
550 CHAR16 IconFileName
[256];
551 CHAR16
*FileName
, *PathOnly
, *OSIconName
= NULL
, *Temp
;
552 CHAR16 ShortcutLetter
= 0;
554 FileName
= Basename(LoaderPath
);
555 PathOnly
= FindPath(LoaderPath
);
557 // locate a custom icon for the loader
558 StrCpy(IconFileName
, LoaderPath
);
559 ReplaceExtension(IconFileName
, L
".icns");
560 if (FileExists(Volume
->RootDir
, IconFileName
)) {
561 Entry
->me
.Image
= LoadIcns(Volume
->RootDir
, IconFileName
, 128);
562 } else if ((StrLen(PathOnly
) == 0) && (Volume
->VolIconImage
!= NULL
)) {
563 Entry
->me
.Image
= Volume
->VolIconImage
;
564 } // icon matched to loader or volume
566 Temp
= FindLastDirName(LoaderPath
);
567 MergeStrings(&OSIconName
, Temp
, L
',');
569 if (OSIconName
!= NULL
) {
570 ShortcutLetter
= OSIconName
[0];
573 // detect specific loaders
574 if (StriSubCmp(L
"bzImage", LoaderPath
) || StriSubCmp(L
"vmlinuz", LoaderPath
)) {
575 MergeStrings(&OSIconName
, L
"linux", L
',');
577 if (ShortcutLetter
== 0)
578 ShortcutLetter
= 'L';
579 Entry
->LoadOptions
= GetMainLinuxOptions(LoaderPath
, Volume
);
580 } else if (StriSubCmp(L
"refit", LoaderPath
)) {
581 MergeStrings(&OSIconName
, L
"refit", L
',');
583 ShortcutLetter
= 'R';
584 } else if (StriCmp(LoaderPath
, MACOSX_LOADER_PATH
) == 0) {
585 if (Volume
->VolIconImage
!= NULL
) { // custom icon file found
586 Entry
->me
.Image
= Volume
->VolIconImage
;
588 MergeStrings(&OSIconName
, L
"mac", L
',');
589 Entry
->UseGraphicsMode
= TRUE
;
591 ShortcutLetter
= 'M';
592 } else if (StriCmp(FileName
, L
"diags.efi") == 0) {
593 MergeStrings(&OSIconName
, L
"hwtest", L
',');
594 } else if (StriCmp(FileName
, L
"e.efi") == 0 || StriCmp(FileName
, L
"elilo.efi") == 0) {
595 MergeStrings(&OSIconName
, L
"elilo,linux", L
',');
597 if (ShortcutLetter
== 0)
598 ShortcutLetter
= 'L';
599 } else if (StriCmp(FileName
, L
"cdboot.efi") == 0 ||
600 StriCmp(FileName
, L
"bootmgr.efi") == 0 ||
601 StriCmp(FileName
, L
"Bootmgfw.efi") == 0) {
602 MergeStrings(&OSIconName
, L
"win", L
',');
604 ShortcutLetter
= 'W';
605 } else if (StriCmp(FileName
, L
"xom.efi") == 0) {
606 MergeStrings(&OSIconName
, L
"xom,win", L
',');
607 Entry
->UseGraphicsMode
= TRUE
;
609 ShortcutLetter
= 'W';
612 if ((ShortcutLetter
>= 'a') && (ShortcutLetter
<= 'z'))
613 ShortcutLetter
= ShortcutLetter
- 'a' + 'A'; // convert lowercase to uppercase
614 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
615 if (Entry
->me
.Image
== NULL
)
616 Entry
->me
.Image
= LoadOSIcon(OSIconName
, L
"unknown", FALSE
);
617 if (PathOnly
!= NULL
)
619 } // VOID SetLoaderDefaults()
621 // Add a specified EFI boot loader to the list, using automatic settings
622 // for icons, options, etc.
623 LOADER_ENTRY
* AddLoaderEntry(IN CHAR16
*LoaderPath
, IN CHAR16
*LoaderTitle
, IN REFIT_VOLUME
*Volume
) {
626 CleanUpPathNameSlashes(LoaderPath
);
627 Entry
= InitializeLoaderEntry(NULL
);
629 Entry
->Title
= StrDuplicate(LoaderTitle
);
630 Entry
->me
.Title
= PoolPrint(L
"Boot %s from %s", (LoaderTitle
!= NULL
) ? LoaderTitle
: LoaderPath
, Volume
->VolName
);
632 Entry
->me
.BadgeImage
= Volume
->VolBadgeImage
;
633 Entry
->LoaderPath
= StrDuplicate(LoaderPath
);
634 Entry
->VolName
= Volume
->VolName
;
635 Entry
->DevicePath
= FileDevicePath(Volume
->DeviceHandle
, Entry
->LoaderPath
);
636 SetLoaderDefaults(Entry
, LoaderPath
, Volume
);
637 GenerateSubScreen(Entry
, Volume
);
638 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
642 } // LOADER_ENTRY * AddLoaderEntry()
644 // Scan an individual directory for EFI boot loader files and, if found,
645 // add them to the list.
646 static VOID
ScanLoaderDir(IN REFIT_VOLUME
*Volume
, IN CHAR16
*Path
, IN CHAR16
*Pattern
)
649 REFIT_DIR_ITER DirIter
;
650 EFI_FILE_INFO
*DirEntry
;
651 CHAR16 FileName
[256];
653 if (!SelfDirPath
|| !Path
|| ((StriCmp(Path
, SelfDirPath
) == 0) && Volume
!= SelfVolume
) ||
654 (StriCmp(Path
, SelfDirPath
) != 0)) {
655 // look through contents of the directory
656 DirIterOpen(Volume
->RootDir
, Path
, &DirIter
);
657 while (DirIterNext(&DirIter
, 2, Pattern
, &DirEntry
)) {
658 if (DirEntry
->FileName
[0] == '.' ||
659 StriCmp(DirEntry
->FileName
, L
"TextMode.efi") == 0 ||
660 StriCmp(DirEntry
->FileName
, L
"ebounce.efi") == 0 ||
661 StriCmp(DirEntry
->FileName
, L
"GraphicsConsole.efi") == 0 ||
662 StriSubCmp(L
"shell", DirEntry
->FileName
))
663 continue; // skip this
666 SPrint(FileName
, 255, L
"\\%s\\%s", Path
, DirEntry
->FileName
);
668 SPrint(FileName
, 255, L
"\\%s", DirEntry
->FileName
);
669 AddLoaderEntry(FileName
, NULL
, Volume
);
671 Status
= DirIterClose(&DirIter
);
672 if (Status
!= EFI_NOT_FOUND
) {
674 SPrint(FileName
, 255, L
"while scanning the %s directory", Path
);
676 StrCpy(FileName
, L
"while scanning the root directory");
677 CheckError(Status
, FileName
);
678 } // if (Status != EFI_NOT_FOUND)
679 } // if not scanning our own directory
680 } /* static VOID ScanLoaderDir() */
682 static VOID
ScanEfiFiles(REFIT_VOLUME
*Volume
) {
684 REFIT_DIR_ITER EfiDirIter
;
685 EFI_FILE_INFO
*EfiDirEntry
;
686 CHAR16 FileName
[256], *Directory
, *MatchPatterns
;
689 MatchPatterns
= StrDuplicate(LOADER_MATCH_PATTERNS
);
690 if (GlobalConfig
.ScanAllLinux
)
691 MergeStrings(&MatchPatterns
, LINUX_MATCH_PATTERNS
, L
',');
693 if ((Volume
->RootDir
!= NULL
) && (Volume
->VolName
!= NULL
)) {
694 // check for Mac OS X boot loader
695 StrCpy(FileName
, MACOSX_LOADER_PATH
);
696 if (FileExists(Volume
->RootDir
, FileName
)) {
697 AddLoaderEntry(FileName
, L
"Mac OS X", Volume
);
701 StrCpy(FileName
, L
"System\\Library\\CoreServices\\xom.efi");
702 if (FileExists(Volume
->RootDir
, FileName
)) {
703 AddLoaderEntry(FileName
, L
"Windows XP (XoM)", Volume
);
706 // check for Microsoft boot loader/menu
707 StrCpy(FileName
, L
"EFI\\Microsoft\\Boot\\Bootmgfw.efi");
708 if (FileExists(Volume
->RootDir
, FileName
)) {
709 AddLoaderEntry(FileName
, L
"Microsoft EFI boot", Volume
);
712 // scan the root directory for EFI executables
713 ScanLoaderDir(Volume
, NULL
, MatchPatterns
);
715 // scan subdirectories of the EFI directory (as per the standard)
716 DirIterOpen(Volume
->RootDir
, L
"EFI", &EfiDirIter
);
717 while (DirIterNext(&EfiDirIter
, 1, NULL
, &EfiDirEntry
)) {
718 if (StriCmp(EfiDirEntry
->FileName
, L
"tools") == 0 || EfiDirEntry
->FileName
[0] == '.')
719 continue; // skip this, doesn't contain boot loaders
720 SPrint(FileName
, 255, L
"EFI\\%s", EfiDirEntry
->FileName
);
721 ScanLoaderDir(Volume
, FileName
, MatchPatterns
);
723 Status
= DirIterClose(&EfiDirIter
);
724 if (Status
!= EFI_NOT_FOUND
)
725 CheckError(Status
, L
"while scanning the EFI directory");
727 // Scan user-specified (or additional default) directories....
729 while ((Directory
= FindCommaDelimited(GlobalConfig
.AlsoScan
, i
++)) != NULL
) {
730 Length
= StrLen(Directory
);
731 // Some EFI implementations won't read a directory if the path ends in
732 // a backslash, so eliminate this character, if it's present....
733 while ((Length
> 0) && (Directory
[Length
- 1] == L
'\\')) {
734 Directory
[--Length
] = 0;
737 ScanLoaderDir(Volume
, Directory
, MatchPatterns
);
741 } // static VOID ScanEfiFiles()
743 // Scan internal disks for valid EFI boot loaders....
744 static VOID
ScanInternal(VOID
) {
747 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
748 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_INTERNAL
) {
749 ScanEfiFiles(Volumes
[VolumeIndex
]);
752 } // static VOID ScanInternal()
754 // Scan external disks for valid EFI boot loaders....
755 static VOID
ScanExternal(VOID
) {
758 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
759 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_EXTERNAL
) {
760 ScanEfiFiles(Volumes
[VolumeIndex
]);
763 } // static VOID ScanExternal()
765 // Scan internal disks for valid EFI boot loaders....
766 static VOID
ScanOptical(VOID
) {
769 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
770 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_OPTICAL
) {
771 ScanEfiFiles(Volumes
[VolumeIndex
]);
774 } // static VOID ScanOptical()
777 // legacy boot functions
780 static EFI_STATUS
ActivateMbrPartition(IN EFI_BLOCK_IO
*BlockIO
, IN UINTN PartitionIndex
)
783 UINT8 SectorBuffer
[512];
784 MBR_PARTITION_INFO
*MbrTable
, *EMbrTable
;
785 UINT32 ExtBase
, ExtCurrent
, NextExtCurrent
;
786 UINTN LogicalPartitionIndex
= 4;
788 BOOLEAN HaveBootCode
;
791 Status
= refit_call5_wrapper(BlockIO
->ReadBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, 0, 512, SectorBuffer
);
792 if (EFI_ERROR(Status
))
794 if (*((UINT16
*)(SectorBuffer
+ 510)) != 0xaa55)
795 return EFI_NOT_FOUND
; // safety measure #1
797 // add boot code if necessary
798 HaveBootCode
= FALSE
;
799 for (i
= 0; i
< MBR_BOOTCODE_SIZE
; i
++) {
800 if (SectorBuffer
[i
] != 0) {
806 // no boot code found in the MBR, add the syslinux MBR code
807 SetMem(SectorBuffer
, MBR_BOOTCODE_SIZE
, 0);
808 CopyMem(SectorBuffer
, syslinux_mbr
, SYSLINUX_MBR_SIZE
);
811 // set the partition active
812 MbrTable
= (MBR_PARTITION_INFO
*)(SectorBuffer
+ 446);
814 for (i
= 0; i
< 4; i
++) {
815 if (MbrTable
[i
].Flags
!= 0x00 && MbrTable
[i
].Flags
!= 0x80)
816 return EFI_NOT_FOUND
; // safety measure #2
817 if (i
== PartitionIndex
)
818 MbrTable
[i
].Flags
= 0x80;
819 else if (PartitionIndex
>= 4 && IS_EXTENDED_PART_TYPE(MbrTable
[i
].Type
)) {
820 MbrTable
[i
].Flags
= 0x80;
821 ExtBase
= MbrTable
[i
].StartLBA
;
823 MbrTable
[i
].Flags
= 0x00;
827 Status
= refit_call5_wrapper(BlockIO
->WriteBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, 0, 512, SectorBuffer
);
828 if (EFI_ERROR(Status
))
831 if (PartitionIndex
>= 4) {
832 // we have to activate a logical partition, so walk the EMBR chain
834 // NOTE: ExtBase was set above while looking at the MBR table
835 for (ExtCurrent
= ExtBase
; ExtCurrent
; ExtCurrent
= NextExtCurrent
) {
837 Status
= refit_call5_wrapper(BlockIO
->ReadBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, ExtCurrent
, 512, SectorBuffer
);
838 if (EFI_ERROR(Status
))
840 if (*((UINT16
*)(SectorBuffer
+ 510)) != 0xaa55)
841 return EFI_NOT_FOUND
; // safety measure #3
843 // scan EMBR, set appropriate partition active
844 EMbrTable
= (MBR_PARTITION_INFO
*)(SectorBuffer
+ 446);
846 for (i
= 0; i
< 4; i
++) {
847 if (EMbrTable
[i
].Flags
!= 0x00 && EMbrTable
[i
].Flags
!= 0x80)
848 return EFI_NOT_FOUND
; // safety measure #4
849 if (EMbrTable
[i
].StartLBA
== 0 || EMbrTable
[i
].Size
== 0)
851 if (IS_EXTENDED_PART_TYPE(EMbrTable
[i
].Type
)) {
853 NextExtCurrent
= ExtBase
+ EMbrTable
[i
].StartLBA
;
854 EMbrTable
[i
].Flags
= (PartitionIndex
>= LogicalPartitionIndex
) ? 0x80 : 0x00;
858 EMbrTable
[i
].Flags
= (PartitionIndex
== LogicalPartitionIndex
) ? 0x80 : 0x00;
859 LogicalPartitionIndex
++;
863 // write current EMBR
864 Status
= refit_call5_wrapper(BlockIO
->WriteBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, ExtCurrent
, 512, SectorBuffer
);
865 if (EFI_ERROR(Status
))
868 if (PartitionIndex
< LogicalPartitionIndex
)
869 break; // stop the loop, no need to touch further EMBRs
875 } /* static EFI_STATUS ActivateMbrPartition() */
877 // early 2006 Core Duo / Core Solo models
878 static UINT8 LegacyLoaderDevicePath1Data
[] = {
879 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
880 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
881 0xFF, 0xFF, 0xF9, 0xFF, 0x00, 0x00, 0x00, 0x00,
882 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
883 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
884 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
886 // mid-2006 Mac Pro (and probably other Core 2 models)
887 static UINT8 LegacyLoaderDevicePath2Data
[] = {
888 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
889 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
890 0xFF, 0xFF, 0xF7, 0xFF, 0x00, 0x00, 0x00, 0x00,
891 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
892 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
893 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
895 // mid-2007 MBP ("Santa Rosa" based models)
896 static UINT8 LegacyLoaderDevicePath3Data
[] = {
897 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
898 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
899 0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
900 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
901 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
902 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
905 static UINT8 LegacyLoaderDevicePath4Data
[] = {
906 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
907 0x00, 0x00, 0xC0, 0xFF, 0x00, 0x00, 0x00, 0x00,
908 0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
909 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
910 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
911 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
913 // late-2008 MB/MBP (NVidia chipset)
914 static UINT8 LegacyLoaderDevicePath5Data
[] = {
915 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
916 0x00, 0x40, 0xCB, 0xFF, 0x00, 0x00, 0x00, 0x00,
917 0xFF, 0xBF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
918 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
919 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
920 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
923 static EFI_DEVICE_PATH
*LegacyLoaderList
[] = {
924 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath1Data
,
925 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath2Data
,
926 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath3Data
,
927 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath4Data
,
928 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath5Data
,
932 #define MAX_DISCOVERED_PATHS (16)
934 static VOID
StartLegacy(IN LEGACY_ENTRY
*Entry
)
937 EG_IMAGE
*BootLogoImage
;
938 UINTN ErrorInStep
= 0;
939 EFI_DEVICE_PATH
*DiscoveredPathList
[MAX_DISCOVERED_PATHS
];
941 BeginExternalScreen(TRUE
, L
"Booting Legacy OS");
943 BootLogoImage
= LoadOSIcon(Entry
->Volume
->OSIconName
, L
"legacy", TRUE
);
944 if (BootLogoImage
!= NULL
)
945 BltImageAlpha(BootLogoImage
,
946 (UGAWidth
- BootLogoImage
->Width
) >> 1,
947 (UGAHeight
- BootLogoImage
->Height
) >> 1,
948 &StdBackgroundPixel
);
950 if (Entry
->Volume
->IsMbrPartition
)
951 ActivateMbrPartition(Entry
->Volume
->WholeDiskBlockIO
, Entry
->Volume
->MbrPartitionIndex
);
953 ExtractLegacyLoaderPaths(DiscoveredPathList
, MAX_DISCOVERED_PATHS
, LegacyLoaderList
);
955 Status
= StartEFIImageList(DiscoveredPathList
, Entry
->LoadOptions
, NULL
, L
"legacy loader", &ErrorInStep
, TRUE
);
956 if (Status
== EFI_NOT_FOUND
) {
957 if (ErrorInStep
== 1) {
958 Print(L
"\nPlease make sure that you have the latest firmware update installed.\n");
959 } else if (ErrorInStep
== 3) {
960 Print(L
"\nThe firmware refused to boot from the selected volume. Note that external\n"
961 L
"hard drives are not well-supported by Apple's firmware for legacy OS booting.\n");
964 FinishExternalScreen();
965 } /* static VOID StartLegacy() */
967 static LEGACY_ENTRY
* AddLegacyEntry(IN CHAR16
*LoaderTitle
, IN REFIT_VOLUME
*Volume
)
969 LEGACY_ENTRY
*Entry
, *SubEntry
;
970 REFIT_MENU_SCREEN
*SubScreen
;
972 CHAR16 ShortcutLetter
= 0;
974 if (LoaderTitle
== NULL
) {
975 if (Volume
->OSName
!= NULL
) {
976 LoaderTitle
= Volume
->OSName
;
977 if (LoaderTitle
[0] == 'W' || LoaderTitle
[0] == 'L')
978 ShortcutLetter
= LoaderTitle
[0];
980 LoaderTitle
= L
"Legacy OS";
982 if (Volume
->VolName
!= NULL
)
983 VolDesc
= Volume
->VolName
;
985 VolDesc
= (Volume
->DiskKind
== DISK_KIND_OPTICAL
) ? L
"CD" : L
"HD";
987 // prepare the menu entry
988 Entry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
989 Entry
->me
.Title
= PoolPrint(L
"Boot %s from %s", LoaderTitle
, VolDesc
);
990 Entry
->me
.Tag
= TAG_LEGACY
;
992 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
993 Entry
->me
.Image
= LoadOSIcon(Volume
->OSIconName
, L
"legacy", FALSE
);
994 Entry
->me
.BadgeImage
= Volume
->VolBadgeImage
;
995 Entry
->Volume
= Volume
;
996 Entry
->LoadOptions
= (Volume
->DiskKind
== DISK_KIND_OPTICAL
) ? L
"CD" :
997 ((Volume
->DiskKind
== DISK_KIND_EXTERNAL
) ? L
"USB" : L
"HD");
998 Entry
->Enabled
= TRUE
;
1000 // create the submenu
1001 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
1002 SubScreen
->Title
= PoolPrint(L
"Boot Options for %s on %s", LoaderTitle
, VolDesc
);
1003 SubScreen
->TitleImage
= Entry
->me
.Image
;
1006 SubEntry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1007 SubEntry
->me
.Title
= PoolPrint(L
"Boot %s", LoaderTitle
);
1008 SubEntry
->me
.Tag
= TAG_LEGACY
;
1009 SubEntry
->Volume
= Entry
->Volume
;
1010 SubEntry
->LoadOptions
= Entry
->LoadOptions
;
1011 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
1013 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
1014 Entry
->me
.SubScreen
= SubScreen
;
1015 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1017 } /* static LEGACY_ENTRY * AddLegacyEntry() */
1019 static VOID
ScanLegacyVolume(REFIT_VOLUME
*Volume
, UINTN VolumeIndex
) {
1021 BOOLEAN ShowVolume
, HideIfOthersFound
;
1024 HideIfOthersFound
= FALSE
;
1025 if (Volume
->IsAppleLegacy
) {
1027 HideIfOthersFound
= TRUE
;
1028 } else if (Volume
->HasBootCode
) {
1030 if (Volume
->BlockIO
== Volume
->WholeDiskBlockIO
&&
1031 Volume
->BlockIOOffset
== 0 &&
1032 Volume
->OSName
== NULL
)
1033 // this is a whole disk (MBR) entry; hide if we have entries for partitions
1034 HideIfOthersFound
= TRUE
;
1036 if (HideIfOthersFound
) {
1037 // check for other bootable entries on the same disk
1038 for (VolumeIndex2
= 0; VolumeIndex2
< VolumesCount
; VolumeIndex2
++) {
1039 if (VolumeIndex2
!= VolumeIndex
&& Volumes
[VolumeIndex2
]->HasBootCode
&&
1040 Volumes
[VolumeIndex2
]->WholeDiskBlockIO
== Volume
->WholeDiskBlockIO
)
1046 AddLegacyEntry(NULL
, Volume
);
1047 } // static VOID ScanLegacyVolume()
1049 // Scan attached optical discs for legacy (BIOS) boot code
1050 // and add anything found to the list....
1051 static VOID
ScanLegacyDisc(VOID
)
1054 REFIT_VOLUME
*Volume
;
1056 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1057 Volume
= Volumes
[VolumeIndex
];
1058 if (Volume
->DiskKind
== DISK_KIND_OPTICAL
)
1059 ScanLegacyVolume(Volume
, VolumeIndex
);
1061 } /* static VOID ScanLegacyDisc() */
1063 // Scan internal hard disks for legacy (BIOS) boot code
1064 // and add anything found to the list....
1065 static VOID
ScanLegacyInternal(VOID
)
1068 REFIT_VOLUME
*Volume
;
1070 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1071 Volume
= Volumes
[VolumeIndex
];
1072 if (Volume
->DiskKind
== DISK_KIND_INTERNAL
)
1073 ScanLegacyVolume(Volume
, VolumeIndex
);
1075 } /* static VOID ScanLegacyInternal() */
1077 // Scan external disks for legacy (BIOS) boot code
1078 // and add anything found to the list....
1079 static VOID
ScanLegacyExternal(VOID
)
1082 REFIT_VOLUME
*Volume
;
1084 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1085 Volume
= Volumes
[VolumeIndex
];
1086 if (Volume
->DiskKind
== DISK_KIND_EXTERNAL
)
1087 ScanLegacyVolume(Volume
, VolumeIndex
);
1089 } /* static VOID ScanLegacyExternal() */
1092 // pre-boot tool functions
1095 static VOID
StartTool(IN LOADER_ENTRY
*Entry
)
1097 BeginExternalScreen(Entry
->UseGraphicsMode
, Entry
->me
.Title
+ 6); // assumes "Start <title>" as assigned below
1098 StartEFIImage(Entry
->DevicePath
, Entry
->LoadOptions
, Basename(Entry
->LoaderPath
),
1099 Basename(Entry
->LoaderPath
), NULL
, TRUE
);
1100 FinishExternalScreen();
1101 } /* static VOID StartTool() */
1103 static LOADER_ENTRY
* AddToolEntry(IN CHAR16
*LoaderPath
, IN CHAR16
*LoaderTitle
, IN EG_IMAGE
*Image
,
1104 IN CHAR16 ShortcutLetter
, IN BOOLEAN UseGraphicsMode
)
1106 LOADER_ENTRY
*Entry
;
1108 Entry
= AllocateZeroPool(sizeof(LOADER_ENTRY
));
1110 Entry
->me
.Title
= PoolPrint(L
"Start %s", LoaderTitle
);
1111 Entry
->me
.Tag
= TAG_TOOL
;
1113 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1114 Entry
->me
.Image
= Image
;
1115 Entry
->LoaderPath
= StrDuplicate(LoaderPath
);
1116 Entry
->DevicePath
= FileDevicePath(SelfLoadedImage
->DeviceHandle
, Entry
->LoaderPath
);
1117 Entry
->UseGraphicsMode
= UseGraphicsMode
;
1119 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1121 } /* static LOADER_ENTRY * AddToolEntry() */
1124 // pre-boot driver functions
1127 static UINTN
ScanDriverDir(IN CHAR16
*Path
)
1130 REFIT_DIR_ITER DirIter
;
1132 EFI_FILE_INFO
*DirEntry
;
1133 CHAR16 FileName
[256];
1135 // look through contents of the directory
1136 DirIterOpen(SelfRootDir
, Path
, &DirIter
);
1137 while (DirIterNext(&DirIter
, 2, LOADER_MATCH_PATTERNS
, &DirEntry
)) {
1138 if (DirEntry
->FileName
[0] == '.')
1139 continue; // skip this
1141 SPrint(FileName
, 255, L
"%s\\%s", Path
, DirEntry
->FileName
);
1143 Status
= StartEFIImage(FileDevicePath(SelfLoadedImage
->DeviceHandle
, FileName
),
1144 L
"", DirEntry
->FileName
, DirEntry
->FileName
, NULL
, FALSE
);
1146 Status
= DirIterClose(&DirIter
);
1147 if (Status
!= EFI_NOT_FOUND
) {
1148 SPrint(FileName
, 255, L
"while scanning the %s directory", Path
);
1149 CheckError(Status
, FileName
);
1154 static EFI_STATUS
ConnectAllDriversToAllControllers(VOID
)
1157 UINTN AllHandleCount
;
1158 EFI_HANDLE
*AllHandleBuffer
;
1161 EFI_HANDLE
*HandleBuffer
;
1167 Status
= LibLocateHandle(AllHandles
,
1172 if (EFI_ERROR(Status
))
1175 for (Index
= 0; Index
< AllHandleCount
; Index
++) {
1177 // Scan the handle database
1179 Status
= LibScanHandleDatabase(NULL
,
1181 AllHandleBuffer
[Index
],
1186 if (EFI_ERROR (Status
))
1190 if (HandleType
[Index
] & EFI_HANDLE_TYPE_DRIVER_BINDING_HANDLE
)
1192 if (HandleType
[Index
] & EFI_HANDLE_TYPE_IMAGE_HANDLE
)
1197 for (HandleIndex
= 0; HandleIndex
< HandleCount
; HandleIndex
++) {
1198 if (HandleType
[HandleIndex
] & EFI_HANDLE_TYPE_PARENT_HANDLE
)
1203 if (HandleType
[Index
] & EFI_HANDLE_TYPE_DEVICE_HANDLE
) {
1204 Status
= refit_call4_wrapper(BS
->ConnectController
,
1205 AllHandleBuffer
[Index
],
1213 FreePool (HandleBuffer
);
1214 FreePool (HandleType
);
1218 FreePool (AllHandleBuffer
);
1220 } /* EFI_STATUS ConnectAllDriversToAllControllers() */
1222 static VOID
LoadDrivers(VOID
)
1225 UINTN i
= 0, Length
, NumFound
= 0;
1227 // load drivers from the "drivers" subdirectory of rEFInd's home directory
1228 Directory
= StrDuplicate(SelfDirPath
);
1229 MergeStrings(&Directory
, L
"drivers", L
'\\');
1230 NumFound
+= ScanDriverDir(Directory
);
1232 // Scan additional user-specified driver directories....
1233 while ((Directory
= FindCommaDelimited(GlobalConfig
.DriverDirs
, i
++)) != NULL
) {
1234 Length
= StrLen(Directory
);
1235 // Some EFI implementations won't read a directory if the path ends in
1236 // a backslash, so eliminate this character, if it's present....
1237 while ((Length
> 0) && (Directory
[Length
- 1] == L
'\\')) {
1238 Directory
[--Length
] = 0;
1241 NumFound
+= ScanDriverDir(Directory
);
1242 FreePool(Directory
);
1245 // connect all devices
1247 ConnectAllDriversToAllControllers();
1250 static VOID
ScanForBootloaders(VOID
) {
1254 // Commented-out below: Was part of an attempt to get rEFInd to
1255 // re-scan disk devices on pressing Esc; but doesn't work (yet), so
1257 // MainMenu.Title = StrDuplicate(L"Main Menu 2");
1258 // MainMenu.TitleImage = NULL;
1259 // MainMenu.InfoLineCount = 0;
1260 // MainMenu.InfoLines = NULL;
1261 // MainMenu.EntryCount = 0;
1262 // MainMenu.Entries = NULL;
1263 // MainMenu.TimeoutSeconds = 20;
1264 // MainMenu.TimeoutText = StrDuplicate(L"Automatic boot");
1267 // scan for loaders and tools, add them to the menu
1268 for (i
= 0; i
< NUM_SCAN_OPTIONS
; i
++) {
1269 switch(GlobalConfig
.ScanFor
[i
]) {
1274 ScanLegacyInternal();
1277 ScanLegacyExternal();
1280 ScanUserConfigured();
1294 // assign shortcut keys
1295 for (i
= 0; i
< MainMenu
.EntryCount
&& MainMenu
.Entries
[i
]->Row
== 0 && i
< 9; i
++)
1296 MainMenu
.Entries
[i
]->ShortcutDigit
= (CHAR16
)('1' + i
);
1298 // wait for user ACK when there were errors
1299 FinishTextScreen(FALSE
);
1300 } // static VOID ScanForBootloaders()
1302 // Add the second-row tags containing built-in and external tools (EFI shell,
1304 static VOID
ScanForTools(VOID
) {
1305 CHAR16
*FileName
= NULL
;
1308 for (i
= 0; i
< NUM_TOOLS
; i
++) {
1309 switch(GlobalConfig
.ShowTools
[i
]) {
1311 MenuEntryShutdown
.Image
= BuiltinIcon(BUILTIN_ICON_FUNC_SHUTDOWN
);
1312 AddMenuEntry(&MainMenu
, &MenuEntryShutdown
);
1315 MenuEntryReset
.Image
= BuiltinIcon(BUILTIN_ICON_FUNC_RESET
);
1316 AddMenuEntry(&MainMenu
, &MenuEntryReset
);
1319 MenuEntryAbout
.Image
= BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT
);
1320 AddMenuEntry(&MainMenu
, &MenuEntryAbout
);
1323 MenuEntryExit
.Image
= BuiltinIcon(BUILTIN_ICON_FUNC_EXIT
);
1324 AddMenuEntry(&MainMenu
, &MenuEntryExit
);
1328 while ((FileName
= FindCommaDelimited(SHELL_NAMES
, j
++)) != NULL
) {
1329 if (FileExists(SelfRootDir
, FileName
)) {
1330 AddToolEntry(FileName
, L
"EFI Shell", BuiltinIcon(BUILTIN_ICON_TOOL_SHELL
), 'E', FALSE
);
1335 MergeStrings(&FileName
, L
"\\efi\\tools\\gptsync.efi", 0);
1336 if (FileExists(SelfRootDir
, FileName
)) {
1337 AddToolEntry(FileName
, L
"Make Hybrid MBR", BuiltinIcon(BUILTIN_ICON_TOOL_PART
), 'P', FALSE
);
1341 if (FileName
!= NULL
) {
1346 } // static VOID ScanForTools
1354 efi_main (IN EFI_HANDLE ImageHandle
, IN EFI_SYSTEM_TABLE
*SystemTable
)
1357 BOOLEAN MainLoopRunning
= TRUE
;
1358 REFIT_MENU_ENTRY
*ChosenEntry
;
1362 InitializeLib(ImageHandle
, SystemTable
);
1364 Status
= InitRefitLib(ImageHandle
);
1365 if (EFI_ERROR(Status
))
1368 // read configuration
1369 CopyMem(GlobalConfig
.ScanFor
, "ieo ", NUM_SCAN_OPTIONS
);
1371 MainMenu
.TimeoutSeconds
= GlobalConfig
.Timeout
;
1373 // disable EFI watchdog timer
1374 refit_call4_wrapper(BS
->SetWatchdogTimer
, 0x0000, 0x0000, 0x0000, NULL
);
1376 // further bootstrap (now with config available)
1379 ScanForBootloaders();
1382 while (MainLoopRunning
) {
1383 MenuExit
= RunMainMenu(&MainMenu
, GlobalConfig
.DefaultSelection
, &ChosenEntry
);
1385 // We don't allow exiting the main menu with the Escape key.
1386 if (MenuExit
== MENU_EXIT_ESCAPE
) {
1387 // Commented-out below: Was part of an attempt to get rEFInd to
1388 // re-scan disk devices on pressing Esc; but doesn't work (yet), so
1391 // ScanForBootloaders();
1396 switch (ChosenEntry
->Tag
) {
1398 case TAG_REBOOT
: // Reboot
1400 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);
1401 MainLoopRunning
= FALSE
; // just in case we get this far
1404 case TAG_SHUTDOWN
: // Shut Down
1406 refit_call4_wrapper(RT
->ResetSystem
, EfiResetShutdown
, EFI_SUCCESS
, 0, NULL
);
1407 MainLoopRunning
= FALSE
; // just in case we get this far
1410 case TAG_ABOUT
: // About rEFInd
1414 case TAG_LOADER
: // Boot OS via .EFI loader
1415 StartLoader((LOADER_ENTRY
*)ChosenEntry
);
1418 case TAG_LEGACY
: // Boot legacy OS
1419 StartLegacy((LEGACY_ENTRY
*)ChosenEntry
);
1422 case TAG_TOOL
: // Start a EFI tool
1423 StartTool((LOADER_ENTRY
*)ChosenEntry
);
1426 case TAG_EXIT
: // Terminate rEFInd
1427 BeginTextScreen(L
" ");
1434 // If we end up here, things have gone wrong. Try to reboot, and if that
1435 // fails, go into an endless loop.
1436 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);