]> code.delx.au - refind/blob - refind/main.c
Limit length of firmware description string in Info screen, to avoid
[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.7.9.5");
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 // UINTN ExitDataSize = 0;
1657 // CHAR16 *ExitData = NULL;
1658 // EFI_STATUS Status;
1659
1660 BeginExternalScreen(TRUE, L"Booting Legacy OS (UEFI mode)");
1661 // Print(L"Launching from '%s'\n", DevicePathToStr(Entry->BdsOption->DevicePath));
1662 // PauseForKey();
1663
1664 // Status = BdsLibBootViaBootOption(Entry->BdsOption, Entry->BdsOption->DevicePath, &ExitDataSize, &ExitData);
1665 // Print(L"BdsLibBootViaBootOption() returned %d\n", Status);
1666 BdsLibConnectDevicePath (Entry->BdsOption->DevicePath);
1667 BdsLibDoLegacyBoot(Entry->BdsOption);
1668
1669 // If we get here, it means that there was a failure....
1670 Print(L"Failure booting legacy (BIOS) OS.");
1671 PauseForKey();
1672 FinishExternalScreen();
1673 } // static VOID StartLegacyUEFI()
1674 #endif // __MAKEWITH_TIANO
1675
1676 static LEGACY_ENTRY * AddLegacyEntry(IN CHAR16 *LoaderTitle, IN REFIT_VOLUME *Volume)
1677 {
1678 LEGACY_ENTRY *Entry, *SubEntry;
1679 REFIT_MENU_SCREEN *SubScreen;
1680 CHAR16 *VolDesc;
1681 CHAR16 ShortcutLetter = 0;
1682
1683 if (LoaderTitle == NULL) {
1684 if (Volume->OSName != NULL) {
1685 LoaderTitle = Volume->OSName;
1686 if (LoaderTitle[0] == 'W' || LoaderTitle[0] == 'L')
1687 ShortcutLetter = LoaderTitle[0];
1688 } else
1689 LoaderTitle = L"Legacy OS";
1690 }
1691 if (Volume->VolName != NULL)
1692 VolDesc = Volume->VolName;
1693 else
1694 VolDesc = (Volume->DiskKind == DISK_KIND_OPTICAL) ? L"CD" : L"HD";
1695
1696 // prepare the menu entry
1697 Entry = AllocateZeroPool(sizeof(LEGACY_ENTRY));
1698 Entry->me.Title = AllocateZeroPool(256 * sizeof(CHAR16));
1699 SPrint(Entry->me.Title, 255, L"Boot %s from %s", LoaderTitle, VolDesc);
1700 Entry->me.Tag = TAG_LEGACY;
1701 Entry->me.Row = 0;
1702 Entry->me.ShortcutLetter = ShortcutLetter;
1703 Entry->me.Image = LoadOSIcon(Volume->OSIconName, L"legacy", FALSE);
1704 Entry->me.BadgeImage = Volume->VolBadgeImage;
1705 Entry->Volume = Volume;
1706 Entry->LoadOptions = (Volume->DiskKind == DISK_KIND_OPTICAL) ? L"CD" :
1707 ((Volume->DiskKind == DISK_KIND_EXTERNAL) ? L"USB" : L"HD");
1708 Entry->Enabled = TRUE;
1709
1710 // create the submenu
1711 SubScreen = AllocateZeroPool(sizeof(REFIT_MENU_SCREEN));
1712 SubScreen->Title = AllocateZeroPool(256 * sizeof(CHAR16));
1713 SPrint(SubScreen->Title, 255, L"Boot Options for %s on %s", LoaderTitle, VolDesc);
1714 SubScreen->TitleImage = Entry->me.Image;
1715 SubScreen->Hint1 = StrDuplicate(SUBSCREEN_HINT1);
1716 if (GlobalConfig.HideUIFlags & HIDEUI_FLAG_EDITOR) {
1717 SubScreen->Hint2 = StrDuplicate(SUBSCREEN_HINT2_NO_EDITOR);
1718 } else {
1719 SubScreen->Hint2 = StrDuplicate(SUBSCREEN_HINT2);
1720 } // if/else
1721
1722 // default entry
1723 SubEntry = AllocateZeroPool(sizeof(LEGACY_ENTRY));
1724 SubEntry->me.Title = AllocateZeroPool(256 * sizeof(CHAR16));
1725 SPrint(SubEntry->me.Title, 255, L"Boot %s", LoaderTitle);
1726 SubEntry->me.Tag = TAG_LEGACY;
1727 SubEntry->Volume = Entry->Volume;
1728 SubEntry->LoadOptions = Entry->LoadOptions;
1729 AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry);
1730
1731 AddMenuEntry(SubScreen, &MenuEntryReturn);
1732 Entry->me.SubScreen = SubScreen;
1733 AddMenuEntry(&MainMenu, (REFIT_MENU_ENTRY *)Entry);
1734 return Entry;
1735 } /* static LEGACY_ENTRY * AddLegacyEntry() */
1736
1737
1738 #ifdef __MAKEWITH_GNUEFI
1739 static VOID ScanLegacyUEFI(IN UINTN DiskType){}
1740 #else
1741 // default volume badge icon based on disk kind
1742 static EG_IMAGE * GetDiskBadge(IN UINTN DiskType) {
1743 EG_IMAGE * Badge = NULL;
1744
1745 switch (DiskType) {
1746 case BBS_HARDDISK:
1747 Badge = BuiltinIcon(BUILTIN_ICON_VOL_INTERNAL);
1748 break;
1749 case BBS_USB:
1750 Badge = BuiltinIcon(BUILTIN_ICON_VOL_EXTERNAL);
1751 break;
1752 case BBS_CDROM:
1753 Badge = BuiltinIcon(BUILTIN_ICON_VOL_OPTICAL);
1754 break;
1755 } // switch()
1756 return Badge;
1757 } // static EG_IMAGE * GetDiskBadge()
1758
1759 /**
1760 Create a rEFInd boot option from a Legacy BIOS protocol option.
1761 */
1762 static LEGACY_ENTRY * AddLegacyEntryUEFI(BDS_COMMON_OPTION *BdsOption, IN UINT16 DiskType)
1763 {
1764 LEGACY_ENTRY *Entry, *SubEntry;
1765 REFIT_MENU_SCREEN *SubScreen;
1766 CHAR16 ShortcutLetter = 0;
1767 CHAR16 *LegacyDescription = BdsOption->Description;
1768
1769 // prepare the menu entry
1770 Entry = AllocateZeroPool(sizeof(LEGACY_ENTRY));
1771 Entry->me.Title = AllocateZeroPool(256 * sizeof(CHAR16));
1772 SPrint(Entry->me.Title, 255, L"Boot legacy target %s", LegacyDescription);
1773 Entry->me.Tag = TAG_LEGACY_UEFI;
1774 Entry->me.Row = 0;
1775 Entry->me.ShortcutLetter = ShortcutLetter;
1776 Entry->me.Image = LoadOSIcon(L"legacy", L"legacy", TRUE);
1777 Entry->LoadOptions = (DiskType == BBS_CDROM) ? L"CD" :
1778 ((DiskType == BBS_USB) ? L"USB" : L"HD");
1779 Entry->me.BadgeImage = GetDiskBadge(DiskType);
1780 Entry->BdsOption = BdsOption;
1781 Entry->Enabled = TRUE;
1782
1783 // create the submenu
1784 SubScreen = AllocateZeroPool(sizeof(REFIT_MENU_SCREEN));
1785 SubScreen->Title = AllocateZeroPool(256 * sizeof(CHAR16));
1786 SPrint(SubScreen->Title, 255, L"No boot options for legacy target");
1787 SubScreen->TitleImage = Entry->me.Image;
1788 SubScreen->Hint1 = StrDuplicate(SUBSCREEN_HINT1);
1789 if (GlobalConfig.HideUIFlags & HIDEUI_FLAG_EDITOR) {
1790 SubScreen->Hint2 = StrDuplicate(SUBSCREEN_HINT2_NO_EDITOR);
1791 } else {
1792 SubScreen->Hint2 = StrDuplicate(SUBSCREEN_HINT2);
1793 } // if/else
1794
1795 // default entry
1796 SubEntry = AllocateZeroPool(sizeof(LEGACY_ENTRY));
1797 SubEntry->me.Title = AllocateZeroPool(256 * sizeof(CHAR16));
1798 SPrint(SubEntry->me.Title, 255, L"Boot %s", LegacyDescription);
1799 SubEntry->me.Tag = TAG_LEGACY_UEFI;
1800 Entry->BdsOption = BdsOption;
1801 AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry);
1802
1803 AddMenuEntry(SubScreen, &MenuEntryReturn);
1804 Entry->me.SubScreen = SubScreen;
1805 AddMenuEntry(&MainMenu, (REFIT_MENU_ENTRY *)Entry);
1806 return Entry;
1807 } /* static LEGACY_ENTRY * AddLegacyEntryUEFI() */
1808
1809 /**
1810 Scan for legacy BIOS targets on machines that implement EFI_LEGACY_BIOS_PROTOCOL.
1811 In testing, protocol has not been implemented on Macs but has been
1812 implemented on several Dell PCs and an ASUS motherboard.
1813 Restricts output to disks of the specified DiskType.
1814 */
1815 static VOID ScanLegacyUEFI(IN UINTN DiskType)
1816 {
1817 EFI_STATUS Status;
1818 EFI_LEGACY_BIOS_PROTOCOL *LegacyBios;
1819 UINT16 *BootOrder = NULL;
1820 UINTN Index = 0;
1821 CHAR16 BootOption[10];
1822 UINTN BootOrderSize = 0;
1823 CHAR16 Buffer[20];
1824 BDS_COMMON_OPTION *BdsOption;
1825 LIST_ENTRY TempList;
1826 BBS_BBS_DEVICE_PATH * BbsDevicePath = NULL;
1827
1828 InitializeListHead (&TempList);
1829 ZeroMem (Buffer, sizeof (Buffer));
1830
1831 // If LegacyBios protocol is not implemented on this platform, then
1832 //we do not support this type of legacy boot on this machine.
1833 Status = gBS->LocateProtocol(&gEfiLegacyBootProtocolGuid, NULL, (VOID **) &LegacyBios);
1834 if (EFI_ERROR (Status))
1835 return;
1836
1837 // Grab the boot order
1838 BootOrder = BdsLibGetVariableAndSize(L"BootOrder", &gEfiGlobalVariableGuid, &BootOrderSize);
1839 if (BootOrder == NULL) {
1840 BootOrderSize = 0;
1841 }
1842
1843 Index = 0;
1844 while (Index < BootOrderSize / sizeof (UINT16))
1845 {
1846 // Grab each boot option variable from the boot order, and convert
1847 // the variable into a BDS boot option
1848 UnicodeSPrint (BootOption, sizeof (BootOption), L"Boot%04x", BootOrder[Index]);
1849 BdsOption = BdsLibVariableToOption (&TempList, BootOption);
1850
1851 if (BdsOption != NULL) {
1852 BbsDevicePath = (BBS_BBS_DEVICE_PATH *)BdsOption->DevicePath;
1853
1854 // Only add the entry if it is of a requested type (e.g. USB, HD)
1855
1856 // Two checks necessary because some systems return EFI boot loaders
1857 // with a DeviceType value that would inappropriately include them
1858 // as legacy loaders....
1859 if ((BbsDevicePath->DeviceType == DiskType) && (BdsOption->DevicePath->Type == DEVICE_TYPE_BIOS)) {
1860 AddLegacyEntryUEFI(BdsOption, BbsDevicePath->DeviceType);
1861 }
1862 }
1863 Index++;
1864 }
1865 } /* static VOID ScanLegacyUEFI() */
1866 #endif // __MAKEWITH_GNUEFI
1867
1868 static VOID ScanLegacyVolume(REFIT_VOLUME *Volume, UINTN VolumeIndex) {
1869 UINTN VolumeIndex2;
1870 BOOLEAN ShowVolume, HideIfOthersFound;
1871
1872 ShowVolume = FALSE;
1873 HideIfOthersFound = FALSE;
1874 if (Volume->IsAppleLegacy) {
1875 ShowVolume = TRUE;
1876 HideIfOthersFound = TRUE;
1877 } else if (Volume->HasBootCode) {
1878 ShowVolume = TRUE;
1879 if (Volume->BlockIO == Volume->WholeDiskBlockIO &&
1880 Volume->BlockIOOffset == 0 &&
1881 Volume->OSName == NULL)
1882 // this is a whole disk (MBR) entry; hide if we have entries for partitions
1883 HideIfOthersFound = TRUE;
1884 }
1885 if (HideIfOthersFound) {
1886 // check for other bootable entries on the same disk
1887 for (VolumeIndex2 = 0; VolumeIndex2 < VolumesCount; VolumeIndex2++) {
1888 if (VolumeIndex2 != VolumeIndex && Volumes[VolumeIndex2]->HasBootCode &&
1889 Volumes[VolumeIndex2]->WholeDiskBlockIO == Volume->WholeDiskBlockIO)
1890 ShowVolume = FALSE;
1891 }
1892 }
1893
1894 if (ShowVolume)
1895 AddLegacyEntry(NULL, Volume);
1896 } // static VOID ScanLegacyVolume()
1897
1898 // Scan attached optical discs for legacy (BIOS) boot code
1899 // and add anything found to the list....
1900 static VOID ScanLegacyDisc(VOID)
1901 {
1902 UINTN VolumeIndex;
1903 REFIT_VOLUME *Volume;
1904
1905 if (GlobalConfig.LegacyType == LEGACY_TYPE_MAC) {
1906 for (VolumeIndex = 0; VolumeIndex < VolumesCount; VolumeIndex++) {
1907 Volume = Volumes[VolumeIndex];
1908 if (Volume->DiskKind == DISK_KIND_OPTICAL)
1909 ScanLegacyVolume(Volume, VolumeIndex);
1910 } // for
1911 } else if (GlobalConfig.LegacyType == LEGACY_TYPE_UEFI) {
1912 ScanLegacyUEFI(BBS_CDROM);
1913 }
1914 } /* static VOID ScanLegacyDisc() */
1915
1916 // Scan internal hard disks for legacy (BIOS) boot code
1917 // and add anything found to the list....
1918 static VOID ScanLegacyInternal(VOID)
1919 {
1920 UINTN VolumeIndex;
1921 REFIT_VOLUME *Volume;
1922
1923 if (GlobalConfig.LegacyType == LEGACY_TYPE_MAC) {
1924 for (VolumeIndex = 0; VolumeIndex < VolumesCount; VolumeIndex++) {
1925 Volume = Volumes[VolumeIndex];
1926 if (Volume->DiskKind == DISK_KIND_INTERNAL)
1927 ScanLegacyVolume(Volume, VolumeIndex);
1928 } // for
1929 } else if (GlobalConfig.LegacyType == LEGACY_TYPE_UEFI) {
1930 ScanLegacyUEFI(BBS_HARDDISK);
1931 }
1932 } /* static VOID ScanLegacyInternal() */
1933
1934 // Scan external disks for legacy (BIOS) boot code
1935 // and add anything found to the list....
1936 static VOID ScanLegacyExternal(VOID)
1937 {
1938 UINTN VolumeIndex;
1939 REFIT_VOLUME *Volume;
1940
1941 if (GlobalConfig.LegacyType == LEGACY_TYPE_MAC) {
1942 for (VolumeIndex = 0; VolumeIndex < VolumesCount; VolumeIndex++) {
1943 Volume = Volumes[VolumeIndex];
1944 if (Volume->DiskKind == DISK_KIND_EXTERNAL)
1945 ScanLegacyVolume(Volume, VolumeIndex);
1946 } // for
1947 } else if (GlobalConfig.LegacyType == LEGACY_TYPE_UEFI) {
1948 ScanLegacyUEFI(BBS_USB);
1949 }
1950 } /* static VOID ScanLegacyExternal() */
1951
1952 //
1953 // pre-boot tool functions
1954 //
1955
1956 static VOID StartTool(IN LOADER_ENTRY *Entry)
1957 {
1958 BeginExternalScreen(Entry->UseGraphicsMode, Entry->me.Title + 6); // assumes "Start <title>" as assigned below
1959 StartEFIImage(Entry->DevicePath, Entry->LoadOptions, TYPE_EFI,
1960 Basename(Entry->LoaderPath), Entry->OSType, NULL, TRUE);
1961 FinishExternalScreen();
1962 } /* static VOID StartTool() */
1963
1964 static LOADER_ENTRY * AddToolEntry(EFI_HANDLE DeviceHandle, IN CHAR16 *LoaderPath, IN CHAR16 *LoaderTitle, IN EG_IMAGE *Image,
1965 IN CHAR16 ShortcutLetter, IN BOOLEAN UseGraphicsMode)
1966 {
1967 LOADER_ENTRY *Entry;
1968 CHAR16 *TitleStr = NULL;
1969
1970 Entry = AllocateZeroPool(sizeof(LOADER_ENTRY));
1971
1972 TitleStr = PoolPrint(L"Start %s", LoaderTitle);
1973 Entry->me.Title = TitleStr;
1974 Entry->me.Tag = TAG_TOOL;
1975 Entry->me.Row = 1;
1976 Entry->me.ShortcutLetter = ShortcutLetter;
1977 Entry->me.Image = Image;
1978 Entry->LoaderPath = (LoaderPath) ? StrDuplicate(LoaderPath) : NULL;
1979 Entry->DevicePath = FileDevicePath(DeviceHandle, Entry->LoaderPath);
1980 Entry->UseGraphicsMode = UseGraphicsMode;
1981
1982 AddMenuEntry(&MainMenu, (REFIT_MENU_ENTRY *)Entry);
1983 return Entry;
1984 } /* static LOADER_ENTRY * AddToolEntry() */
1985
1986 //
1987 // pre-boot driver functions
1988 //
1989
1990 static UINTN ScanDriverDir(IN CHAR16 *Path)
1991 {
1992 EFI_STATUS Status;
1993 REFIT_DIR_ITER DirIter;
1994 UINTN NumFound = 0;
1995 EFI_FILE_INFO *DirEntry;
1996 CHAR16 FileName[256];
1997
1998 CleanUpPathNameSlashes(Path);
1999 // look through contents of the directory
2000 DirIterOpen(SelfRootDir, Path, &DirIter);
2001 while (DirIterNext(&DirIter, 2, LOADER_MATCH_PATTERNS, &DirEntry)) {
2002 if (DirEntry->FileName[0] == '.')
2003 continue; // skip this
2004
2005 SPrint(FileName, 255, L"%s\\%s", Path, DirEntry->FileName);
2006 NumFound++;
2007 Status = StartEFIImage(FileDevicePath(SelfLoadedImage->DeviceHandle, FileName),
2008 L"", TYPE_EFI, DirEntry->FileName, 0, NULL, FALSE);
2009 }
2010 Status = DirIterClose(&DirIter);
2011 if (Status != EFI_NOT_FOUND) {
2012 SPrint(FileName, 255, L"while scanning the %s directory", Path);
2013 CheckError(Status, FileName);
2014 }
2015 return (NumFound);
2016 }
2017
2018 #ifdef __MAKEWITH_GNUEFI
2019 static EFI_STATUS ConnectAllDriversToAllControllers(VOID)
2020 {
2021 EFI_STATUS Status;
2022 UINTN AllHandleCount;
2023 EFI_HANDLE *AllHandleBuffer;
2024 UINTN Index;
2025 UINTN HandleCount;
2026 EFI_HANDLE *HandleBuffer;
2027 UINT32 *HandleType;
2028 UINTN HandleIndex;
2029 BOOLEAN Parent;
2030 BOOLEAN Device;
2031
2032 Status = LibLocateHandle(AllHandles,
2033 NULL,
2034 NULL,
2035 &AllHandleCount,
2036 &AllHandleBuffer);
2037 if (EFI_ERROR(Status))
2038 return Status;
2039
2040 for (Index = 0; Index < AllHandleCount; Index++) {
2041 //
2042 // Scan the handle database
2043 //
2044 Status = LibScanHandleDatabase(NULL,
2045 NULL,
2046 AllHandleBuffer[Index],
2047 NULL,
2048 &HandleCount,
2049 &HandleBuffer,
2050 &HandleType);
2051 if (EFI_ERROR (Status))
2052 goto Done;
2053
2054 Device = TRUE;
2055 if (HandleType[Index] & EFI_HANDLE_TYPE_DRIVER_BINDING_HANDLE)
2056 Device = FALSE;
2057 if (HandleType[Index] & EFI_HANDLE_TYPE_IMAGE_HANDLE)
2058 Device = FALSE;
2059
2060 if (Device) {
2061 Parent = FALSE;
2062 for (HandleIndex = 0; HandleIndex < HandleCount; HandleIndex++) {
2063 if (HandleType[HandleIndex] & EFI_HANDLE_TYPE_PARENT_HANDLE)
2064 Parent = TRUE;
2065 } // for
2066
2067 if (!Parent) {
2068 if (HandleType[Index] & EFI_HANDLE_TYPE_DEVICE_HANDLE) {
2069 Status = refit_call4_wrapper(BS->ConnectController,
2070 AllHandleBuffer[Index],
2071 NULL,
2072 NULL,
2073 TRUE);
2074 }
2075 }
2076 }
2077
2078 MyFreePool (HandleBuffer);
2079 MyFreePool (HandleType);
2080 }
2081
2082 Done:
2083 MyFreePool (AllHandleBuffer);
2084 return Status;
2085 } /* EFI_STATUS ConnectAllDriversToAllControllers() */
2086 #else
2087 static EFI_STATUS ConnectAllDriversToAllControllers(VOID) {
2088 BdsLibConnectAllDriversToAllControllers();
2089 return 0;
2090 }
2091 #endif
2092
2093 // Load all EFI drivers from rEFInd's "drivers" subdirectory and from the
2094 // directories specified by the user in the "scan_driver_dirs" configuration
2095 // file line.
2096 static VOID LoadDrivers(VOID)
2097 {
2098 CHAR16 *Directory, *SelfDirectory;
2099 UINTN i = 0, Length, NumFound = 0;
2100
2101 // load drivers from the subdirectories of rEFInd's home directory specified
2102 // in the DRIVER_DIRS constant.
2103 while ((Directory = FindCommaDelimited(DRIVER_DIRS, i++)) != NULL) {
2104 SelfDirectory = SelfDirPath ? StrDuplicate(SelfDirPath) : NULL;
2105 CleanUpPathNameSlashes(SelfDirectory);
2106 MergeStrings(&SelfDirectory, Directory, L'\\');
2107 NumFound += ScanDriverDir(SelfDirectory);
2108 MyFreePool(Directory);
2109 MyFreePool(SelfDirectory);
2110 }
2111
2112 // Scan additional user-specified driver directories....
2113 i = 0;
2114 while ((Directory = FindCommaDelimited(GlobalConfig.DriverDirs, i++)) != NULL) {
2115 CleanUpPathNameSlashes(Directory);
2116 Length = StrLen(Directory);
2117 if (Length > 0) {
2118 NumFound += ScanDriverDir(Directory);
2119 } // if
2120 MyFreePool(Directory);
2121 } // while
2122
2123 // connect all devices
2124 if (NumFound > 0)
2125 ConnectAllDriversToAllControllers();
2126 } /* static VOID LoadDrivers() */
2127
2128 // Determine what (if any) type of legacy (BIOS) boot support is available
2129 static VOID FindLegacyBootType(VOID) {
2130 #ifdef __MAKEWITH_TIANO
2131 EFI_STATUS Status;
2132 EFI_LEGACY_BIOS_PROTOCOL *LegacyBios;
2133 #endif
2134
2135 GlobalConfig.LegacyType = LEGACY_TYPE_NONE;
2136
2137 // UEFI-style legacy BIOS support is available only with the TianoCore EDK2
2138 // build environment, and then only with some EFI implementations....
2139 #ifdef __MAKEWITH_TIANO
2140 Status = gBS->LocateProtocol (&gEfiLegacyBootProtocolGuid, NULL, (VOID **) &LegacyBios);
2141 if (!EFI_ERROR (Status))
2142 GlobalConfig.LegacyType = LEGACY_TYPE_UEFI;
2143 #endif
2144
2145 // Macs have their own system. If the firmware vendor code contains the
2146 // string "Apple", assume it's available. Note that this overrides the
2147 // UEFI type, and might yield false positives if the vendor string
2148 // contains "Apple" as part of something bigger, so this isn't 100%
2149 // perfect.
2150 if (StriSubCmp(L"Apple", ST->FirmwareVendor))
2151 GlobalConfig.LegacyType = LEGACY_TYPE_MAC;
2152 } // static VOID FindLegacyBootType
2153
2154 // Warn the user if legacy OS scans are enabled but the firmware or this
2155 // application can't support them....
2156 static VOID WarnIfLegacyProblems() {
2157 BOOLEAN found = FALSE;
2158 UINTN i = 0;
2159
2160 if (GlobalConfig.LegacyType == LEGACY_TYPE_NONE) {
2161 do {
2162 if (GlobalConfig.ScanFor[i] == 'h' || GlobalConfig.ScanFor[i] == 'b' || GlobalConfig.ScanFor[i] == 'c')
2163 found = TRUE;
2164 i++;
2165 } while ((i < NUM_SCAN_OPTIONS) && (!found));
2166 if (found) {
2167 Print(L"NOTE: refind.conf's 'scanfor' line specifies scanning for one or more legacy\n");
2168 Print(L"(BIOS) boot options; however, this is not possible because ");
2169 #ifdef __MAKEWITH_TIANO
2170 Print(L"your computer lacks\n");
2171 Print(L"the necessary Compatibility Support Module (CSM) support.\n");
2172 #else
2173 Print(L"this program was\n");
2174 Print(L"compiled without the necessary support. Please visit\n");
2175 Print(L"http://www.rodsbooks.com/refind/getting.html and download and install a rEFInd\n");
2176 Print(L"binary built with the TianoCore EDK2 to enable legacy boot support.\n");
2177 #endif
2178 PauseForKey();
2179 } // if (found)
2180 } // if no legacy support
2181 } // static VOID WarnIfLegacyProblems()
2182
2183 // Locates boot loaders. NOTE: This assumes that GlobalConfig.LegacyType is set correctly.
2184 static VOID ScanForBootloaders(VOID) {
2185 UINTN i;
2186
2187 // if (GlobalConfig.LegacyType == LEGACY_TYPE_UEFI) {
2188 // Print(L"About to call BdsDeleteAllInvalidLegacyBootOptions()\n");
2189 // BdsDeleteAllInvalidLegacyBootOptions();
2190 // Print(L"About to call BdsAddNonExistingLegacyBootOptions()\n");
2191 // BdsAddNonExistingLegacyBootOptions();
2192 // Print(L"About to call BdsUpdateLegacyDevOrder()\n");
2193 // // BdsUpdateLegacyDevOrder(); // EXTREME CAUTION: HOSED ONE FIRMWARE!
2194 // Print(L"Done with legacy boot updates!\n");
2195 // PauseForKey();
2196 // }
2197
2198 // ScanVolumes();
2199
2200 // scan for loaders and tools, add them to the menu
2201 for (i = 0; i < NUM_SCAN_OPTIONS; i++) {
2202 switch(GlobalConfig.ScanFor[i]) {
2203 case 'c': case 'C':
2204 ScanLegacyDisc();
2205 break;
2206 case 'h': case 'H':
2207 ScanLegacyInternal();
2208 break;
2209 case 'b': case 'B':
2210 ScanLegacyExternal();
2211 break;
2212 case 'm': case 'M':
2213 ScanUserConfigured(GlobalConfig.ConfigFilename);
2214 break;
2215 case 'e': case 'E':
2216 ScanExternal();
2217 break;
2218 case 'i': case 'I':
2219 ScanInternal();
2220 break;
2221 case 'o': case 'O':
2222 ScanOptical();
2223 break;
2224 } // switch()
2225 } // for
2226
2227 // assign shortcut keys
2228 for (i = 0; i < MainMenu.EntryCount && MainMenu.Entries[i]->Row == 0 && i < 9; i++)
2229 MainMenu.Entries[i]->ShortcutDigit = (CHAR16)('1' + i);
2230
2231 // wait for user ACK when there were errors
2232 FinishTextScreen(FALSE);
2233 } // static VOID ScanForBootloaders()
2234
2235 // Locate a single tool from the specified Locations using one of the
2236 // specified Names and add it to the menu.
2237 static VOID FindTool(CHAR16 *Locations, CHAR16 *Names, CHAR16 *Description, UINTN Icon) {
2238 UINTN j = 0, k, VolumeIndex;
2239 CHAR16 *DirName, *FileName, *PathName, FullDescription[256];
2240
2241 while ((DirName = FindCommaDelimited(Locations, j++)) != NULL) {
2242 k = 0;
2243 while ((FileName = FindCommaDelimited(Names, k++)) != NULL) {
2244 PathName = StrDuplicate(DirName);
2245 MergeStrings(&PathName, FileName, (StriCmp(PathName, L"\\") == 0) ? 0 : L'\\');
2246 for (VolumeIndex = 0; VolumeIndex < VolumesCount; VolumeIndex++) {
2247 if ((Volumes[VolumeIndex]->RootDir != NULL) && (FileExists(Volumes[VolumeIndex]->RootDir, PathName))) {
2248 SPrint(FullDescription, 255, L"%s at %s on %s", Description, PathName, Volumes[VolumeIndex]->VolName);
2249 AddToolEntry(Volumes[VolumeIndex]->DeviceHandle, PathName, FullDescription, BuiltinIcon(Icon), 'S', FALSE);
2250 } // if
2251 } // for
2252 MyFreePool(PathName);
2253 MyFreePool(FileName);
2254 } // while Names
2255 MyFreePool(DirName);
2256 } // while Locations
2257 } // VOID FindTool()
2258
2259 // Add the second-row tags containing built-in and external tools (EFI shell,
2260 // reboot, etc.)
2261 static VOID ScanForTools(VOID) {
2262 CHAR16 *FileName = NULL, *VolName = NULL, *MokLocations, Description[256];
2263 REFIT_MENU_ENTRY *TempMenuEntry;
2264 UINTN i, j, VolumeIndex;
2265 UINT64 osind;
2266 CHAR8 *b = 0;
2267
2268 MokLocations = StrDuplicate(MOK_LOCATIONS);
2269 if (MokLocations != NULL)
2270 MergeStrings(&MokLocations, SelfDirPath, L',');
2271
2272 for (i = 0; i < NUM_TOOLS; i++) {
2273 switch(GlobalConfig.ShowTools[i]) {
2274 // NOTE: Be sure that FileName is NULL at the end of each case.
2275 case TAG_SHUTDOWN:
2276 TempMenuEntry = CopyMenuEntry(&MenuEntryShutdown);
2277 TempMenuEntry->Image = BuiltinIcon(BUILTIN_ICON_FUNC_SHUTDOWN);
2278 AddMenuEntry(&MainMenu, TempMenuEntry);
2279 break;
2280
2281 case TAG_REBOOT:
2282 TempMenuEntry = CopyMenuEntry(&MenuEntryReset);
2283 TempMenuEntry->Image = BuiltinIcon(BUILTIN_ICON_FUNC_RESET);
2284 AddMenuEntry(&MainMenu, TempMenuEntry);
2285 break;
2286
2287 case TAG_ABOUT:
2288 TempMenuEntry = CopyMenuEntry(&MenuEntryAbout);
2289 TempMenuEntry->Image = BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT);
2290 AddMenuEntry(&MainMenu, TempMenuEntry);
2291 break;
2292
2293 case TAG_EXIT:
2294 TempMenuEntry = CopyMenuEntry(&MenuEntryExit);
2295 TempMenuEntry->Image = BuiltinIcon(BUILTIN_ICON_FUNC_EXIT);
2296 AddMenuEntry(&MainMenu, TempMenuEntry);
2297 break;
2298
2299 case TAG_FIRMWARE:
2300 if (EfivarGetRaw(&GlobalGuid, L"OsIndicationsSupported", &b, &j) == EFI_SUCCESS) {
2301 osind = (UINT64)*b;
2302 if (osind & EFI_OS_INDICATIONS_BOOT_TO_FW_UI) {
2303 TempMenuEntry = CopyMenuEntry(&MenuEntryFirmware);
2304 TempMenuEntry->Image = BuiltinIcon(BUILTIN_ICON_FUNC_FIRMWARE);
2305 AddMenuEntry(&MainMenu, TempMenuEntry);
2306 } // if
2307 } // if
2308 break;
2309
2310 case TAG_SHELL:
2311 j = 0;
2312 while ((FileName = FindCommaDelimited(SHELL_NAMES, j++)) != NULL) {
2313 if (FileExists(SelfRootDir, FileName)) {
2314 AddToolEntry(SelfLoadedImage->DeviceHandle, FileName, L"EFI Shell", BuiltinIcon(BUILTIN_ICON_TOOL_SHELL),
2315 'S', FALSE);
2316 }
2317 MyFreePool(FileName);
2318 } // while
2319 break;
2320
2321 case TAG_GPTSYNC:
2322 j = 0;
2323 while ((FileName = FindCommaDelimited(GPTSYNC_NAMES, j++)) != NULL) {
2324 if (FileExists(SelfRootDir, FileName)) {
2325 AddToolEntry(SelfLoadedImage->DeviceHandle, FileName, L"Hybrid MBR tool", BuiltinIcon(BUILTIN_ICON_TOOL_PART),
2326 'P', FALSE);
2327 } // if
2328 MyFreePool(FileName);
2329 } // while
2330 FileName = NULL;
2331 break;
2332
2333 case TAG_GDISK:
2334 j = 0;
2335 while ((FileName = FindCommaDelimited(GDISK_NAMES, j++)) != NULL) {
2336 if (FileExists(SelfRootDir, FileName)) {
2337 AddToolEntry(SelfLoadedImage->DeviceHandle, FileName, L"disk partitioning tool",
2338 BuiltinIcon(BUILTIN_ICON_TOOL_PART), 'G', FALSE);
2339 } // if
2340 MyFreePool(FileName);
2341 } // while
2342 FileName = NULL;
2343 break;
2344
2345 case TAG_APPLE_RECOVERY:
2346 FileName = StrDuplicate(L"\\com.apple.recovery.boot\\boot.efi");
2347 for (VolumeIndex = 0; VolumeIndex < VolumesCount; VolumeIndex++) {
2348 if ((Volumes[VolumeIndex]->RootDir != NULL) && (FileExists(Volumes[VolumeIndex]->RootDir, FileName))) {
2349 SPrint(Description, 255, L"Apple Recovery on %s", Volumes[VolumeIndex]->VolName);
2350 AddToolEntry(Volumes[VolumeIndex]->DeviceHandle, FileName, Description,
2351 BuiltinIcon(BUILTIN_ICON_TOOL_APPLE_RESCUE), 'R', TRUE);
2352 } // if
2353 } // for
2354 MyFreePool(FileName);
2355 FileName = NULL;
2356 break;
2357
2358 case TAG_WINDOWS_RECOVERY:
2359 j = 0;
2360 while ((FileName = FindCommaDelimited(GlobalConfig.WindowsRecoveryFiles, j++)) != NULL) {
2361 SplitVolumeAndFilename(&FileName, &VolName);
2362 for (VolumeIndex = 0; VolumeIndex < VolumesCount; VolumeIndex++) {
2363 if ((Volumes[VolumeIndex]->RootDir != NULL) && (FileExists(Volumes[VolumeIndex]->RootDir, FileName)) &&
2364 ((VolName == NULL) || (StriCmp(VolName, Volumes[VolumeIndex]->VolName) == 0))) {
2365 SPrint(Description, 255, L"Microsoft Recovery on %s", Volumes[VolumeIndex]->VolName);
2366 AddToolEntry(Volumes[VolumeIndex]->DeviceHandle, FileName, Description,
2367 BuiltinIcon(BUILTIN_ICON_TOOL_WINDOWS_RESCUE), 'R', TRUE);
2368 } // if
2369 } // for
2370 } // while
2371 MyFreePool(FileName);
2372 FileName = NULL;
2373 MyFreePool(VolName);
2374 VolName = NULL;
2375 break;
2376
2377 case TAG_MOK_TOOL:
2378 FindTool(MokLocations, MOK_NAMES, L"MOK utility", BUILTIN_ICON_TOOL_MOK_TOOL);
2379 break;
2380
2381 case TAG_MEMTEST:
2382 FindTool(MEMTEST_LOCATIONS, MEMTEST_NAMES, L"Memory test utility", BUILTIN_ICON_TOOL_MEMTEST);
2383 break;
2384
2385 } // switch()
2386 } // for
2387 } // static VOID ScanForTools
2388
2389 // Rescan for boot loaders
2390 VOID RescanAll(VOID) {
2391 EG_PIXEL BGColor;
2392
2393 BGColor.b = 255;
2394 BGColor.g = 175;
2395 BGColor.r = 100;
2396 BGColor.a = 0;
2397 egDisplayMessage(L"Scanning for new boot loaders; please wait....", &BGColor);
2398 FreeList((VOID ***) &(MainMenu.Entries), &MainMenu.EntryCount);
2399 MainMenu.Entries = NULL;
2400 MainMenu.EntryCount = 0;
2401 ReadConfig(GlobalConfig.ConfigFilename);
2402 ConnectAllDriversToAllControllers();
2403 ScanVolumes();
2404 ScanForBootloaders();
2405 ScanForTools();
2406 SetupScreen();
2407 } // VOID RescanAll()
2408
2409 #ifdef __MAKEWITH_TIANO
2410
2411 // Minimal initialization function
2412 static VOID InitializeLib(IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable) {
2413 gST = SystemTable;
2414 // gImageHandle = ImageHandle;
2415 gBS = SystemTable->BootServices;
2416 // gRS = SystemTable->RuntimeServices;
2417 gRT = SystemTable->RuntimeServices; // Some BDS functions need gRT to be set
2418 EfiGetSystemConfigurationTable (&gEfiDxeServicesTableGuid, (VOID **) &gDS);
2419
2420 InitializeConsoleSim();
2421 }
2422
2423 #endif
2424
2425 // Set up our own Secure Boot extensions....
2426 // Returns TRUE on success, FALSE otherwise
2427 static BOOLEAN SecureBootSetup(VOID) {
2428 EFI_STATUS Status;
2429 BOOLEAN Success = FALSE;
2430
2431 if (secure_mode() && ShimLoaded()) {
2432 Status = security_policy_install();
2433 if (Status == EFI_SUCCESS) {
2434 Success = TRUE;
2435 } else {
2436 Print(L"Failed to install MOK Secure Boot extensions");
2437 }
2438 }
2439 return Success;
2440 } // VOID SecureBootSetup()
2441
2442 // Remove our own Secure Boot extensions....
2443 // Returns TRUE on success, FALSE otherwise
2444 static BOOLEAN SecureBootUninstall(VOID) {
2445 EFI_STATUS Status;
2446 BOOLEAN Success = TRUE;
2447
2448 if (secure_mode()) {
2449 Status = security_policy_uninstall();
2450 if (Status != EFI_SUCCESS) {
2451 Success = FALSE;
2452 BeginTextScreen(L"Secure Boot Policy Failure");
2453 Print(L"Failed to uninstall MOK Secure Boot extensions; forcing a reboot.");
2454 PauseForKey();
2455 refit_call4_wrapper(RT->ResetSystem, EfiResetCold, EFI_SUCCESS, 0, NULL);
2456 }
2457 }
2458 return Success;
2459 } // VOID SecureBootUninstall
2460
2461 // Sets the global configuration filename; will be CONFIG_FILE_NAME unless the
2462 // "-c" command-line option is set, in which case that takes precedence.
2463 // If an error is encountered, leaves the value alone (it should be set to
2464 // CONFIG_FILE_NAME when GlobalConfig is initialized).
2465 static VOID SetConfigFilename(EFI_HANDLE ImageHandle) {
2466 EFI_LOADED_IMAGE *Info;
2467 CHAR16 *Options, *FileName;
2468 EFI_STATUS Status;
2469 INTN Where;
2470
2471 Status = refit_call3_wrapper(BS->HandleProtocol, ImageHandle, &LoadedImageProtocol, (VOID **) &Info);
2472 if ((Status == EFI_SUCCESS) && (Info->LoadOptionsSize > 0)) {
2473 Options = (CHAR16 *) Info->LoadOptions;
2474 Where = FindSubString(L" -c ", Options);
2475 if (Where >= 0) {
2476 FileName = StrDuplicate(&Options[Where + 4]);
2477 Where = FindSubString(L" ", FileName);
2478 if (Where > 0)
2479 FileName[Where] = L'\0';
2480
2481 if (FileExists(SelfDir, FileName)) {
2482 GlobalConfig.ConfigFilename = FileName;
2483 } else {
2484 Print(L"Specified configuration file (%s) doesn't exist; using\n'refind.conf' default\n", FileName);
2485 MyFreePool(FileName);
2486 } // if/else
2487 } // if
2488 } // if
2489 } // VOID SetConfigFilename()
2490
2491 //
2492 // main entry point
2493 //
2494 EFI_STATUS
2495 EFIAPI
2496 efi_main (EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable)
2497 {
2498 EFI_STATUS Status;
2499 BOOLEAN MainLoopRunning = TRUE;
2500 BOOLEAN MokProtocol;
2501 REFIT_MENU_ENTRY *ChosenEntry;
2502 UINTN MenuExit, i;
2503 CHAR16 *Selection = NULL;
2504 EG_PIXEL BGColor;
2505
2506 // bootstrap
2507 InitializeLib(ImageHandle, SystemTable);
2508 Status = InitRefitLib(ImageHandle);
2509 if (EFI_ERROR(Status))
2510 return Status;
2511
2512 // read configuration
2513 CopyMem(GlobalConfig.ScanFor, "ieom ", NUM_SCAN_OPTIONS);
2514 FindLegacyBootType();
2515 if (GlobalConfig.LegacyType == LEGACY_TYPE_MAC)
2516 CopyMem(GlobalConfig.ScanFor, "ihebocm ", NUM_SCAN_OPTIONS);
2517 SetConfigFilename(ImageHandle);
2518 ReadConfig(GlobalConfig.ConfigFilename);
2519 // ScanVolumes();
2520
2521 InitScreen();
2522 WarnIfLegacyProblems();
2523 MainMenu.TimeoutSeconds = GlobalConfig.Timeout;
2524
2525 // disable EFI watchdog timer
2526 refit_call4_wrapper(BS->SetWatchdogTimer, 0x0000, 0x0000, 0x0000, NULL);
2527
2528 // further bootstrap (now with config available)
2529 MokProtocol = SecureBootSetup();
2530 LoadDrivers();
2531 ScanVolumes();
2532 ScanForBootloaders();
2533 ScanForTools();
2534 SetupScreen();
2535
2536 if (GlobalConfig.ScanDelay > 0) {
2537 BGColor.b = 255;
2538 BGColor.g = 175;
2539 BGColor.r = 100;
2540 BGColor.a = 0;
2541 egDisplayMessage(L"Pausing before disk scan; please wait....", &BGColor);
2542 for (i = 0; i < GlobalConfig.ScanDelay; i++)
2543 refit_call1_wrapper(BS->Stall, 1000000);
2544 RescanAll();
2545 } // if
2546
2547 if (GlobalConfig.DefaultSelection)
2548 Selection = StrDuplicate(GlobalConfig.DefaultSelection);
2549
2550 while (MainLoopRunning) {
2551 MenuExit = RunMainMenu(&MainMenu, Selection, &ChosenEntry);
2552
2553 // The Escape key triggers a re-scan operation....
2554 if (MenuExit == MENU_EXIT_ESCAPE) {
2555 MenuExit = 0;
2556 RescanAll();
2557 continue;
2558 }
2559
2560 switch (ChosenEntry->Tag) {
2561
2562 case TAG_REBOOT: // Reboot
2563 TerminateScreen();
2564 refit_call4_wrapper(RT->ResetSystem, EfiResetCold, EFI_SUCCESS, 0, NULL);
2565 MainLoopRunning = FALSE; // just in case we get this far
2566 break;
2567
2568 case TAG_SHUTDOWN: // Shut Down
2569 TerminateScreen();
2570 refit_call4_wrapper(RT->ResetSystem, EfiResetShutdown, EFI_SUCCESS, 0, NULL);
2571 MainLoopRunning = FALSE; // just in case we get this far
2572 break;
2573
2574 case TAG_ABOUT: // About rEFInd
2575 AboutrEFInd();
2576 break;
2577
2578 case TAG_LOADER: // Boot OS via .EFI loader
2579 StartLoader((LOADER_ENTRY *)ChosenEntry);
2580 break;
2581
2582 case TAG_LEGACY: // Boot legacy OS
2583 StartLegacy((LEGACY_ENTRY *)ChosenEntry);
2584 break;
2585
2586 #ifdef __MAKEWITH_TIANO
2587 case TAG_LEGACY_UEFI: // Boot a legacy OS on a non-Mac
2588 StartLegacyUEFI((LEGACY_ENTRY *)ChosenEntry);
2589 break;
2590 #endif
2591
2592 case TAG_TOOL: // Start a EFI tool
2593 StartTool((LOADER_ENTRY *)ChosenEntry);
2594 break;
2595
2596 case TAG_EXIT: // Terminate rEFInd
2597 if ((MokProtocol) && !SecureBootUninstall()) {
2598 MainLoopRunning = FALSE; // just in case we get this far
2599 } else {
2600 BeginTextScreen(L" ");
2601 return EFI_SUCCESS;
2602 }
2603 break;
2604
2605 case TAG_FIRMWARE: // Reboot into firmware's user interface
2606 RebootIntoFirmware();
2607 break;
2608
2609 } // switch()
2610 MyFreePool(Selection);
2611 Selection = (ChosenEntry->Title) ? StrDuplicate(ChosenEntry->Title) : NULL;
2612 } // while()
2613
2614 // If we end up here, things have gone wrong. Try to reboot, and if that
2615 // fails, go into an endless loop.
2616 refit_call4_wrapper(RT->ResetSystem, EfiResetCold, EFI_SUCCESS, 0, NULL);
2617 EndlessIdleLoop();
2618
2619 return EFI_SUCCESS;
2620 } /* efi_main() */