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