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