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