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