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