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