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