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