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