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