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