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