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