]> code.delx.au - refind/blob - refind/main.c
Remove redundant Ubuntu kernel entries; attempt at fix for re-scanning
[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.8.6");
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 ((DontScanDir = FindCommaDelimited(GlobalConfig.DontScanDirs, i++)) && ScanIt) {
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 } // while()
1128
1129 return ScanIt;
1130 } // BOOLEAN ShouldScan()
1131
1132 // Returns TRUE if the file is byte-for-byte identical with the fallback file
1133 // on the volume AND if the file is not itself the fallback file; returns
1134 // FALSE if the file is not identical to the fallback file OR if the file
1135 // IS the fallback file. Intended for use in excluding the fallback boot
1136 // loader when it's a duplicate of another boot loader.
1137 static BOOLEAN DuplicatesFallback(IN REFIT_VOLUME *Volume, IN CHAR16 *FileName) {
1138 CHAR8 *FileContents, *FallbackContents;
1139 EFI_FILE_HANDLE FileHandle, FallbackHandle;
1140 EFI_FILE_INFO *FileInfo, *FallbackInfo;
1141 UINTN FileSize = 0, FallbackSize = 0;
1142 EFI_STATUS Status;
1143 BOOLEAN AreIdentical = FALSE;
1144
1145 if (!FileExists(Volume->RootDir, FileName) || !FileExists(Volume->RootDir, FALLBACK_FULLNAME))
1146 return FALSE;
1147
1148 CleanUpPathNameSlashes(FileName);
1149
1150 if (StriCmp(FileName, FALLBACK_FULLNAME) == 0)
1151 return FALSE; // identical filenames, so not a duplicate....
1152
1153 Status = refit_call5_wrapper(Volume->RootDir->Open, Volume->RootDir, &FileHandle, FileName, EFI_FILE_MODE_READ, 0);
1154 if (Status == EFI_SUCCESS) {
1155 FileInfo = LibFileInfo(FileHandle);
1156 FileSize = FileInfo->FileSize;
1157 } else {
1158 return FALSE;
1159 }
1160
1161 Status = refit_call5_wrapper(Volume->RootDir->Open, Volume->RootDir, &FallbackHandle, FALLBACK_FULLNAME, EFI_FILE_MODE_READ, 0);
1162 if (Status == EFI_SUCCESS) {
1163 FallbackInfo = LibFileInfo(FallbackHandle);
1164 FallbackSize = FallbackInfo->FileSize;
1165 } else {
1166 refit_call1_wrapper(FileHandle->Close, FileHandle);
1167 return FALSE;
1168 }
1169
1170 if (FallbackSize != FileSize) { // not same size, so can't be identical
1171 AreIdentical = FALSE;
1172 } else { // could be identical; do full check....
1173 FileContents = AllocatePool(FileSize);
1174 FallbackContents = AllocatePool(FallbackSize);
1175 if (FileContents && FallbackContents) {
1176 Status = refit_call3_wrapper(FileHandle->Read, FileHandle, &FileSize, FileContents);
1177 if (Status == EFI_SUCCESS) {
1178 Status = refit_call3_wrapper(FallbackHandle->Read, FallbackHandle, &FallbackSize, FallbackContents);
1179 }
1180 if (Status == EFI_SUCCESS) {
1181 AreIdentical = (CompareMem(FileContents, FallbackContents, FileSize) == 0);
1182 } // if
1183 } // if
1184 MyFreePool(FileContents);
1185 MyFreePool(FallbackContents);
1186 } // if/else
1187
1188 // BUG ALERT: Some systems (e.g., DUET, some Macs with large displays) crash if the
1189 // following two calls are reversed. Go figure....
1190 refit_call1_wrapper(FileHandle->Close, FallbackHandle);
1191 refit_call1_wrapper(FileHandle->Close, FileHandle);
1192 return AreIdentical;
1193 } // BOOLEAN DuplicatesFallback()
1194
1195 // Returns FALSE if two measures of file size are identical for a single file,
1196 // TRUE if not or if the file can't be opened and the other measure is non-0.
1197 // Despite the function's name, this isn't really a direct test of symbolic
1198 // link status, since EFI doesn't officially support symlinks. It does seem
1199 // to be a reliable indicator, though. (OTOH, some disk errors might cause a
1200 // file to fail to open, which would return a false positive -- but as I use
1201 // this function to exclude symbolic links from the list of boot loaders,
1202 // that would be fine, since such boot loaders wouldn't work.)
1203 static BOOLEAN IsSymbolicLink(REFIT_VOLUME *Volume, CHAR16 *Path, EFI_FILE_INFO *DirEntry) {
1204 EFI_FILE_HANDLE FileHandle;
1205 EFI_FILE_INFO *FileInfo = NULL;
1206 EFI_STATUS Status;
1207 UINTN FileSize2 = 0;
1208 CHAR16 *FileName;
1209
1210 FileName = StrDuplicate(Path);
1211 MergeStrings(&FileName, DirEntry->FileName, L'\\');
1212 CleanUpPathNameSlashes(FileName);
1213
1214 Status = refit_call5_wrapper(Volume->RootDir->Open, Volume->RootDir, &FileHandle, FileName, EFI_FILE_MODE_READ, 0);
1215 if (Status == EFI_SUCCESS) {
1216 FileInfo = LibFileInfo(FileHandle);
1217 if (FileInfo != NULL)
1218 FileSize2 = FileInfo->FileSize;
1219 }
1220
1221 MyFreePool(FileName);
1222 MyFreePool(FileInfo);
1223
1224 return (DirEntry->FileSize != FileSize2);
1225 } // BOOLEAN IsSymbolicLink()
1226
1227 // Returns TRUE if a file with the same name as the original but with
1228 // ".efi.signed" is also present in the same directory. Ubuntu is using
1229 // this filename as a signed version of the original unsigned kernel, and
1230 // there's no point in cluttering the display with two kernels that will
1231 // behave identically on non-SB systems, or when one will fail when SB
1232 // is active.
1233 static BOOLEAN HasSignedCounterpart(IN REFIT_VOLUME *Volume, IN CHAR16 *Path, IN CHAR16 *Filename) {
1234 CHAR16 *NewFile = NULL;
1235 BOOLEAN retval = FALSE;
1236
1237 MergeStrings(&NewFile, Path, 0);
1238 MergeStrings(&NewFile, Filename, L'\\');
1239 MergeStrings(&NewFile, L".efi.signed", 0);
1240 if (FileExists(Volume->RootDir, NewFile))
1241 retval = TRUE;
1242 MyFreePool(NewFile);
1243
1244 return retval;
1245 } // BOOLEAN HasSignedCounterpart()
1246
1247 // Scan an individual directory for EFI boot loader files and, if found,
1248 // add them to the list. Exception: Ignores FALLBACK_FULLNAME, which is picked
1249 // up in ScanEfiFiles(). Sorts the entries within the loader directory so that
1250 // the most recent one appears first in the list.
1251 // Returns TRUE if a duplicate for FALLBACK_FILENAME was found, FALSE if not.
1252 static BOOLEAN ScanLoaderDir(IN REFIT_VOLUME *Volume, IN CHAR16 *Path, IN CHAR16 *Pattern)
1253 {
1254 EFI_STATUS Status;
1255 REFIT_DIR_ITER DirIter;
1256 EFI_FILE_INFO *DirEntry;
1257 CHAR16 FileName[256], *Extension;
1258 struct LOADER_LIST *LoaderList = NULL, *NewLoader;
1259 BOOLEAN FoundFallbackDuplicate = FALSE;
1260
1261 if ((!SelfDirPath || !Path || ((StriCmp(Path, SelfDirPath) == 0) && (Volume->DeviceHandle != SelfVolume->DeviceHandle)) ||
1262 (StriCmp(Path, SelfDirPath) != 0)) &&
1263 (ShouldScan(Volume, Path))) {
1264 // look through contents of the directory
1265 DirIterOpen(Volume->RootDir, Path, &DirIter);
1266 while (DirIterNext(&DirIter, 2, Pattern, &DirEntry)) {
1267 Extension = FindExtension(DirEntry->FileName);
1268 if (DirEntry->FileName[0] == '.' ||
1269 StriCmp(Extension, L".icns") == 0 ||
1270 StriCmp(Extension, L".png") == 0 ||
1271 (StriCmp(DirEntry->FileName, FALLBACK_BASENAME) == 0 && (StriCmp(Path, L"EFI\\BOOT") == 0)) ||
1272 StriSubCmp(L"shell", DirEntry->FileName) ||
1273 IsSymbolicLink(Volume, Path, DirEntry) || /* is symbolic link */
1274 HasSignedCounterpart(Volume, Path, DirEntry->FileName) || /* a file with same name plus ".efi.signed" is present */
1275 FilenameIn(Volume, Path, DirEntry->FileName, GlobalConfig.DontScanFiles))
1276 continue; // skip this
1277
1278 if (Path)
1279 SPrint(FileName, 255, L"\\%s\\%s", Path, DirEntry->FileName);
1280 else
1281 SPrint(FileName, 255, L"\\%s", DirEntry->FileName);
1282 CleanUpPathNameSlashes(FileName);
1283
1284 if(!IsValidLoader(Volume->RootDir, FileName))
1285 continue;
1286
1287 NewLoader = AllocateZeroPool(sizeof(struct LOADER_LIST));
1288 if (NewLoader != NULL) {
1289 NewLoader->FileName = StrDuplicate(FileName);
1290 NewLoader->TimeStamp = DirEntry->ModificationTime;
1291 LoaderList = AddLoaderListEntry(LoaderList, NewLoader);
1292 if (DuplicatesFallback(Volume, FileName))
1293 FoundFallbackDuplicate = TRUE;
1294 } // if
1295 MyFreePool(Extension);
1296 } // while
1297
1298 NewLoader = LoaderList;
1299 while (NewLoader != NULL) {
1300 AddLoaderEntry(NewLoader->FileName, NULL, Volume);
1301 NewLoader = NewLoader->NextEntry;
1302 } // while
1303
1304 CleanUpLoaderList(LoaderList);
1305 Status = DirIterClose(&DirIter);
1306 // NOTE: EFI_INVALID_PARAMETER really is an error that should be reported;
1307 // but I've gotten reports from users who are getting this error occasionally
1308 // and I can't find anything wrong or reproduce the problem, so I'm putting
1309 // it down to buggy EFI implementations and ignoring that particular error....
1310 if ((Status != EFI_NOT_FOUND) && (Status != EFI_INVALID_PARAMETER)) {
1311 if (Path)
1312 SPrint(FileName, 255, L"while scanning the %s directory", Path);
1313 else
1314 StrCpy(FileName, L"while scanning the root directory");
1315 CheckError(Status, FileName);
1316 } // if (Status != EFI_NOT_FOUND)
1317 } // if not scanning a blacklisted directory
1318
1319 return FoundFallbackDuplicate;
1320 } /* static VOID ScanLoaderDir() */
1321
1322 static VOID ScanEfiFiles(REFIT_VOLUME *Volume) {
1323 EFI_STATUS Status;
1324 REFIT_DIR_ITER EfiDirIter;
1325 EFI_FILE_INFO *EfiDirEntry;
1326 CHAR16 FileName[256], *Directory = NULL, *MatchPatterns, *VolName = NULL, *SelfPath;
1327 UINTN i, Length;
1328 BOOLEAN ScanFallbackLoader = TRUE;
1329 BOOLEAN FoundBRBackup = FALSE;
1330
1331 MatchPatterns = StrDuplicate(LOADER_MATCH_PATTERNS);
1332 if (GlobalConfig.ScanAllLinux)
1333 MergeStrings(&MatchPatterns, LINUX_MATCH_PATTERNS, L',');
1334
1335 if ((Volume->RootDir != NULL) && (Volume->VolName != NULL) && (Volume->IsReadable)) {
1336 // check for Mac OS X boot loader
1337 if (ShouldScan(Volume, L"System\\Library\\CoreServices")) {
1338 StrCpy(FileName, MACOSX_LOADER_PATH);
1339 if (FileExists(Volume->RootDir, FileName) && !FilenameIn(Volume, Directory, L"boot.efi", GlobalConfig.DontScanFiles)) {
1340 AddLoaderEntry(FileName, L"Mac OS X", Volume);
1341 if (DuplicatesFallback(Volume, FileName))
1342 ScanFallbackLoader = FALSE;
1343 }
1344
1345 // check for XOM
1346 StrCpy(FileName, L"System\\Library\\CoreServices\\xom.efi");
1347 if (FileExists(Volume->RootDir, FileName) && !FilenameIn(Volume, Directory, L"boot.efi", GlobalConfig.DontScanFiles)) {
1348 AddLoaderEntry(FileName, L"Windows XP (XoM)", Volume);
1349 if (DuplicatesFallback(Volume, FileName))
1350 ScanFallbackLoader = FALSE;
1351 }
1352 } // if should scan Mac directory
1353
1354 // check for Microsoft boot loader/menu
1355 if (ShouldScan(Volume, L"EFI\\Microsoft\\Boot")) {
1356 StrCpy(FileName, L"EFI\\Microsoft\\Boot\\bkpbootmgfw.efi");
1357 if (FileExists(Volume->RootDir, FileName) && !FilenameIn(Volume, Directory, L"bkpbootmgfw.efi", GlobalConfig.DontScanFiles)) {
1358 AddLoaderEntry(FileName, L"Microsoft EFI boot (Boot Repair backup)", Volume);
1359 FoundBRBackup = TRUE;
1360 if (DuplicatesFallback(Volume, FileName))
1361 ScanFallbackLoader = FALSE;
1362 }
1363 StrCpy(FileName, L"EFI\\Microsoft\\Boot\\bootmgfw.efi");
1364 if (FileExists(Volume->RootDir, FileName) && !FilenameIn(Volume, Directory, L"bootmgfw.efi", GlobalConfig.DontScanFiles)) {
1365 if (FoundBRBackup)
1366 AddLoaderEntry(FileName, L"Supposed Microsoft EFI boot (probably GRUB)", Volume);
1367 else
1368 AddLoaderEntry(FileName, L"Microsoft EFI boot", Volume);
1369 if (DuplicatesFallback(Volume, FileName))
1370 ScanFallbackLoader = FALSE;
1371 }
1372 } // if
1373
1374 // scan the root directory for EFI executables
1375 if (ScanLoaderDir(Volume, L"\\", MatchPatterns))
1376 ScanFallbackLoader = FALSE;
1377
1378 // scan subdirectories of the EFI directory (as per the standard)
1379 DirIterOpen(Volume->RootDir, L"EFI", &EfiDirIter);
1380 while (DirIterNext(&EfiDirIter, 1, NULL, &EfiDirEntry)) {
1381 if (StriCmp(EfiDirEntry->FileName, L"tools") == 0 || EfiDirEntry->FileName[0] == '.')
1382 continue; // skip this, doesn't contain boot loaders or is scanned later
1383 SPrint(FileName, 255, L"EFI\\%s", EfiDirEntry->FileName);
1384 if (ScanLoaderDir(Volume, FileName, MatchPatterns))
1385 ScanFallbackLoader = FALSE;
1386 } // while()
1387 Status = DirIterClose(&EfiDirIter);
1388 if (Status != EFI_NOT_FOUND)
1389 CheckError(Status, L"while scanning the EFI directory");
1390
1391 // Scan user-specified (or additional default) directories....
1392 i = 0;
1393 while ((Directory = FindCommaDelimited(GlobalConfig.AlsoScan, i++)) != NULL) {
1394 if (ShouldScan(Volume, Directory)) {
1395 SplitVolumeAndFilename(&Directory, &VolName);
1396 CleanUpPathNameSlashes(Directory);
1397 Length = StrLen(Directory);
1398 if ((Length > 0) && ScanLoaderDir(Volume, Directory, MatchPatterns))
1399 ScanFallbackLoader = FALSE;
1400 MyFreePool(VolName);
1401 } // if should scan dir
1402 MyFreePool(Directory);
1403 } // while
1404
1405 // Don't scan the fallback loader if it's on the same volume and a duplicate of rEFInd itself....
1406 SelfPath = DevicePathToStr(SelfLoadedImage->FilePath);
1407 CleanUpPathNameSlashes(SelfPath);
1408 if ((Volume->DeviceHandle == SelfLoadedImage->DeviceHandle) && DuplicatesFallback(Volume, SelfPath))
1409 ScanFallbackLoader = FALSE;
1410
1411 // If not a duplicate & if it exists & if it's not us, create an entry
1412 // for the fallback boot loader
1413 if (ScanFallbackLoader && FileExists(Volume->RootDir, FALLBACK_FULLNAME) && ShouldScan(Volume, L"EFI\\BOOT"))
1414 AddLoaderEntry(FALLBACK_FULLNAME, L"Fallback boot loader", Volume);
1415 } // if
1416 } // static VOID ScanEfiFiles()
1417
1418 // Scan internal disks for valid EFI boot loaders....
1419 static VOID ScanInternal(VOID) {
1420 UINTN VolumeIndex;
1421
1422 for (VolumeIndex = 0; VolumeIndex < VolumesCount; VolumeIndex++) {
1423 if (Volumes[VolumeIndex]->DiskKind == DISK_KIND_INTERNAL) {
1424 ScanEfiFiles(Volumes[VolumeIndex]);
1425 }
1426 } // for
1427 } // static VOID ScanInternal()
1428
1429 // Scan external disks for valid EFI boot loaders....
1430 static VOID ScanExternal(VOID) {
1431 UINTN VolumeIndex;
1432
1433 for (VolumeIndex = 0; VolumeIndex < VolumesCount; VolumeIndex++) {
1434 if (Volumes[VolumeIndex]->DiskKind == DISK_KIND_EXTERNAL) {
1435 ScanEfiFiles(Volumes[VolumeIndex]);
1436 }
1437 } // for
1438 } // static VOID ScanExternal()
1439
1440 // Scan internal disks for valid EFI boot loaders....
1441 static VOID ScanOptical(VOID) {
1442 UINTN VolumeIndex;
1443
1444 for (VolumeIndex = 0; VolumeIndex < VolumesCount; VolumeIndex++) {
1445 if (Volumes[VolumeIndex]->DiskKind == DISK_KIND_OPTICAL) {
1446 ScanEfiFiles(Volumes[VolumeIndex]);
1447 }
1448 } // for
1449 } // static VOID ScanOptical()
1450
1451 //
1452 // legacy boot functions
1453 //
1454
1455 static EFI_STATUS ActivateMbrPartition(IN EFI_BLOCK_IO *BlockIO, IN UINTN PartitionIndex)
1456 {
1457 EFI_STATUS Status;
1458 UINT8 SectorBuffer[512];
1459 MBR_PARTITION_INFO *MbrTable, *EMbrTable;
1460 UINT32 ExtBase, ExtCurrent, NextExtCurrent;
1461 UINTN LogicalPartitionIndex = 4;
1462 UINTN i;
1463 BOOLEAN HaveBootCode;
1464
1465 // read MBR
1466 Status = refit_call5_wrapper(BlockIO->ReadBlocks, BlockIO, BlockIO->Media->MediaId, 0, 512, SectorBuffer);
1467 if (EFI_ERROR(Status))
1468 return Status;
1469 if (*((UINT16 *)(SectorBuffer + 510)) != 0xaa55)
1470 return EFI_NOT_FOUND; // safety measure #1
1471
1472 // add boot code if necessary
1473 HaveBootCode = FALSE;
1474 for (i = 0; i < MBR_BOOTCODE_SIZE; i++) {
1475 if (SectorBuffer[i] != 0) {
1476 HaveBootCode = TRUE;
1477 break;
1478 }
1479 }
1480 if (!HaveBootCode) {
1481 // no boot code found in the MBR, add the syslinux MBR code
1482 SetMem(SectorBuffer, MBR_BOOTCODE_SIZE, 0);
1483 CopyMem(SectorBuffer, syslinux_mbr, SYSLINUX_MBR_SIZE);
1484 }
1485
1486 // set the partition active
1487 MbrTable = (MBR_PARTITION_INFO *)(SectorBuffer + 446);
1488 ExtBase = 0;
1489 for (i = 0; i < 4; i++) {
1490 if (MbrTable[i].Flags != 0x00 && MbrTable[i].Flags != 0x80)
1491 return EFI_NOT_FOUND; // safety measure #2
1492 if (i == PartitionIndex)
1493 MbrTable[i].Flags = 0x80;
1494 else if (PartitionIndex >= 4 && IS_EXTENDED_PART_TYPE(MbrTable[i].Type)) {
1495 MbrTable[i].Flags = 0x80;
1496 ExtBase = MbrTable[i].StartLBA;
1497 } else
1498 MbrTable[i].Flags = 0x00;
1499 }
1500
1501 // write MBR
1502 Status = refit_call5_wrapper(BlockIO->WriteBlocks, BlockIO, BlockIO->Media->MediaId, 0, 512, SectorBuffer);
1503 if (EFI_ERROR(Status))
1504 return Status;
1505
1506 if (PartitionIndex >= 4) {
1507 // we have to activate a logical partition, so walk the EMBR chain
1508
1509 // NOTE: ExtBase was set above while looking at the MBR table
1510 for (ExtCurrent = ExtBase; ExtCurrent; ExtCurrent = NextExtCurrent) {
1511 // read current EMBR
1512 Status = refit_call5_wrapper(BlockIO->ReadBlocks, BlockIO, BlockIO->Media->MediaId, ExtCurrent, 512, SectorBuffer);
1513 if (EFI_ERROR(Status))
1514 return Status;
1515 if (*((UINT16 *)(SectorBuffer + 510)) != 0xaa55)
1516 return EFI_NOT_FOUND; // safety measure #3
1517
1518 // scan EMBR, set appropriate partition active
1519 EMbrTable = (MBR_PARTITION_INFO *)(SectorBuffer + 446);
1520 NextExtCurrent = 0;
1521 for (i = 0; i < 4; i++) {
1522 if (EMbrTable[i].Flags != 0x00 && EMbrTable[i].Flags != 0x80)
1523 return EFI_NOT_FOUND; // safety measure #4
1524 if (EMbrTable[i].StartLBA == 0 || EMbrTable[i].Size == 0)
1525 break;
1526 if (IS_EXTENDED_PART_TYPE(EMbrTable[i].Type)) {
1527 // link to next EMBR
1528 NextExtCurrent = ExtBase + EMbrTable[i].StartLBA;
1529 EMbrTable[i].Flags = (PartitionIndex >= LogicalPartitionIndex) ? 0x80 : 0x00;
1530 break;
1531 } else {
1532 // logical partition
1533 EMbrTable[i].Flags = (PartitionIndex == LogicalPartitionIndex) ? 0x80 : 0x00;
1534 LogicalPartitionIndex++;
1535 }
1536 }
1537
1538 // write current EMBR
1539 Status = refit_call5_wrapper(BlockIO->WriteBlocks, BlockIO, BlockIO->Media->MediaId, ExtCurrent, 512, SectorBuffer);
1540 if (EFI_ERROR(Status))
1541 return Status;
1542
1543 if (PartitionIndex < LogicalPartitionIndex)
1544 break; // stop the loop, no need to touch further EMBRs
1545 }
1546
1547 }
1548
1549 return EFI_SUCCESS;
1550 } /* static EFI_STATUS ActivateMbrPartition() */
1551
1552 // early 2006 Core Duo / Core Solo models
1553 static UINT8 LegacyLoaderDevicePath1Data[] = {
1554 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1555 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1556 0xFF, 0xFF, 0xF9, 0xFF, 0x00, 0x00, 0x00, 0x00,
1557 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1558 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1559 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1560 };
1561 // mid-2006 Mac Pro (and probably other Core 2 models)
1562 static UINT8 LegacyLoaderDevicePath2Data[] = {
1563 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1564 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1565 0xFF, 0xFF, 0xF7, 0xFF, 0x00, 0x00, 0x00, 0x00,
1566 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1567 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1568 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1569 };
1570 // mid-2007 MBP ("Santa Rosa" based models)
1571 static UINT8 LegacyLoaderDevicePath3Data[] = {
1572 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1573 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1574 0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
1575 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1576 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1577 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1578 };
1579 // early-2008 MBA
1580 static UINT8 LegacyLoaderDevicePath4Data[] = {
1581 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1582 0x00, 0x00, 0xC0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1583 0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
1584 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1585 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1586 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1587 };
1588 // late-2008 MB/MBP (NVidia chipset)
1589 static UINT8 LegacyLoaderDevicePath5Data[] = {
1590 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1591 0x00, 0x40, 0xCB, 0xFF, 0x00, 0x00, 0x00, 0x00,
1592 0xFF, 0xBF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
1593 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1594 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1595 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1596 };
1597
1598 static EFI_DEVICE_PATH *LegacyLoaderList[] = {
1599 (EFI_DEVICE_PATH *)LegacyLoaderDevicePath1Data,
1600 (EFI_DEVICE_PATH *)LegacyLoaderDevicePath2Data,
1601 (EFI_DEVICE_PATH *)LegacyLoaderDevicePath3Data,
1602 (EFI_DEVICE_PATH *)LegacyLoaderDevicePath4Data,
1603 (EFI_DEVICE_PATH *)LegacyLoaderDevicePath5Data,
1604 NULL
1605 };
1606
1607 #define MAX_DISCOVERED_PATHS (16)
1608
1609 static VOID StartLegacy(IN LEGACY_ENTRY *Entry)
1610 {
1611 EFI_STATUS Status;
1612 EG_IMAGE *BootLogoImage;
1613 UINTN ErrorInStep = 0;
1614 EFI_DEVICE_PATH *DiscoveredPathList[MAX_DISCOVERED_PATHS];
1615
1616 BeginExternalScreen(TRUE, L"Booting Legacy OS (Mac mode)");
1617
1618 BootLogoImage = LoadOSIcon(Entry->Volume->OSIconName, L"legacy", TRUE);
1619 if (BootLogoImage != NULL)
1620 BltImageAlpha(BootLogoImage,
1621 (UGAWidth - BootLogoImage->Width ) >> 1,
1622 (UGAHeight - BootLogoImage->Height) >> 1,
1623 &StdBackgroundPixel);
1624
1625 if (Entry->Volume->IsMbrPartition) {
1626 ActivateMbrPartition(Entry->Volume->WholeDiskBlockIO, Entry->Volume->MbrPartitionIndex);
1627 }
1628
1629 ExtractLegacyLoaderPaths(DiscoveredPathList, MAX_DISCOVERED_PATHS, LegacyLoaderList);
1630
1631 Status = StartEFIImageList(DiscoveredPathList, Entry->LoadOptions, TYPE_LEGACY, L"legacy loader", 0, &ErrorInStep, TRUE);
1632 if (Status == EFI_NOT_FOUND) {
1633 if (ErrorInStep == 1) {
1634 Print(L"\nPlease make sure that you have the latest firmware update installed.\n");
1635 } else if (ErrorInStep == 3) {
1636 Print(L"\nThe firmware refused to boot from the selected volume. Note that external\n"
1637 L"hard drives are not well-supported by Apple's firmware for legacy OS booting.\n");
1638 }
1639 }
1640 FinishExternalScreen();
1641 } /* static VOID StartLegacy() */
1642
1643 // Start a device on a non-Mac using the EFI_LEGACY_BIOS_PROTOCOL
1644 #ifdef __MAKEWITH_TIANO
1645 static VOID StartLegacyUEFI(LEGACY_ENTRY *Entry)
1646 {
1647 // UINTN ExitDataSize = 0;
1648 // CHAR16 *ExitData = NULL;
1649 // EFI_STATUS Status;
1650
1651 BeginExternalScreen(TRUE, L"Booting Legacy OS (UEFI mode)");
1652 // Print(L"Launching from '%s'\n", DevicePathToStr(Entry->BdsOption->DevicePath));
1653 // PauseForKey();
1654
1655 // Status = BdsLibBootViaBootOption(Entry->BdsOption, Entry->BdsOption->DevicePath, &ExitDataSize, &ExitData);
1656 // Print(L"BdsLibBootViaBootOption() returned %d\n", Status);
1657 BdsLibConnectDevicePath (Entry->BdsOption->DevicePath);
1658 BdsLibDoLegacyBoot(Entry->BdsOption);
1659
1660 // If we get here, it means that there was a failure....
1661 Print(L"Failure booting legacy (BIOS) OS.");
1662 PauseForKey();
1663 FinishExternalScreen();
1664 } // static VOID StartLegacyUEFI()
1665 #endif // __MAKEWITH_TIANO
1666
1667 static LEGACY_ENTRY * AddLegacyEntry(IN CHAR16 *LoaderTitle, IN REFIT_VOLUME *Volume)
1668 {
1669 LEGACY_ENTRY *Entry, *SubEntry;
1670 REFIT_MENU_SCREEN *SubScreen;
1671 CHAR16 *VolDesc;
1672 CHAR16 ShortcutLetter = 0;
1673
1674 if (LoaderTitle == NULL) {
1675 if (Volume->OSName != NULL) {
1676 LoaderTitle = Volume->OSName;
1677 if (LoaderTitle[0] == 'W' || LoaderTitle[0] == 'L')
1678 ShortcutLetter = LoaderTitle[0];
1679 } else
1680 LoaderTitle = L"Legacy OS";
1681 }
1682 if (Volume->VolName != NULL)
1683 VolDesc = Volume->VolName;
1684 else
1685 VolDesc = (Volume->DiskKind == DISK_KIND_OPTICAL) ? L"CD" : L"HD";
1686
1687 // prepare the menu entry
1688 Entry = AllocateZeroPool(sizeof(LEGACY_ENTRY));
1689 Entry->me.Title = AllocateZeroPool(256 * sizeof(CHAR16));
1690 SPrint(Entry->me.Title, 255, L"Boot %s from %s", LoaderTitle, VolDesc);
1691 Entry->me.Tag = TAG_LEGACY;
1692 Entry->me.Row = 0;
1693 Entry->me.ShortcutLetter = ShortcutLetter;
1694 Entry->me.Image = LoadOSIcon(Volume->OSIconName, L"legacy", FALSE);
1695 Entry->me.BadgeImage = Volume->VolBadgeImage;
1696 Entry->Volume = Volume;
1697 Entry->LoadOptions = (Volume->DiskKind == DISK_KIND_OPTICAL) ? L"CD" :
1698 ((Volume->DiskKind == DISK_KIND_EXTERNAL) ? L"USB" : L"HD");
1699 Entry->Enabled = TRUE;
1700
1701 // create the submenu
1702 SubScreen = AllocateZeroPool(sizeof(REFIT_MENU_SCREEN));
1703 SubScreen->Title = AllocateZeroPool(256 * sizeof(CHAR16));
1704 SPrint(SubScreen->Title, 255, L"Boot Options for %s on %s", LoaderTitle, VolDesc);
1705 SubScreen->TitleImage = Entry->me.Image;
1706 SubScreen->Hint1 = StrDuplicate(SUBSCREEN_HINT1);
1707 if (GlobalConfig.HideUIFlags & HIDEUI_FLAG_EDITOR) {
1708 SubScreen->Hint2 = StrDuplicate(SUBSCREEN_HINT2_NO_EDITOR);
1709 } else {
1710 SubScreen->Hint2 = StrDuplicate(SUBSCREEN_HINT2);
1711 } // if/else
1712
1713 // default entry
1714 SubEntry = AllocateZeroPool(sizeof(LEGACY_ENTRY));
1715 SubEntry->me.Title = AllocateZeroPool(256 * sizeof(CHAR16));
1716 SPrint(SubEntry->me.Title, 255, L"Boot %s", LoaderTitle);
1717 SubEntry->me.Tag = TAG_LEGACY;
1718 SubEntry->Volume = Entry->Volume;
1719 SubEntry->LoadOptions = Entry->LoadOptions;
1720 AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry);
1721
1722 AddMenuEntry(SubScreen, &MenuEntryReturn);
1723 Entry->me.SubScreen = SubScreen;
1724 AddMenuEntry(&MainMenu, (REFIT_MENU_ENTRY *)Entry);
1725 return Entry;
1726 } /* static LEGACY_ENTRY * AddLegacyEntry() */
1727
1728
1729 #ifdef __MAKEWITH_GNUEFI
1730 static VOID ScanLegacyUEFI(IN UINTN DiskType){}
1731 #else
1732 // default volume badge icon based on disk kind
1733 static EG_IMAGE * GetDiskBadge(IN UINTN DiskType) {
1734 EG_IMAGE * Badge = NULL;
1735
1736 switch (DiskType) {
1737 case BBS_HARDDISK:
1738 Badge = BuiltinIcon(BUILTIN_ICON_VOL_INTERNAL);
1739 break;
1740 case BBS_USB:
1741 Badge = BuiltinIcon(BUILTIN_ICON_VOL_EXTERNAL);
1742 break;
1743 case BBS_CDROM:
1744 Badge = BuiltinIcon(BUILTIN_ICON_VOL_OPTICAL);
1745 break;
1746 } // switch()
1747 return Badge;
1748 } // static EG_IMAGE * GetDiskBadge()
1749
1750 /**
1751 Create a rEFInd boot option from a Legacy BIOS protocol option.
1752 */
1753 static LEGACY_ENTRY * AddLegacyEntryUEFI(BDS_COMMON_OPTION *BdsOption, IN UINT16 DiskType)
1754 {
1755 LEGACY_ENTRY *Entry, *SubEntry;
1756 REFIT_MENU_SCREEN *SubScreen;
1757 CHAR16 ShortcutLetter = 0;
1758 CHAR16 *LegacyDescription = BdsOption->Description;
1759
1760 // prepare the menu entry
1761 Entry = AllocateZeroPool(sizeof(LEGACY_ENTRY));
1762 Entry->me.Title = AllocateZeroPool(256 * sizeof(CHAR16));
1763 SPrint(Entry->me.Title, 255, L"Boot legacy target %s", LegacyDescription);
1764 Entry->me.Tag = TAG_LEGACY_UEFI;
1765 Entry->me.Row = 0;
1766 Entry->me.ShortcutLetter = ShortcutLetter;
1767 Entry->me.Image = LoadOSIcon(L"legacy", L"legacy", TRUE);
1768 Entry->LoadOptions = (DiskType == BBS_CDROM) ? L"CD" :
1769 ((DiskType == BBS_USB) ? L"USB" : L"HD");
1770 Entry->me.BadgeImage = GetDiskBadge(DiskType);
1771 Entry->BdsOption = BdsOption;
1772 Entry->Enabled = TRUE;
1773
1774 // create the submenu
1775 SubScreen = AllocateZeroPool(sizeof(REFIT_MENU_SCREEN));
1776 SubScreen->Title = AllocateZeroPool(256 * sizeof(CHAR16));
1777 SPrint(SubScreen->Title, 255, L"No boot options for legacy target");
1778 SubScreen->TitleImage = Entry->me.Image;
1779 SubScreen->Hint1 = StrDuplicate(SUBSCREEN_HINT1);
1780 if (GlobalConfig.HideUIFlags & HIDEUI_FLAG_EDITOR) {
1781 SubScreen->Hint2 = StrDuplicate(SUBSCREEN_HINT2_NO_EDITOR);
1782 } else {
1783 SubScreen->Hint2 = StrDuplicate(SUBSCREEN_HINT2);
1784 } // if/else
1785
1786 // default entry
1787 SubEntry = AllocateZeroPool(sizeof(LEGACY_ENTRY));
1788 SubEntry->me.Title = AllocateZeroPool(256 * sizeof(CHAR16));
1789 SPrint(SubEntry->me.Title, 255, L"Boot %s", LegacyDescription);
1790 SubEntry->me.Tag = TAG_LEGACY_UEFI;
1791 Entry->BdsOption = BdsOption;
1792 AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry);
1793
1794 AddMenuEntry(SubScreen, &MenuEntryReturn);
1795 Entry->me.SubScreen = SubScreen;
1796 AddMenuEntry(&MainMenu, (REFIT_MENU_ENTRY *)Entry);
1797 return Entry;
1798 } /* static LEGACY_ENTRY * AddLegacyEntryUEFI() */
1799
1800 /**
1801 Scan for legacy BIOS targets on machines that implement EFI_LEGACY_BIOS_PROTOCOL.
1802 In testing, protocol has not been implemented on Macs but has been
1803 implemented on several Dell PCs and an ASUS motherboard.
1804 Restricts output to disks of the specified DiskType.
1805 */
1806 static VOID ScanLegacyUEFI(IN UINTN DiskType)
1807 {
1808 EFI_STATUS Status;
1809 EFI_LEGACY_BIOS_PROTOCOL *LegacyBios;
1810 UINT16 *BootOrder = NULL;
1811 UINTN Index = 0;
1812 CHAR16 BootOption[10];
1813 UINTN BootOrderSize = 0;
1814 CHAR16 Buffer[20];
1815 BDS_COMMON_OPTION *BdsOption;
1816 LIST_ENTRY TempList;
1817 BBS_BBS_DEVICE_PATH * BbsDevicePath = NULL;
1818
1819 InitializeListHead (&TempList);
1820 ZeroMem (Buffer, sizeof (Buffer));
1821
1822 // If LegacyBios protocol is not implemented on this platform, then
1823 //we do not support this type of legacy boot on this machine.
1824 Status = gBS->LocateProtocol(&gEfiLegacyBootProtocolGuid, NULL, (VOID **) &LegacyBios);
1825 if (EFI_ERROR (Status))
1826 return;
1827
1828 // Grab the boot order
1829 BootOrder = BdsLibGetVariableAndSize(L"BootOrder", &gEfiGlobalVariableGuid, &BootOrderSize);
1830 if (BootOrder == NULL) {
1831 BootOrderSize = 0;
1832 }
1833
1834 Index = 0;
1835 while (Index < BootOrderSize / sizeof (UINT16))
1836 {
1837 // Grab each boot option variable from the boot order, and convert
1838 // the variable into a BDS boot option
1839 UnicodeSPrint (BootOption, sizeof (BootOption), L"Boot%04x", BootOrder[Index]);
1840 BdsOption = BdsLibVariableToOption (&TempList, BootOption);
1841
1842 if (BdsOption != NULL) {
1843 BbsDevicePath = (BBS_BBS_DEVICE_PATH *)BdsOption->DevicePath;
1844
1845 // Only add the entry if it is of a requested type (e.g. USB, HD)
1846
1847 // Two checks necessary because some systems return EFI boot loaders
1848 // with a DeviceType value that would inappropriately include them
1849 // as legacy loaders....
1850 if ((BbsDevicePath->DeviceType == DiskType) && (BdsOption->DevicePath->Type == DEVICE_TYPE_BIOS)) {
1851 AddLegacyEntryUEFI(BdsOption, BbsDevicePath->DeviceType);
1852 }
1853 }
1854 Index++;
1855 }
1856 } /* static VOID ScanLegacyUEFI() */
1857 #endif // __MAKEWITH_GNUEFI
1858
1859 static VOID ScanLegacyVolume(REFIT_VOLUME *Volume, UINTN VolumeIndex) {
1860 UINTN VolumeIndex2;
1861 BOOLEAN ShowVolume, HideIfOthersFound;
1862
1863 ShowVolume = FALSE;
1864 HideIfOthersFound = FALSE;
1865 if (Volume->IsAppleLegacy) {
1866 ShowVolume = TRUE;
1867 HideIfOthersFound = TRUE;
1868 } else if (Volume->HasBootCode) {
1869 ShowVolume = TRUE;
1870 if (Volume->BlockIO == Volume->WholeDiskBlockIO &&
1871 Volume->BlockIOOffset == 0 &&
1872 Volume->OSName == NULL)
1873 // this is a whole disk (MBR) entry; hide if we have entries for partitions
1874 HideIfOthersFound = TRUE;
1875 }
1876 if (HideIfOthersFound) {
1877 // check for other bootable entries on the same disk
1878 for (VolumeIndex2 = 0; VolumeIndex2 < VolumesCount; VolumeIndex2++) {
1879 if (VolumeIndex2 != VolumeIndex && Volumes[VolumeIndex2]->HasBootCode &&
1880 Volumes[VolumeIndex2]->WholeDiskBlockIO == Volume->WholeDiskBlockIO)
1881 ShowVolume = FALSE;
1882 }
1883 }
1884
1885 if (ShowVolume)
1886 AddLegacyEntry(NULL, Volume);
1887 } // static VOID ScanLegacyVolume()
1888
1889 // Scan attached optical discs for legacy (BIOS) boot code
1890 // and add anything found to the list....
1891 static VOID ScanLegacyDisc(VOID)
1892 {
1893 UINTN VolumeIndex;
1894 REFIT_VOLUME *Volume;
1895
1896 if (GlobalConfig.LegacyType == LEGACY_TYPE_MAC) {
1897 for (VolumeIndex = 0; VolumeIndex < VolumesCount; VolumeIndex++) {
1898 Volume = Volumes[VolumeIndex];
1899 if (Volume->DiskKind == DISK_KIND_OPTICAL)
1900 ScanLegacyVolume(Volume, VolumeIndex);
1901 } // for
1902 } else if (GlobalConfig.LegacyType == LEGACY_TYPE_UEFI) {
1903 ScanLegacyUEFI(BBS_CDROM);
1904 }
1905 } /* static VOID ScanLegacyDisc() */
1906
1907 // Scan internal hard disks for legacy (BIOS) boot code
1908 // and add anything found to the list....
1909 static VOID ScanLegacyInternal(VOID)
1910 {
1911 UINTN VolumeIndex;
1912 REFIT_VOLUME *Volume;
1913
1914 if (GlobalConfig.LegacyType == LEGACY_TYPE_MAC) {
1915 for (VolumeIndex = 0; VolumeIndex < VolumesCount; VolumeIndex++) {
1916 Volume = Volumes[VolumeIndex];
1917 if (Volume->DiskKind == DISK_KIND_INTERNAL)
1918 ScanLegacyVolume(Volume, VolumeIndex);
1919 } // for
1920 } else if (GlobalConfig.LegacyType == LEGACY_TYPE_UEFI) {
1921 ScanLegacyUEFI(BBS_HARDDISK);
1922 }
1923 } /* static VOID ScanLegacyInternal() */
1924
1925 // Scan external disks for legacy (BIOS) boot code
1926 // and add anything found to the list....
1927 static VOID ScanLegacyExternal(VOID)
1928 {
1929 UINTN VolumeIndex;
1930 REFIT_VOLUME *Volume;
1931
1932 if (GlobalConfig.LegacyType == LEGACY_TYPE_MAC) {
1933 for (VolumeIndex = 0; VolumeIndex < VolumesCount; VolumeIndex++) {
1934 Volume = Volumes[VolumeIndex];
1935 if (Volume->DiskKind == DISK_KIND_EXTERNAL)
1936 ScanLegacyVolume(Volume, VolumeIndex);
1937 } // for
1938 } else if (GlobalConfig.LegacyType == LEGACY_TYPE_UEFI) {
1939 ScanLegacyUEFI(BBS_USB);
1940 }
1941 } /* static VOID ScanLegacyExternal() */
1942
1943 //
1944 // pre-boot tool functions
1945 //
1946
1947 static VOID StartTool(IN LOADER_ENTRY *Entry)
1948 {
1949 BeginExternalScreen(Entry->UseGraphicsMode, Entry->me.Title + 6); // assumes "Start <title>" as assigned below
1950 StartEFIImage(Entry->DevicePath, Entry->LoadOptions, TYPE_EFI,
1951 Basename(Entry->LoaderPath), Entry->OSType, NULL, TRUE);
1952 FinishExternalScreen();
1953 } /* static VOID StartTool() */
1954
1955 static LOADER_ENTRY * AddToolEntry(EFI_HANDLE DeviceHandle, IN CHAR16 *LoaderPath, IN CHAR16 *LoaderTitle, IN EG_IMAGE *Image,
1956 IN CHAR16 ShortcutLetter, IN BOOLEAN UseGraphicsMode)
1957 {
1958 LOADER_ENTRY *Entry;
1959 CHAR16 *TitleStr = NULL;
1960
1961 Entry = AllocateZeroPool(sizeof(LOADER_ENTRY));
1962
1963 TitleStr = PoolPrint(L"Start %s", LoaderTitle);
1964 Entry->me.Title = TitleStr;
1965 Entry->me.Tag = TAG_TOOL;
1966 Entry->me.Row = 1;
1967 Entry->me.ShortcutLetter = ShortcutLetter;
1968 Entry->me.Image = Image;
1969 Entry->LoaderPath = (LoaderPath) ? StrDuplicate(LoaderPath) : NULL;
1970 Entry->DevicePath = FileDevicePath(DeviceHandle, Entry->LoaderPath);
1971 Entry->UseGraphicsMode = UseGraphicsMode;
1972
1973 AddMenuEntry(&MainMenu, (REFIT_MENU_ENTRY *)Entry);
1974 return Entry;
1975 } /* static LOADER_ENTRY * AddToolEntry() */
1976
1977 //
1978 // pre-boot driver functions
1979 //
1980
1981 static UINTN ScanDriverDir(IN CHAR16 *Path)
1982 {
1983 EFI_STATUS Status;
1984 REFIT_DIR_ITER DirIter;
1985 UINTN NumFound = 0;
1986 EFI_FILE_INFO *DirEntry;
1987 CHAR16 FileName[256];
1988
1989 CleanUpPathNameSlashes(Path);
1990 // look through contents of the directory
1991 DirIterOpen(SelfRootDir, Path, &DirIter);
1992 while (DirIterNext(&DirIter, 2, LOADER_MATCH_PATTERNS, &DirEntry)) {
1993 if (DirEntry->FileName[0] == '.')
1994 continue; // skip this
1995
1996 SPrint(FileName, 255, L"%s\\%s", Path, DirEntry->FileName);
1997 NumFound++;
1998 Status = StartEFIImage(FileDevicePath(SelfLoadedImage->DeviceHandle, FileName),
1999 L"", TYPE_EFI, DirEntry->FileName, 0, NULL, FALSE);
2000 }
2001 Status = DirIterClose(&DirIter);
2002 if (Status != EFI_NOT_FOUND) {
2003 SPrint(FileName, 255, L"while scanning the %s directory", Path);
2004 CheckError(Status, FileName);
2005 }
2006 return (NumFound);
2007 }
2008
2009 #ifdef __MAKEWITH_GNUEFI
2010 static EFI_STATUS ConnectAllDriversToAllControllers(VOID)
2011 {
2012 EFI_STATUS Status;
2013 UINTN AllHandleCount;
2014 EFI_HANDLE *AllHandleBuffer;
2015 UINTN Index;
2016 UINTN HandleCount;
2017 EFI_HANDLE *HandleBuffer;
2018 UINT32 *HandleType;
2019 UINTN HandleIndex;
2020 BOOLEAN Parent;
2021 BOOLEAN Device;
2022
2023 Status = LibLocateHandle(AllHandles,
2024 NULL,
2025 NULL,
2026 &AllHandleCount,
2027 &AllHandleBuffer);
2028 if (EFI_ERROR(Status))
2029 return Status;
2030
2031 for (Index = 0; Index < AllHandleCount; Index++) {
2032 //
2033 // Scan the handle database
2034 //
2035 Status = LibScanHandleDatabase(NULL,
2036 NULL,
2037 AllHandleBuffer[Index],
2038 NULL,
2039 &HandleCount,
2040 &HandleBuffer,
2041 &HandleType);
2042 if (EFI_ERROR (Status))
2043 goto Done;
2044
2045 Device = TRUE;
2046 if (HandleType[Index] & EFI_HANDLE_TYPE_DRIVER_BINDING_HANDLE)
2047 Device = FALSE;
2048 if (HandleType[Index] & EFI_HANDLE_TYPE_IMAGE_HANDLE)
2049 Device = FALSE;
2050
2051 if (Device) {
2052 Parent = FALSE;
2053 for (HandleIndex = 0; HandleIndex < HandleCount; HandleIndex++) {
2054 if (HandleType[HandleIndex] & EFI_HANDLE_TYPE_PARENT_HANDLE)
2055 Parent = TRUE;
2056 } // for
2057
2058 if (!Parent) {
2059 if (HandleType[Index] & EFI_HANDLE_TYPE_DEVICE_HANDLE) {
2060 Status = refit_call4_wrapper(BS->ConnectController,
2061 AllHandleBuffer[Index],
2062 NULL,
2063 NULL,
2064 TRUE);
2065 }
2066 }
2067 }
2068
2069 MyFreePool (HandleBuffer);
2070 MyFreePool (HandleType);
2071 }
2072
2073 Done:
2074 MyFreePool (AllHandleBuffer);
2075 return Status;
2076 } /* EFI_STATUS ConnectAllDriversToAllControllers() */
2077 #else
2078 static EFI_STATUS ConnectAllDriversToAllControllers(VOID) {
2079 BdsLibConnectAllDriversToAllControllers();
2080 return 0;
2081 }
2082 #endif
2083
2084 // Load all EFI drivers from rEFInd's "drivers" subdirectory and from the
2085 // directories specified by the user in the "scan_driver_dirs" configuration
2086 // file line.
2087 static VOID LoadDrivers(VOID)
2088 {
2089 CHAR16 *Directory, *SelfDirectory;
2090 UINTN i = 0, Length, NumFound = 0;
2091
2092 // load drivers from the subdirectories of rEFInd's home directory specified
2093 // in the DRIVER_DIRS constant.
2094 while ((Directory = FindCommaDelimited(DRIVER_DIRS, i++)) != NULL) {
2095 SelfDirectory = SelfDirPath ? StrDuplicate(SelfDirPath) : NULL;
2096 CleanUpPathNameSlashes(SelfDirectory);
2097 MergeStrings(&SelfDirectory, Directory, L'\\');
2098 NumFound += ScanDriverDir(SelfDirectory);
2099 MyFreePool(Directory);
2100 MyFreePool(SelfDirectory);
2101 }
2102
2103 // Scan additional user-specified driver directories....
2104 i = 0;
2105 while ((Directory = FindCommaDelimited(GlobalConfig.DriverDirs, i++)) != NULL) {
2106 CleanUpPathNameSlashes(Directory);
2107 Length = StrLen(Directory);
2108 if (Length > 0) {
2109 NumFound += ScanDriverDir(Directory);
2110 } // if
2111 MyFreePool(Directory);
2112 } // while
2113
2114 // connect all devices
2115 if (NumFound > 0)
2116 ConnectAllDriversToAllControllers();
2117 } /* static VOID LoadDrivers() */
2118
2119 // Determine what (if any) type of legacy (BIOS) boot support is available
2120 static VOID FindLegacyBootType(VOID) {
2121 #ifdef __MAKEWITH_TIANO
2122 EFI_STATUS Status;
2123 EFI_LEGACY_BIOS_PROTOCOL *LegacyBios;
2124 #endif
2125
2126 GlobalConfig.LegacyType = LEGACY_TYPE_NONE;
2127
2128 // UEFI-style legacy BIOS support is available only with the TianoCore EDK2
2129 // build environment, and then only with some EFI implementations....
2130 #ifdef __MAKEWITH_TIANO
2131 Status = gBS->LocateProtocol (&gEfiLegacyBootProtocolGuid, NULL, (VOID **) &LegacyBios);
2132 if (!EFI_ERROR (Status))
2133 GlobalConfig.LegacyType = LEGACY_TYPE_UEFI;
2134 #endif
2135
2136 // Macs have their own system. If the firmware vendor code contains the
2137 // string "Apple", assume it's available. Note that this overrides the
2138 // UEFI type, and might yield false positives if the vendor string
2139 // contains "Apple" as part of something bigger, so this isn't 100%
2140 // perfect.
2141 if (StriSubCmp(L"Apple", ST->FirmwareVendor))
2142 GlobalConfig.LegacyType = LEGACY_TYPE_MAC;
2143 } // static VOID FindLegacyBootType
2144
2145 // Warn the user if legacy OS scans are enabled but the firmware or this
2146 // application can't support them....
2147 static VOID WarnIfLegacyProblems() {
2148 BOOLEAN found = FALSE;
2149 UINTN i = 0;
2150
2151 if (GlobalConfig.LegacyType == LEGACY_TYPE_NONE) {
2152 do {
2153 if (GlobalConfig.ScanFor[i] == 'h' || GlobalConfig.ScanFor[i] == 'b' || GlobalConfig.ScanFor[i] == 'c')
2154 found = TRUE;
2155 i++;
2156 } while ((i < NUM_SCAN_OPTIONS) && (!found));
2157 if (found) {
2158 Print(L"NOTE: refind.conf's 'scanfor' line specifies scanning for one or more legacy\n");
2159 Print(L"(BIOS) boot options; however, this is not possible because ");
2160 #ifdef __MAKEWITH_TIANO
2161 Print(L"your computer lacks\n");
2162 Print(L"the necessary Compatibility Support Module (CSM) support.\n");
2163 #else
2164 Print(L"this program was\n");
2165 Print(L"compiled without the necessary support. Please visit\n");
2166 Print(L"http://www.rodsbooks.com/refind/getting.html and download and install a rEFInd\n");
2167 Print(L"binary built with the TianoCore EDK2 to enable legacy boot support.\n");
2168 #endif
2169 PauseForKey();
2170 } // if (found)
2171 } // if no legacy support
2172 } // static VOID WarnIfLegacyProblems()
2173
2174 // Locates boot loaders. NOTE: This assumes that GlobalConfig.LegacyType is set correctly.
2175 static VOID ScanForBootloaders(VOID) {
2176 UINTN i;
2177
2178 // if (GlobalConfig.LegacyType == LEGACY_TYPE_UEFI) {
2179 // Print(L"About to call BdsDeleteAllInvalidLegacyBootOptions()\n");
2180 // BdsDeleteAllInvalidLegacyBootOptions();
2181 // Print(L"About to call BdsAddNonExistingLegacyBootOptions()\n");
2182 // BdsAddNonExistingLegacyBootOptions();
2183 // Print(L"About to call BdsUpdateLegacyDevOrder()\n");
2184 // // BdsUpdateLegacyDevOrder(); // EXTREME CAUTION: HOSED ONE FIRMWARE!
2185 // Print(L"Done with legacy boot updates!\n");
2186 // PauseForKey();
2187 // }
2188
2189 ScanVolumes();
2190
2191 // scan for loaders and tools, add them to the menu
2192 for (i = 0; i < NUM_SCAN_OPTIONS; i++) {
2193 switch(GlobalConfig.ScanFor[i]) {
2194 case 'c': case 'C':
2195 ScanLegacyDisc();
2196 break;
2197 case 'h': case 'H':
2198 ScanLegacyInternal();
2199 break;
2200 case 'b': case 'B':
2201 ScanLegacyExternal();
2202 break;
2203 case 'm': case 'M':
2204 ScanUserConfigured(GlobalConfig.ConfigFilename);
2205 break;
2206 case 'e': case 'E':
2207 ScanExternal();
2208 break;
2209 case 'i': case 'I':
2210 ScanInternal();
2211 break;
2212 case 'o': case 'O':
2213 ScanOptical();
2214 break;
2215 } // switch()
2216 } // for
2217
2218 // assign shortcut keys
2219 for (i = 0; i < MainMenu.EntryCount && MainMenu.Entries[i]->Row == 0 && i < 9; i++)
2220 MainMenu.Entries[i]->ShortcutDigit = (CHAR16)('1' + i);
2221
2222 // wait for user ACK when there were errors
2223 FinishTextScreen(FALSE);
2224 } // static VOID ScanForBootloaders()
2225
2226 // Locate a single tool from the specified Locations using one of the
2227 // specified Names and add it to the menu.
2228 static VOID FindTool(CHAR16 *Locations, CHAR16 *Names, CHAR16 *Description, UINTN Icon) {
2229 UINTN j = 0, k, VolumeIndex;
2230 CHAR16 *DirName, *FileName, *PathName, FullDescription[256];
2231
2232 while ((DirName = FindCommaDelimited(Locations, j++)) != NULL) {
2233 k = 0;
2234 while ((FileName = FindCommaDelimited(Names, k++)) != NULL) {
2235 PathName = StrDuplicate(DirName);
2236 MergeStrings(&PathName, FileName, (StriCmp(PathName, L"\\") == 0) ? 0 : L'\\');
2237 for (VolumeIndex = 0; VolumeIndex < VolumesCount; VolumeIndex++) {
2238 if ((Volumes[VolumeIndex]->RootDir != NULL) && (FileExists(Volumes[VolumeIndex]->RootDir, PathName))) {
2239 SPrint(FullDescription, 255, L"%s at %s on %s", Description, PathName, Volumes[VolumeIndex]->VolName);
2240 AddToolEntry(Volumes[VolumeIndex]->DeviceHandle, PathName, FullDescription, BuiltinIcon(Icon), 'S', FALSE);
2241 } // if
2242 } // for
2243 MyFreePool(PathName);
2244 MyFreePool(FileName);
2245 } // while Names
2246 MyFreePool(DirName);
2247 } // while Locations
2248 } // VOID FindTool()
2249
2250 // Add the second-row tags containing built-in and external tools (EFI shell,
2251 // reboot, etc.)
2252 static VOID ScanForTools(VOID) {
2253 CHAR16 *FileName = NULL, *VolName = NULL, *MokLocations, Description[256];
2254 REFIT_MENU_ENTRY *TempMenuEntry;
2255 UINTN i, j, VolumeIndex;
2256 UINT64 osind;
2257 CHAR8 *b = 0;
2258
2259 MokLocations = StrDuplicate(MOK_LOCATIONS);
2260 if (MokLocations != NULL)
2261 MergeStrings(&MokLocations, SelfDirPath, L',');
2262
2263 for (i = 0; i < NUM_TOOLS; i++) {
2264 switch(GlobalConfig.ShowTools[i]) {
2265 // NOTE: Be sure that FileName is NULL at the end of each case.
2266 case TAG_SHUTDOWN:
2267 TempMenuEntry = CopyMenuEntry(&MenuEntryShutdown);
2268 TempMenuEntry->Image = BuiltinIcon(BUILTIN_ICON_FUNC_SHUTDOWN);
2269 AddMenuEntry(&MainMenu, TempMenuEntry);
2270 break;
2271
2272 case TAG_REBOOT:
2273 TempMenuEntry = CopyMenuEntry(&MenuEntryReset);
2274 TempMenuEntry->Image = BuiltinIcon(BUILTIN_ICON_FUNC_RESET);
2275 AddMenuEntry(&MainMenu, TempMenuEntry);
2276 break;
2277
2278 case TAG_ABOUT:
2279 TempMenuEntry = CopyMenuEntry(&MenuEntryAbout);
2280 TempMenuEntry->Image = BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT);
2281 AddMenuEntry(&MainMenu, TempMenuEntry);
2282 break;
2283
2284 case TAG_EXIT:
2285 TempMenuEntry = CopyMenuEntry(&MenuEntryExit);
2286 TempMenuEntry->Image = BuiltinIcon(BUILTIN_ICON_FUNC_EXIT);
2287 AddMenuEntry(&MainMenu, TempMenuEntry);
2288 break;
2289
2290 case TAG_FIRMWARE:
2291 if (EfivarGetRaw(&GlobalGuid, L"OsIndicationsSupported", &b, &j) == EFI_SUCCESS) {
2292 osind = (UINT64)*b;
2293 if (osind & EFI_OS_INDICATIONS_BOOT_TO_FW_UI) {
2294 TempMenuEntry = CopyMenuEntry(&MenuEntryFirmware);
2295 TempMenuEntry->Image = BuiltinIcon(BUILTIN_ICON_FUNC_FIRMWARE);
2296 AddMenuEntry(&MainMenu, TempMenuEntry);
2297 } // if
2298 } // if
2299 break;
2300
2301 case TAG_SHELL:
2302 j = 0;
2303 while ((FileName = FindCommaDelimited(SHELL_NAMES, j++)) != NULL) {
2304 if (FileExists(SelfRootDir, FileName)) {
2305 AddToolEntry(SelfLoadedImage->DeviceHandle, FileName, L"EFI Shell", BuiltinIcon(BUILTIN_ICON_TOOL_SHELL),
2306 'S', FALSE);
2307 }
2308 MyFreePool(FileName);
2309 } // while
2310 break;
2311
2312 case TAG_GPTSYNC:
2313 j = 0;
2314 while ((FileName = FindCommaDelimited(GPTSYNC_NAMES, j++)) != NULL) {
2315 if (FileExists(SelfRootDir, FileName)) {
2316 AddToolEntry(SelfLoadedImage->DeviceHandle, FileName, L"Hybrid MBR tool", BuiltinIcon(BUILTIN_ICON_TOOL_PART),
2317 'P', FALSE);
2318 } // if
2319 MyFreePool(FileName);
2320 } // while
2321 FileName = NULL;
2322 break;
2323
2324 case TAG_GDISK:
2325 j = 0;
2326 while ((FileName = FindCommaDelimited(GDISK_NAMES, j++)) != NULL) {
2327 if (FileExists(SelfRootDir, FileName)) {
2328 AddToolEntry(SelfLoadedImage->DeviceHandle, FileName, L"disk partitioning tool",
2329 BuiltinIcon(BUILTIN_ICON_TOOL_PART), 'G', FALSE);
2330 } // if
2331 MyFreePool(FileName);
2332 } // while
2333 FileName = NULL;
2334 break;
2335
2336 case TAG_APPLE_RECOVERY:
2337 FileName = StrDuplicate(L"\\com.apple.recovery.boot\\boot.efi");
2338 for (VolumeIndex = 0; VolumeIndex < VolumesCount; VolumeIndex++) {
2339 if ((Volumes[VolumeIndex]->RootDir != NULL) && (FileExists(Volumes[VolumeIndex]->RootDir, FileName))) {
2340 SPrint(Description, 255, L"Apple Recovery on %s", Volumes[VolumeIndex]->VolName);
2341 AddToolEntry(Volumes[VolumeIndex]->DeviceHandle, FileName, Description,
2342 BuiltinIcon(BUILTIN_ICON_TOOL_APPLE_RESCUE), 'R', TRUE);
2343 } // if
2344 } // for
2345 MyFreePool(FileName);
2346 FileName = NULL;
2347 break;
2348
2349 case TAG_WINDOWS_RECOVERY:
2350 j = 0;
2351 while ((FileName = FindCommaDelimited(GlobalConfig.WindowsRecoveryFiles, j++)) != NULL) {
2352 SplitVolumeAndFilename(&FileName, &VolName);
2353 for (VolumeIndex = 0; VolumeIndex < VolumesCount; VolumeIndex++) {
2354 if ((Volumes[VolumeIndex]->RootDir != NULL) && (FileExists(Volumes[VolumeIndex]->RootDir, FileName)) &&
2355 ((VolName == NULL) || (StriCmp(VolName, Volumes[VolumeIndex]->VolName) == 0))) {
2356 SPrint(Description, 255, L"Microsoft Recovery on %s", Volumes[VolumeIndex]->VolName);
2357 AddToolEntry(Volumes[VolumeIndex]->DeviceHandle, FileName, Description,
2358 BuiltinIcon(BUILTIN_ICON_TOOL_WINDOWS_RESCUE), 'R', TRUE);
2359 } // if
2360 } // for
2361 } // while
2362 MyFreePool(FileName);
2363 FileName = NULL;
2364 MyFreePool(VolName);
2365 VolName = NULL;
2366 break;
2367
2368 case TAG_MOK_TOOL:
2369 FindTool(MokLocations, MOK_NAMES, L"MOK utility", BUILTIN_ICON_TOOL_MOK_TOOL);
2370 break;
2371
2372 case TAG_MEMTEST:
2373 FindTool(MEMTEST_LOCATIONS, MEMTEST_NAMES, L"Memory test utility", BUILTIN_ICON_TOOL_MEMTEST);
2374 break;
2375
2376 } // switch()
2377 } // for
2378 } // static VOID ScanForTools
2379
2380 // Rescan for boot loaders
2381 VOID RescanAll(VOID) {
2382 EG_PIXEL BGColor;
2383
2384 BGColor.b = 255;
2385 BGColor.g = 175;
2386 BGColor.r = 100;
2387 BGColor.a = 0;
2388 egDisplayMessage(L"Scanning for new boot loaders; please wait....", &BGColor);
2389 FreeList((VOID ***) &(MainMenu.Entries), &MainMenu.EntryCount);
2390 MainMenu.Entries = NULL;
2391 MainMenu.EntryCount = 0;
2392 ReadConfig(GlobalConfig.ConfigFilename);
2393 ConnectAllDriversToAllControllers();
2394 ScanVolumes();
2395 ScanForBootloaders();
2396 ScanForTools();
2397 SetupScreen();
2398 } // VOID RescanAll()
2399
2400 #ifdef __MAKEWITH_TIANO
2401
2402 // Minimal initialization function
2403 static VOID InitializeLib(IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable) {
2404 gST = SystemTable;
2405 // gImageHandle = ImageHandle;
2406 gBS = SystemTable->BootServices;
2407 // gRS = SystemTable->RuntimeServices;
2408 gRT = SystemTable->RuntimeServices; // Some BDS functions need gRT to be set
2409 EfiGetSystemConfigurationTable (&gEfiDxeServicesTableGuid, (VOID **) &gDS);
2410
2411 InitializeConsoleSim();
2412 }
2413
2414 #endif
2415
2416 // Set up our own Secure Boot extensions....
2417 // Returns TRUE on success, FALSE otherwise
2418 static BOOLEAN SecureBootSetup(VOID) {
2419 EFI_STATUS Status;
2420 BOOLEAN Success = FALSE;
2421
2422 if (secure_mode() && ShimLoaded()) {
2423 Status = security_policy_install();
2424 if (Status == EFI_SUCCESS) {
2425 Success = TRUE;
2426 } else {
2427 Print(L"Failed to install MOK Secure Boot extensions");
2428 }
2429 }
2430 return Success;
2431 } // VOID SecureBootSetup()
2432
2433 // Remove our own Secure Boot extensions....
2434 // Returns TRUE on success, FALSE otherwise
2435 static BOOLEAN SecureBootUninstall(VOID) {
2436 EFI_STATUS Status;
2437 BOOLEAN Success = TRUE;
2438
2439 if (secure_mode()) {
2440 Status = security_policy_uninstall();
2441 if (Status != EFI_SUCCESS) {
2442 Success = FALSE;
2443 BeginTextScreen(L"Secure Boot Policy Failure");
2444 Print(L"Failed to uninstall MOK Secure Boot extensions; forcing a reboot.");
2445 PauseForKey();
2446 refit_call4_wrapper(RT->ResetSystem, EfiResetCold, EFI_SUCCESS, 0, NULL);
2447 }
2448 }
2449 return Success;
2450 } // VOID SecureBootUninstall
2451
2452 // Sets the global configuration filename; will be CONFIG_FILE_NAME unless the
2453 // "-c" command-line option is set, in which case that takes precedence.
2454 // If an error is encountered, leaves the value alone (it should be set to
2455 // CONFIG_FILE_NAME when GlobalConfig is initialized).
2456 static VOID SetConfigFilename(EFI_HANDLE ImageHandle) {
2457 EFI_LOADED_IMAGE *Info;
2458 CHAR16 *Options, *FileName;
2459 EFI_STATUS Status;
2460 INTN Where;
2461
2462 Status = refit_call3_wrapper(BS->HandleProtocol, ImageHandle, &LoadedImageProtocol, (VOID **) &Info);
2463 if ((Status == EFI_SUCCESS) && (Info->LoadOptionsSize > 0)) {
2464 Options = (CHAR16 *) Info->LoadOptions;
2465 Where = FindSubString(L" -c ", Options);
2466 if (Where >= 0) {
2467 FileName = StrDuplicate(&Options[Where + 4]);
2468 Where = FindSubString(L" ", FileName);
2469 if (Where > 0)
2470 FileName[Where] = L'\0';
2471
2472 if (FileExists(SelfDir, FileName)) {
2473 GlobalConfig.ConfigFilename = FileName;
2474 } else {
2475 Print(L"Specified configuration file (%s) doesn't exist; using\n'refind.conf' default\n", FileName);
2476 MyFreePool(FileName);
2477 } // if/else
2478 } // if
2479 } // if
2480 } // VOID SetConfigFilename()
2481
2482 //
2483 // main entry point
2484 //
2485 EFI_STATUS
2486 EFIAPI
2487 efi_main (EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable)
2488 {
2489 EFI_STATUS Status;
2490 BOOLEAN MainLoopRunning = TRUE;
2491 BOOLEAN MokProtocol;
2492 REFIT_MENU_ENTRY *ChosenEntry;
2493 UINTN MenuExit, i;
2494 CHAR16 *Selection = NULL;
2495 EG_PIXEL BGColor;
2496
2497 // bootstrap
2498 InitializeLib(ImageHandle, SystemTable);
2499 Status = InitRefitLib(ImageHandle);
2500 if (EFI_ERROR(Status))
2501 return Status;
2502
2503 // read configuration
2504 CopyMem(GlobalConfig.ScanFor, "ieom ", NUM_SCAN_OPTIONS);
2505 FindLegacyBootType();
2506 if (GlobalConfig.LegacyType == LEGACY_TYPE_MAC)
2507 CopyMem(GlobalConfig.ScanFor, "ihebocm ", NUM_SCAN_OPTIONS);
2508 SetConfigFilename(ImageHandle);
2509 ReadConfig(GlobalConfig.ConfigFilename);
2510 ScanVolumes();
2511
2512 InitScreen();
2513 WarnIfLegacyProblems();
2514 MainMenu.TimeoutSeconds = GlobalConfig.Timeout;
2515
2516 // disable EFI watchdog timer
2517 refit_call4_wrapper(BS->SetWatchdogTimer, 0x0000, 0x0000, 0x0000, NULL);
2518
2519 // further bootstrap (now with config available)
2520 MokProtocol = SecureBootSetup();
2521 LoadDrivers();
2522 ScanForBootloaders();
2523 ScanForTools();
2524 SetupScreen();
2525
2526 if (GlobalConfig.ScanDelay > 0) {
2527 BGColor.b = 255;
2528 BGColor.g = 175;
2529 BGColor.r = 100;
2530 BGColor.a = 0;
2531 egDisplayMessage(L"Pausing before disk scan; please wait....", &BGColor);
2532 for (i = 0; i < GlobalConfig.ScanDelay; i++)
2533 refit_call1_wrapper(BS->Stall, 1000000);
2534 RescanAll();
2535 } // if
2536
2537 if (GlobalConfig.DefaultSelection)
2538 Selection = StrDuplicate(GlobalConfig.DefaultSelection);
2539
2540 while (MainLoopRunning) {
2541 MenuExit = RunMainMenu(&MainMenu, Selection, &ChosenEntry);
2542
2543 // The Escape key triggers a re-scan operation....
2544 if (MenuExit == MENU_EXIT_ESCAPE) {
2545 MenuExit = 0;
2546 RescanAll();
2547 continue;
2548 }
2549
2550 switch (ChosenEntry->Tag) {
2551
2552 case TAG_REBOOT: // Reboot
2553 TerminateScreen();
2554 refit_call4_wrapper(RT->ResetSystem, EfiResetCold, EFI_SUCCESS, 0, NULL);
2555 MainLoopRunning = FALSE; // just in case we get this far
2556 break;
2557
2558 case TAG_SHUTDOWN: // Shut Down
2559 TerminateScreen();
2560 refit_call4_wrapper(RT->ResetSystem, EfiResetShutdown, EFI_SUCCESS, 0, NULL);
2561 MainLoopRunning = FALSE; // just in case we get this far
2562 break;
2563
2564 case TAG_ABOUT: // About rEFInd
2565 AboutrEFInd();
2566 break;
2567
2568 case TAG_LOADER: // Boot OS via .EFI loader
2569 StartLoader((LOADER_ENTRY *)ChosenEntry);
2570 break;
2571
2572 case TAG_LEGACY: // Boot legacy OS
2573 StartLegacy((LEGACY_ENTRY *)ChosenEntry);
2574 break;
2575
2576 #ifdef __MAKEWITH_TIANO
2577 case TAG_LEGACY_UEFI: // Boot a legacy OS on a non-Mac
2578 StartLegacyUEFI((LEGACY_ENTRY *)ChosenEntry);
2579 break;
2580 #endif
2581
2582 case TAG_TOOL: // Start a EFI tool
2583 StartTool((LOADER_ENTRY *)ChosenEntry);
2584 break;
2585
2586 case TAG_EXIT: // Terminate rEFInd
2587 if ((MokProtocol) && !SecureBootUninstall()) {
2588 MainLoopRunning = FALSE; // just in case we get this far
2589 } else {
2590 BeginTextScreen(L" ");
2591 return EFI_SUCCESS;
2592 }
2593 break;
2594
2595 case TAG_FIRMWARE: // Reboot into firmware's user interface
2596 RebootIntoFirmware();
2597 break;
2598
2599 } // switch()
2600 MyFreePool(Selection);
2601 Selection = (ChosenEntry->Title) ? StrDuplicate(ChosenEntry->Title) : NULL;
2602 } // while()
2603
2604 // If we end up here, things have gone wrong. Try to reboot, and if that
2605 // fails, go into an endless loop.
2606 refit_call4_wrapper(RT->ResetSystem, EfiResetCold, EFI_SUCCESS, 0, NULL);
2607 EndlessIdleLoop();
2608
2609 return EFI_SUCCESS;
2610 } /* efi_main() */