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