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