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