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