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