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
, 0, 0, 20, 0, 0, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
89 {TAG_SHELL
, TAG_ABOUT
, TAG_SHUTDOWN
, TAG_REBOOT
, 0, 0, 0, 0, 0 }};
91 // Structure used to hold boot loader filenames and time stamps in
92 // a linked list; used to sort entries within a directory.
96 struct LOADER_LIST
*NextEntry
;
103 static VOID
AboutrEFInd(VOID
)
105 if (AboutMenu
.EntryCount
== 0) {
106 AboutMenu
.TitleImage
= BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT
);
107 AddMenuInfoLine(&AboutMenu
, L
"rEFInd Version 0.3.0.2");
108 AddMenuInfoLine(&AboutMenu
, L
"");
109 AddMenuInfoLine(&AboutMenu
, L
"Copyright (c) 2006-2010 Christoph Pfisterer");
110 AddMenuInfoLine(&AboutMenu
, L
"Copyright (c) 2012 Roderick W. Smith");
111 AddMenuInfoLine(&AboutMenu
, L
"Portions Copyright (c) Intel Corporation and others");
112 AddMenuInfoLine(&AboutMenu
, L
"Distributed under the terms of the GNU GPLv3 license");
113 AddMenuInfoLine(&AboutMenu
, L
"");
114 AddMenuInfoLine(&AboutMenu
, L
"Running on:");
115 AddMenuInfoLine(&AboutMenu
, PoolPrint(L
" EFI Revision %d.%02d",
116 ST
->Hdr
.Revision
>> 16, ST
->Hdr
.Revision
& ((1 << 16) - 1)));
118 AddMenuInfoLine(&AboutMenu
, L
" Platform: x86 (32 bit)");
119 #elif defined(EFIX64)
120 AddMenuInfoLine(&AboutMenu
, L
" Platform: x86_64 (64 bit)");
122 AddMenuInfoLine(&AboutMenu
, L
" Platform: unknown");
124 AddMenuInfoLine(&AboutMenu
, PoolPrint(L
" Firmware: %s %d.%02d",
125 ST
->FirmwareVendor
, ST
->FirmwareRevision
>> 16, ST
->FirmwareRevision
& ((1 << 16) - 1)));
126 AddMenuInfoLine(&AboutMenu
, PoolPrint(L
" Screen Output: %s", egScreenDescription()));
127 AddMenuInfoLine(&AboutMenu
, L
"");
128 AddMenuInfoLine(&AboutMenu
, L
"For more information, see the rEFInd Web site:");
129 AddMenuInfoLine(&AboutMenu
, L
"http://www.rodsbooks.com/refind/");
130 AddMenuEntry(&AboutMenu
, &MenuEntryReturn
);
133 RunMenu(&AboutMenu
, NULL
);
134 } /* VOID AboutrEFInd() */
136 static EFI_STATUS
StartEFIImageList(IN EFI_DEVICE_PATH
**DevicePaths
,
137 IN CHAR16
*LoadOptions
, IN CHAR16
*LoadOptionsPrefix
,
138 IN CHAR16
*ImageTitle
,
139 OUT UINTN
*ErrorInStep
,
142 EFI_STATUS Status
, ReturnStatus
;
143 EFI_HANDLE ChildImageHandle
;
144 EFI_LOADED_IMAGE
*ChildLoadedImage
;
145 UINTN DevicePathIndex
;
146 CHAR16 ErrorInfo
[256];
147 CHAR16
*FullLoadOptions
= NULL
;
150 Print(L
"Starting %s\n", ImageTitle
);
151 if (ErrorInStep
!= NULL
)
154 // load the image into memory
155 ReturnStatus
= Status
= EFI_NOT_FOUND
; // in case the list is empty
156 for (DevicePathIndex
= 0; DevicePaths
[DevicePathIndex
] != NULL
; DevicePathIndex
++) {
157 ReturnStatus
= Status
= refit_call6_wrapper(BS
->LoadImage
, FALSE
, SelfImageHandle
, DevicePaths
[DevicePathIndex
], NULL
, 0, &ChildImageHandle
);
158 if (ReturnStatus
!= EFI_NOT_FOUND
)
161 SPrint(ErrorInfo
, 255, L
"while loading %s", ImageTitle
);
162 if (CheckError(Status
, ErrorInfo
)) {
163 if (ErrorInStep
!= NULL
)
169 if (LoadOptions
!= NULL
) {
170 ReturnStatus
= Status
= refit_call3_wrapper(BS
->HandleProtocol
, ChildImageHandle
, &LoadedImageProtocol
, (VOID
**) &ChildLoadedImage
);
171 if (CheckError(Status
, L
"while getting a LoadedImageProtocol handle")) {
172 if (ErrorInStep
!= NULL
)
177 if (LoadOptionsPrefix
!= NULL
) {
178 FullLoadOptions
= PoolPrint(L
"%s %s ", LoadOptionsPrefix
, LoadOptions
);
179 // NOTE: That last space is also added by the EFI shell and seems to be significant
180 // when passing options to Apple's boot.efi...
181 LoadOptions
= FullLoadOptions
;
183 // NOTE: We also include the terminating null in the length for safety.
184 ChildLoadedImage
->LoadOptions
= (VOID
*)LoadOptions
;
185 ChildLoadedImage
->LoadOptionsSize
= ((UINT32
)StrLen(LoadOptions
) + 1) * sizeof(CHAR16
);
187 Print(L
"Using load options '%s'\n", LoadOptions
);
190 // close open file handles
193 // turn control over to the image
194 // TODO: (optionally) re-enable the EFI watchdog timer!
195 ReturnStatus
= Status
= refit_call3_wrapper(BS
->StartImage
, ChildImageHandle
, NULL
, NULL
);
196 // control returns here when the child image calls Exit()
197 SPrint(ErrorInfo
, 255, L
"returned from %s", ImageTitle
);
198 if (CheckError(Status
, ErrorInfo
)) {
199 if (ErrorInStep
!= NULL
)
203 // re-open file handles
207 // unload the image, we don't care if it works or not...
208 Status
= refit_call1_wrapper(BS
->UnloadImage
, ChildImageHandle
);
210 if (FullLoadOptions
!= NULL
)
211 FreePool(FullLoadOptions
);
213 } /* static EFI_STATUS StartEFIImageList() */
215 static EFI_STATUS
StartEFIImage(IN EFI_DEVICE_PATH
*DevicePath
,
216 IN CHAR16
*LoadOptions
, IN CHAR16
*LoadOptionsPrefix
,
217 IN CHAR16
*ImageTitle
,
218 OUT UINTN
*ErrorInStep
,
221 EFI_DEVICE_PATH
*DevicePaths
[2];
223 DevicePaths
[0] = DevicePath
;
224 DevicePaths
[1] = NULL
;
225 return StartEFIImageList(DevicePaths
, LoadOptions
, LoadOptionsPrefix
, ImageTitle
, ErrorInStep
, Verbose
);
226 } /* static EFI_STATUS StartEFIImage() */
229 // EFI OS loader functions
232 static VOID
StartLoader(IN LOADER_ENTRY
*Entry
)
234 UINTN ErrorInStep
= 0;
236 BeginExternalScreen(Entry
->UseGraphicsMode
, L
"Booting OS");
237 StartEFIImage(Entry
->DevicePath
, Entry
->LoadOptions
,
238 Basename(Entry
->LoaderPath
), Basename(Entry
->LoaderPath
), &ErrorInStep
, TRUE
);
239 FinishExternalScreen();
242 // Locate an initrd or initramfs file that matches the kernel specified by LoaderPath.
243 // The matching file has a name that begins with "init" and includes the same version
244 // number string as is found in LoaderPath -- but not a longer version number string.
245 // For instance, if LoaderPath is \EFI\kernels\bzImage-3.3.0.efi, and if \EFI\kernels
246 // has a file called initramfs-3.3.0.img, this function will return the string
247 // '\EFI\kernels\initramfs-3.3.0.img'. If the directory ALSO contains the file
248 // initramfs-3.3.0-rc7.img or initramfs-13.3.0.img, those files will NOT match;
249 // however, initmine-3.3.0.img might match. (FindInitrd() returns the first match it
250 // finds). Thus, care should be taken to avoid placing duplicate matching files in
251 // the kernel's directory.
252 // If no matching init file can be found, returns NULL.
253 static CHAR16
* FindInitrd(IN CHAR16
*LoaderPath
, IN REFIT_VOLUME
*Volume
) {
254 CHAR16
*InitrdName
= NULL
, *FileName
, *KernelVersion
, *InitrdVersion
, *Path
;
255 REFIT_DIR_ITER DirIter
;
256 EFI_FILE_INFO
*DirEntry
;
258 FileName
= Basename(LoaderPath
);
259 KernelVersion
= FindNumbers(FileName
);
260 Path
= FindPath(LoaderPath
);
262 // Add trailing backslash for root directory; necessary on some systems, but must
263 // NOT be added to all directories, since on other systems, a trailing backslash on
264 // anything but the root directory causes them to flake out!
265 if (StrLen(Path
) == 0) {
266 MergeStrings(&Path
, L
"\\", 0);
268 DirIterOpen(Volume
->RootDir
, Path
, &DirIter
);
269 // Now add a trailing backslash if it was NOT added earlier, for consistency in
270 // building the InitrdName later....
271 if ((StrLen(Path
) > 0) && (Path
[StrLen(Path
) - 1] != L
'\\'))
272 MergeStrings(&Path
, L
"\\", 0);
273 while ((DirIterNext(&DirIter
, 2, L
"init*", &DirEntry
)) && (InitrdName
== NULL
)) {
274 InitrdVersion
= FindNumbers(DirEntry
->FileName
);
275 if (KernelVersion
!= NULL
) {
276 if (StriCmp(InitrdVersion
, KernelVersion
) == 0)
277 InitrdName
= PoolPrint(L
"%s%s", Path
, DirEntry
->FileName
);
279 if (InitrdVersion
== NULL
)
280 InitrdName
= PoolPrint(L
"%s%s", Path
, DirEntry
->FileName
);
282 if (InitrdVersion
!= NULL
)
283 FreePool(InitrdVersion
);
285 DirIterClose(&DirIter
);
287 // Note: Don't FreePool(FileName), since Basename returns a pointer WITHIN the string it's passed.
288 FreePool(KernelVersion
);
291 } // static CHAR16 * FindInitrd()
293 LOADER_ENTRY
* AddPreparedLoaderEntry(LOADER_ENTRY
*Entry
) {
294 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
297 } // LOADER_ENTRY * AddPreparedLoaderEntry()
299 // Creates a new LOADER_ENTRY data structure and populates it with
300 // default values from the specified Entry, or NULL values if Entry
301 // is unspecified (NULL).
302 // Returns a pointer to the new data structure, or NULL if it
303 // couldn't be allocated
304 LOADER_ENTRY
*InitializeLoaderEntry(IN LOADER_ENTRY
*Entry
) {
305 LOADER_ENTRY
*NewEntry
= NULL
;
307 NewEntry
= AllocateZeroPool(sizeof(LOADER_ENTRY
));
308 if (NewEntry
!= NULL
) {
309 NewEntry
->me
.Title
= NULL
;
310 NewEntry
->me
.Tag
= TAG_LOADER
;
311 NewEntry
->Enabled
= TRUE
;
312 NewEntry
->UseGraphicsMode
= FALSE
;
313 NewEntry
->OSType
= 0;
315 NewEntry
->LoaderPath
= StrDuplicate(Entry
->LoaderPath
);
316 NewEntry
->VolName
= StrDuplicate(Entry
->VolName
);
317 NewEntry
->DevicePath
= Entry
->DevicePath
;
318 NewEntry
->UseGraphicsMode
= Entry
->UseGraphicsMode
;
319 NewEntry
->LoadOptions
= StrDuplicate(Entry
->LoadOptions
);
320 NewEntry
->InitrdPath
= StrDuplicate(Entry
->InitrdPath
);
324 } // LOADER_ENTRY *InitializeLoaderEntry()
326 // Prepare a REFIT_MENU_SCREEN data structure for a subscreen entry. This sets up
327 // the default entry that launches the boot loader using the same options as the
328 // main Entry does. Subsequent options can be added by the calling function.
329 // If a subscreen already exists in the Entry that's passed to this function,
330 // it's left unchanged and a pointer to it is returned.
331 // Returns a pointer to the new subscreen data structure, or NULL if there
332 // were problems allocating memory.
333 REFIT_MENU_SCREEN
*InitializeSubScreen(IN LOADER_ENTRY
*Entry
) {
334 CHAR16
*FileName
, *Temp
;
335 REFIT_MENU_SCREEN
*SubScreen
= NULL
;
336 LOADER_ENTRY
*SubEntry
;
338 FileName
= Basename(Entry
->LoaderPath
);
339 if (Entry
->me
.SubScreen
== NULL
) { // No subscreen yet; initialize default entry....
340 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
341 if (SubScreen
!= NULL
) {
342 SubScreen
->Title
= PoolPrint(L
"Boot Options for %s on %s", (Entry
->Title
!= NULL
) ? Entry
->Title
: FileName
, Entry
->VolName
);
343 SubScreen
->TitleImage
= Entry
->me
.Image
;
345 SubEntry
= InitializeLoaderEntry(Entry
);
346 if (SubEntry
!= NULL
) {
347 SubEntry
->me
.Title
= L
"Boot using default options";
348 if ((SubEntry
->InitrdPath
!= NULL
) && (StrLen(SubEntry
->InitrdPath
) > 0) && (!StriSubCmp(L
"initrd", SubEntry
->LoadOptions
))) {
349 Temp
= PoolPrint(L
"initrd=%s", SubEntry
->InitrdPath
);
350 MergeStrings(&SubEntry
->LoadOptions
, Temp
, L
' ');
353 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
354 } // if (SubEntry != NULL)
355 } // if (SubScreen != NULL)
356 } else { // existing subscreen; less initialization, and just add new entry later....
357 SubScreen
= Entry
->me
.SubScreen
;
360 } // REFIT_MENU_SCREEN *InitializeSubScreen()
362 VOID
GenerateSubScreen(LOADER_ENTRY
*Entry
, IN REFIT_VOLUME
*Volume
) {
363 REFIT_MENU_SCREEN
*SubScreen
;
364 LOADER_ENTRY
*SubEntry
;
365 CHAR16
*FileName
, *InitrdOption
= NULL
, *Temp
;
366 CHAR16 DiagsFileName
[256];
371 FileName
= Basename(Entry
->LoaderPath
);
372 // create the submenu
373 if (StrLen(Entry
->Title
) == 0) {
374 FreePool(Entry
->Title
);
377 SubScreen
= InitializeSubScreen(Entry
);
379 // loader-specific submenu entries
380 if (Entry
->OSType
== 'M') { // entries for Mac OS X
382 SubEntry
= InitializeLoaderEntry(Entry
);
383 if (SubEntry
!= NULL
) {
384 SubEntry
->me
.Title
= L
"Boot Mac OS X with a 64-bit kernel";
385 SubEntry
->LoadOptions
= L
"arch=x86_64";
386 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
389 SubEntry
= InitializeLoaderEntry(Entry
);
390 if (SubEntry
!= NULL
) {
391 SubEntry
->me
.Title
= L
"Boot Mac OS X with a 32-bit kernel";
392 SubEntry
->LoadOptions
= L
"arch=i386";
393 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
397 if (!(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_SINGLEUSER
)) {
398 SubEntry
= InitializeLoaderEntry(Entry
);
399 if (SubEntry
!= NULL
) {
400 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode";
401 SubEntry
->UseGraphicsMode
= FALSE
;
402 SubEntry
->LoadOptions
= L
"-v";
403 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 (64-bit)";
410 SubEntry
->UseGraphicsMode
= FALSE
;
411 SubEntry
->LoadOptions
= L
"-v arch=x86_64";
412 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
415 SubEntry
= InitializeLoaderEntry(Entry
);
416 if (SubEntry
!= NULL
) {
417 SubEntry
->me
.Title
= L
"Boot Mac OS X in verbose mode (32-bit)";
418 SubEntry
->UseGraphicsMode
= FALSE
;
419 SubEntry
->LoadOptions
= L
"-v arch=i386";
420 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
424 SubEntry
= InitializeLoaderEntry(Entry
);
425 if (SubEntry
!= NULL
) {
426 SubEntry
->me
.Title
= L
"Boot Mac OS X in single user mode";
427 SubEntry
->UseGraphicsMode
= FALSE
;
428 SubEntry
->LoadOptions
= L
"-v -s";
429 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
433 // check for Apple hardware diagnostics
434 StrCpy(DiagsFileName
, L
"\\System\\Library\\CoreServices\\.diagnostics\\diags.efi");
435 if (FileExists(Volume
->RootDir
, DiagsFileName
) && !(GlobalConfig
.HideUIFlags
& HIDEUI_FLAG_HWTEST
)) {
436 SubEntry
= InitializeLoaderEntry(Entry
);
437 if (SubEntry
!= NULL
) {
438 SubEntry
->me
.Title
= L
"Run Apple Hardware Test";
439 FreePool(SubEntry
->LoaderPath
);
440 SubEntry
->LoaderPath
= StrDuplicate(DiagsFileName
);
441 SubEntry
->DevicePath
= FileDevicePath(Volume
->DeviceHandle
, SubEntry
->LoaderPath
);
442 SubEntry
->UseGraphicsMode
= TRUE
;
443 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
445 } // if diagnostics entry found
447 } else if (Entry
->OSType
== 'L') { // entries for Linux kernels with EFI stub loaders
448 File
= ReadLinuxOptionsFile(Entry
->LoaderPath
, Volume
);
450 if ((Temp
= FindInitrd(Entry
->LoaderPath
, Volume
)) != NULL
)
451 InitrdOption
= PoolPrint(L
"initrd=%s", Temp
);
452 TokenCount
= ReadTokenLine(File
, &TokenList
); // read and discard first entry, since it's
453 FreeTokenLine(&TokenList
, &TokenCount
); // set up by InitializeSubScreen(), earlier....
454 while ((TokenCount
= ReadTokenLine(File
, &TokenList
)) > 1) {
455 SubEntry
= InitializeLoaderEntry(Entry
);
456 SubEntry
->me
.Title
= StrDuplicate(TokenList
[0]);
457 if (SubEntry
->LoadOptions
!= NULL
)
458 FreePool(SubEntry
->LoadOptions
);
459 SubEntry
->LoadOptions
= StrDuplicate(TokenList
[1]);
460 MergeStrings(&SubEntry
->LoadOptions
, InitrdOption
, L
' ');
461 FreeTokenLine(&TokenList
, &TokenCount
);
462 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
465 FreePool(InitrdOption
);
469 } // if Linux options file exists
471 } else if (Entry
->OSType
== 'E') { // entries for ELILO
472 SubEntry
= InitializeLoaderEntry(Entry
);
473 if (SubEntry
!= NULL
) {
474 SubEntry
->me
.Title
= PoolPrint(L
"Run %s in interactive mode", FileName
);
475 SubEntry
->LoadOptions
= L
"-p";
476 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
479 SubEntry
= InitializeLoaderEntry(Entry
);
480 if (SubEntry
!= NULL
) {
481 SubEntry
->me
.Title
= L
"Boot Linux for a 17\" iMac or a 15\" MacBook Pro (*)";
482 SubEntry
->UseGraphicsMode
= TRUE
;
483 SubEntry
->LoadOptions
= L
"-d 0 i17";
484 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
487 SubEntry
= InitializeLoaderEntry(Entry
);
488 if (SubEntry
!= NULL
) {
489 SubEntry
->me
.Title
= L
"Boot Linux for a 20\" iMac (*)";
490 SubEntry
->UseGraphicsMode
= TRUE
;
491 SubEntry
->LoadOptions
= L
"-d 0 i20";
492 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
495 SubEntry
= InitializeLoaderEntry(Entry
);
496 if (SubEntry
!= NULL
) {
497 SubEntry
->me
.Title
= L
"Boot Linux for a Mac Mini (*)";
498 SubEntry
->UseGraphicsMode
= TRUE
;
499 SubEntry
->LoadOptions
= L
"-d 0 mini";
500 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
503 AddMenuInfoLine(SubScreen
, L
"NOTE: This is an example. Entries");
504 AddMenuInfoLine(SubScreen
, L
"marked with (*) may not work.");
506 } else if (Entry
->OSType
== 'X') { // entries for xom.efi
507 // by default, skip the built-in selection and boot from hard disk only
508 Entry
->LoadOptions
= L
"-s -h";
510 SubEntry
= InitializeLoaderEntry(Entry
);
511 if (SubEntry
!= NULL
) {
512 SubEntry
->me
.Title
= L
"Boot Windows from Hard Disk";
513 SubEntry
->LoadOptions
= L
"-s -h";
514 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
517 SubEntry
= InitializeLoaderEntry(Entry
);
518 if (SubEntry
!= NULL
) {
519 SubEntry
->me
.Title
= L
"Boot Windows from CD-ROM";
520 SubEntry
->LoadOptions
= L
"-s -c";
521 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
524 SubEntry
= InitializeLoaderEntry(Entry
);
525 if (SubEntry
!= NULL
) {
526 SubEntry
->me
.Title
= PoolPrint(L
"Run %s in text mode", FileName
);
527 SubEntry
->UseGraphicsMode
= FALSE
;
528 SubEntry
->LoadOptions
= L
"-v";
529 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
531 } // entries for xom.efi
532 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
533 Entry
->me
.SubScreen
= SubScreen
;
534 } // VOID GenerateSubScreen()
536 // Returns options for a Linux kernel. Reads them from an options file in the
537 // kernel's directory; and if present, adds an initrd= option for an initial
538 // RAM disk file with the same version number as the kernel file.
539 static CHAR16
* GetMainLinuxOptions(IN CHAR16
* LoaderPath
, IN REFIT_VOLUME
*Volume
) {
540 CHAR16
*Options
= NULL
, *InitrdName
, *InitrdOption
= NULL
;
542 Options
= GetFirstOptionsFromFile(LoaderPath
, Volume
);
543 InitrdName
= FindInitrd(LoaderPath
, Volume
);
544 if (InitrdName
!= NULL
)
545 InitrdOption
= PoolPrint(L
"initrd=%s", InitrdName
);
546 MergeStrings(&Options
, InitrdOption
, ' ');
547 if (InitrdOption
!= NULL
)
548 FreePool(InitrdOption
);
549 if (InitrdName
!= NULL
)
550 FreePool(InitrdName
);
552 } // static CHAR16 * GetMainLinuxOptions()
554 // Sets a few defaults for a loader entry -- mainly the icon, but also the OS type
555 // code and shortcut letter. For Linux EFI stub loaders, also sets kernel options
556 // that will (with luck) work fairly automatically.
557 VOID
SetLoaderDefaults(LOADER_ENTRY
*Entry
, CHAR16
*LoaderPath
, IN REFIT_VOLUME
*Volume
) {
558 CHAR16 IconFileName
[256];
559 CHAR16
*FileName
, *PathOnly
, *OSIconName
= NULL
, *Temp
;
560 CHAR16 ShortcutLetter
= 0;
562 FileName
= Basename(LoaderPath
);
563 PathOnly
= FindPath(LoaderPath
);
565 // locate a custom icon for the loader
566 StrCpy(IconFileName
, LoaderPath
);
567 ReplaceEfiExtension(IconFileName
, L
".icns");
568 if (FileExists(Volume
->RootDir
, IconFileName
)) {
569 Entry
->me
.Image
= LoadIcns(Volume
->RootDir
, IconFileName
, 128);
570 } else if ((StrLen(PathOnly
) == 0) && (Volume
->VolIconImage
!= NULL
)) {
571 Entry
->me
.Image
= Volume
->VolIconImage
;
572 } // icon matched to loader or volume
574 Temp
= FindLastDirName(LoaderPath
);
575 MergeStrings(&OSIconName
, Temp
, L
',');
577 if (OSIconName
!= NULL
) {
578 ShortcutLetter
= OSIconName
[0];
581 // detect specific loaders
582 if (StriSubCmp(L
"bzImage", LoaderPath
) || StriSubCmp(L
"vmlinuz", LoaderPath
)) {
583 MergeStrings(&OSIconName
, L
"linux", L
',');
585 if (ShortcutLetter
== 0)
586 ShortcutLetter
= 'L';
587 Entry
->LoadOptions
= GetMainLinuxOptions(LoaderPath
, Volume
);
588 } else if (StriSubCmp(L
"refit", LoaderPath
)) {
589 MergeStrings(&OSIconName
, L
"refit", L
',');
591 ShortcutLetter
= 'R';
592 } else if (StriCmp(LoaderPath
, MACOSX_LOADER_PATH
) == 0) {
593 if (Volume
->VolIconImage
!= NULL
) { // custom icon file found
594 Entry
->me
.Image
= Volume
->VolIconImage
;
596 MergeStrings(&OSIconName
, L
"mac", L
',');
597 Entry
->UseGraphicsMode
= TRUE
;
599 ShortcutLetter
= 'M';
600 } else if (StriCmp(FileName
, L
"diags.efi") == 0) {
601 MergeStrings(&OSIconName
, L
"hwtest", L
',');
602 } else if (StriCmp(FileName
, L
"e.efi") == 0 || StriCmp(FileName
, L
"elilo.efi") == 0) {
603 MergeStrings(&OSIconName
, L
"elilo,linux", L
',');
605 if (ShortcutLetter
== 0)
606 ShortcutLetter
= 'L';
607 } else if (StriCmp(FileName
, L
"cdboot.efi") == 0 ||
608 StriCmp(FileName
, L
"bootmgr.efi") == 0 ||
609 StriCmp(FileName
, L
"Bootmgfw.efi") == 0) {
610 MergeStrings(&OSIconName
, L
"win", L
',');
612 ShortcutLetter
= 'W';
613 } else if (StriCmp(FileName
, L
"xom.efi") == 0) {
614 MergeStrings(&OSIconName
, L
"xom,win", L
',');
615 Entry
->UseGraphicsMode
= TRUE
;
617 ShortcutLetter
= 'W';
620 if ((ShortcutLetter
>= 'a') && (ShortcutLetter
<= 'z'))
621 ShortcutLetter
= ShortcutLetter
- 'a' + 'A'; // convert lowercase to uppercase
622 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
623 if (Entry
->me
.Image
== NULL
)
624 Entry
->me
.Image
= LoadOSIcon(OSIconName
, L
"unknown", FALSE
);
625 if (PathOnly
!= NULL
)
627 } // VOID SetLoaderDefaults()
629 // Add a specified EFI boot loader to the list, using automatic settings
630 // for icons, options, etc.
631 LOADER_ENTRY
* AddLoaderEntry(IN CHAR16
*LoaderPath
, IN CHAR16
*LoaderTitle
, IN REFIT_VOLUME
*Volume
) {
634 CleanUpPathNameSlashes(LoaderPath
);
635 Entry
= InitializeLoaderEntry(NULL
);
637 Entry
->Title
= StrDuplicate((LoaderTitle
!= NULL
) ? LoaderTitle
: LoaderPath
);
638 // Entry->Title = StrDuplicate(LoaderTitle);
639 Entry
->me
.Title
= PoolPrint(L
"Boot %s from %s", (LoaderTitle
!= NULL
) ? LoaderTitle
: LoaderPath
, Volume
->VolName
);
641 Entry
->me
.BadgeImage
= Volume
->VolBadgeImage
;
642 Entry
->LoaderPath
= StrDuplicate(LoaderPath
);
643 Entry
->VolName
= Volume
->VolName
;
644 Entry
->DevicePath
= FileDevicePath(Volume
->DeviceHandle
, Entry
->LoaderPath
);
645 SetLoaderDefaults(Entry
, LoaderPath
, Volume
);
646 GenerateSubScreen(Entry
, Volume
);
647 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
651 } // LOADER_ENTRY * AddLoaderEntry()
653 // Returns -1 if (Time1 < Time2), +1 if (Time1 > Time2), or 0 if
654 // (Time1 == Time2). Precision is only to the nearest second; since
655 // this is used for sorting boot loader entries, differences smaller
656 // than this are likely to be meaningless (and unlikely!).
657 INTN
TimeComp(EFI_TIME
*Time1
, EFI_TIME
*Time2
) {
658 INT64 Time1InSeconds
, Time2InSeconds
;
660 // Following values are overestimates; I'm assuming 31 days in every month.
661 // This is fine for the purpose of this function, which has a limited
663 Time1InSeconds
= Time1
->Second
+ (Time1
->Minute
* 60) + (Time1
->Hour
* 3600) + (Time1
->Day
* 86400) +
664 (Time1
->Month
* 2678400) + ((Time1
->Year
- 1998) * 32140800);
665 Time2InSeconds
= Time2
->Second
+ (Time2
->Minute
* 60) + (Time2
->Hour
* 3600) + (Time2
->Day
* 86400) +
666 (Time2
->Month
* 2678400) + ((Time2
->Year
- 1998) * 32140800);
667 if (Time1InSeconds
< Time2InSeconds
)
669 else if (Time1InSeconds
> Time2InSeconds
)
675 // Adds a loader list element, keeping it sorted by date. Returns the new
676 // first element (the one with the most recent date).
677 static struct LOADER_LIST
* AddLoaderListEntry(struct LOADER_LIST
*LoaderList
, struct LOADER_LIST
*NewEntry
) {
678 struct LOADER_LIST
*LatestEntry
, *CurrentEntry
, *PrevEntry
= NULL
;
680 LatestEntry
= CurrentEntry
= LoaderList
;
681 if (LoaderList
== NULL
) {
682 LatestEntry
= NewEntry
;
684 while ((CurrentEntry
!= NULL
) && (TimeComp(&(NewEntry
->TimeStamp
), &(CurrentEntry
->TimeStamp
)) < 0)) {
685 PrevEntry
= CurrentEntry
;
686 CurrentEntry
= CurrentEntry
->NextEntry
;
688 NewEntry
->NextEntry
= CurrentEntry
;
689 if (PrevEntry
== NULL
) {
690 LatestEntry
= NewEntry
;
692 PrevEntry
->NextEntry
= NewEntry
;
695 return (LatestEntry
);
696 } // static VOID AddLoaderListEntry()
698 // Delete the LOADER_LIST linked list
699 static VOID
CleanUpLoaderList(struct LOADER_LIST
*LoaderList
) {
700 struct LOADER_LIST
*Temp
;
702 while (LoaderList
!= NULL
) {
704 LoaderList
= LoaderList
->NextEntry
;
705 FreePool(Temp
->FileName
);
708 } // static VOID CleanUpLoaderList()
710 // Scan an individual directory for EFI boot loader files and, if found,
711 // add them to the list. Sorts the entries within the loader directory
712 // so that the most recent one appears first in the list.
713 static VOID
ScanLoaderDir(IN REFIT_VOLUME
*Volume
, IN CHAR16
*Path
, IN CHAR16
*Pattern
)
716 REFIT_DIR_ITER DirIter
;
717 EFI_FILE_INFO
*DirEntry
;
718 CHAR16 FileName
[256], *Extension
;
719 struct LOADER_LIST
*LoaderList
= NULL
, *NewLoader
;
721 if (!SelfDirPath
|| !Path
|| ((StriCmp(Path
, SelfDirPath
) == 0) && Volume
!= SelfVolume
) ||
722 (StriCmp(Path
, SelfDirPath
) != 0)) {
723 // look through contents of the directory
724 DirIterOpen(Volume
->RootDir
, Path
, &DirIter
);
725 while (DirIterNext(&DirIter
, 2, Pattern
, &DirEntry
)) {
726 Extension
= FindExtension(DirEntry
->FileName
);
727 if (DirEntry
->FileName
[0] == '.' ||
728 StriCmp(DirEntry
->FileName
, L
"TextMode.efi") == 0 ||
729 StriCmp(DirEntry
->FileName
, L
"ebounce.efi") == 0 ||
730 StriCmp(DirEntry
->FileName
, L
"GraphicsConsole.efi") == 0 ||
731 StriCmp(Extension
, L
".icns") == 0 ||
732 StriSubCmp(L
"shell", DirEntry
->FileName
))
733 continue; // skip this
736 SPrint(FileName
, 255, L
"\\%s\\%s", Path
, DirEntry
->FileName
);
738 SPrint(FileName
, 255, L
"\\%s", DirEntry
->FileName
);
739 NewLoader
= AllocateZeroPool(sizeof(struct LOADER_LIST
));
740 if (NewLoader
!= NULL
) {
741 NewLoader
->FileName
= StrDuplicate(FileName
);
742 NewLoader
->TimeStamp
= DirEntry
->ModificationTime
;
743 LoaderList
= AddLoaderListEntry(LoaderList
, NewLoader
);
747 NewLoader
= LoaderList
;
748 while (NewLoader
!= NULL
) {
749 AddLoaderEntry(NewLoader
->FileName
, NULL
, Volume
);
750 NewLoader
= NewLoader
->NextEntry
;
752 CleanUpLoaderList(LoaderList
);
753 Status
= DirIterClose(&DirIter
);
754 if (Status
!= EFI_NOT_FOUND
) {
756 SPrint(FileName
, 255, L
"while scanning the %s directory", Path
);
758 StrCpy(FileName
, L
"while scanning the root directory");
759 CheckError(Status
, FileName
);
760 } // if (Status != EFI_NOT_FOUND)
761 } // if not scanning our own directory
762 } /* static VOID ScanLoaderDir() */
764 static VOID
ScanEfiFiles(REFIT_VOLUME
*Volume
) {
766 REFIT_DIR_ITER EfiDirIter
;
767 EFI_FILE_INFO
*EfiDirEntry
;
768 CHAR16 FileName
[256], *Directory
, *MatchPatterns
;
771 MatchPatterns
= StrDuplicate(LOADER_MATCH_PATTERNS
);
772 if (GlobalConfig
.ScanAllLinux
)
773 MergeStrings(&MatchPatterns
, LINUX_MATCH_PATTERNS
, L
',');
775 if ((Volume
->RootDir
!= NULL
) && (Volume
->VolName
!= NULL
)) {
776 // check for Mac OS X boot loader
777 StrCpy(FileName
, MACOSX_LOADER_PATH
);
778 if (FileExists(Volume
->RootDir
, FileName
)) {
779 AddLoaderEntry(FileName
, L
"Mac OS X", Volume
);
783 StrCpy(FileName
, L
"System\\Library\\CoreServices\\xom.efi");
784 if (FileExists(Volume
->RootDir
, FileName
)) {
785 AddLoaderEntry(FileName
, L
"Windows XP (XoM)", Volume
);
788 // check for Microsoft boot loader/menu
789 StrCpy(FileName
, L
"EFI\\Microsoft\\Boot\\Bootmgfw.efi");
790 if (FileExists(Volume
->RootDir
, FileName
)) {
791 AddLoaderEntry(FileName
, L
"Microsoft EFI boot", Volume
);
794 // scan the root directory for EFI executables
795 ScanLoaderDir(Volume
, NULL
, MatchPatterns
);
797 // scan subdirectories of the EFI directory (as per the standard)
798 DirIterOpen(Volume
->RootDir
, L
"EFI", &EfiDirIter
);
799 while (DirIterNext(&EfiDirIter
, 1, NULL
, &EfiDirEntry
)) {
800 if (StriCmp(EfiDirEntry
->FileName
, L
"tools") == 0 || EfiDirEntry
->FileName
[0] == '.')
801 continue; // skip this, doesn't contain boot loaders
802 SPrint(FileName
, 255, L
"EFI\\%s", EfiDirEntry
->FileName
);
803 ScanLoaderDir(Volume
, FileName
, MatchPatterns
);
805 Status
= DirIterClose(&EfiDirIter
);
806 if (Status
!= EFI_NOT_FOUND
)
807 CheckError(Status
, L
"while scanning the EFI directory");
809 // Scan user-specified (or additional default) directories....
811 while ((Directory
= FindCommaDelimited(GlobalConfig
.AlsoScan
, i
++)) != NULL
) {
812 CleanUpPathNameSlashes(Directory
);
813 Length
= StrLen(Directory
);
815 ScanLoaderDir(Volume
, Directory
, MatchPatterns
);
819 } // static VOID ScanEfiFiles()
821 // Scan internal disks for valid EFI boot loaders....
822 static VOID
ScanInternal(VOID
) {
825 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
826 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_INTERNAL
) {
827 ScanEfiFiles(Volumes
[VolumeIndex
]);
830 } // static VOID ScanInternal()
832 // Scan external disks for valid EFI boot loaders....
833 static VOID
ScanExternal(VOID
) {
836 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
837 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_EXTERNAL
) {
838 ScanEfiFiles(Volumes
[VolumeIndex
]);
841 } // static VOID ScanExternal()
843 // Scan internal disks for valid EFI boot loaders....
844 static VOID
ScanOptical(VOID
) {
847 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
848 if (Volumes
[VolumeIndex
]->DiskKind
== DISK_KIND_OPTICAL
) {
849 ScanEfiFiles(Volumes
[VolumeIndex
]);
852 } // static VOID ScanOptical()
855 // legacy boot functions
858 static EFI_STATUS
ActivateMbrPartition(IN EFI_BLOCK_IO
*BlockIO
, IN UINTN PartitionIndex
)
861 UINT8 SectorBuffer
[512];
862 MBR_PARTITION_INFO
*MbrTable
, *EMbrTable
;
863 UINT32 ExtBase
, ExtCurrent
, NextExtCurrent
;
864 UINTN LogicalPartitionIndex
= 4;
866 BOOLEAN HaveBootCode
;
869 Status
= refit_call5_wrapper(BlockIO
->ReadBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, 0, 512, SectorBuffer
);
870 if (EFI_ERROR(Status
))
872 if (*((UINT16
*)(SectorBuffer
+ 510)) != 0xaa55)
873 return EFI_NOT_FOUND
; // safety measure #1
875 // add boot code if necessary
876 HaveBootCode
= FALSE
;
877 for (i
= 0; i
< MBR_BOOTCODE_SIZE
; i
++) {
878 if (SectorBuffer
[i
] != 0) {
884 // no boot code found in the MBR, add the syslinux MBR code
885 SetMem(SectorBuffer
, MBR_BOOTCODE_SIZE
, 0);
886 CopyMem(SectorBuffer
, syslinux_mbr
, SYSLINUX_MBR_SIZE
);
889 // set the partition active
890 MbrTable
= (MBR_PARTITION_INFO
*)(SectorBuffer
+ 446);
892 for (i
= 0; i
< 4; i
++) {
893 if (MbrTable
[i
].Flags
!= 0x00 && MbrTable
[i
].Flags
!= 0x80)
894 return EFI_NOT_FOUND
; // safety measure #2
895 if (i
== PartitionIndex
)
896 MbrTable
[i
].Flags
= 0x80;
897 else if (PartitionIndex
>= 4 && IS_EXTENDED_PART_TYPE(MbrTable
[i
].Type
)) {
898 MbrTable
[i
].Flags
= 0x80;
899 ExtBase
= MbrTable
[i
].StartLBA
;
901 MbrTable
[i
].Flags
= 0x00;
905 Status
= refit_call5_wrapper(BlockIO
->WriteBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, 0, 512, SectorBuffer
);
906 if (EFI_ERROR(Status
))
909 if (PartitionIndex
>= 4) {
910 // we have to activate a logical partition, so walk the EMBR chain
912 // NOTE: ExtBase was set above while looking at the MBR table
913 for (ExtCurrent
= ExtBase
; ExtCurrent
; ExtCurrent
= NextExtCurrent
) {
915 Status
= refit_call5_wrapper(BlockIO
->ReadBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, ExtCurrent
, 512, SectorBuffer
);
916 if (EFI_ERROR(Status
))
918 if (*((UINT16
*)(SectorBuffer
+ 510)) != 0xaa55)
919 return EFI_NOT_FOUND
; // safety measure #3
921 // scan EMBR, set appropriate partition active
922 EMbrTable
= (MBR_PARTITION_INFO
*)(SectorBuffer
+ 446);
924 for (i
= 0; i
< 4; i
++) {
925 if (EMbrTable
[i
].Flags
!= 0x00 && EMbrTable
[i
].Flags
!= 0x80)
926 return EFI_NOT_FOUND
; // safety measure #4
927 if (EMbrTable
[i
].StartLBA
== 0 || EMbrTable
[i
].Size
== 0)
929 if (IS_EXTENDED_PART_TYPE(EMbrTable
[i
].Type
)) {
931 NextExtCurrent
= ExtBase
+ EMbrTable
[i
].StartLBA
;
932 EMbrTable
[i
].Flags
= (PartitionIndex
>= LogicalPartitionIndex
) ? 0x80 : 0x00;
936 EMbrTable
[i
].Flags
= (PartitionIndex
== LogicalPartitionIndex
) ? 0x80 : 0x00;
937 LogicalPartitionIndex
++;
941 // write current EMBR
942 Status
= refit_call5_wrapper(BlockIO
->WriteBlocks
, BlockIO
, BlockIO
->Media
->MediaId
, ExtCurrent
, 512, SectorBuffer
);
943 if (EFI_ERROR(Status
))
946 if (PartitionIndex
< LogicalPartitionIndex
)
947 break; // stop the loop, no need to touch further EMBRs
953 } /* static EFI_STATUS ActivateMbrPartition() */
955 // early 2006 Core Duo / Core Solo models
956 static UINT8 LegacyLoaderDevicePath1Data
[] = {
957 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
958 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
959 0xFF, 0xFF, 0xF9, 0xFF, 0x00, 0x00, 0x00, 0x00,
960 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
961 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
962 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
964 // mid-2006 Mac Pro (and probably other Core 2 models)
965 static UINT8 LegacyLoaderDevicePath2Data
[] = {
966 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
967 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
968 0xFF, 0xFF, 0xF7, 0xFF, 0x00, 0x00, 0x00, 0x00,
969 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
970 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
971 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
973 // mid-2007 MBP ("Santa Rosa" based models)
974 static UINT8 LegacyLoaderDevicePath3Data
[] = {
975 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
976 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
977 0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
978 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
979 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
980 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
983 static UINT8 LegacyLoaderDevicePath4Data
[] = {
984 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
985 0x00, 0x00, 0xC0, 0xFF, 0x00, 0x00, 0x00, 0x00,
986 0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
987 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
988 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
989 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
991 // late-2008 MB/MBP (NVidia chipset)
992 static UINT8 LegacyLoaderDevicePath5Data
[] = {
993 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
994 0x00, 0x40, 0xCB, 0xFF, 0x00, 0x00, 0x00, 0x00,
995 0xFF, 0xBF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
996 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
997 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
998 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1001 static EFI_DEVICE_PATH
*LegacyLoaderList
[] = {
1002 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath1Data
,
1003 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath2Data
,
1004 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath3Data
,
1005 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath4Data
,
1006 (EFI_DEVICE_PATH
*)LegacyLoaderDevicePath5Data
,
1010 #define MAX_DISCOVERED_PATHS (16)
1012 static VOID
StartLegacy(IN LEGACY_ENTRY
*Entry
)
1015 EG_IMAGE
*BootLogoImage
;
1016 UINTN ErrorInStep
= 0;
1017 EFI_DEVICE_PATH
*DiscoveredPathList
[MAX_DISCOVERED_PATHS
];
1019 BeginExternalScreen(TRUE
, L
"Booting Legacy OS");
1021 BootLogoImage
= LoadOSIcon(Entry
->Volume
->OSIconName
, L
"legacy", TRUE
);
1022 if (BootLogoImage
!= NULL
)
1023 BltImageAlpha(BootLogoImage
,
1024 (UGAWidth
- BootLogoImage
->Width
) >> 1,
1025 (UGAHeight
- BootLogoImage
->Height
) >> 1,
1026 &StdBackgroundPixel
);
1028 if (Entry
->Volume
->IsMbrPartition
)
1029 ActivateMbrPartition(Entry
->Volume
->WholeDiskBlockIO
, Entry
->Volume
->MbrPartitionIndex
);
1031 ExtractLegacyLoaderPaths(DiscoveredPathList
, MAX_DISCOVERED_PATHS
, LegacyLoaderList
);
1033 Status
= StartEFIImageList(DiscoveredPathList
, Entry
->LoadOptions
, NULL
, L
"legacy loader", &ErrorInStep
, TRUE
);
1034 if (Status
== EFI_NOT_FOUND
) {
1035 if (ErrorInStep
== 1) {
1036 Print(L
"\nPlease make sure that you have the latest firmware update installed.\n");
1037 } else if (ErrorInStep
== 3) {
1038 Print(L
"\nThe firmware refused to boot from the selected volume. Note that external\n"
1039 L
"hard drives are not well-supported by Apple's firmware for legacy OS booting.\n");
1042 FinishExternalScreen();
1043 } /* static VOID StartLegacy() */
1045 static LEGACY_ENTRY
* AddLegacyEntry(IN CHAR16
*LoaderTitle
, IN REFIT_VOLUME
*Volume
)
1047 LEGACY_ENTRY
*Entry
, *SubEntry
;
1048 REFIT_MENU_SCREEN
*SubScreen
;
1050 CHAR16 ShortcutLetter
= 0;
1052 if (LoaderTitle
== NULL
) {
1053 if (Volume
->OSName
!= NULL
) {
1054 LoaderTitle
= Volume
->OSName
;
1055 if (LoaderTitle
[0] == 'W' || LoaderTitle
[0] == 'L')
1056 ShortcutLetter
= LoaderTitle
[0];
1058 LoaderTitle
= L
"Legacy OS";
1060 if (Volume
->VolName
!= NULL
)
1061 VolDesc
= Volume
->VolName
;
1063 VolDesc
= (Volume
->DiskKind
== DISK_KIND_OPTICAL
) ? L
"CD" : L
"HD";
1065 // prepare the menu entry
1066 Entry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1067 Entry
->me
.Title
= PoolPrint(L
"Boot %s from %s", LoaderTitle
, VolDesc
);
1068 Entry
->me
.Tag
= TAG_LEGACY
;
1070 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1071 Entry
->me
.Image
= LoadOSIcon(Volume
->OSIconName
, L
"legacy", FALSE
);
1072 Entry
->me
.BadgeImage
= Volume
->VolBadgeImage
;
1073 Entry
->Volume
= Volume
;
1074 Entry
->LoadOptions
= (Volume
->DiskKind
== DISK_KIND_OPTICAL
) ? L
"CD" :
1075 ((Volume
->DiskKind
== DISK_KIND_EXTERNAL
) ? L
"USB" : L
"HD");
1076 Entry
->Enabled
= TRUE
;
1078 // create the submenu
1079 SubScreen
= AllocateZeroPool(sizeof(REFIT_MENU_SCREEN
));
1080 SubScreen
->Title
= PoolPrint(L
"Boot Options for %s on %s", LoaderTitle
, VolDesc
);
1081 SubScreen
->TitleImage
= Entry
->me
.Image
;
1084 SubEntry
= AllocateZeroPool(sizeof(LEGACY_ENTRY
));
1085 SubEntry
->me
.Title
= PoolPrint(L
"Boot %s", LoaderTitle
);
1086 SubEntry
->me
.Tag
= TAG_LEGACY
;
1087 SubEntry
->Volume
= Entry
->Volume
;
1088 SubEntry
->LoadOptions
= Entry
->LoadOptions
;
1089 AddMenuEntry(SubScreen
, (REFIT_MENU_ENTRY
*)SubEntry
);
1091 AddMenuEntry(SubScreen
, &MenuEntryReturn
);
1092 Entry
->me
.SubScreen
= SubScreen
;
1093 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1095 } /* static LEGACY_ENTRY * AddLegacyEntry() */
1097 static VOID
ScanLegacyVolume(REFIT_VOLUME
*Volume
, UINTN VolumeIndex
) {
1099 BOOLEAN ShowVolume
, HideIfOthersFound
;
1102 HideIfOthersFound
= FALSE
;
1103 if (Volume
->IsAppleLegacy
) {
1105 HideIfOthersFound
= TRUE
;
1106 } else if (Volume
->HasBootCode
) {
1108 if (Volume
->BlockIO
== Volume
->WholeDiskBlockIO
&&
1109 Volume
->BlockIOOffset
== 0 &&
1110 Volume
->OSName
== NULL
)
1111 // this is a whole disk (MBR) entry; hide if we have entries for partitions
1112 HideIfOthersFound
= TRUE
;
1114 if (HideIfOthersFound
) {
1115 // check for other bootable entries on the same disk
1116 for (VolumeIndex2
= 0; VolumeIndex2
< VolumesCount
; VolumeIndex2
++) {
1117 if (VolumeIndex2
!= VolumeIndex
&& Volumes
[VolumeIndex2
]->HasBootCode
&&
1118 Volumes
[VolumeIndex2
]->WholeDiskBlockIO
== Volume
->WholeDiskBlockIO
)
1124 AddLegacyEntry(NULL
, Volume
);
1125 } // static VOID ScanLegacyVolume()
1127 // Scan attached optical discs for legacy (BIOS) boot code
1128 // and add anything found to the list....
1129 static VOID
ScanLegacyDisc(VOID
)
1132 REFIT_VOLUME
*Volume
;
1134 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1135 Volume
= Volumes
[VolumeIndex
];
1136 if (Volume
->DiskKind
== DISK_KIND_OPTICAL
)
1137 ScanLegacyVolume(Volume
, VolumeIndex
);
1139 } /* static VOID ScanLegacyDisc() */
1141 // Scan internal hard disks for legacy (BIOS) boot code
1142 // and add anything found to the list....
1143 static VOID
ScanLegacyInternal(VOID
)
1146 REFIT_VOLUME
*Volume
;
1148 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1149 Volume
= Volumes
[VolumeIndex
];
1150 if (Volume
->DiskKind
== DISK_KIND_INTERNAL
)
1151 ScanLegacyVolume(Volume
, VolumeIndex
);
1153 } /* static VOID ScanLegacyInternal() */
1155 // Scan external disks for legacy (BIOS) boot code
1156 // and add anything found to the list....
1157 static VOID
ScanLegacyExternal(VOID
)
1160 REFIT_VOLUME
*Volume
;
1162 for (VolumeIndex
= 0; VolumeIndex
< VolumesCount
; VolumeIndex
++) {
1163 Volume
= Volumes
[VolumeIndex
];
1164 if (Volume
->DiskKind
== DISK_KIND_EXTERNAL
)
1165 ScanLegacyVolume(Volume
, VolumeIndex
);
1167 } /* static VOID ScanLegacyExternal() */
1170 // pre-boot tool functions
1173 static VOID
StartTool(IN LOADER_ENTRY
*Entry
)
1175 BeginExternalScreen(Entry
->UseGraphicsMode
, Entry
->me
.Title
+ 6); // assumes "Start <title>" as assigned below
1176 StartEFIImage(Entry
->DevicePath
, Entry
->LoadOptions
, Basename(Entry
->LoaderPath
),
1177 Basename(Entry
->LoaderPath
), NULL
, TRUE
);
1178 FinishExternalScreen();
1179 } /* static VOID StartTool() */
1181 static LOADER_ENTRY
* AddToolEntry(IN CHAR16
*LoaderPath
, IN CHAR16
*LoaderTitle
, IN EG_IMAGE
*Image
,
1182 IN CHAR16 ShortcutLetter
, IN BOOLEAN UseGraphicsMode
)
1184 LOADER_ENTRY
*Entry
;
1186 Entry
= AllocateZeroPool(sizeof(LOADER_ENTRY
));
1188 Entry
->me
.Title
= PoolPrint(L
"Start %s", LoaderTitle
);
1189 Entry
->me
.Tag
= TAG_TOOL
;
1191 Entry
->me
.ShortcutLetter
= ShortcutLetter
;
1192 Entry
->me
.Image
= Image
;
1193 Entry
->LoaderPath
= StrDuplicate(LoaderPath
);
1194 Entry
->DevicePath
= FileDevicePath(SelfLoadedImage
->DeviceHandle
, Entry
->LoaderPath
);
1195 Entry
->UseGraphicsMode
= UseGraphicsMode
;
1197 AddMenuEntry(&MainMenu
, (REFIT_MENU_ENTRY
*)Entry
);
1199 } /* static LOADER_ENTRY * AddToolEntry() */
1202 // pre-boot driver functions
1205 static UINTN
ScanDriverDir(IN CHAR16
*Path
)
1208 REFIT_DIR_ITER DirIter
;
1210 EFI_FILE_INFO
*DirEntry
;
1211 CHAR16 FileName
[256];
1213 CleanUpPathNameSlashes(Path
);
1214 // look through contents of the directory
1215 DirIterOpen(SelfRootDir
, Path
, &DirIter
);
1216 while (DirIterNext(&DirIter
, 2, LOADER_MATCH_PATTERNS
, &DirEntry
)) {
1217 if (DirEntry
->FileName
[0] == '.')
1218 continue; // skip this
1220 SPrint(FileName
, 255, L
"%s\\%s", Path
, DirEntry
->FileName
);
1222 Status
= StartEFIImage(FileDevicePath(SelfLoadedImage
->DeviceHandle
, FileName
),
1223 L
"", DirEntry
->FileName
, DirEntry
->FileName
, NULL
, FALSE
);
1225 Status
= DirIterClose(&DirIter
);
1226 if (Status
!= EFI_NOT_FOUND
) {
1227 SPrint(FileName
, 255, L
"while scanning the %s directory", Path
);
1228 CheckError(Status
, FileName
);
1233 static EFI_STATUS
ConnectAllDriversToAllControllers(VOID
)
1236 UINTN AllHandleCount
;
1237 EFI_HANDLE
*AllHandleBuffer
;
1240 EFI_HANDLE
*HandleBuffer
;
1246 Status
= LibLocateHandle(AllHandles
,
1251 if (EFI_ERROR(Status
))
1254 for (Index
= 0; Index
< AllHandleCount
; Index
++) {
1256 // Scan the handle database
1258 Status
= LibScanHandleDatabase(NULL
,
1260 AllHandleBuffer
[Index
],
1265 if (EFI_ERROR (Status
))
1269 if (HandleType
[Index
] & EFI_HANDLE_TYPE_DRIVER_BINDING_HANDLE
)
1271 if (HandleType
[Index
] & EFI_HANDLE_TYPE_IMAGE_HANDLE
)
1276 for (HandleIndex
= 0; HandleIndex
< HandleCount
; HandleIndex
++) {
1277 if (HandleType
[HandleIndex
] & EFI_HANDLE_TYPE_PARENT_HANDLE
)
1282 if (HandleType
[Index
] & EFI_HANDLE_TYPE_DEVICE_HANDLE
) {
1283 Status
= refit_call4_wrapper(BS
->ConnectController
,
1284 AllHandleBuffer
[Index
],
1292 FreePool (HandleBuffer
);
1293 FreePool (HandleType
);
1297 FreePool (AllHandleBuffer
);
1299 } /* EFI_STATUS ConnectAllDriversToAllControllers() */
1301 // Load all EFI drivers from rEFInd's "drivers" subdirectory and from the
1302 // directories specified by the user in the "scan_driver_dirs" configuration
1304 static VOID
LoadDrivers(VOID
)
1307 UINTN i
= 0, Length
, NumFound
= 0;
1309 // load drivers from the "drivers" subdirectory of rEFInd's home directory
1310 Directory
= StrDuplicate(SelfDirPath
);
1311 CleanUpPathNameSlashes(Directory
);
1312 MergeStrings(&Directory
, L
"drivers", L
'\\');
1313 NumFound
+= ScanDriverDir(Directory
);
1315 // Scan additional user-specified driver directories....
1316 while ((Directory
= FindCommaDelimited(GlobalConfig
.DriverDirs
, i
++)) != NULL
) {
1317 CleanUpPathNameSlashes(Directory
);
1318 Length
= StrLen(Directory
);
1320 NumFound
+= ScanDriverDir(Directory
);
1321 FreePool(Directory
);
1324 // connect all devices
1326 ConnectAllDriversToAllControllers();
1327 } /* static VOID LoadDrivers() */
1329 static VOID
ScanForBootloaders(VOID
) {
1333 // Commented-out below: Was part of an attempt to get rEFInd to
1334 // re-scan disk devices on pressing Esc; but doesn't work (yet), so
1336 // MainMenu.Title = StrDuplicate(L"Main Menu 2");
1337 // MainMenu.TitleImage = NULL;
1338 // MainMenu.InfoLineCount = 0;
1339 // MainMenu.InfoLines = NULL;
1340 // MainMenu.EntryCount = 0;
1341 // MainMenu.Entries = NULL;
1342 // MainMenu.TimeoutSeconds = 20;
1343 // MainMenu.TimeoutText = StrDuplicate(L"Automatic boot");
1346 // scan for loaders and tools, add them to the menu
1347 for (i
= 0; i
< NUM_SCAN_OPTIONS
; i
++) {
1348 switch(GlobalConfig
.ScanFor
[i
]) {
1353 ScanLegacyInternal();
1356 ScanLegacyExternal();
1359 ScanUserConfigured();
1373 // assign shortcut keys
1374 for (i
= 0; i
< MainMenu
.EntryCount
&& MainMenu
.Entries
[i
]->Row
== 0 && i
< 9; i
++)
1375 MainMenu
.Entries
[i
]->ShortcutDigit
= (CHAR16
)('1' + i
);
1377 // wait for user ACK when there were errors
1378 FinishTextScreen(FALSE
);
1379 } // static VOID ScanForBootloaders()
1381 // Add the second-row tags containing built-in and external tools (EFI shell,
1383 static VOID
ScanForTools(VOID
) {
1384 CHAR16
*FileName
= NULL
;
1387 for (i
= 0; i
< NUM_TOOLS
; i
++) {
1388 switch(GlobalConfig
.ShowTools
[i
]) {
1390 MenuEntryShutdown
.Image
= BuiltinIcon(BUILTIN_ICON_FUNC_SHUTDOWN
);
1391 AddMenuEntry(&MainMenu
, &MenuEntryShutdown
);
1394 MenuEntryReset
.Image
= BuiltinIcon(BUILTIN_ICON_FUNC_RESET
);
1395 AddMenuEntry(&MainMenu
, &MenuEntryReset
);
1398 MenuEntryAbout
.Image
= BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT
);
1399 AddMenuEntry(&MainMenu
, &MenuEntryAbout
);
1402 MenuEntryExit
.Image
= BuiltinIcon(BUILTIN_ICON_FUNC_EXIT
);
1403 AddMenuEntry(&MainMenu
, &MenuEntryExit
);
1407 while ((FileName
= FindCommaDelimited(SHELL_NAMES
, j
++)) != NULL
) {
1408 if (FileExists(SelfRootDir
, FileName
)) {
1409 AddToolEntry(FileName
, L
"EFI Shell", BuiltinIcon(BUILTIN_ICON_TOOL_SHELL
), 'E', FALSE
);
1414 MergeStrings(&FileName
, L
"\\efi\\tools\\gptsync.efi", 0);
1415 if (FileExists(SelfRootDir
, FileName
)) {
1416 AddToolEntry(FileName
, L
"Make Hybrid MBR", BuiltinIcon(BUILTIN_ICON_TOOL_PART
), 'P', FALSE
);
1420 if (FileName
!= NULL
) {
1425 } // static VOID ScanForTools
1433 efi_main (IN EFI_HANDLE ImageHandle
, IN EFI_SYSTEM_TABLE
*SystemTable
)
1436 BOOLEAN MainLoopRunning
= TRUE
;
1437 REFIT_MENU_ENTRY
*ChosenEntry
;
1441 InitializeLib(ImageHandle
, SystemTable
);
1443 Status
= InitRefitLib(ImageHandle
);
1444 if (EFI_ERROR(Status
))
1447 // read configuration
1448 CopyMem(GlobalConfig
.ScanFor
, "ieo ", NUM_SCAN_OPTIONS
);
1450 MainMenu
.TimeoutSeconds
= GlobalConfig
.Timeout
;
1452 // disable EFI watchdog timer
1453 refit_call4_wrapper(BS
->SetWatchdogTimer
, 0x0000, 0x0000, 0x0000, NULL
);
1455 // further bootstrap (now with config available)
1458 ScanForBootloaders();
1461 while (MainLoopRunning
) {
1462 MenuExit
= RunMainMenu(&MainMenu
, GlobalConfig
.DefaultSelection
, &ChosenEntry
);
1464 // We don't allow exiting the main menu with the Escape key.
1465 if (MenuExit
== MENU_EXIT_ESCAPE
) {
1466 // Commented-out below: Was part of an attempt to get rEFInd to
1467 // re-scan disk devices on pressing Esc; but doesn't work (yet), so
1470 // ScanForBootloaders();
1475 switch (ChosenEntry
->Tag
) {
1477 case TAG_REBOOT
: // Reboot
1479 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);
1480 MainLoopRunning
= FALSE
; // just in case we get this far
1483 case TAG_SHUTDOWN
: // Shut Down
1485 refit_call4_wrapper(RT
->ResetSystem
, EfiResetShutdown
, EFI_SUCCESS
, 0, NULL
);
1486 MainLoopRunning
= FALSE
; // just in case we get this far
1489 case TAG_ABOUT
: // About rEFInd
1493 case TAG_LOADER
: // Boot OS via .EFI loader
1494 StartLoader((LOADER_ENTRY
*)ChosenEntry
);
1497 case TAG_LEGACY
: // Boot legacy OS
1498 StartLegacy((LEGACY_ENTRY
*)ChosenEntry
);
1501 case TAG_TOOL
: // Start a EFI tool
1502 StartTool((LOADER_ENTRY
*)ChosenEntry
);
1505 case TAG_EXIT
: // Terminate rEFInd
1506 BeginTextScreen(L
" ");
1513 // If we end up here, things have gone wrong. Try to reboot, and if that
1514 // fails, go into an endless loop.
1515 refit_call4_wrapper(RT
->ResetSystem
, EfiResetCold
, EFI_SUCCESS
, 0, NULL
);