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