]> code.delx.au - refind/blob - refind/main.c
37a0b18ec7e0e11b55163fe7d135f04eeaa18884
[refind] / refind / main.c
1 /*
2 * refind/main.c
3 * Main code for the boot menu
4 *
5 * Copyright (c) 2006-2010 Christoph Pfisterer
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions are
10 * met:
11 *
12 * * Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 *
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
18 * distribution.
19 *
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.
23 *
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.
35 */
36 /*
37 * Modifications copyright (c) 2012-2013 Roderick W. Smith
38 *
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.
42 *
43 */
44
45 #include "global.h"
46 #include "config.h"
47 #include "screen.h"
48 #include "lib.h"
49 #include "icns.h"
50 #include "menu.h"
51 #include "mok.h"
52 #include "security_policy.h"
53 #include "../include/Handle.h"
54 #include "../include/refit_call_wrapper.h"
55 #include "driver_support.h"
56 #include "../include/syslinux_mbr.h"
57
58 #ifdef __MAKEWITH_GNUEFI
59 #define EFI_SECURITY_VIOLATION EFIERR (26)
60 #else
61 #include "../EfiLib/BdsHelper.h"
62 #endif // __MAKEWITH_GNUEFI
63
64 #ifndef EFI_OS_INDICATIONS_BOOT_TO_FW_UI
65 #define EFI_OS_INDICATIONS_BOOT_TO_FW_UI 0x0000000000000001ULL
66 #endif
67
68 //
69 // constants
70
71 #define MACOSX_LOADER_PATH L"System\\Library\\CoreServices\\boot.efi"
72 #define MEMTEST_LOCATIONS L"EFI\\tools,EFI\\tools\\memtest86,EFI\\tools\\memtest,EFI\\memtest86,EFI\\memtest"
73 #if defined (EFIX64)
74 #define SHELL_NAMES L"\\EFI\\tools\\shell.efi,\\EFI\\tools\\shellx64.efi,\\shell.efi,\\shellx64.efi"
75 #define GPTSYNC_NAMES L"\\EFI\\tools\\gptsync.efi,\\EFI\\tools\\gptsync_x64.efi"
76 #define MEMTEST_NAMES L"memtest86.efi,memtest86_x64.efi,memtest86x64.efi,bootx64.efi"
77 #define DRIVER_DIRS L"drivers,drivers_x64"
78 #define FALLBACK_FULLNAME L"EFI\\BOOT\\bootx64.efi"
79 #define FALLBACK_BASENAME L"bootx64.efi"
80 #define EFI_STUB_ARCH 0x8664
81 #elif defined (EFI32)
82 #define SHELL_NAMES L"\\EFI\\tools\\shell.efi,\\EFI\\tools\\shellia32.efi,\\shell.efi,\\shellia32.efi"
83 #define GPTSYNC_NAMES L"\\EFI\\tools\\gptsync.efi,\\EFI\\tools\\gptsync_ia32.efi"
84 #define MEMTEST_NAMES L"memtest86.efi,memtest86_ia32.efi,memtest86ia32.efi,bootia32.efi"
85 #define DRIVER_DIRS L"drivers,drivers_ia32"
86 #define FALLBACK_FULLNAME L"EFI\\BOOT\\bootia32.efi"
87 #define FALLBACK_BASENAME L"bootia32.efi"
88 #define EFI_STUB_ARCH 0x014c
89 #else
90 #define SHELL_NAMES L"\\EFI\\tools\\shell.efi,\\shell.efi"
91 #define GPTSYNC_NAMES L"\\EFI\\tools\\gptsync.efi"
92 #define MEMTEST_NAMES L"memtest86.efi"
93 #define DRIVER_DIRS L"drivers"
94 #define FALLBACK_FULLNAME L"EFI\\BOOT\\boot.efi" /* Not really correct */
95 #define FALLBACK_BASENAME L"boot.efi" /* Not really correct */
96 #endif
97 #define FAT_ARCH 0x0ef1fab9 /* ID for Apple "fat" binary */
98
99 // Filename patterns that identify EFI boot loaders. Note that a single case (either L"*.efi" or
100 // L"*.EFI") is fine for most systems; but Gigabyte's buggy Hybrid EFI does a case-sensitive
101 // comparison when it should do a case-insensitive comparison, so I'm doubling this up. It does
102 // no harm on other computers, AFAIK. In theory, every case variation should be done for
103 // completeness, but that's ridiculous....
104 #define LOADER_MATCH_PATTERNS L"*.efi,*.EFI"
105
106 // Patterns that identify Linux kernels. Added to the loader match pattern when the
107 // scan_all_linux_kernels option is set in the configuration file. Causes kernels WITHOUT
108 // a ".efi" extension to be found when scanning for boot loaders.
109 #define LINUX_MATCH_PATTERNS L"vmlinuz*,bzImage*"
110
111 // Default hint text for program-launch submenus
112 #define SUBSCREEN_HINT1 L"Use arrow keys to move cursor; Enter to boot;"
113 #define SUBSCREEN_HINT2 L"Insert or F2 to edit options; Esc to return to main menu"
114 #define SUBSCREEN_HINT2_NO_EDITOR L"Esc to return to main menu"
115
116 // Load types
117 #define TYPE_EFI 1
118 #define TYPE_LEGACY 2
119
120 static REFIT_MENU_ENTRY MenuEntryAbout = { L"About rEFInd", TAG_ABOUT, 1, 0, 'A', NULL, NULL, NULL };
121 static REFIT_MENU_ENTRY MenuEntryReset = { L"Reboot Computer", TAG_REBOOT, 1, 0, 'R', NULL, NULL, NULL };
122 static REFIT_MENU_ENTRY MenuEntryShutdown = { L"Shut Down Computer", TAG_SHUTDOWN, 1, 0, 'U', NULL, NULL, NULL };
123 static REFIT_MENU_ENTRY MenuEntryReturn = { L"Return to Main Menu", TAG_RETURN, 1, 0, 0, NULL, NULL, NULL };
124 static REFIT_MENU_ENTRY MenuEntryExit = { L"Exit rEFInd", TAG_EXIT, 1, 0, 0, NULL, NULL, NULL };
125 static REFIT_MENU_ENTRY MenuEntryFirmware = { L"Reboot to Computer Setup Utility", TAG_FIRMWARE, 1, 0, 0, NULL, NULL, NULL };
126
127 static REFIT_MENU_SCREEN MainMenu = { L"Main Menu", NULL, 0, NULL, 0, NULL, 0, L"Automatic boot",
128 L"Use arrow keys to move cursor; Enter to boot;",
129 L"Insert or F2 for more options; Esc to refresh" };
130 static REFIT_MENU_SCREEN AboutMenu = { L"About", NULL, 0, NULL, 0, NULL, 0, NULL, L"Press Enter to return to main menu", L"" };
131
132 REFIT_CONFIG GlobalConfig = { FALSE, FALSE, 0, 0, 0, DONT_CHANGE_TEXT_MODE, 20, 0, 0, GRAPHICS_FOR_OSX, LEGACY_TYPE_MAC, 0, 0,
133 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
134 { TAG_SHELL, TAG_MEMTEST, TAG_APPLE_RECOVERY, TAG_MOK_TOOL, TAG_ABOUT, TAG_SHUTDOWN,
135 TAG_REBOOT, TAG_FIRMWARE, 0, 0, 0, 0, 0, 0 }
136 };
137
138 EFI_GUID GlobalGuid = EFI_GLOBAL_VARIABLE;
139
140 // Structure used to hold boot loader filenames and time stamps in
141 // a linked list; used to sort entries within a directory.
142 struct LOADER_LIST {
143 CHAR16 *FileName;
144 EFI_TIME TimeStamp;
145 struct LOADER_LIST *NextEntry;
146 };
147
148 //
149 // misc functions
150 //
151
152 static VOID AboutrEFInd(VOID)
153 {
154 CHAR16 *TempStr; // Note: Don't deallocate; moved to menu structure
155
156 if (AboutMenu.EntryCount == 0) {
157 AboutMenu.TitleImage = BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT);
158 AddMenuInfoLine(&AboutMenu, L"rEFInd Version 0.7.3.6");
159 AddMenuInfoLine(&AboutMenu, L"");
160 AddMenuInfoLine(&AboutMenu, L"Copyright (c) 2006-2010 Christoph Pfisterer");
161 AddMenuInfoLine(&AboutMenu, L"Copyright (c) 2012-2013 Roderick W. Smith");
162 AddMenuInfoLine(&AboutMenu, L"Portions Copyright (c) Intel Corporation and others");
163 AddMenuInfoLine(&AboutMenu, L"Distributed under the terms of the GNU GPLv3 license");
164 AddMenuInfoLine(&AboutMenu, L"");
165 AddMenuInfoLine(&AboutMenu, L"Running on:");
166 TempStr = AllocateZeroPool(256 * sizeof(CHAR16));
167 SPrint(TempStr, 255, L" EFI Revision %d.%02d", ST->Hdr.Revision >> 16, ST->Hdr.Revision & ((1 << 16) - 1));
168 AddMenuInfoLine(&AboutMenu, TempStr);
169 #if defined(EFI32)
170 AddMenuInfoLine(&AboutMenu, L" Platform: x86 (32 bit)");
171 #elif defined(EFIX64)
172 TempStr = AllocateZeroPool(256 * sizeof(CHAR16));
173 SPrint(TempStr, 255, L" Platform: x86_64 (64 bit); Secure Boot %s", secure_mode() ? L"active" : L"inactive");
174 AddMenuInfoLine(&AboutMenu, TempStr);
175 #else
176 AddMenuInfoLine(&AboutMenu, L" Platform: unknown");
177 #endif
178 TempStr = AllocateZeroPool(256 * sizeof(CHAR16));
179 SPrint(TempStr, 255, L" Firmware: %s %d.%02d",
180 ST->FirmwareVendor, ST->FirmwareRevision >> 16, ST->FirmwareRevision & ((1 << 16) - 1));
181 AddMenuInfoLine(&AboutMenu, TempStr);
182 TempStr = AllocateZeroPool(256 * sizeof(CHAR16));
183 SPrint(TempStr, 255, L" Screen Output: %s", egScreenDescription());
184 AddMenuInfoLine(&AboutMenu, TempStr);
185 AddMenuInfoLine(&AboutMenu, L"");
186 #if defined(__MAKEWITH_GNUEFI)
187 AddMenuInfoLine(&AboutMenu, L"Built with GNU-EFI");
188 #else
189 AddMenuInfoLine(&AboutMenu, L"Built with TianoCore EDK2");
190 #endif
191 AddMenuInfoLine(&AboutMenu, L"");
192 AddMenuInfoLine(&AboutMenu, L"For more information, see the rEFInd Web site:");
193 AddMenuInfoLine(&AboutMenu, L"http://www.rodsbooks.com/refind/");
194 AddMenuEntry(&AboutMenu, &MenuEntryReturn);
195 }
196
197 RunMenu(&AboutMenu, NULL);
198 } /* VOID AboutrEFInd() */
199
200 static VOID WarnSecureBootError(CHAR16 *Name, BOOLEAN Verbose) {
201 if (Name == NULL)
202 Name = L"the loader";
203
204 refit_call2_wrapper(ST->ConOut->SetAttribute, ST->ConOut, ATTR_ERROR);
205 Print(L"Secure Boot validation failure loading %s!\n", Name);
206 refit_call2_wrapper(ST->ConOut->SetAttribute, ST->ConOut, ATTR_BASIC);
207 if (Verbose && secure_mode()) {
208 Print(L"\nThis computer is configured with Secure Boot active, but\n%s has failed validation.\n", Name);
209 Print(L"\nYou can:\n * Launch another boot loader\n");
210 Print(L" * Disable Secure Boot in your firmware\n");
211 Print(L" * Sign %s with a machine owner key (MOK)\n", Name);
212 Print(L" * Use a MOK utility (often present on the second row) to add a MOK with which\n");
213 Print(L" %s has already been signed.\n", Name);
214 Print(L" * Use a MOK utility to register %s (\"enroll its hash\") without\n", Name);
215 Print(L" signing it.\n");
216 Print(L"\nSee http://www.rodsbooks.com/refind/secureboot.html for more information\n");
217 PauseForKey();
218 } // if
219 } // VOID WarnSecureBootError()
220
221 // Returns TRUE if this file is a valid EFI loader file, and is proper ARCH
222 static BOOLEAN IsValidLoader(EFI_FILE *RootDir, CHAR16 *FileName) {
223 BOOLEAN IsValid = TRUE;
224 #if defined (EFIX64) | defined (EFI32)
225 EFI_STATUS Status;
226 EFI_FILE_HANDLE FileHandle;
227 CHAR8 Header[512];
228 UINTN Size = sizeof(Header);
229
230 if ((RootDir == NULL) || (FileName == NULL)) {
231 // Assume valid here, because Macs produce NULL RootDir (& maybe FileName)
232 // when launching from a Firewire drive. This should be handled better, but
233 // fix would have to be in StartEFIImageList() and/or in FindVolumeAndFilename().
234 return TRUE;
235 } // if
236
237 Status = refit_call5_wrapper(RootDir->Open, RootDir, &FileHandle, FileName, EFI_FILE_MODE_READ, 0);
238 if (EFI_ERROR(Status))
239 return FALSE;
240
241 Status = refit_call3_wrapper(FileHandle->Read, FileHandle, &Size, Header);
242 refit_call1_wrapper(FileHandle->Close, FileHandle);
243
244 IsValid = !EFI_ERROR(Status) &&
245 Size == sizeof(Header) &&
246 ((Header[0] == 'M' && Header[1] == 'Z' &&
247 (Size = *(UINT32 *)&Header[0x3c]) < 0x180 &&
248 Header[Size] == 'P' && Header[Size+1] == 'E' &&
249 Header[Size+2] == 0 && Header[Size+3] == 0 &&
250 *(UINT16 *)&Header[Size+4] == EFI_STUB_ARCH) ||
251 (*(UINT32 *)&Header == FAT_ARCH));
252 #endif
253 return IsValid;
254 } // BOOLEAN IsValidLoader()
255
256 // Launch an EFI binary.
257 static EFI_STATUS StartEFIImageList(IN EFI_DEVICE_PATH **DevicePaths,
258 IN CHAR16 *LoadOptions, IN UINTN LoaderType,
259 IN CHAR16 *ImageTitle, IN CHAR8 OSType,
260 OUT UINTN *ErrorInStep,
261 IN BOOLEAN Verbose)
262 {
263 EFI_STATUS Status, ReturnStatus;
264 EFI_HANDLE ChildImageHandle;
265 EFI_LOADED_IMAGE *ChildLoadedImage = NULL;
266 REFIT_VOLUME *Volume = NULL;
267 UINTN DevicePathIndex;
268 CHAR16 ErrorInfo[256];
269 CHAR16 *FullLoadOptions = NULL;
270 CHAR16 *Filename = NULL;
271
272 if (ErrorInStep != NULL)
273 *ErrorInStep = 0;
274
275 // set load options
276 if (LoadOptions != NULL) {
277 if (LoaderType == TYPE_EFI) {
278 MergeStrings(&FullLoadOptions, LoadOptions, L' ');
279 if (OSType == 'M') {
280 MergeStrings(&FullLoadOptions, L" ", 0);
281 // NOTE: That last space is also added by the EFI shell and seems to be significant
282 // when passing options to Apple's boot.efi...
283 } // if
284 } else {
285 MergeStrings(&FullLoadOptions, LoadOptions, 0);
286 } // if/else
287 } else { // LoadOptions == NULL
288 // NOTE: We provide a non-null string when no options are specified for safety;
289 // some systems (at least DUET) can hang when launching some programs (such as
290 // an EFI shell) without this.
291 FullLoadOptions = StrDuplicate(L" ");
292 }
293 if (Verbose)
294 Print(L"Starting %s\nUsing load options '%s'\n", ImageTitle, FullLoadOptions);
295
296 // load the image into memory (and execute it, in the case of a shim/MOK image).
297 ReturnStatus = Status = EFI_NOT_FOUND; // in case the list is empty
298 for (DevicePathIndex = 0; DevicePaths[DevicePathIndex] != NULL; DevicePathIndex++) {
299 FindVolumeAndFilename(DevicePaths[DevicePathIndex], &Volume, &Filename);
300 // Some EFIs crash if attempting to load driver for invalid architecture, so
301 // protect for this condition; but sometimes Volume comes back NULL, so provide
302 // an exception. (TODO: Handle this special condition better.)
303 if ((LoaderType == TYPE_LEGACY) || (Volume == NULL) || IsValidLoader(Volume->RootDir, Filename)) {
304 // NOTE: Below commented-out line could be more efficient if file were read ahead of
305 // time and passed as a pre-loaded image to LoadImage(), but it doesn't work on my
306 // 32-bit Mac Mini or my 64-bit Intel box when launching a Linux kernel; the
307 // kernel returns a "Failed to handle fs_proto" error message.
308 // TODO: Track down the cause of this error and fix it, if possible.
309 // ReturnStatus = Status = refit_call6_wrapper(BS->LoadImage, FALSE, SelfImageHandle, DevicePaths[DevicePathIndex],
310 // ImageData, ImageSize, &ChildImageHandle);
311 ReturnStatus = Status = refit_call6_wrapper(BS->LoadImage, FALSE, SelfImageHandle, DevicePaths[DevicePathIndex],
312 NULL, 0, &ChildImageHandle);
313 } else {
314 Print(L"Invalid loader file!\n");
315 ReturnStatus = EFI_LOAD_ERROR;
316 }
317 if (ReturnStatus != EFI_NOT_FOUND) {
318 break;
319 }
320 }
321 if ((Status == EFI_ACCESS_DENIED) || (Status == EFI_SECURITY_VIOLATION)) {
322 WarnSecureBootError(ImageTitle, Verbose);
323 goto bailout;
324 }
325 SPrint(ErrorInfo, 255, L"while loading %s", ImageTitle);
326 if (CheckError(Status, ErrorInfo)) {
327 if (ErrorInStep != NULL)
328 *ErrorInStep = 1;
329 goto bailout;
330 }
331
332 ReturnStatus = Status = refit_call3_wrapper(BS->HandleProtocol, ChildImageHandle, &LoadedImageProtocol,
333 (VOID **) &ChildLoadedImage);
334 if (CheckError(Status, L"while getting a LoadedImageProtocol handle")) {
335 if (ErrorInStep != NULL)
336 *ErrorInStep = 2;
337 goto bailout_unload;
338 }
339 ChildLoadedImage->LoadOptions = (VOID *)FullLoadOptions;
340 ChildLoadedImage->LoadOptionsSize = ((UINT32)StrLen(FullLoadOptions) + 1) * sizeof(CHAR16);
341 // turn control over to the image
342 // TODO: (optionally) re-enable the EFI watchdog timer!
343
344 // close open file handles
345 UninitRefitLib();
346 ReturnStatus = Status = refit_call3_wrapper(BS->StartImage, ChildImageHandle, NULL, NULL);
347
348 // control returns here when the child image calls Exit()
349 SPrint(ErrorInfo, 255, L"returned from %s", ImageTitle);
350 if (CheckError(Status, ErrorInfo)) {
351 if (ErrorInStep != NULL)
352 *ErrorInStep = 3;
353 }
354
355 // re-open file handles
356 ReinitRefitLib();
357
358 bailout_unload:
359 // unload the image, we don't care if it works or not...
360 Status = refit_call1_wrapper(BS->UnloadImage, ChildImageHandle);
361
362 bailout:
363 MyFreePool(FullLoadOptions);
364 return ReturnStatus;
365 } /* static EFI_STATUS StartEFIImageList() */
366
367 static EFI_STATUS StartEFIImage(IN EFI_DEVICE_PATH *DevicePath,
368 IN CHAR16 *LoadOptions, IN UINTN LoaderType,
369 IN CHAR16 *ImageTitle, IN CHAR8 OSType,
370 OUT UINTN *ErrorInStep,
371 IN BOOLEAN Verbose)
372 {
373 EFI_DEVICE_PATH *DevicePaths[2];
374
375 DevicePaths[0] = DevicePath;
376 DevicePaths[1] = NULL;
377 return StartEFIImageList(DevicePaths, LoadOptions, LoaderType, ImageTitle, OSType, ErrorInStep, Verbose);
378 } /* static EFI_STATUS StartEFIImage() */
379
380 // From gummiboot: Retrieve a raw EFI variable.
381 // Returns EFI status
382 static EFI_STATUS EfivarGetRaw(EFI_GUID *vendor, CHAR16 *name, CHAR8 **buffer, UINTN *size) {
383 CHAR8 *buf;
384 UINTN l;
385 EFI_STATUS err;
386
387 l = sizeof(CHAR16 *) * EFI_MAXIMUM_VARIABLE_SIZE;
388 buf = AllocatePool(l);
389 if (!buf)
390 return EFI_OUT_OF_RESOURCES;
391
392 err = refit_call5_wrapper(RT->GetVariable, name, vendor, NULL, &l, buf);
393 if (EFI_ERROR(err) == EFI_SUCCESS) {
394 *buffer = buf;
395 if (size)
396 *size = l;
397 } else
398 MyFreePool(buf);
399 return err;
400 } // EFI_STATUS EfivarGetRaw()
401
402 // From gummiboot: Set an EFI variable
403 static EFI_STATUS EfivarSetRaw(EFI_GUID *vendor, CHAR16 *name, CHAR8 *buf, UINTN size, BOOLEAN persistent) {
404 UINT32 flags;
405
406 flags = EFI_VARIABLE_BOOTSERVICE_ACCESS|EFI_VARIABLE_RUNTIME_ACCESS;
407 if (persistent)
408 flags |= EFI_VARIABLE_NON_VOLATILE;
409
410 return refit_call5_wrapper(RT->SetVariable, name, vendor, flags, size, buf);
411 } // EFI_STATUS EfivarSetRaw()
412
413 // From gummiboot: Reboot the computer into its built-in user interface
414 static EFI_STATUS RebootIntoFirmware(VOID) {
415 CHAR8 *b;
416 UINTN size;
417 UINT64 osind;
418 EFI_STATUS err;
419
420 osind = EFI_OS_INDICATIONS_BOOT_TO_FW_UI;
421
422 err = EfivarGetRaw(&GlobalGuid, L"OsIndications", &b, &size);
423 if (err == EFI_SUCCESS)
424 osind |= (UINT64)*b;
425 MyFreePool(b);
426
427 err = EfivarSetRaw(&GlobalGuid, L"OsIndications", (CHAR8 *)&osind, sizeof(UINT64), TRUE);
428 if (err != EFI_SUCCESS)
429 return err;
430
431 refit_call4_wrapper(RT->ResetSystem, EfiResetCold, EFI_SUCCESS, 0, NULL);
432 Print(L"Error calling ResetSystem: %r", err);
433 PauseForKey();
434 return err;
435 }
436
437
438 //
439 // EFI OS loader functions
440 //
441
442 static VOID StartLoader(LOADER_ENTRY *Entry)
443 {
444 UINTN ErrorInStep = 0;
445
446 BeginExternalScreen(Entry->UseGraphicsMode, L"Booting OS");
447 StartEFIImage(Entry->DevicePath, Entry->LoadOptions, TYPE_EFI,
448 Basename(Entry->LoaderPath), Entry->OSType, &ErrorInStep, !Entry->UseGraphicsMode);
449 FinishExternalScreen();
450 }
451
452 // Locate an initrd or initramfs file that matches the kernel specified by LoaderPath.
453 // The matching file has a name that begins with "init" and includes the same version
454 // number string as is found in LoaderPath -- but not a longer version number string.
455 // For instance, if LoaderPath is \EFI\kernels\bzImage-3.3.0.efi, and if \EFI\kernels
456 // has a file called initramfs-3.3.0.img, this function will return the string
457 // '\EFI\kernels\initramfs-3.3.0.img'. If the directory ALSO contains the file
458 // initramfs-3.3.0-rc7.img or initramfs-13.3.0.img, those files will NOT match;
459 // however, initmine-3.3.0.img might match. (FindInitrd() returns the first match it
460 // finds). Thus, care should be taken to avoid placing duplicate matching files in
461 // the kernel's directory.
462 // If no matching init file can be found, returns NULL.
463 static CHAR16 * FindInitrd(IN CHAR16 *LoaderPath, IN REFIT_VOLUME *Volume) {
464 CHAR16 *InitrdName = NULL, *FileName, *KernelVersion, *InitrdVersion, *Path;
465 REFIT_DIR_ITER DirIter;
466 EFI_FILE_INFO *DirEntry;
467
468 FileName = Basename(LoaderPath);
469 KernelVersion = FindNumbers(FileName);
470 Path = FindPath(LoaderPath);
471
472 // Add trailing backslash for root directory; necessary on some systems, but must
473 // NOT be added to all directories, since on other systems, a trailing backslash on
474 // anything but the root directory causes them to flake out!
475 if (StrLen(Path) == 0) {
476 MergeStrings(&Path, L"\\", 0);
477 } // if
478 DirIterOpen(Volume->RootDir, Path, &DirIter);
479 // Now add a trailing backslash if it was NOT added earlier, for consistency in
480 // building the InitrdName later....
481 if ((StrLen(Path) > 0) && (Path[StrLen(Path) - 1] != L'\\'))
482 MergeStrings(&Path, L"\\", 0);
483 while ((DirIterNext(&DirIter, 2, L"init*", &DirEntry)) && (InitrdName == NULL)) {
484 InitrdVersion = FindNumbers(DirEntry->FileName);
485 if (KernelVersion != NULL) {
486 if (StriCmp(InitrdVersion, KernelVersion) == 0) {
487 MergeStrings(&InitrdName, Path, 0);
488 MergeStrings(&InitrdName, DirEntry->FileName, 0);
489 } // if
490 } else {
491 if (InitrdVersion == NULL) {
492 MergeStrings(&InitrdName, Path, 0);
493 MergeStrings(&InitrdName, DirEntry->FileName, 0);
494 } // if
495 } // if/else
496 MyFreePool(InitrdVersion);
497 } // while
498 DirIterClose(&DirIter);
499
500 // Note: Don't FreePool(FileName), since Basename returns a pointer WITHIN the string it's passed.
501 MyFreePool(KernelVersion);
502 MyFreePool(Path);
503 return (InitrdName);
504 } // static CHAR16 * FindInitrd()
505
506 LOADER_ENTRY * AddPreparedLoaderEntry(LOADER_ENTRY *Entry) {
507 AddMenuEntry(&MainMenu, (REFIT_MENU_ENTRY *)Entry);
508
509 return(Entry);
510 } // LOADER_ENTRY * AddPreparedLoaderEntry()
511
512 // Creates a copy of a menu screen.
513 // Returns a pointer to the copy of the menu screen.
514 static REFIT_MENU_SCREEN* CopyMenuScreen(REFIT_MENU_SCREEN *Entry) {
515 REFIT_MENU_SCREEN *NewEntry;
516 UINTN i;
517
518 NewEntry = AllocateZeroPool(sizeof(REFIT_MENU_SCREEN));
519 if ((Entry != NULL) && (NewEntry != NULL)) {
520 CopyMem(NewEntry, Entry, sizeof(REFIT_MENU_SCREEN));
521 NewEntry->Title = (Entry->Title) ? StrDuplicate(Entry->Title) : NULL;
522 NewEntry->TimeoutText = (Entry->TimeoutText) ? StrDuplicate(Entry->TimeoutText) : NULL;
523 if (Entry->TitleImage != NULL) {
524 NewEntry->TitleImage = AllocatePool(sizeof(EG_IMAGE));
525 if (NewEntry->TitleImage != NULL)
526 CopyMem(NewEntry->TitleImage, Entry->TitleImage, sizeof(EG_IMAGE));
527 } // if
528 NewEntry->InfoLines = (CHAR16**) AllocateZeroPool(Entry->InfoLineCount * (sizeof(CHAR16*)));
529 for (i = 0; i < Entry->InfoLineCount && NewEntry->InfoLines; i++) {
530 NewEntry->InfoLines[i] = (Entry->InfoLines[i]) ? StrDuplicate(Entry->InfoLines[i]) : NULL;
531 } // for
532 NewEntry->Entries = (REFIT_MENU_ENTRY**) AllocateZeroPool(Entry->EntryCount * (sizeof (REFIT_MENU_ENTRY*)));
533 for (i = 0; i < Entry->EntryCount && NewEntry->Entries; i++) {
534 AddMenuEntry(NewEntry, Entry->Entries[i]);
535 } // for
536 NewEntry->Hint1 = (Entry->Hint1) ? StrDuplicate(Entry->Hint1) : NULL;
537 NewEntry->Hint2 = (Entry->Hint2) ? StrDuplicate(Entry->Hint2) : NULL;
538 } // if
539 return (NewEntry);
540 } // static REFIT_MENU_SCREEN* CopyMenuScreen()
541
542 // Creates a copy of a menu entry. Intended to enable moving a stack-based
543 // menu entry (such as the ones for the "reboot" and "exit" functions) to
544 // to the heap. This enables easier deletion of the whole set of menu
545 // entries when re-scanning.
546 // Returns a pointer to the copy of the menu entry.
547 static REFIT_MENU_ENTRY* CopyMenuEntry(REFIT_MENU_ENTRY *Entry) {
548 REFIT_MENU_ENTRY *NewEntry;
549
550 NewEntry = AllocateZeroPool(sizeof(REFIT_MENU_ENTRY));
551 if ((Entry != NULL) && (NewEntry != NULL)) {
552 CopyMem(NewEntry, Entry, sizeof(REFIT_MENU_ENTRY));
553 NewEntry->Title = (Entry->Title) ? StrDuplicate(Entry->Title) : NULL;
554 if (Entry->BadgeImage != NULL) {
555 NewEntry->BadgeImage = AllocatePool(sizeof(EG_IMAGE));
556 if (NewEntry->BadgeImage != NULL)
557 CopyMem(NewEntry->BadgeImage, Entry->BadgeImage, sizeof(EG_IMAGE));
558 }
559 if (Entry->Image != NULL) {
560 NewEntry->Image = AllocatePool(sizeof(EG_IMAGE));
561 if (NewEntry->Image != NULL)
562 CopyMem(NewEntry->Image, Entry->Image, sizeof(EG_IMAGE));
563 }
564 if (Entry->SubScreen != NULL) {
565 NewEntry->SubScreen = CopyMenuScreen(Entry->SubScreen);
566 }
567 } // if
568 return (NewEntry);
569 } // REFIT_MENU_ENTRY* CopyMenuEntry()
570
571 // Creates a new LOADER_ENTRY data structure and populates it with
572 // default values from the specified Entry, or NULL values if Entry
573 // is unspecified (NULL).
574 // Returns a pointer to the new data structure, or NULL if it
575 // couldn't be allocated
576 LOADER_ENTRY *InitializeLoaderEntry(IN LOADER_ENTRY *Entry) {
577 LOADER_ENTRY *NewEntry = NULL;
578
579 NewEntry = AllocateZeroPool(sizeof(LOADER_ENTRY));
580 if (NewEntry != NULL) {
581 NewEntry->me.Title = NULL;
582 NewEntry->me.Tag = TAG_LOADER;
583 NewEntry->Enabled = TRUE;
584 NewEntry->UseGraphicsMode = FALSE;
585 NewEntry->OSType = 0;
586 if (Entry != NULL) {
587 NewEntry->LoaderPath = (Entry->LoaderPath) ? StrDuplicate(Entry->LoaderPath) : NULL;
588 NewEntry->VolName = (Entry->VolName) ? StrDuplicate(Entry->VolName) : NULL;
589 NewEntry->DevicePath = Entry->DevicePath;
590 NewEntry->UseGraphicsMode = Entry->UseGraphicsMode;
591 NewEntry->LoadOptions = (Entry->LoadOptions) ? StrDuplicate(Entry->LoadOptions) : NULL;
592 NewEntry->InitrdPath = (Entry->InitrdPath) ? StrDuplicate(Entry->InitrdPath) : NULL;
593 }
594 } // if
595 return (NewEntry);
596 } // LOADER_ENTRY *InitializeLoaderEntry()
597
598 // Adds InitrdPath to Options, but only if Options doesn't already include an
599 // initrd= line. Done to enable overriding the default initrd selection in a
600 // refind_linux.conf file's options list.
601 // Returns a pointer to a new string. The calling function is responsible for
602 // freeing its memory.
603 static CHAR16 *AddInitrdToOptions(CHAR16 *Options, CHAR16 *InitrdPath) {
604 CHAR16 *NewOptions = NULL;
605
606 if (Options != NULL)
607 NewOptions = StrDuplicate(Options);
608 if ((InitrdPath != NULL) && !StriSubCmp(L"initrd=", Options)) {
609 MergeStrings(&NewOptions, L"initrd=", L' ');
610 MergeStrings(&NewOptions, InitrdPath, 0);
611 }
612 return NewOptions;
613 } // CHAR16 *AddInitrdToOptions()
614
615 // Prepare a REFIT_MENU_SCREEN data structure for a subscreen entry. This sets up
616 // the default entry that launches the boot loader using the same options as the
617 // main Entry does. Subsequent options can be added by the calling function.
618 // If a subscreen already exists in the Entry that's passed to this function,
619 // it's left unchanged and a pointer to it is returned.
620 // Returns a pointer to the new subscreen data structure, or NULL if there
621 // were problems allocating memory.
622 REFIT_MENU_SCREEN *InitializeSubScreen(IN LOADER_ENTRY *Entry) {
623 CHAR16 *FileName, *MainOptions = NULL;
624 REFIT_MENU_SCREEN *SubScreen = NULL;
625 LOADER_ENTRY *SubEntry;
626
627 FileName = Basename(Entry->LoaderPath);
628 if (Entry->me.SubScreen == NULL) { // No subscreen yet; initialize default entry....
629 SubScreen = AllocateZeroPool(sizeof(REFIT_MENU_SCREEN));
630 if (SubScreen != NULL) {
631 SubScreen->Title = AllocateZeroPool(sizeof(CHAR16) * 256);
632 SPrint(SubScreen->Title, 255, L"Boot Options for %s on %s",
633 (Entry->Title != NULL) ? Entry->Title : FileName, Entry->VolName);
634 SubScreen->TitleImage = Entry->me.Image;
635 // default entry
636 SubEntry = InitializeLoaderEntry(Entry);
637 if (SubEntry != NULL) {
638 SubEntry->me.Title = StrDuplicate(L"Boot using default options");
639 MainOptions = SubEntry->LoadOptions;
640 SubEntry->LoadOptions = AddInitrdToOptions(MainOptions, SubEntry->InitrdPath);
641 MyFreePool(MainOptions);
642 AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry);
643 } // if (SubEntry != NULL)
644 SubScreen->Hint1 = StrDuplicate(SUBSCREEN_HINT1);
645 if (GlobalConfig.HideUIFlags & HIDEUI_FLAG_EDITOR) {
646 SubScreen->Hint2 = StrDuplicate(SUBSCREEN_HINT2_NO_EDITOR);
647 } else {
648 SubScreen->Hint2 = StrDuplicate(SUBSCREEN_HINT2);
649 } // if/else
650 } // if (SubScreen != NULL)
651 } else { // existing subscreen; less initialization, and just add new entry later....
652 SubScreen = Entry->me.SubScreen;
653 } // if/else
654 return SubScreen;
655 } // REFIT_MENU_SCREEN *InitializeSubScreen()
656
657 VOID GenerateSubScreen(LOADER_ENTRY *Entry, IN REFIT_VOLUME *Volume) {
658 REFIT_MENU_SCREEN *SubScreen;
659 LOADER_ENTRY *SubEntry;
660 CHAR16 *InitrdName;
661 CHAR16 DiagsFileName[256];
662 REFIT_FILE *File;
663 UINTN TokenCount;
664 CHAR16 **TokenList;
665
666 // create the submenu
667 if (StrLen(Entry->Title) == 0) {
668 MyFreePool(Entry->Title);
669 Entry->Title = NULL;
670 }
671 SubScreen = InitializeSubScreen(Entry);
672
673 // loader-specific submenu entries
674 if (Entry->OSType == 'M') { // entries for Mac OS X
675 #if defined(EFIX64)
676 SubEntry = InitializeLoaderEntry(Entry);
677 if (SubEntry != NULL) {
678 SubEntry->me.Title = L"Boot Mac OS X with a 64-bit kernel";
679 SubEntry->LoadOptions = L"arch=x86_64";
680 SubEntry->UseGraphicsMode = GlobalConfig.GraphicsFor & GRAPHICS_FOR_OSX;
681 AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry);
682 } // if
683
684 SubEntry = InitializeLoaderEntry(Entry);
685 if (SubEntry != NULL) {
686 SubEntry->me.Title = L"Boot Mac OS X with a 32-bit kernel";
687 SubEntry->LoadOptions = L"arch=i386";
688 SubEntry->UseGraphicsMode = GlobalConfig.GraphicsFor & GRAPHICS_FOR_OSX;
689 AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry);
690 } // if
691 #endif
692
693 if (!(GlobalConfig.HideUIFlags & HIDEUI_FLAG_SINGLEUSER)) {
694 SubEntry = InitializeLoaderEntry(Entry);
695 if (SubEntry != NULL) {
696 SubEntry->me.Title = L"Boot Mac OS X in verbose mode";
697 SubEntry->UseGraphicsMode = FALSE;
698 SubEntry->LoadOptions = L"-v";
699 AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry);
700 } // if
701
702 #if defined(EFIX64)
703 SubEntry = InitializeLoaderEntry(Entry);
704 if (SubEntry != NULL) {
705 SubEntry->me.Title = L"Boot Mac OS X in verbose mode (64-bit)";
706 SubEntry->UseGraphicsMode = FALSE;
707 SubEntry->LoadOptions = L"-v arch=x86_64";
708 AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry);
709 }
710
711 SubEntry = InitializeLoaderEntry(Entry);
712 if (SubEntry != NULL) {
713 SubEntry->me.Title = L"Boot Mac OS X in verbose mode (32-bit)";
714 SubEntry->UseGraphicsMode = FALSE;
715 SubEntry->LoadOptions = L"-v arch=i386";
716 AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry);
717 }
718 #endif
719
720 SubEntry = InitializeLoaderEntry(Entry);
721 if (SubEntry != NULL) {
722 SubEntry->me.Title = L"Boot Mac OS X in single user mode";
723 SubEntry->UseGraphicsMode = FALSE;
724 SubEntry->LoadOptions = L"-v -s";
725 AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry);
726 } // if
727 } // single-user mode allowed
728
729 if (!(GlobalConfig.HideUIFlags & HIDEUI_FLAG_SAFEMODE)) {
730 SubEntry = InitializeLoaderEntry(Entry);
731 if (SubEntry != NULL) {
732 SubEntry->me.Title = L"Boot Mac OS X in safe mode";
733 SubEntry->UseGraphicsMode = FALSE;
734 SubEntry->LoadOptions = L"-v -x";
735 AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry);
736 } // if
737 } // safe mode allowed
738
739 // check for Apple hardware diagnostics
740 StrCpy(DiagsFileName, L"System\\Library\\CoreServices\\.diagnostics\\diags.efi");
741 if (FileExists(Volume->RootDir, DiagsFileName) && !(GlobalConfig.HideUIFlags & HIDEUI_FLAG_HWTEST)) {
742 SubEntry = InitializeLoaderEntry(Entry);
743 if (SubEntry != NULL) {
744 SubEntry->me.Title = L"Run Apple Hardware Test";
745 MyFreePool(SubEntry->LoaderPath);
746 SubEntry->LoaderPath = StrDuplicate(DiagsFileName);
747 SubEntry->DevicePath = FileDevicePath(Volume->DeviceHandle, SubEntry->LoaderPath);
748 SubEntry->UseGraphicsMode = GlobalConfig.GraphicsFor & GRAPHICS_FOR_OSX;
749 AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry);
750 } // if
751 } // if diagnostics entry found
752
753 } else if (Entry->OSType == 'L') { // entries for Linux kernels with EFI stub loaders
754 File = ReadLinuxOptionsFile(Entry->LoaderPath, Volume);
755 if (File != NULL) {
756 InitrdName = FindInitrd(Entry->LoaderPath, Volume);
757 TokenCount = ReadTokenLine(File, &TokenList);
758 // first entry requires special processing, since it was initially set
759 // up with a default title but correct options by InitializeSubScreen(),
760 // earlier....
761 if ((SubScreen->Entries != NULL) && (SubScreen->Entries[0] != NULL)) {
762 MyFreePool(SubScreen->Entries[0]->Title);
763 SubScreen->Entries[0]->Title = TokenList[0] ? StrDuplicate(TokenList[0]) : StrDuplicate(L"Boot Linux");
764 } // if
765 FreeTokenLine(&TokenList, &TokenCount);
766 while ((TokenCount = ReadTokenLine(File, &TokenList)) > 1) {
767 SubEntry = InitializeLoaderEntry(Entry);
768 SubEntry->me.Title = TokenList[0] ? StrDuplicate(TokenList[0]) : StrDuplicate(L"Boot Linux");
769 MyFreePool(SubEntry->LoadOptions);
770 SubEntry->LoadOptions = AddInitrdToOptions(TokenList[1], InitrdName);
771 FreeTokenLine(&TokenList, &TokenCount);
772 SubEntry->UseGraphicsMode = GlobalConfig.GraphicsFor & GRAPHICS_FOR_LINUX;
773 AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry);
774 } // while
775 MyFreePool(InitrdName);
776 MyFreePool(File);
777 } // if
778
779 } else if (Entry->OSType == 'E') { // entries for ELILO
780 SubEntry = InitializeLoaderEntry(Entry);
781 if (SubEntry != NULL) {
782 SubEntry->me.Title = L"Run ELILO in interactive mode";
783 SubEntry->LoadOptions = L"-p";
784 SubEntry->UseGraphicsMode = GlobalConfig.GraphicsFor & GRAPHICS_FOR_ELILO;
785 AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry);
786 }
787
788 SubEntry = InitializeLoaderEntry(Entry);
789 if (SubEntry != NULL) {
790 SubEntry->me.Title = L"Boot Linux for a 17\" iMac or a 15\" MacBook Pro (*)";
791 SubEntry->UseGraphicsMode = TRUE;
792 SubEntry->LoadOptions = L"-d 0 i17";
793 SubEntry->UseGraphicsMode = GlobalConfig.GraphicsFor & GRAPHICS_FOR_ELILO;
794 AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry);
795 }
796
797 SubEntry = InitializeLoaderEntry(Entry);
798 if (SubEntry != NULL) {
799 SubEntry->me.Title = L"Boot Linux for a 20\" iMac (*)";
800 SubEntry->UseGraphicsMode = TRUE;
801 SubEntry->LoadOptions = L"-d 0 i20";
802 SubEntry->UseGraphicsMode = GlobalConfig.GraphicsFor & GRAPHICS_FOR_ELILO;
803 AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry);
804 }
805
806 SubEntry = InitializeLoaderEntry(Entry);
807 if (SubEntry != NULL) {
808 SubEntry->me.Title = L"Boot Linux for a Mac Mini (*)";
809 SubEntry->UseGraphicsMode = TRUE;
810 SubEntry->LoadOptions = L"-d 0 mini";
811 SubEntry->UseGraphicsMode = GlobalConfig.GraphicsFor & GRAPHICS_FOR_ELILO;
812 AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry);
813 }
814
815 AddMenuInfoLine(SubScreen, L"NOTE: This is an example. Entries");
816 AddMenuInfoLine(SubScreen, L"marked with (*) may not work.");
817
818 } else if (Entry->OSType == 'X') { // entries for xom.efi
819 // by default, skip the built-in selection and boot from hard disk only
820 Entry->LoadOptions = L"-s -h";
821
822 SubEntry = InitializeLoaderEntry(Entry);
823 if (SubEntry != NULL) {
824 SubEntry->me.Title = L"Boot Windows from Hard Disk";
825 SubEntry->LoadOptions = L"-s -h";
826 SubEntry->UseGraphicsMode = GlobalConfig.GraphicsFor & GRAPHICS_FOR_WINDOWS;
827 AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry);
828 }
829
830 SubEntry = InitializeLoaderEntry(Entry);
831 if (SubEntry != NULL) {
832 SubEntry->me.Title = L"Boot Windows from CD-ROM";
833 SubEntry->LoadOptions = L"-s -c";
834 SubEntry->UseGraphicsMode = GlobalConfig.GraphicsFor & GRAPHICS_FOR_WINDOWS;
835 AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry);
836 }
837
838 SubEntry = InitializeLoaderEntry(Entry);
839 if (SubEntry != NULL) {
840 SubEntry->me.Title = L"Run XOM in text mode";
841 SubEntry->UseGraphicsMode = FALSE;
842 SubEntry->LoadOptions = L"-v";
843 SubEntry->UseGraphicsMode = GlobalConfig.GraphicsFor & GRAPHICS_FOR_WINDOWS;
844 AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry);
845 }
846 } // entries for xom.efi
847 AddMenuEntry(SubScreen, &MenuEntryReturn);
848 Entry->me.SubScreen = SubScreen;
849 } // VOID GenerateSubScreen()
850
851 // Returns options for a Linux kernel. Reads them from an options file in the
852 // kernel's directory; and if present, adds an initrd= option for an initial
853 // RAM disk file with the same version number as the kernel file.
854 static CHAR16 * GetMainLinuxOptions(IN CHAR16 * LoaderPath, IN REFIT_VOLUME *Volume) {
855 CHAR16 *Options = NULL, *InitrdName, *FullOptions = NULL;
856
857 Options = GetFirstOptionsFromFile(LoaderPath, Volume);
858 InitrdName = FindInitrd(LoaderPath, Volume);
859 FullOptions = AddInitrdToOptions(Options, InitrdName);
860
861 MyFreePool(Options);
862 MyFreePool(InitrdName);
863 return (FullOptions);
864 } // static CHAR16 * GetMainLinuxOptions()
865
866 // Try to guess the name of the Linux distribution & add that name to
867 // OSIconName list.
868 static VOID GuessLinuxDistribution(CHAR16 **OSIconName, REFIT_VOLUME *Volume, CHAR16 *LoaderPath) {
869 UINTN FileSize = 0;
870 REFIT_FILE File;
871 CHAR16** TokenList;
872 UINTN TokenCount = 0;
873
874 // If on Linux root fs, /etc/os-release file probably has clues....
875 if (FileExists(Volume->RootDir, L"etc\\os-release") &&
876 (ReadFile(Volume->RootDir, L"etc\\os-release", &File, &FileSize) == EFI_SUCCESS)) {
877 do {
878 TokenCount = ReadTokenLine(&File, &TokenList);
879 if ((TokenCount > 1) && ((StriCmp(TokenList[0], L"ID") == 0) || (StriCmp(TokenList[0], L"NAME") == 0))) {
880 MergeStrings(OSIconName, TokenList[1], L',');
881 } // if
882 FreeTokenLine(&TokenList, &TokenCount);
883 } while (TokenCount > 0);
884 MyFreePool(File.Buffer);
885 } // if
886
887 // Search for clues in the kernel's filename....
888 if (StriSubCmp(L".fc", LoaderPath))
889 MergeStrings(OSIconName, L"fedora", L',');
890 if (StriSubCmp(L".el", LoaderPath))
891 MergeStrings(OSIconName, L"redhat", L',');
892 } // VOID GuessLinuxDistribution()
893
894 // Sets a few defaults for a loader entry -- mainly the icon, but also the OS type
895 // code and shortcut letter. For Linux EFI stub loaders, also sets kernel options
896 // that will (with luck) work fairly automatically.
897 VOID SetLoaderDefaults(LOADER_ENTRY *Entry, CHAR16 *LoaderPath, REFIT_VOLUME *Volume) {
898 CHAR16 *FileName, *PathOnly, *NoExtension, *OSIconName = NULL, *Temp, *SubString;
899 CHAR16 ShortcutLetter = 0;
900 UINTN i = 0, Length;
901
902 FileName = Basename(LoaderPath);
903 PathOnly = FindPath(LoaderPath);
904 NoExtension = StripEfiExtension(FileName);
905
906 // locate a custom icon for the loader
907 // Anything found here takes precedence over the "hints" in the OSIconName variable
908 if (!Entry->me.Image)
909 Entry->me.Image = egLoadIconAnyType(Volume->RootDir, PathOnly, NoExtension, 128);
910 if (!Entry->me.Image)
911 Entry->me.Image = egCopyImage(Volume->VolIconImage);
912
913 // Begin creating icon "hints" by using last part of directory path leading
914 // to the loader
915 Temp = FindLastDirName(LoaderPath);
916 MergeStrings(&OSIconName, Temp, L',');
917 MyFreePool(Temp);
918 Temp = NULL;
919 if (OSIconName != NULL) {
920 ShortcutLetter = OSIconName[0];
921 }
922
923 // Add every "word" in the volume label, delimited by spaces, dashes (-), or
924 // underscores (_), to the list of hints to be used in searching for OS
925 // icons.
926 if ((Volume->VolName) && (StrLen(Volume->VolName) > 0)) {
927 Temp = SubString = StrDuplicate(Volume->VolName);
928 if (Temp != NULL) {
929 Length = StrLen(Temp);
930 for (i = 0; i < Length; i++) {
931 if ((Temp[i] == L' ') || (Temp[i] == L'_') || (Temp[i] == L'-')) {
932 Temp[i] = 0;
933 if (StrLen(SubString) > 0)
934 MergeStrings(&OSIconName, SubString, L',');
935 SubString = Temp + i + 1;
936 } // if
937 } // for
938 MergeStrings(&OSIconName, SubString, L',');
939 MyFreePool(Temp);
940 } // if
941 } // if
942
943 // detect specific loaders
944 if (StriSubCmp(L"bzImage", LoaderPath) || StriSubCmp(L"vmlinuz", LoaderPath)) {
945 GuessLinuxDistribution(&OSIconName, Volume, LoaderPath);
946 MergeStrings(&OSIconName, L"linux", L',');
947 Entry->OSType = 'L';
948 if (ShortcutLetter == 0)
949 ShortcutLetter = 'L';
950 Entry->LoadOptions = GetMainLinuxOptions(LoaderPath, Volume);
951 Entry->UseGraphicsMode = GlobalConfig.GraphicsFor & GRAPHICS_FOR_LINUX;
952 } else if (StriSubCmp(L"refit", LoaderPath)) {
953 MergeStrings(&OSIconName, L"refit", L',');
954 Entry->OSType = 'R';
955 ShortcutLetter = 'R';
956 } else if (StriSubCmp(L"refind", LoaderPath)) {
957 MergeStrings(&OSIconName, L"refind", L',');
958 Entry->OSType = 'R';
959 ShortcutLetter = 'R';
960 } else if (StriCmp(LoaderPath, MACOSX_LOADER_PATH) == 0) {
961 if (Volume->VolIconImage != NULL) { // custom icon file found
962 Entry->me.Image = Volume->VolIconImage;
963 }
964 MergeStrings(&OSIconName, L"mac", L',');
965 Entry->OSType = 'M';
966 ShortcutLetter = 'M';
967 Entry->UseGraphicsMode = GlobalConfig.GraphicsFor & GRAPHICS_FOR_OSX;
968 } else if (StriCmp(FileName, L"diags.efi") == 0) {
969 MergeStrings(&OSIconName, L"hwtest", L',');
970 } else if (StriCmp(FileName, L"e.efi") == 0 || StriCmp(FileName, L"elilo.efi") == 0 || StriSubCmp(L"elilo", FileName)) {
971 MergeStrings(&OSIconName, L"elilo,linux", L',');
972 Entry->OSType = 'E';
973 if (ShortcutLetter == 0)
974 ShortcutLetter = 'L';
975 Entry->UseGraphicsMode = GlobalConfig.GraphicsFor & GRAPHICS_FOR_ELILO;
976 } else if (StriSubCmp(L"grub", FileName)) {
977 Entry->OSType = 'G';
978 ShortcutLetter = 'G';
979 Entry->UseGraphicsMode = GlobalConfig.GraphicsFor & GRAPHICS_FOR_GRUB;
980 } else if (StriCmp(FileName, L"cdboot.efi") == 0 ||
981 StriCmp(FileName, L"bootmgr.efi") == 0 ||
982 StriCmp(FileName, L"bootmgfw.efi") == 0 ||
983 StriCmp(FileName, L"bkpbootmgfw.efi") == 0) {
984 MergeStrings(&OSIconName, L"win", L',');
985 Entry->OSType = 'W';
986 ShortcutLetter = 'W';
987 Entry->UseGraphicsMode = GlobalConfig.GraphicsFor & GRAPHICS_FOR_WINDOWS;
988 } else if (StriCmp(FileName, L"xom.efi") == 0) {
989 MergeStrings(&OSIconName, L"xom,win", L',');
990 Entry->UseGraphicsMode = TRUE;
991 Entry->OSType = 'X';
992 ShortcutLetter = 'W';
993 Entry->UseGraphicsMode = GlobalConfig.GraphicsFor & GRAPHICS_FOR_WINDOWS;
994 }
995
996 if ((ShortcutLetter >= 'a') && (ShortcutLetter <= 'z'))
997 ShortcutLetter = ShortcutLetter - 'a' + 'A'; // convert lowercase to uppercase
998 Entry->me.ShortcutLetter = ShortcutLetter;
999 if (Entry->me.Image == NULL)
1000 Entry->me.Image = LoadOSIcon(OSIconName, L"unknown", FALSE);
1001 MyFreePool(PathOnly);
1002 } // VOID SetLoaderDefaults()
1003
1004 // Add a specified EFI boot loader to the list, using automatic settings
1005 // for icons, options, etc.
1006 LOADER_ENTRY * AddLoaderEntry(IN CHAR16 *LoaderPath, IN CHAR16 *LoaderTitle, IN REFIT_VOLUME *Volume) {
1007 LOADER_ENTRY *Entry;
1008
1009 CleanUpPathNameSlashes(LoaderPath);
1010 Entry = InitializeLoaderEntry(NULL);
1011 if (Entry != NULL) {
1012 Entry->Title = StrDuplicate((LoaderTitle != NULL) ? LoaderTitle : LoaderPath);
1013 Entry->me.Title = AllocateZeroPool(sizeof(CHAR16) * 256);
1014 SPrint(Entry->me.Title, 255, L"Boot %s from %s ", (LoaderTitle != NULL) ? LoaderTitle : LoaderPath, Volume->VolName);
1015 Entry->me.Row = 0;
1016 Entry->me.BadgeImage = Volume->VolBadgeImage;
1017 if ((LoaderPath != NULL) && (LoaderPath[0] != L'\\')) {
1018 Entry->LoaderPath = StrDuplicate(L"\\");
1019 } else {
1020 Entry->LoaderPath = NULL;
1021 }
1022 MergeStrings(&(Entry->LoaderPath), LoaderPath, 0);
1023 Entry->VolName = Volume->VolName;
1024 Entry->DevicePath = FileDevicePath(Volume->DeviceHandle, Entry->LoaderPath);
1025 SetLoaderDefaults(Entry, LoaderPath, Volume);
1026 GenerateSubScreen(Entry, Volume);
1027 AddMenuEntry(&MainMenu, (REFIT_MENU_ENTRY *)Entry);
1028 }
1029
1030 return(Entry);
1031 } // LOADER_ENTRY * AddLoaderEntry()
1032
1033 // Returns -1 if (Time1 < Time2), +1 if (Time1 > Time2), or 0 if
1034 // (Time1 == Time2). Precision is only to the nearest second; since
1035 // this is used for sorting boot loader entries, differences smaller
1036 // than this are likely to be meaningless (and unlikely!).
1037 INTN TimeComp(IN EFI_TIME *Time1, IN EFI_TIME *Time2) {
1038 INT64 Time1InSeconds, Time2InSeconds;
1039
1040 // Following values are overestimates; I'm assuming 31 days in every month.
1041 // This is fine for the purpose of this function, which is limited
1042 Time1InSeconds = Time1->Second + (Time1->Minute * 60) + (Time1->Hour * 3600) + (Time1->Day * 86400) +
1043 (Time1->Month * 2678400) + ((Time1->Year - 1998) * 32140800);
1044 Time2InSeconds = Time2->Second + (Time2->Minute * 60) + (Time2->Hour * 3600) + (Time2->Day * 86400) +
1045 (Time2->Month * 2678400) + ((Time2->Year - 1998) * 32140800);
1046 if (Time1InSeconds < Time2InSeconds)
1047 return (-1);
1048 else if (Time1InSeconds > Time2InSeconds)
1049 return (1);
1050
1051 return 0;
1052 } // INTN TimeComp()
1053
1054 // Adds a loader list element, keeping it sorted by date. Returns the new
1055 // first element (the one with the most recent date).
1056 static struct LOADER_LIST * AddLoaderListEntry(struct LOADER_LIST *LoaderList, struct LOADER_LIST *NewEntry) {
1057 struct LOADER_LIST *LatestEntry, *CurrentEntry, *PrevEntry = NULL;
1058
1059 LatestEntry = CurrentEntry = LoaderList;
1060 if (LoaderList == NULL) {
1061 LatestEntry = NewEntry;
1062 } else {
1063 while ((CurrentEntry != NULL) && (TimeComp(&(NewEntry->TimeStamp), &(CurrentEntry->TimeStamp)) < 0)) {
1064 PrevEntry = CurrentEntry;
1065 CurrentEntry = CurrentEntry->NextEntry;
1066 } // while
1067 NewEntry->NextEntry = CurrentEntry;
1068 if (PrevEntry == NULL) {
1069 LatestEntry = NewEntry;
1070 } else {
1071 PrevEntry->NextEntry = NewEntry;
1072 } // if/else
1073 } // if/else
1074 return (LatestEntry);
1075 } // static VOID AddLoaderListEntry()
1076
1077 // Delete the LOADER_LIST linked list
1078 static VOID CleanUpLoaderList(struct LOADER_LIST *LoaderList) {
1079 struct LOADER_LIST *Temp;
1080
1081 while (LoaderList != NULL) {
1082 Temp = LoaderList;
1083 LoaderList = LoaderList->NextEntry;
1084 MyFreePool(Temp->FileName);
1085 MyFreePool(Temp);
1086 } // while
1087 } // static VOID CleanUpLoaderList()
1088
1089 // Returns FALSE if the specified file/volume matches the GlobalConfig.DontScanDirs
1090 // or GlobalConfig.DontScanVolumes specification, or if Path points to a volume
1091 // other than the one specified by Volume, or if the specified path is SelfDir.
1092 // Returns TRUE if none of these conditions is met -- that is, if the path is
1093 // eligible for scanning.
1094 static BOOLEAN ShouldScan(REFIT_VOLUME *Volume, CHAR16 *Path) {
1095 CHAR16 *VolName = NULL, *DontScanDir, *PathCopy = NULL;
1096 UINTN i = 0, VolNum;
1097 BOOLEAN ScanIt = TRUE;
1098
1099 if (IsIn(Volume->VolName, GlobalConfig.DontScanVolumes))
1100 return FALSE;
1101
1102 if ((StriCmp(Path, SelfDirPath) == 0) && (Volume->DeviceHandle == SelfVolume->DeviceHandle))
1103 return FALSE;
1104
1105 // See if Path includes an explicit volume declaration that's NOT Volume....
1106 PathCopy = StrDuplicate(Path);
1107 if (SplitVolumeAndFilename(&PathCopy, &VolName)) {
1108 if (StriCmp(VolName, Volume->VolName) != 0) {
1109 if ((StrLen(VolName) > 2) && (VolName[0] == L'f') && (VolName[1] == L's') && (VolName[2] >= L'0') && (VolName[2] <= L'9')) {
1110 VolNum = Atoi(VolName + 2);
1111 if (VolNum != Volume->VolNumber) {
1112 ScanIt = FALSE;
1113 }
1114 } else {
1115 ScanIt = FALSE;
1116 }
1117 } // if
1118 } // if Path includes volume specification
1119 MyFreePool(PathCopy);
1120 MyFreePool(VolName);
1121 VolName = NULL;
1122
1123 // See if Volume is in GlobalConfig.DontScanDirs....
1124 while ((DontScanDir = FindCommaDelimited(GlobalConfig.DontScanDirs, i++)) && ScanIt) {
1125 SplitVolumeAndFilename(&DontScanDir, &VolName);
1126 CleanUpPathNameSlashes(DontScanDir);
1127 if (VolName != NULL) {
1128 if ((StriCmp(VolName, Volume->VolName) == 0) && (StriCmp(DontScanDir, Path) == 0))
1129 ScanIt = FALSE;
1130 if ((StrLen(VolName) > 2) && (VolName[0] == L'f') && (VolName[1] == L's') && (VolName[2] >= L'0') && (VolName[2] <= L'9')) {
1131 VolNum = Atoi(VolName + 2);
1132 if ((VolNum == Volume->VolNumber) && (StriCmp(DontScanDir, Path) == 0))
1133 ScanIt = FALSE;
1134 }
1135 } else {
1136 if (StriCmp(DontScanDir, Path) == 0)
1137 ScanIt = FALSE;
1138 }
1139 MyFreePool(DontScanDir);
1140 MyFreePool(VolName);
1141 DontScanDir = NULL;
1142 } // while()
1143
1144 return ScanIt;
1145 } // BOOLEAN ShouldScan()
1146
1147 // Returns TRUE if the file is byte-for-byte identical with the fallback file
1148 // on the volume AND if the file is not itself the fallback file; returns
1149 // FALSE if the file is not identical to the fallback file OR if the file
1150 // IS the fallback file. Intended for use in excluding the fallback boot
1151 // loader when it's a duplicate of another boot loader.
1152 static BOOLEAN DuplicatesFallback(IN REFIT_VOLUME *Volume, IN CHAR16 *FileName) {
1153 CHAR8 *FileContents, *FallbackContents;
1154 EFI_FILE_HANDLE FileHandle, FallbackHandle;
1155 EFI_FILE_INFO *FileInfo, *FallbackInfo;
1156 UINTN FileSize = 0, FallbackSize = 0;
1157 EFI_STATUS Status;
1158 BOOLEAN AreIdentical = FALSE;
1159
1160 if (!FileExists(Volume->RootDir, FileName) || !FileExists(Volume->RootDir, FALLBACK_FULLNAME))
1161 return FALSE;
1162
1163 CleanUpPathNameSlashes(FileName);
1164
1165 if (StriCmp(FileName, FALLBACK_FULLNAME) == 0)
1166 return FALSE; // identical filenames, so not a duplicate....
1167
1168 Status = refit_call5_wrapper(Volume->RootDir->Open, Volume->RootDir, &FileHandle, FileName, EFI_FILE_MODE_READ, 0);
1169 if (Status == EFI_SUCCESS) {
1170 FileInfo = LibFileInfo(FileHandle);
1171 FileSize = FileInfo->FileSize;
1172 } else {
1173 return FALSE;
1174 }
1175
1176 Status = refit_call5_wrapper(Volume->RootDir->Open, Volume->RootDir, &FallbackHandle, FALLBACK_FULLNAME, EFI_FILE_MODE_READ, 0);
1177 if (Status == EFI_SUCCESS) {
1178 FallbackInfo = LibFileInfo(FallbackHandle);
1179 FallbackSize = FallbackInfo->FileSize;
1180 } else {
1181 refit_call1_wrapper(FileHandle->Close, FileHandle);
1182 return FALSE;
1183 }
1184
1185 if (FallbackSize != FileSize) { // not same size, so can't be identical
1186 AreIdentical = FALSE;
1187 } else { // could be identical; do full check....
1188 FileContents = AllocatePool(FileSize);
1189 FallbackContents = AllocatePool(FallbackSize);
1190 if (FileContents && FallbackContents) {
1191 Status = refit_call3_wrapper(FileHandle->Read, FileHandle, &FileSize, FileContents);
1192 if (Status == EFI_SUCCESS) {
1193 Status = refit_call3_wrapper(FallbackHandle->Read, FallbackHandle, &FallbackSize, FallbackContents);
1194 }
1195 if (Status == EFI_SUCCESS) {
1196 AreIdentical = (CompareMem(FileContents, FallbackContents, FileSize) == 0);
1197 } // if
1198 } // if
1199 MyFreePool(FileContents);
1200 MyFreePool(FallbackContents);
1201 } // if/else
1202
1203 // BUG ALERT: Some systems (e.g., DUET, some Macs with large displays) crash if the
1204 // following two calls are reversed. Go figure....
1205 refit_call1_wrapper(FileHandle->Close, FallbackHandle);
1206 refit_call1_wrapper(FileHandle->Close, FileHandle);
1207 return AreIdentical;
1208 } // BOOLEAN DuplicatesFallback()
1209
1210 // Returns FALSE if two measures of file size are identical for a single file,
1211 // TRUE if not or if the file can't be opened and the other measure is non-0.
1212 // Despite the function's name, this isn't really a direct test of symbolic
1213 // link status, since EFI doesn't officially support symlinks. It does seem
1214 // to be a reliable indicator, though. (OTOH, some disk errors might cause a
1215 // file to fail to open, which would return a false positive -- but as I use
1216 // this function to exclude symbolic links from the list of boot loaders,
1217 // that would be fine, since such boot loaders wouldn't work.)
1218 static BOOLEAN IsSymbolicLink(REFIT_VOLUME *Volume, CHAR16 *Path, EFI_FILE_INFO *DirEntry) {
1219 EFI_FILE_HANDLE FileHandle;
1220 EFI_FILE_INFO *FileInfo = NULL;
1221 EFI_STATUS Status;
1222 UINTN FileSize2 = 0;
1223 CHAR16 *FileName;
1224
1225 FileName = StrDuplicate(Path);
1226 MergeStrings(&FileName, DirEntry->FileName, L'\\');
1227 CleanUpPathNameSlashes(FileName);
1228
1229 Status = refit_call5_wrapper(Volume->RootDir->Open, Volume->RootDir, &FileHandle, FileName, EFI_FILE_MODE_READ, 0);
1230 if (Status == EFI_SUCCESS) {
1231 FileInfo = LibFileInfo(FileHandle);
1232 if (FileInfo != NULL)
1233 FileSize2 = FileInfo->FileSize;
1234 }
1235
1236 MyFreePool(FileName);
1237 MyFreePool(FileInfo);
1238
1239 return (DirEntry->FileSize != FileSize2);
1240 } // BOOLEAN IsSymbolicLink()
1241
1242 // Scan an individual directory for EFI boot loader files and, if found,
1243 // add them to the list. Exception: Ignores FALLBACK_FULLNAME, which is picked
1244 // up in ScanEfiFiles(). Sorts the entries within the loader directory so that
1245 // the most recent one appears first in the list.
1246 // Returns TRUE if a duplicate for FALLBACK_FILENAME was found, FALSE if not.
1247 static BOOLEAN ScanLoaderDir(IN REFIT_VOLUME *Volume, IN CHAR16 *Path, IN CHAR16 *Pattern)
1248 {
1249 EFI_STATUS Status;
1250 REFIT_DIR_ITER DirIter;
1251 EFI_FILE_INFO *DirEntry;
1252 CHAR16 FileName[256], *Extension;
1253 struct LOADER_LIST *LoaderList = NULL, *NewLoader;
1254 BOOLEAN FoundFallbackDuplicate = FALSE;
1255
1256 if ((!SelfDirPath || !Path || ((StriCmp(Path, SelfDirPath) == 0) && (Volume->DeviceHandle != SelfVolume->DeviceHandle)) ||
1257 (StriCmp(Path, SelfDirPath) != 0)) &&
1258 (ShouldScan(Volume, Path))) {
1259 // look through contents of the directory
1260 DirIterOpen(Volume->RootDir, Path, &DirIter);
1261 while (DirIterNext(&DirIter, 2, Pattern, &DirEntry)) {
1262 Extension = FindExtension(DirEntry->FileName);
1263 if (DirEntry->FileName[0] == '.' ||
1264 StriCmp(Extension, L".icns") == 0 ||
1265 StriCmp(Extension, L".png") == 0 ||
1266 (StriCmp(DirEntry->FileName, FALLBACK_BASENAME) == 0 && (StriCmp(Path, L"EFI\\BOOT") == 0)) ||
1267 StriSubCmp(L"shell", DirEntry->FileName) ||
1268 IsSymbolicLink(Volume, Path, DirEntry) || /* is symbolic link */
1269 IsIn(DirEntry->FileName, GlobalConfig.DontScanFiles))
1270 continue; // skip this
1271
1272 if (Path)
1273 SPrint(FileName, 255, L"\\%s\\%s", Path, DirEntry->FileName);
1274 else
1275 SPrint(FileName, 255, L"\\%s", DirEntry->FileName);
1276 CleanUpPathNameSlashes(FileName);
1277
1278 if(!IsValidLoader(Volume->RootDir, FileName))
1279 continue;
1280
1281 NewLoader = AllocateZeroPool(sizeof(struct LOADER_LIST));
1282 if (NewLoader != NULL) {
1283 NewLoader->FileName = StrDuplicate(FileName);
1284 NewLoader->TimeStamp = DirEntry->ModificationTime;
1285 LoaderList = AddLoaderListEntry(LoaderList, NewLoader);
1286 if (DuplicatesFallback(Volume, FileName))
1287 FoundFallbackDuplicate = TRUE;
1288 } // if
1289 MyFreePool(Extension);
1290 } // while
1291
1292 NewLoader = LoaderList;
1293 while (NewLoader != NULL) {
1294 AddLoaderEntry(NewLoader->FileName, NULL, Volume);
1295 NewLoader = NewLoader->NextEntry;
1296 } // while
1297
1298 CleanUpLoaderList(LoaderList);
1299 Status = DirIterClose(&DirIter);
1300 // NOTE: EFI_INVALID_PARAMETER really is an error that should be reported;
1301 // but I've gotten reports from users who are getting this error occasionally
1302 // and I can't find anything wrong or reproduce the problem, so I'm putting
1303 // it down to buggy EFI implementations and ignoring that particular error....
1304 if ((Status != EFI_NOT_FOUND) && (Status != EFI_INVALID_PARAMETER)) {
1305 if (Path)
1306 SPrint(FileName, 255, L"while scanning the %s directory", Path);
1307 else
1308 StrCpy(FileName, L"while scanning the root directory");
1309 CheckError(Status, FileName);
1310 } // if (Status != EFI_NOT_FOUND)
1311 } // if not scanning a blacklisted directory
1312
1313 return FoundFallbackDuplicate;
1314 } /* static VOID ScanLoaderDir() */
1315
1316 static VOID ScanEfiFiles(REFIT_VOLUME *Volume) {
1317 EFI_STATUS Status;
1318 REFIT_DIR_ITER EfiDirIter;
1319 EFI_FILE_INFO *EfiDirEntry;
1320 CHAR16 FileName[256], *Directory, *MatchPatterns, *VolName = NULL, *SelfPath;
1321 UINTN i, Length;
1322 BOOLEAN ScanFallbackLoader = TRUE;
1323 BOOLEAN FoundBRBackup = FALSE;
1324
1325 MatchPatterns = StrDuplicate(LOADER_MATCH_PATTERNS);
1326 if (GlobalConfig.ScanAllLinux)
1327 MergeStrings(&MatchPatterns, LINUX_MATCH_PATTERNS, L',');
1328
1329 if ((Volume->RootDir != NULL) && (Volume->VolName != NULL)) {
1330 // check for Mac OS X boot loader
1331 if (ShouldScan(Volume, L"System\\Library\\CoreServices")) {
1332 StrCpy(FileName, MACOSX_LOADER_PATH);
1333 if (FileExists(Volume->RootDir, FileName) && !IsIn(L"boot.efi", GlobalConfig.DontScanFiles)) {
1334 AddLoaderEntry(FileName, L"Mac OS X", Volume);
1335 if (DuplicatesFallback(Volume, FileName))
1336 ScanFallbackLoader = FALSE;
1337 }
1338
1339 // check for XOM
1340 StrCpy(FileName, L"System\\Library\\CoreServices\\xom.efi");
1341 if (FileExists(Volume->RootDir, FileName) && !IsIn(L"boot.efi", GlobalConfig.DontScanFiles)) {
1342 AddLoaderEntry(FileName, L"Windows XP (XoM)", Volume);
1343 if (DuplicatesFallback(Volume, FileName))
1344 ScanFallbackLoader = FALSE;
1345 }
1346 } // if should scan Mac directory
1347
1348 // check for Microsoft boot loader/menu
1349 if (ShouldScan(Volume, L"EFI\\Microsoft\\Boot")) {
1350 StrCpy(FileName, L"EFI\\Microsoft\\Boot\\bkpbootmgfw.efi");
1351 if (FileExists(Volume->RootDir, FileName) && !IsIn(L"bkpbootmgfw.efi", GlobalConfig.DontScanFiles)) {
1352 AddLoaderEntry(FileName, L"Microsoft EFI boot (Boot Repair backup)", Volume);
1353 FoundBRBackup = TRUE;
1354 if (DuplicatesFallback(Volume, FileName))
1355 ScanFallbackLoader = FALSE;
1356 }
1357 StrCpy(FileName, L"EFI\\Microsoft\\Boot\\bootmgfw.efi");
1358 if (FileExists(Volume->RootDir, FileName) && !IsIn(L"bootmgfw.efi", GlobalConfig.DontScanFiles)) {
1359 if (FoundBRBackup)
1360 AddLoaderEntry(FileName, L"Supposed Microsoft EFI boot (probably GRUB)", Volume);
1361 else
1362 AddLoaderEntry(FileName, L"Microsoft EFI boot", Volume);
1363 if (DuplicatesFallback(Volume, FileName))
1364 ScanFallbackLoader = FALSE;
1365 }
1366 } // if
1367
1368 // scan the root directory for EFI executables
1369 if (ScanLoaderDir(Volume, L"\\", MatchPatterns))
1370 ScanFallbackLoader = FALSE;
1371
1372 // scan subdirectories of the EFI directory (as per the standard)
1373 DirIterOpen(Volume->RootDir, L"EFI", &EfiDirIter);
1374 while (DirIterNext(&EfiDirIter, 1, NULL, &EfiDirEntry)) {
1375 if (StriCmp(EfiDirEntry->FileName, L"tools") == 0 || (StriCmp(EfiDirEntry->FileName, L"memtest") == 0) ||
1376 (StriCmp(EfiDirEntry->FileName, L"memtest86") == 0) || EfiDirEntry->FileName[0] == '.')
1377 continue; // skip this, doesn't contain boot loaders or is scanned later
1378 SPrint(FileName, 255, L"EFI\\%s", EfiDirEntry->FileName);
1379 if (ScanLoaderDir(Volume, FileName, MatchPatterns))
1380 ScanFallbackLoader = FALSE;
1381 } // while()
1382 Status = DirIterClose(&EfiDirIter);
1383 if (Status != EFI_NOT_FOUND)
1384 CheckError(Status, L"while scanning the EFI directory");
1385
1386 // Scan user-specified (or additional default) directories....
1387 i = 0;
1388 while ((Directory = FindCommaDelimited(GlobalConfig.AlsoScan, i++)) != NULL) {
1389 if (ShouldScan(Volume, Directory)) {
1390 SplitVolumeAndFilename(&Directory, &VolName);
1391 CleanUpPathNameSlashes(Directory);
1392 Length = StrLen(Directory);
1393 if ((Length > 0) && ScanLoaderDir(Volume, Directory, MatchPatterns))
1394 ScanFallbackLoader = FALSE;
1395 MyFreePool(VolName);
1396 } // if should scan dir
1397 MyFreePool(Directory);
1398 } // while
1399
1400 // Don't scan the fallback loader if it's on the same volume and a duplicate of rEFInd itself....
1401 SelfPath = DevicePathToStr(SelfLoadedImage->FilePath);
1402 CleanUpPathNameSlashes(SelfPath);
1403 if ((Volume->DeviceHandle == SelfLoadedImage->DeviceHandle) && DuplicatesFallback(Volume, SelfPath))
1404 ScanFallbackLoader = FALSE;
1405
1406 // If not a duplicate & if it exists & if it's not us, create an entry
1407 // for the fallback boot loader
1408 if (ScanFallbackLoader && FileExists(Volume->RootDir, FALLBACK_FULLNAME) && ShouldScan(Volume, L"EFI\\BOOT"))
1409 AddLoaderEntry(FALLBACK_FULLNAME, L"Fallback boot loader", Volume);
1410 } // if
1411 } // static VOID ScanEfiFiles()
1412
1413 // Scan internal disks for valid EFI boot loaders....
1414 static VOID ScanInternal(VOID) {
1415 UINTN VolumeIndex;
1416
1417 for (VolumeIndex = 0; VolumeIndex < VolumesCount; VolumeIndex++) {
1418 if (Volumes[VolumeIndex]->DiskKind == DISK_KIND_INTERNAL) {
1419 ScanEfiFiles(Volumes[VolumeIndex]);
1420 }
1421 } // for
1422 } // static VOID ScanInternal()
1423
1424 // Scan external disks for valid EFI boot loaders....
1425 static VOID ScanExternal(VOID) {
1426 UINTN VolumeIndex;
1427
1428 for (VolumeIndex = 0; VolumeIndex < VolumesCount; VolumeIndex++) {
1429 if (Volumes[VolumeIndex]->DiskKind == DISK_KIND_EXTERNAL) {
1430 ScanEfiFiles(Volumes[VolumeIndex]);
1431 }
1432 } // for
1433 } // static VOID ScanExternal()
1434
1435 // Scan internal disks for valid EFI boot loaders....
1436 static VOID ScanOptical(VOID) {
1437 UINTN VolumeIndex;
1438
1439 for (VolumeIndex = 0; VolumeIndex < VolumesCount; VolumeIndex++) {
1440 if (Volumes[VolumeIndex]->DiskKind == DISK_KIND_OPTICAL) {
1441 ScanEfiFiles(Volumes[VolumeIndex]);
1442 }
1443 } // for
1444 } // static VOID ScanOptical()
1445
1446 //
1447 // legacy boot functions
1448 //
1449
1450 static EFI_STATUS ActivateMbrPartition(IN EFI_BLOCK_IO *BlockIO, IN UINTN PartitionIndex)
1451 {
1452 EFI_STATUS Status;
1453 UINT8 SectorBuffer[512];
1454 MBR_PARTITION_INFO *MbrTable, *EMbrTable;
1455 UINT32 ExtBase, ExtCurrent, NextExtCurrent;
1456 UINTN LogicalPartitionIndex = 4;
1457 UINTN i;
1458 BOOLEAN HaveBootCode;
1459
1460 // read MBR
1461 Status = refit_call5_wrapper(BlockIO->ReadBlocks, BlockIO, BlockIO->Media->MediaId, 0, 512, SectorBuffer);
1462 if (EFI_ERROR(Status))
1463 return Status;
1464 if (*((UINT16 *)(SectorBuffer + 510)) != 0xaa55)
1465 return EFI_NOT_FOUND; // safety measure #1
1466
1467 // add boot code if necessary
1468 HaveBootCode = FALSE;
1469 for (i = 0; i < MBR_BOOTCODE_SIZE; i++) {
1470 if (SectorBuffer[i] != 0) {
1471 HaveBootCode = TRUE;
1472 break;
1473 }
1474 }
1475 if (!HaveBootCode) {
1476 // no boot code found in the MBR, add the syslinux MBR code
1477 SetMem(SectorBuffer, MBR_BOOTCODE_SIZE, 0);
1478 CopyMem(SectorBuffer, syslinux_mbr, SYSLINUX_MBR_SIZE);
1479 }
1480
1481 // set the partition active
1482 MbrTable = (MBR_PARTITION_INFO *)(SectorBuffer + 446);
1483 ExtBase = 0;
1484 for (i = 0; i < 4; i++) {
1485 if (MbrTable[i].Flags != 0x00 && MbrTable[i].Flags != 0x80)
1486 return EFI_NOT_FOUND; // safety measure #2
1487 if (i == PartitionIndex)
1488 MbrTable[i].Flags = 0x80;
1489 else if (PartitionIndex >= 4 && IS_EXTENDED_PART_TYPE(MbrTable[i].Type)) {
1490 MbrTable[i].Flags = 0x80;
1491 ExtBase = MbrTable[i].StartLBA;
1492 } else
1493 MbrTable[i].Flags = 0x00;
1494 }
1495
1496 // write MBR
1497 Status = refit_call5_wrapper(BlockIO->WriteBlocks, BlockIO, BlockIO->Media->MediaId, 0, 512, SectorBuffer);
1498 if (EFI_ERROR(Status))
1499 return Status;
1500
1501 if (PartitionIndex >= 4) {
1502 // we have to activate a logical partition, so walk the EMBR chain
1503
1504 // NOTE: ExtBase was set above while looking at the MBR table
1505 for (ExtCurrent = ExtBase; ExtCurrent; ExtCurrent = NextExtCurrent) {
1506 // read current EMBR
1507 Status = refit_call5_wrapper(BlockIO->ReadBlocks, BlockIO, BlockIO->Media->MediaId, ExtCurrent, 512, SectorBuffer);
1508 if (EFI_ERROR(Status))
1509 return Status;
1510 if (*((UINT16 *)(SectorBuffer + 510)) != 0xaa55)
1511 return EFI_NOT_FOUND; // safety measure #3
1512
1513 // scan EMBR, set appropriate partition active
1514 EMbrTable = (MBR_PARTITION_INFO *)(SectorBuffer + 446);
1515 NextExtCurrent = 0;
1516 for (i = 0; i < 4; i++) {
1517 if (EMbrTable[i].Flags != 0x00 && EMbrTable[i].Flags != 0x80)
1518 return EFI_NOT_FOUND; // safety measure #4
1519 if (EMbrTable[i].StartLBA == 0 || EMbrTable[i].Size == 0)
1520 break;
1521 if (IS_EXTENDED_PART_TYPE(EMbrTable[i].Type)) {
1522 // link to next EMBR
1523 NextExtCurrent = ExtBase + EMbrTable[i].StartLBA;
1524 EMbrTable[i].Flags = (PartitionIndex >= LogicalPartitionIndex) ? 0x80 : 0x00;
1525 break;
1526 } else {
1527 // logical partition
1528 EMbrTable[i].Flags = (PartitionIndex == LogicalPartitionIndex) ? 0x80 : 0x00;
1529 LogicalPartitionIndex++;
1530 }
1531 }
1532
1533 // write current EMBR
1534 Status = refit_call5_wrapper(BlockIO->WriteBlocks, BlockIO, BlockIO->Media->MediaId, ExtCurrent, 512, SectorBuffer);
1535 if (EFI_ERROR(Status))
1536 return Status;
1537
1538 if (PartitionIndex < LogicalPartitionIndex)
1539 break; // stop the loop, no need to touch further EMBRs
1540 }
1541
1542 }
1543
1544 return EFI_SUCCESS;
1545 } /* static EFI_STATUS ActivateMbrPartition() */
1546
1547 // early 2006 Core Duo / Core Solo models
1548 static UINT8 LegacyLoaderDevicePath1Data[] = {
1549 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1550 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1551 0xFF, 0xFF, 0xF9, 0xFF, 0x00, 0x00, 0x00, 0x00,
1552 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1553 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1554 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1555 };
1556 // mid-2006 Mac Pro (and probably other Core 2 models)
1557 static UINT8 LegacyLoaderDevicePath2Data[] = {
1558 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1559 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1560 0xFF, 0xFF, 0xF7, 0xFF, 0x00, 0x00, 0x00, 0x00,
1561 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1562 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1563 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1564 };
1565 // mid-2007 MBP ("Santa Rosa" based models)
1566 static UINT8 LegacyLoaderDevicePath3Data[] = {
1567 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1568 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1569 0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
1570 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1571 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1572 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1573 };
1574 // early-2008 MBA
1575 static UINT8 LegacyLoaderDevicePath4Data[] = {
1576 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1577 0x00, 0x00, 0xC0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1578 0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
1579 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1580 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1581 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1582 };
1583 // late-2008 MB/MBP (NVidia chipset)
1584 static UINT8 LegacyLoaderDevicePath5Data[] = {
1585 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1586 0x00, 0x40, 0xCB, 0xFF, 0x00, 0x00, 0x00, 0x00,
1587 0xFF, 0xBF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
1588 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1589 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1590 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1591 };
1592
1593 static EFI_DEVICE_PATH *LegacyLoaderList[] = {
1594 (EFI_DEVICE_PATH *)LegacyLoaderDevicePath1Data,
1595 (EFI_DEVICE_PATH *)LegacyLoaderDevicePath2Data,
1596 (EFI_DEVICE_PATH *)LegacyLoaderDevicePath3Data,
1597 (EFI_DEVICE_PATH *)LegacyLoaderDevicePath4Data,
1598 (EFI_DEVICE_PATH *)LegacyLoaderDevicePath5Data,
1599 NULL
1600 };
1601
1602 #define MAX_DISCOVERED_PATHS (16)
1603
1604 static VOID StartLegacy(IN LEGACY_ENTRY *Entry)
1605 {
1606 EFI_STATUS Status;
1607 EG_IMAGE *BootLogoImage;
1608 UINTN ErrorInStep = 0;
1609 EFI_DEVICE_PATH *DiscoveredPathList[MAX_DISCOVERED_PATHS];
1610
1611 BeginExternalScreen(TRUE, L"Booting Legacy OS (Mac mode)");
1612
1613 BootLogoImage = LoadOSIcon(Entry->Volume->OSIconName, L"legacy", TRUE);
1614 if (BootLogoImage != NULL)
1615 BltImageAlpha(BootLogoImage,
1616 (UGAWidth - BootLogoImage->Width ) >> 1,
1617 (UGAHeight - BootLogoImage->Height) >> 1,
1618 &StdBackgroundPixel);
1619
1620 if (Entry->Volume->IsMbrPartition) {
1621 ActivateMbrPartition(Entry->Volume->WholeDiskBlockIO, Entry->Volume->MbrPartitionIndex);
1622 }
1623
1624 ExtractLegacyLoaderPaths(DiscoveredPathList, MAX_DISCOVERED_PATHS, LegacyLoaderList);
1625
1626 Status = StartEFIImageList(DiscoveredPathList, Entry->LoadOptions, TYPE_LEGACY, L"legacy loader", 0, &ErrorInStep, TRUE);
1627 if (Status == EFI_NOT_FOUND) {
1628 if (ErrorInStep == 1) {
1629 Print(L"\nPlease make sure that you have the latest firmware update installed.\n");
1630 } else if (ErrorInStep == 3) {
1631 Print(L"\nThe firmware refused to boot from the selected volume. Note that external\n"
1632 L"hard drives are not well-supported by Apple's firmware for legacy OS booting.\n");
1633 }
1634 }
1635 FinishExternalScreen();
1636 } /* static VOID StartLegacy() */
1637
1638 // Start a device on a non-Mac using the EFI_LEGACY_BIOS_PROTOCOL
1639 #ifdef __MAKEWITH_TIANO
1640 static VOID StartLegacyUEFI(IN LEGACY_ENTRY *Entry)
1641 {
1642 BeginExternalScreen(TRUE, L"Booting Legacy OS (UEFI mode)");
1643
1644 BdsLibConnectDevicePath (Entry->BdsOption->DevicePath);
1645 BdsLibDoLegacyBoot(Entry->BdsOption);
1646
1647 // If we get here, it means that there was a failure....
1648 Print(L"Failure booting legacy (BIOS) OS.");
1649 PauseForKey();
1650 FinishExternalScreen();
1651 } // static VOID StartLegacyUEFI()
1652 #endif // __MAKEWITH_TIANO
1653
1654 static LEGACY_ENTRY * AddLegacyEntry(IN CHAR16 *LoaderTitle, IN REFIT_VOLUME *Volume)
1655 {
1656 LEGACY_ENTRY *Entry, *SubEntry;
1657 REFIT_MENU_SCREEN *SubScreen;
1658 CHAR16 *VolDesc;
1659 CHAR16 ShortcutLetter = 0;
1660
1661 if (LoaderTitle == NULL) {
1662 if (Volume->OSName != NULL) {
1663 LoaderTitle = Volume->OSName;
1664 if (LoaderTitle[0] == 'W' || LoaderTitle[0] == 'L')
1665 ShortcutLetter = LoaderTitle[0];
1666 } else
1667 LoaderTitle = L"Legacy OS";
1668 }
1669 if (Volume->VolName != NULL)
1670 VolDesc = Volume->VolName;
1671 else
1672 VolDesc = (Volume->DiskKind == DISK_KIND_OPTICAL) ? L"CD" : L"HD";
1673
1674 // prepare the menu entry
1675 Entry = AllocateZeroPool(sizeof(LEGACY_ENTRY));
1676 Entry->me.Title = AllocateZeroPool(256 * sizeof(CHAR16));
1677 SPrint(Entry->me.Title, 255, L"Boot %s from %s", LoaderTitle, VolDesc);
1678 Entry->me.Tag = TAG_LEGACY;
1679 Entry->me.Row = 0;
1680 Entry->me.ShortcutLetter = ShortcutLetter;
1681 Entry->me.Image = LoadOSIcon(Volume->OSIconName, L"legacy", FALSE);
1682 Entry->me.BadgeImage = Volume->VolBadgeImage;
1683 Entry->Volume = Volume;
1684 Entry->LoadOptions = (Volume->DiskKind == DISK_KIND_OPTICAL) ? L"CD" :
1685 ((Volume->DiskKind == DISK_KIND_EXTERNAL) ? L"USB" : L"HD");
1686 Entry->Enabled = TRUE;
1687
1688 // create the submenu
1689 SubScreen = AllocateZeroPool(sizeof(REFIT_MENU_SCREEN));
1690 SubScreen->Title = AllocateZeroPool(256 * sizeof(CHAR16));
1691 SPrint(SubScreen->Title, 255, L"Boot Options for %s on %s", LoaderTitle, VolDesc);
1692 SubScreen->TitleImage = Entry->me.Image;
1693 SubScreen->Hint1 = StrDuplicate(SUBSCREEN_HINT1);
1694 if (GlobalConfig.HideUIFlags & HIDEUI_FLAG_EDITOR) {
1695 SubScreen->Hint2 = StrDuplicate(SUBSCREEN_HINT2_NO_EDITOR);
1696 } else {
1697 SubScreen->Hint2 = StrDuplicate(SUBSCREEN_HINT2);
1698 } // if/else
1699
1700 // default entry
1701 SubEntry = AllocateZeroPool(sizeof(LEGACY_ENTRY));
1702 SubEntry->me.Title = AllocateZeroPool(256 * sizeof(CHAR16));
1703 SPrint(SubEntry->me.Title, 255, L"Boot %s", LoaderTitle);
1704 SubEntry->me.Tag = TAG_LEGACY;
1705 SubEntry->Volume = Entry->Volume;
1706 SubEntry->LoadOptions = Entry->LoadOptions;
1707 AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry);
1708
1709 AddMenuEntry(SubScreen, &MenuEntryReturn);
1710 Entry->me.SubScreen = SubScreen;
1711 AddMenuEntry(&MainMenu, (REFIT_MENU_ENTRY *)Entry);
1712 return Entry;
1713 } /* static LEGACY_ENTRY * AddLegacyEntry() */
1714
1715
1716 #ifdef __MAKEWITH_GNUEFI
1717 static VOID ScanLegacyUEFI(IN UINTN DiskType){}
1718 #else
1719 // default volume badge icon based on disk kind
1720 static EG_IMAGE * GetDiskBadge(IN UINTN DiskType) {
1721 EG_IMAGE * Badge = NULL;
1722
1723 switch (DiskType) {
1724 case BBS_HARDDISK:
1725 Badge = BuiltinIcon(BUILTIN_ICON_VOL_INTERNAL);
1726 break;
1727 case BBS_USB:
1728 Badge = BuiltinIcon(BUILTIN_ICON_VOL_EXTERNAL);
1729 break;
1730 case BBS_CDROM:
1731 Badge = BuiltinIcon(BUILTIN_ICON_VOL_OPTICAL);
1732 break;
1733 } // switch()
1734 return Badge;
1735 } // static EG_IMAGE * GetDiskBadge()
1736
1737 /**
1738 Create a rEFInd boot option from a Legacy BIOS protocol option.
1739 */
1740 static LEGACY_ENTRY * AddLegacyEntryUEFI(BDS_COMMON_OPTION *BdsOption, IN UINT16 DiskType)
1741 {
1742 LEGACY_ENTRY *Entry, *SubEntry;
1743 REFIT_MENU_SCREEN *SubScreen;
1744 CHAR16 ShortcutLetter = 0;
1745 CHAR16 *LegacyDescription = BdsOption->Description;
1746
1747 // prepare the menu entry
1748 Entry = AllocateZeroPool(sizeof(LEGACY_ENTRY));
1749 Entry->me.Title = AllocateZeroPool(256 * sizeof(CHAR16));
1750 SPrint(Entry->me.Title, 255, L"Boot legacy target %s", LegacyDescription);
1751 Entry->me.Tag = TAG_LEGACY_UEFI;
1752 Entry->me.Row = 0;
1753 Entry->me.ShortcutLetter = ShortcutLetter;
1754 Entry->me.Image = LoadOSIcon(L"legacy", L"legacy", TRUE);
1755 Entry->LoadOptions = (DiskType == BBS_CDROM) ? L"CD" :
1756 ((DiskType == BBS_USB) ? L"USB" : L"HD");
1757 Entry->me.BadgeImage = GetDiskBadge(DiskType);
1758 Entry->BdsOption = BdsOption;
1759 Entry->Enabled = TRUE;
1760
1761 // create the submenu
1762 SubScreen = AllocateZeroPool(sizeof(REFIT_MENU_SCREEN));
1763 SubScreen->Title = AllocateZeroPool(256 * sizeof(CHAR16));
1764 SPrint(SubScreen->Title, 255, L"No boot options for legacy target");
1765 SubScreen->TitleImage = Entry->me.Image;
1766 SubScreen->Hint1 = StrDuplicate(SUBSCREEN_HINT1);
1767 if (GlobalConfig.HideUIFlags & HIDEUI_FLAG_EDITOR) {
1768 SubScreen->Hint2 = StrDuplicate(SUBSCREEN_HINT2_NO_EDITOR);
1769 } else {
1770 SubScreen->Hint2 = StrDuplicate(SUBSCREEN_HINT2);
1771 } // if/else
1772
1773 // default entry
1774 SubEntry = AllocateZeroPool(sizeof(LEGACY_ENTRY));
1775 SubEntry->me.Title = AllocateZeroPool(256 * sizeof(CHAR16));
1776 SPrint(SubEntry->me.Title, 255, L"Boot %s", LegacyDescription);
1777 SubEntry->me.Tag = TAG_LEGACY_UEFI;
1778 Entry->BdsOption = BdsOption;
1779 AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry);
1780
1781 AddMenuEntry(SubScreen, &MenuEntryReturn);
1782 Entry->me.SubScreen = SubScreen;
1783 AddMenuEntry(&MainMenu, (REFIT_MENU_ENTRY *)Entry);
1784 return Entry;
1785 } /* static LEGACY_ENTRY * AddLegacyEntryUEFI() */
1786
1787 /**
1788 Scan for legacy BIOS targets on machines that implement EFI_LEGACY_BIOS_PROTOCOL.
1789 In testing, protocol has not been implemented on Macs but has been
1790 implemented on several Dell PCs and an ASUS motherboard.
1791 Restricts output to disks of the specified DiskType.
1792 */
1793 static VOID ScanLegacyUEFI(IN UINTN DiskType)
1794 {
1795 EFI_STATUS Status;
1796 EFI_LEGACY_BIOS_PROTOCOL *LegacyBios;
1797 UINT16 *BootOrder = NULL;
1798 UINTN Index = 0;
1799 CHAR16 BootOption[10];
1800 UINTN BootOrderSize = 0;
1801 CHAR16 Buffer[20];
1802 BDS_COMMON_OPTION *BdsOption;
1803 LIST_ENTRY TempList;
1804 BBS_BBS_DEVICE_PATH * BbsDevicePath = NULL;
1805
1806 InitializeListHead (&TempList);
1807 ZeroMem (Buffer, sizeof (Buffer));
1808
1809 // If LegacyBios protocol is not implemented on this platform, then
1810 //we do not support this type of legacy boot on this machine.
1811 Status = gBS->LocateProtocol(&gEfiLegacyBootProtocolGuid, NULL, (VOID **) &LegacyBios);
1812 if (EFI_ERROR (Status))
1813 return;
1814
1815 // Grab the boot order
1816 BootOrder = BdsLibGetVariableAndSize(L"BootOrder", &gEfiGlobalVariableGuid, &BootOrderSize);
1817 if (BootOrder == NULL) {
1818 BootOrderSize = 0;
1819 }
1820
1821 Index = 0;
1822 while (Index < BootOrderSize / sizeof (UINT16))
1823 {
1824 // Grab each boot option variable from the boot order, and convert
1825 // the variable into a BDS boot option
1826 UnicodeSPrint (BootOption, sizeof (BootOption), L"Boot%04x", BootOrder[Index]);
1827 BdsOption = BdsLibVariableToOption (&TempList, BootOption);
1828
1829 if (BdsOption != NULL) {
1830 BbsDevicePath = (BBS_BBS_DEVICE_PATH *)BdsOption->DevicePath;
1831
1832 // Only add the entry if it is of a requested type (e.g. USB, HD)
1833
1834 // Two checks necessary because some systems return EFI boot loaders
1835 // with a DeviceType value that would inappropriately include them
1836 // as legacy loaders....
1837 if ((BbsDevicePath->DeviceType == DiskType) && (BdsOption->DevicePath->Type == DEVICE_TYPE_BIOS)) {
1838 AddLegacyEntryUEFI(BdsOption, BbsDevicePath->DeviceType);
1839 }
1840 }
1841 Index++;
1842 }
1843 } /* static VOID ScanLegacyUEFI() */
1844 #endif // __MAKEWITH_GNUEFI
1845
1846 static VOID ScanLegacyVolume(REFIT_VOLUME *Volume, UINTN VolumeIndex) {
1847 UINTN VolumeIndex2;
1848 BOOLEAN ShowVolume, HideIfOthersFound;
1849
1850 ShowVolume = FALSE;
1851 HideIfOthersFound = FALSE;
1852 if (Volume->IsAppleLegacy) {
1853 ShowVolume = TRUE;
1854 HideIfOthersFound = TRUE;
1855 } else if (Volume->HasBootCode) {
1856 ShowVolume = TRUE;
1857 if (Volume->BlockIO == Volume->WholeDiskBlockIO &&
1858 Volume->BlockIOOffset == 0 &&
1859 Volume->OSName == NULL)
1860 // this is a whole disk (MBR) entry; hide if we have entries for partitions
1861 HideIfOthersFound = TRUE;
1862 }
1863 if (HideIfOthersFound) {
1864 // check for other bootable entries on the same disk
1865 for (VolumeIndex2 = 0; VolumeIndex2 < VolumesCount; VolumeIndex2++) {
1866 if (VolumeIndex2 != VolumeIndex && Volumes[VolumeIndex2]->HasBootCode &&
1867 Volumes[VolumeIndex2]->WholeDiskBlockIO == Volume->WholeDiskBlockIO)
1868 ShowVolume = FALSE;
1869 }
1870 }
1871
1872 if (ShowVolume)
1873 AddLegacyEntry(NULL, Volume);
1874 } // static VOID ScanLegacyVolume()
1875
1876 // Scan attached optical discs for legacy (BIOS) boot code
1877 // and add anything found to the list....
1878 static VOID ScanLegacyDisc(VOID)
1879 {
1880 UINTN VolumeIndex;
1881 REFIT_VOLUME *Volume;
1882
1883 if (GlobalConfig.LegacyType == LEGACY_TYPE_MAC) {
1884 for (VolumeIndex = 0; VolumeIndex < VolumesCount; VolumeIndex++) {
1885 Volume = Volumes[VolumeIndex];
1886 if (Volume->DiskKind == DISK_KIND_OPTICAL)
1887 ScanLegacyVolume(Volume, VolumeIndex);
1888 } // for
1889 } else if (GlobalConfig.LegacyType == LEGACY_TYPE_UEFI) {
1890 ScanLegacyUEFI(BBS_CDROM);
1891 }
1892 } /* static VOID ScanLegacyDisc() */
1893
1894 // Scan internal hard disks for legacy (BIOS) boot code
1895 // and add anything found to the list....
1896 static VOID ScanLegacyInternal(VOID)
1897 {
1898 UINTN VolumeIndex;
1899 REFIT_VOLUME *Volume;
1900
1901 if (GlobalConfig.LegacyType == LEGACY_TYPE_MAC) {
1902 for (VolumeIndex = 0; VolumeIndex < VolumesCount; VolumeIndex++) {
1903 Volume = Volumes[VolumeIndex];
1904 if (Volume->DiskKind == DISK_KIND_INTERNAL)
1905 ScanLegacyVolume(Volume, VolumeIndex);
1906 } // for
1907 } else if (GlobalConfig.LegacyType == LEGACY_TYPE_UEFI) {
1908 ScanLegacyUEFI(BBS_HARDDISK);
1909 }
1910 } /* static VOID ScanLegacyInternal() */
1911
1912 // Scan external disks for legacy (BIOS) boot code
1913 // and add anything found to the list....
1914 static VOID ScanLegacyExternal(VOID)
1915 {
1916 UINTN VolumeIndex;
1917 REFIT_VOLUME *Volume;
1918
1919 if (GlobalConfig.LegacyType == LEGACY_TYPE_MAC) {
1920 for (VolumeIndex = 0; VolumeIndex < VolumesCount; VolumeIndex++) {
1921 Volume = Volumes[VolumeIndex];
1922 if (Volume->DiskKind == DISK_KIND_EXTERNAL)
1923 ScanLegacyVolume(Volume, VolumeIndex);
1924 } // for
1925 } else if (GlobalConfig.LegacyType == LEGACY_TYPE_UEFI) {
1926 ScanLegacyUEFI(BBS_USB);
1927 }
1928 } /* static VOID ScanLegacyExternal() */
1929
1930 //
1931 // pre-boot tool functions
1932 //
1933
1934 static VOID StartTool(IN LOADER_ENTRY *Entry)
1935 {
1936 BeginExternalScreen(Entry->UseGraphicsMode, Entry->me.Title + 6); // assumes "Start <title>" as assigned below
1937 StartEFIImage(Entry->DevicePath, Entry->LoadOptions, TYPE_EFI,
1938 Basename(Entry->LoaderPath), Entry->OSType, NULL, TRUE);
1939 FinishExternalScreen();
1940 } /* static VOID StartTool() */
1941
1942 static LOADER_ENTRY * AddToolEntry(EFI_HANDLE DeviceHandle, IN CHAR16 *LoaderPath, IN CHAR16 *LoaderTitle, IN EG_IMAGE *Image,
1943 IN CHAR16 ShortcutLetter, IN BOOLEAN UseGraphicsMode)
1944 {
1945 LOADER_ENTRY *Entry;
1946 CHAR16 *TitleStr = NULL;
1947
1948 Entry = AllocateZeroPool(sizeof(LOADER_ENTRY));
1949
1950 MergeStrings(&TitleStr, L"Start ", 0);
1951 MergeStrings(&TitleStr, LoaderTitle, 0);
1952 Entry->me.Title = TitleStr;
1953 Entry->me.Tag = TAG_TOOL;
1954 Entry->me.Row = 1;
1955 Entry->me.ShortcutLetter = ShortcutLetter;
1956 Entry->me.Image = Image;
1957 Entry->LoaderPath = (LoaderPath) ? StrDuplicate(LoaderPath) : NULL;
1958 Entry->DevicePath = FileDevicePath(DeviceHandle, Entry->LoaderPath);
1959 Entry->UseGraphicsMode = UseGraphicsMode;
1960
1961 AddMenuEntry(&MainMenu, (REFIT_MENU_ENTRY *)Entry);
1962 return Entry;
1963 } /* static LOADER_ENTRY * AddToolEntry() */
1964
1965 //
1966 // pre-boot driver functions
1967 //
1968
1969 static UINTN ScanDriverDir(IN CHAR16 *Path)
1970 {
1971 EFI_STATUS Status;
1972 REFIT_DIR_ITER DirIter;
1973 UINTN NumFound = 0;
1974 EFI_FILE_INFO *DirEntry;
1975 CHAR16 FileName[256];
1976
1977 CleanUpPathNameSlashes(Path);
1978 // look through contents of the directory
1979 DirIterOpen(SelfRootDir, Path, &DirIter);
1980 while (DirIterNext(&DirIter, 2, LOADER_MATCH_PATTERNS, &DirEntry)) {
1981 if (DirEntry->FileName[0] == '.')
1982 continue; // skip this
1983
1984 SPrint(FileName, 255, L"%s\\%s", Path, DirEntry->FileName);
1985 NumFound++;
1986 Status = StartEFIImage(FileDevicePath(SelfLoadedImage->DeviceHandle, FileName),
1987 L"", TYPE_EFI, DirEntry->FileName, 0, NULL, FALSE);
1988 }
1989 Status = DirIterClose(&DirIter);
1990 if (Status != EFI_NOT_FOUND) {
1991 SPrint(FileName, 255, L"while scanning the %s directory", Path);
1992 CheckError(Status, FileName);
1993 }
1994 return (NumFound);
1995 }
1996
1997 #ifdef __MAKEWITH_GNUEFI
1998 static EFI_STATUS ConnectAllDriversToAllControllers(VOID)
1999 {
2000 EFI_STATUS Status;
2001 UINTN AllHandleCount;
2002 EFI_HANDLE *AllHandleBuffer;
2003 UINTN Index;
2004 UINTN HandleCount;
2005 EFI_HANDLE *HandleBuffer;
2006 UINT32 *HandleType;
2007 UINTN HandleIndex;
2008 BOOLEAN Parent;
2009 BOOLEAN Device;
2010
2011 Status = LibLocateHandle(AllHandles,
2012 NULL,
2013 NULL,
2014 &AllHandleCount,
2015 &AllHandleBuffer);
2016 if (EFI_ERROR(Status))
2017 return Status;
2018
2019 for (Index = 0; Index < AllHandleCount; Index++) {
2020 //
2021 // Scan the handle database
2022 //
2023 Status = LibScanHandleDatabase(NULL,
2024 NULL,
2025 AllHandleBuffer[Index],
2026 NULL,
2027 &HandleCount,
2028 &HandleBuffer,
2029 &HandleType);
2030 if (EFI_ERROR (Status))
2031 goto Done;
2032
2033 Device = TRUE;
2034 if (HandleType[Index] & EFI_HANDLE_TYPE_DRIVER_BINDING_HANDLE)
2035 Device = FALSE;
2036 if (HandleType[Index] & EFI_HANDLE_TYPE_IMAGE_HANDLE)
2037 Device = FALSE;
2038
2039 if (Device) {
2040 Parent = FALSE;
2041 for (HandleIndex = 0; HandleIndex < HandleCount; HandleIndex++) {
2042 if (HandleType[HandleIndex] & EFI_HANDLE_TYPE_PARENT_HANDLE)
2043 Parent = TRUE;
2044 } // for
2045
2046 if (!Parent) {
2047 if (HandleType[Index] & EFI_HANDLE_TYPE_DEVICE_HANDLE) {
2048 Status = refit_call4_wrapper(BS->ConnectController,
2049 AllHandleBuffer[Index],
2050 NULL,
2051 NULL,
2052 TRUE);
2053 }
2054 }
2055 }
2056
2057 MyFreePool (HandleBuffer);
2058 MyFreePool (HandleType);
2059 }
2060
2061 Done:
2062 MyFreePool (AllHandleBuffer);
2063 return Status;
2064 } /* EFI_STATUS ConnectAllDriversToAllControllers() */
2065 #else
2066 static EFI_STATUS ConnectAllDriversToAllControllers(VOID) {
2067 BdsLibConnectAllDriversToAllControllers();
2068 return 0;
2069 }
2070 #endif
2071
2072 // Load all EFI drivers from rEFInd's "drivers" subdirectory and from the
2073 // directories specified by the user in the "scan_driver_dirs" configuration
2074 // file line.
2075 static VOID LoadDrivers(VOID)
2076 {
2077 CHAR16 *Directory, *SelfDirectory;
2078 UINTN i = 0, Length, NumFound = 0;
2079
2080 // load drivers from the subdirectories of rEFInd's home directory specified
2081 // in the DRIVER_DIRS constant.
2082 while ((Directory = FindCommaDelimited(DRIVER_DIRS, i++)) != NULL) {
2083 SelfDirectory = SelfDirPath ? StrDuplicate(SelfDirPath) : NULL;
2084 CleanUpPathNameSlashes(SelfDirectory);
2085 MergeStrings(&SelfDirectory, Directory, L'\\');
2086 NumFound += ScanDriverDir(SelfDirectory);
2087 MyFreePool(Directory);
2088 MyFreePool(SelfDirectory);
2089 }
2090
2091 // Scan additional user-specified driver directories....
2092 i = 0;
2093 while ((Directory = FindCommaDelimited(GlobalConfig.DriverDirs, i++)) != NULL) {
2094 CleanUpPathNameSlashes(Directory);
2095 Length = StrLen(Directory);
2096 if (Length > 0) {
2097 NumFound += ScanDriverDir(Directory);
2098 } // if
2099 MyFreePool(Directory);
2100 } // while
2101
2102 // connect all devices
2103 if (NumFound > 0)
2104 ConnectAllDriversToAllControllers();
2105 } /* static VOID LoadDrivers() */
2106
2107 // Determine what (if any) type of legacy (BIOS) boot support is available
2108 static VOID FindLegacyBootType(VOID) {
2109 #ifdef __MAKEWITH_TIANO
2110 EFI_STATUS Status;
2111 EFI_LEGACY_BIOS_PROTOCOL *LegacyBios;
2112 #endif
2113
2114 GlobalConfig.LegacyType = LEGACY_TYPE_NONE;
2115
2116 // UEFI-style legacy BIOS support is available only with the TianoCore EDK2
2117 // build environment, and then only with some EFI implementations....
2118 #ifdef __MAKEWITH_TIANO
2119 Status = gBS->LocateProtocol (&gEfiLegacyBootProtocolGuid, NULL, (VOID **) &LegacyBios);
2120 if (!EFI_ERROR (Status))
2121 GlobalConfig.LegacyType = LEGACY_TYPE_UEFI;
2122 #endif
2123
2124 // Macs have their own system. If the firmware vendor code contains the
2125 // string "Apple", assume it's available. Note that this overrides the
2126 // UEFI type, and might yield false positives if the vendor string
2127 // contains "Apple" as part of something bigger, so this isn't 100%
2128 // perfect.
2129 if (StriSubCmp(L"Apple", ST->FirmwareVendor))
2130 GlobalConfig.LegacyType = LEGACY_TYPE_MAC;
2131 } // static VOID FindLegacyBootType
2132
2133 // Warn the user if legacy OS scans are enabled but the firmware or this
2134 // application can't support them....
2135 static VOID WarnIfLegacyProblems() {
2136 BOOLEAN found = FALSE;
2137 UINTN i = 0;
2138
2139 if (GlobalConfig.LegacyType == LEGACY_TYPE_NONE) {
2140 do {
2141 if (GlobalConfig.ScanFor[i] == 'h' || GlobalConfig.ScanFor[i] == 'b' || GlobalConfig.ScanFor[i] == 'c')
2142 found = TRUE;
2143 i++;
2144 } while ((i < NUM_SCAN_OPTIONS) && (!found));
2145 if (found) {
2146 Print(L"NOTE: refind.conf's 'scanfor' line specifies scanning for one or more legacy\n");
2147 Print(L"(BIOS) boot options; however, this is not possible because ");
2148 #ifdef __MAKEWITH_TIANO
2149 Print(L"your computer lacks\n");
2150 Print(L"the necessary Compatibility Support Module (CSM) support.\n");
2151 #else
2152 Print(L"this program was\n");
2153 Print(L"compiled without the necessary support. Please visit\n");
2154 Print(L"http://www.rodsbooks.com/refind/getting.html and download and install a rEFInd\n");
2155 Print(L"binary built with the TianoCore EDK2 to enable legacy boot support.\n");
2156 #endif
2157 PauseForKey();
2158 } // if (found)
2159 } // if no legacy support
2160 } // static VOID WarnIfLegacyProblems()
2161
2162 // Locates boot loaders. NOTE: This assumes that GlobalConfig.LegacyType is set correctly.
2163 static VOID ScanForBootloaders(VOID) {
2164 UINTN i;
2165
2166 ScanVolumes();
2167
2168 // scan for loaders and tools, add them to the menu
2169 for (i = 0; i < NUM_SCAN_OPTIONS; i++) {
2170 switch(GlobalConfig.ScanFor[i]) {
2171 case 'c': case 'C':
2172 ScanLegacyDisc();
2173 break;
2174 case 'h': case 'H':
2175 ScanLegacyInternal();
2176 break;
2177 case 'b': case 'B':
2178 ScanLegacyExternal();
2179 break;
2180 case 'm': case 'M':
2181 ScanUserConfigured(CONFIG_FILE_NAME);
2182 break;
2183 case 'e': case 'E':
2184 ScanExternal();
2185 break;
2186 case 'i': case 'I':
2187 ScanInternal();
2188 break;
2189 case 'o': case 'O':
2190 ScanOptical();
2191 break;
2192 } // switch()
2193 } // for
2194
2195 // assign shortcut keys
2196 for (i = 0; i < MainMenu.EntryCount && MainMenu.Entries[i]->Row == 0 && i < 9; i++)
2197 MainMenu.Entries[i]->ShortcutDigit = (CHAR16)('1' + i);
2198
2199 // wait for user ACK when there were errors
2200 FinishTextScreen(FALSE);
2201 } // static VOID ScanForBootloaders()
2202
2203 // Add the second-row tags containing built-in and external tools (EFI shell,
2204 // reboot, etc.)
2205 static VOID ScanForTools(VOID) {
2206 CHAR16 *FileName = NULL, *MokLocations, *MokName, *MemtestName, *PathName, Description[256];
2207 REFIT_MENU_ENTRY *TempMenuEntry;
2208 UINTN i, j, k, VolumeIndex;
2209 UINT64 osind;
2210 CHAR8 *b = 0;
2211
2212 MokLocations = StrDuplicate(MOK_LOCATIONS);
2213 if (MokLocations != NULL)
2214 MergeStrings(&MokLocations, SelfDirPath, L',');
2215
2216 for (i = 0; i < NUM_TOOLS; i++) {
2217 switch(GlobalConfig.ShowTools[i]) {
2218 // NOTE: Be sure that FileName is NULL at the end of each case.
2219 case TAG_SHUTDOWN:
2220 TempMenuEntry = CopyMenuEntry(&MenuEntryShutdown);
2221 TempMenuEntry->Image = BuiltinIcon(BUILTIN_ICON_FUNC_SHUTDOWN);
2222 AddMenuEntry(&MainMenu, TempMenuEntry);
2223 break;
2224 case TAG_REBOOT:
2225 TempMenuEntry = CopyMenuEntry(&MenuEntryReset);
2226 TempMenuEntry->Image = BuiltinIcon(BUILTIN_ICON_FUNC_RESET);
2227 AddMenuEntry(&MainMenu, TempMenuEntry);
2228 break;
2229 case TAG_ABOUT:
2230 TempMenuEntry = CopyMenuEntry(&MenuEntryAbout);
2231 TempMenuEntry->Image = BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT);
2232 AddMenuEntry(&MainMenu, TempMenuEntry);
2233 break;
2234 case TAG_EXIT:
2235 TempMenuEntry = CopyMenuEntry(&MenuEntryExit);
2236 TempMenuEntry->Image = BuiltinIcon(BUILTIN_ICON_FUNC_EXIT);
2237 AddMenuEntry(&MainMenu, TempMenuEntry);
2238 break;
2239 case TAG_FIRMWARE:
2240 if (EfivarGetRaw(&GlobalGuid, L"OsIndicationsSupported", &b, &j) == EFI_SUCCESS) {
2241 osind = (UINT64)*b;
2242 if (osind & EFI_OS_INDICATIONS_BOOT_TO_FW_UI) {
2243 TempMenuEntry = CopyMenuEntry(&MenuEntryFirmware);
2244 TempMenuEntry->Image = BuiltinIcon(BUILTIN_ICON_FUNC_FIRMWARE);
2245 AddMenuEntry(&MainMenu, TempMenuEntry);
2246 } // if
2247 } // if
2248 break;
2249 case TAG_SHELL:
2250 j = 0;
2251 while ((FileName = FindCommaDelimited(SHELL_NAMES, j++)) != NULL) {
2252 if (FileExists(SelfRootDir, FileName)) {
2253 AddToolEntry(SelfLoadedImage->DeviceHandle, FileName, L"EFI Shell", BuiltinIcon(BUILTIN_ICON_TOOL_SHELL),
2254 'S', FALSE);
2255 }
2256 MyFreePool(FileName);
2257 } // while
2258 break;
2259 case TAG_GPTSYNC:
2260 j = 0;
2261 while ((FileName = FindCommaDelimited(GPTSYNC_NAMES, j++)) != NULL) {
2262 if (FileExists(SelfRootDir, FileName)) {
2263 AddToolEntry(SelfLoadedImage->DeviceHandle, FileName, L"Hybrid MBR tool", BuiltinIcon(BUILTIN_ICON_TOOL_PART),
2264 'P', FALSE);
2265 } // if
2266 MyFreePool(FileName);
2267 } // while
2268 FileName = NULL;
2269 break;
2270
2271 case TAG_APPLE_RECOVERY:
2272 FileName = StrDuplicate(L"\\com.apple.recovery.boot\\boot.efi");
2273 for (VolumeIndex = 0; VolumeIndex < VolumesCount; VolumeIndex++) {
2274 if ((Volumes[VolumeIndex]->RootDir != NULL) && (FileExists(Volumes[VolumeIndex]->RootDir, FileName))) {
2275 SPrint(Description, 255, L"Apple Recovery on %s", Volumes[VolumeIndex]->VolName);
2276 AddToolEntry(Volumes[VolumeIndex]->DeviceHandle, FileName, Description,
2277 BuiltinIcon(BUILTIN_ICON_TOOL_APPLE_RESCUE), 'R', TRUE);
2278 }
2279 } // for
2280 MyFreePool(FileName);
2281 FileName = NULL;
2282 break;
2283
2284 case TAG_MOK_TOOL:
2285 j = 0;
2286 while ((FileName = FindCommaDelimited(MokLocations, j++)) != NULL) {
2287 k = 0;
2288 while ((MokName = FindCommaDelimited(MOK_NAMES, k++)) != NULL) {
2289 PathName = StrDuplicate(FileName);
2290 MergeStrings(&PathName, MokName, (StriCmp(PathName, L"\\") == 0) ? 0 : L'\\');
2291 for (VolumeIndex = 0; VolumeIndex < VolumesCount; VolumeIndex++) {
2292 if ((Volumes[VolumeIndex]->RootDir != NULL) && (FileExists(Volumes[VolumeIndex]->RootDir, PathName))) {
2293 SPrint(Description, 255, L"MOK utility at %s on %s", PathName, Volumes[VolumeIndex]->VolName);
2294 AddToolEntry(Volumes[VolumeIndex]->DeviceHandle, PathName, Description,
2295 BuiltinIcon(BUILTIN_ICON_TOOL_MOK_TOOL), 'S', FALSE);
2296 } // if
2297 } // for
2298 MyFreePool(PathName);
2299 MyFreePool(MokName);
2300 } // while MOK_NAMES
2301 MyFreePool(FileName);
2302 } // while MokLocations
2303 break;
2304
2305 case TAG_MEMTEST:
2306 j = 0;
2307 while ((FileName = FindCommaDelimited(MEMTEST_LOCATIONS, j++)) != NULL) {
2308 k = 0;
2309 while ((MemtestName = FindCommaDelimited(MEMTEST_NAMES, k++)) != NULL) {
2310 PathName = StrDuplicate(FileName);
2311 MergeStrings(&PathName, MemtestName, (StriCmp(PathName, L"\\") == 0) ? 0 : L'\\');
2312 for (VolumeIndex = 0; VolumeIndex < VolumesCount; VolumeIndex++) {
2313 if ((Volumes[VolumeIndex]->RootDir != NULL) && (FileExists(Volumes[VolumeIndex]->RootDir, PathName))) {
2314 SPrint(Description, 255, L"Memory test utility at %s on %s", PathName, Volumes[VolumeIndex]->VolName);
2315 AddToolEntry(Volumes[VolumeIndex]->DeviceHandle, PathName, Description,
2316 BuiltinIcon(BUILTIN_ICON_FUNC_FIRMWARE), 'S', FALSE);
2317 } // if
2318 } // for
2319 MyFreePool(PathName);
2320 MyFreePool(MemtestName);
2321 } // while MEMTEST_NAMES
2322 MyFreePool(FileName);
2323 } // while MEMTEST_LOCATIONS
2324 break;
2325
2326 } // switch()
2327 } // for
2328 } // static VOID ScanForTools
2329
2330 // Rescan for boot loaders
2331 VOID RescanAll(VOID) {
2332 EG_PIXEL BGColor;
2333
2334 BGColor.b = 255;
2335 BGColor.g = 175;
2336 BGColor.r = 100;
2337 BGColor.a = 0;
2338 egDisplayMessage(L"Scanning for new boot loaders; please wait....", &BGColor);
2339 FreeList((VOID ***) &(MainMenu.Entries), &MainMenu.EntryCount);
2340 MainMenu.Entries = NULL;
2341 MainMenu.EntryCount = 0;
2342 ReadConfig(CONFIG_FILE_NAME);
2343 ConnectAllDriversToAllControllers();
2344 ScanVolumes();
2345 ScanForBootloaders();
2346 ScanForTools();
2347 SetupScreen();
2348 } // VOID RescanAll()
2349
2350 #ifdef __MAKEWITH_TIANO
2351
2352 // Minimal initialization function
2353 static VOID InitializeLib(IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable) {
2354 gST = SystemTable;
2355 // gImageHandle = ImageHandle;
2356 gBS = SystemTable->BootServices;
2357 // gRS = SystemTable->RuntimeServices;
2358 gRT = SystemTable->RuntimeServices; // Some BDS functions need gRT to be set
2359 EfiGetSystemConfigurationTable (&gEfiDxeServicesTableGuid, (VOID **) &gDS);
2360
2361 InitializeConsoleSim();
2362 }
2363
2364 #endif
2365
2366 // Set up our own Secure Boot extensions....
2367 // Returns TRUE on success, FALSE otherwise
2368 static BOOLEAN SecureBootSetup(VOID) {
2369 EFI_STATUS Status;
2370 BOOLEAN Success = FALSE;
2371
2372 if (secure_mode() && ShimLoaded()) {
2373 Status = security_policy_install();
2374 if (Status == EFI_SUCCESS) {
2375 Success = TRUE;
2376 } else {
2377 Print(L"Failed to install MOK Secure Boot extensions");
2378 }
2379 }
2380 return Success;
2381 } // VOID SecureBootSetup()
2382
2383 // Remove our own Secure Boot extensions....
2384 // Returns TRUE on success, FALSE otherwise
2385 static BOOLEAN SecureBootUninstall(VOID) {
2386 EFI_STATUS Status;
2387 BOOLEAN Success = TRUE;
2388
2389 if (secure_mode()) {
2390 Status = security_policy_uninstall();
2391 if (Status != EFI_SUCCESS) {
2392 Success = FALSE;
2393 BeginTextScreen(L"Secure Boot Policy Failure");
2394 Print(L"Failed to uninstall MOK Secure Boot extensions; forcing a reboot.");
2395 PauseForKey();
2396 refit_call4_wrapper(RT->ResetSystem, EfiResetCold, EFI_SUCCESS, 0, NULL);
2397 }
2398 }
2399 return Success;
2400 } // VOID SecureBootUninstall
2401
2402 //
2403 // main entry point
2404 //
2405 EFI_STATUS
2406 EFIAPI
2407 efi_main (EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable)
2408 {
2409 EFI_STATUS Status;
2410 BOOLEAN MainLoopRunning = TRUE;
2411 BOOLEAN MokProtocol;
2412 REFIT_MENU_ENTRY *ChosenEntry;
2413 UINTN MenuExit, i;
2414 CHAR16 *Selection = NULL;
2415 EG_PIXEL BGColor;
2416
2417 // bootstrap
2418 InitializeLib(ImageHandle, SystemTable);
2419 Status = InitRefitLib(ImageHandle);
2420 if (EFI_ERROR(Status))
2421 return Status;
2422
2423 // read configuration
2424 CopyMem(GlobalConfig.ScanFor, "ieom ", NUM_SCAN_OPTIONS);
2425 FindLegacyBootType();
2426 if (GlobalConfig.LegacyType == LEGACY_TYPE_MAC)
2427 CopyMem(GlobalConfig.ScanFor, "ihebocm ", NUM_SCAN_OPTIONS);
2428 ReadConfig(CONFIG_FILE_NAME);
2429 ScanVolumes();
2430
2431 InitScreen();
2432 WarnIfLegacyProblems();
2433 MainMenu.TimeoutSeconds = GlobalConfig.Timeout;
2434
2435 // disable EFI watchdog timer
2436 refit_call4_wrapper(BS->SetWatchdogTimer, 0x0000, 0x0000, 0x0000, NULL);
2437
2438 // further bootstrap (now with config available)
2439 MokProtocol = SecureBootSetup();
2440 LoadDrivers();
2441 ScanForBootloaders();
2442 ScanForTools();
2443 SetupScreen();
2444
2445 if (GlobalConfig.ScanDelay > 0) {
2446 BGColor.b = 255;
2447 BGColor.g = 175;
2448 BGColor.r = 100;
2449 BGColor.a = 0;
2450 egDisplayMessage(L"Pausing before disk scan; please wait....", &BGColor);
2451 for (i = 0; i < GlobalConfig.ScanDelay; i++)
2452 refit_call1_wrapper(BS->Stall, 1000000);
2453 RescanAll();
2454 } // if
2455
2456 if (GlobalConfig.DefaultSelection)
2457 Selection = StrDuplicate(GlobalConfig.DefaultSelection);
2458
2459 while (MainLoopRunning) {
2460 MenuExit = RunMainMenu(&MainMenu, Selection, &ChosenEntry);
2461
2462 // The Escape key triggers a re-scan operation....
2463 if (MenuExit == MENU_EXIT_ESCAPE) {
2464 RescanAll();
2465 continue;
2466 }
2467
2468 switch (ChosenEntry->Tag) {
2469
2470 case TAG_REBOOT: // Reboot
2471 TerminateScreen();
2472 refit_call4_wrapper(RT->ResetSystem, EfiResetCold, EFI_SUCCESS, 0, NULL);
2473 MainLoopRunning = FALSE; // just in case we get this far
2474 break;
2475
2476 case TAG_SHUTDOWN: // Shut Down
2477 TerminateScreen();
2478 refit_call4_wrapper(RT->ResetSystem, EfiResetShutdown, EFI_SUCCESS, 0, NULL);
2479 MainLoopRunning = FALSE; // just in case we get this far
2480 break;
2481
2482 case TAG_ABOUT: // About rEFInd
2483 AboutrEFInd();
2484 break;
2485
2486 case TAG_LOADER: // Boot OS via .EFI loader
2487 StartLoader((LOADER_ENTRY *)ChosenEntry);
2488 break;
2489
2490 case TAG_LEGACY: // Boot legacy OS
2491 StartLegacy((LEGACY_ENTRY *)ChosenEntry);
2492 break;
2493
2494 #ifdef __MAKEWITH_TIANO
2495 case TAG_LEGACY_UEFI: // Boot a legacy OS on a non-Mac
2496 StartLegacyUEFI((LEGACY_ENTRY *)ChosenEntry);
2497 break;
2498 #endif
2499
2500 case TAG_TOOL: // Start a EFI tool
2501 StartTool((LOADER_ENTRY *)ChosenEntry);
2502 break;
2503
2504 case TAG_EXIT: // Terminate rEFInd
2505 if ((MokProtocol) && !SecureBootUninstall()) {
2506 MainLoopRunning = FALSE; // just in case we get this far
2507 } else {
2508 BeginTextScreen(L" ");
2509 return EFI_SUCCESS;
2510 }
2511 break;
2512
2513 case TAG_FIRMWARE: // Reboot into firmware's user interface
2514 RebootIntoFirmware();
2515 break;
2516
2517 } // switch()
2518 MyFreePool(Selection);
2519 Selection = (ChosenEntry->Title) ? StrDuplicate(ChosenEntry->Title) : NULL;
2520 } // while()
2521
2522 // If we end up here, things have gone wrong. Try to reboot, and if that
2523 // fails, go into an endless loop.
2524 refit_call4_wrapper(RT->ResetSystem, EfiResetCold, EFI_SUCCESS, 0, NULL);
2525 EndlessIdleLoop();
2526
2527 return EFI_SUCCESS;
2528 } /* efi_main() */