]> code.delx.au - refind/blob - refind/main.c
8679fd639f072b632081a071fa36ab920bd55f09
[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 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 Firmware User Interface", 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 const 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.9.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(const 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 = uefi_call_wrapper(RT->GetVariable, 5, 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(const 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 uefi_call_wrapper(RT->SetVariable, 5, 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 uefi_call_wrapper(RT->ResetSystem, 4, 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 Linux options file exists
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 // Sets a few defaults for a loader entry -- mainly the icon, but also the OS type
811 // code and shortcut letter. For Linux EFI stub loaders, also sets kernel options
812 // that will (with luck) work fairly automatically.
813 VOID SetLoaderDefaults(LOADER_ENTRY *Entry, CHAR16 *LoaderPath, REFIT_VOLUME *Volume) {
814 CHAR16 *FileName, *PathOnly, *NoExtension, *OSIconName = NULL, *Temp, *SubString;
815 CHAR16 ShortcutLetter = 0;
816 UINTN i = 0, Length;
817
818 FileName = Basename(LoaderPath);
819 PathOnly = FindPath(LoaderPath);
820 NoExtension = StripEfiExtension(FileName);
821
822 // locate a custom icon for the loader
823 // Anything found here takes precedence over the "hints" in the OSIconName variable
824 if (!Entry->me.Image)
825 Entry->me.Image = egLoadIconAnyType(Volume->RootDir, PathOnly, NoExtension, 128);
826 if (!Entry->me.Image)
827 Entry->me.Image = egCopyImage(Volume->VolIconImage);
828
829 // Begin creating icon "hints" by using last part of directory path leading
830 // to the loader
831 Temp = FindLastDirName(LoaderPath);
832 MergeStrings(&OSIconName, Temp, L',');
833 MyFreePool(Temp);
834 Temp = NULL;
835 if (OSIconName != NULL) {
836 ShortcutLetter = OSIconName[0];
837 }
838
839 // Add every "word" in the volume label, delimited by spaces, dashes (-), or
840 // underscores (_), to the list of hints to be used in searching for OS
841 // icons.
842 if ((Volume->VolName) && (StrLen(Volume->VolName) > 0)) {
843 Temp = SubString = StrDuplicate(Volume->VolName);
844 if (Temp != NULL) {
845 Length = StrLen(Temp);
846 for (i = 0; i < Length; i++) {
847 if ((Temp[i] == L' ') || (Temp[i] == L'_') || (Temp[i] == L'-')) {
848 Temp[i] = 0;
849 if (StrLen(SubString) > 0)
850 MergeStrings(&OSIconName, SubString, L',');
851 SubString = Temp + i + 1;
852 } // if
853 } // for
854 MergeStrings(&OSIconName, SubString, L',');
855 MyFreePool(Temp);
856 } // if
857 } // if
858
859 // detect specific loaders
860 if (StriSubCmp(L"bzImage", LoaderPath) || StriSubCmp(L"vmlinuz", LoaderPath)) {
861 MergeStrings(&OSIconName, L"linux", L',');
862 Entry->OSType = 'L';
863 if (ShortcutLetter == 0)
864 ShortcutLetter = 'L';
865 Entry->LoadOptions = GetMainLinuxOptions(LoaderPath, Volume);
866 Entry->UseGraphicsMode = GlobalConfig.GraphicsFor & GRAPHICS_FOR_LINUX;
867 } else if (StriSubCmp(L"refit", LoaderPath)) {
868 MergeStrings(&OSIconName, L"refit", L',');
869 Entry->OSType = 'R';
870 ShortcutLetter = 'R';
871 } else if (StriCmp(LoaderPath, MACOSX_LOADER_PATH) == 0) {
872 if (Volume->VolIconImage != NULL) { // custom icon file found
873 Entry->me.Image = Volume->VolIconImage;
874 }
875 MergeStrings(&OSIconName, L"mac", L',');
876 Entry->OSType = 'M';
877 ShortcutLetter = 'M';
878 Entry->UseGraphicsMode = GlobalConfig.GraphicsFor & GRAPHICS_FOR_OSX;
879 } else if (StriCmp(FileName, L"diags.efi") == 0) {
880 MergeStrings(&OSIconName, L"hwtest", L',');
881 } else if (StriCmp(FileName, L"e.efi") == 0 || StriCmp(FileName, L"elilo.efi") == 0 || StriSubCmp(L"elilo", FileName)) {
882 MergeStrings(&OSIconName, L"elilo,linux", L',');
883 Entry->OSType = 'E';
884 if (ShortcutLetter == 0)
885 ShortcutLetter = 'L';
886 Entry->UseGraphicsMode = GlobalConfig.GraphicsFor & GRAPHICS_FOR_ELILO;
887 } else if (StriSubCmp(L"grub", FileName)) {
888 Entry->OSType = 'G';
889 ShortcutLetter = 'G';
890 Entry->UseGraphicsMode = GlobalConfig.GraphicsFor & GRAPHICS_FOR_GRUB;
891 } else if (StriCmp(FileName, L"cdboot.efi") == 0 ||
892 StriCmp(FileName, L"bootmgr.efi") == 0 ||
893 StriCmp(FileName, L"bootmgfw.efi") == 0) {
894 MergeStrings(&OSIconName, L"win", L',');
895 Entry->OSType = 'W';
896 ShortcutLetter = 'W';
897 Entry->UseGraphicsMode = GlobalConfig.GraphicsFor & GRAPHICS_FOR_WINDOWS;
898 } else if (StriCmp(FileName, L"xom.efi") == 0) {
899 MergeStrings(&OSIconName, L"xom,win", L',');
900 Entry->UseGraphicsMode = TRUE;
901 Entry->OSType = 'X';
902 ShortcutLetter = 'W';
903 Entry->UseGraphicsMode = GlobalConfig.GraphicsFor & GRAPHICS_FOR_WINDOWS;
904 }
905
906 if ((ShortcutLetter >= 'a') && (ShortcutLetter <= 'z'))
907 ShortcutLetter = ShortcutLetter - 'a' + 'A'; // convert lowercase to uppercase
908 Entry->me.ShortcutLetter = ShortcutLetter;
909 if (Entry->me.Image == NULL)
910 Entry->me.Image = LoadOSIcon(OSIconName, L"unknown", FALSE);
911 MyFreePool(PathOnly);
912 } // VOID SetLoaderDefaults()
913
914 // Add a specified EFI boot loader to the list, using automatic settings
915 // for icons, options, etc.
916 LOADER_ENTRY * AddLoaderEntry(IN CHAR16 *LoaderPath, IN CHAR16 *LoaderTitle, IN REFIT_VOLUME *Volume) {
917 LOADER_ENTRY *Entry;
918
919 CleanUpPathNameSlashes(LoaderPath);
920 Entry = InitializeLoaderEntry(NULL);
921 if (Entry != NULL) {
922 Entry->Title = StrDuplicate((LoaderTitle != NULL) ? LoaderTitle : LoaderPath);
923 Entry->me.Title = AllocateZeroPool(sizeof(CHAR16) * 256);
924 SPrint(Entry->me.Title, 255, L"Boot %s from %s", (LoaderTitle != NULL) ? LoaderTitle : LoaderPath, Volume->VolName);
925 Entry->me.Row = 0;
926 Entry->me.BadgeImage = Volume->VolBadgeImage;
927 if ((LoaderPath != NULL) && (LoaderPath[0] != L'\\')) {
928 Entry->LoaderPath = StrDuplicate(L"\\");
929 } else {
930 Entry->LoaderPath = NULL;
931 }
932 MergeStrings(&(Entry->LoaderPath), LoaderPath, 0);
933 Entry->VolName = Volume->VolName;
934 Entry->DevicePath = FileDevicePath(Volume->DeviceHandle, Entry->LoaderPath);
935 SetLoaderDefaults(Entry, LoaderPath, Volume);
936 GenerateSubScreen(Entry, Volume);
937 AddMenuEntry(&MainMenu, (REFIT_MENU_ENTRY *)Entry);
938 }
939
940 return(Entry);
941 } // LOADER_ENTRY * AddLoaderEntry()
942
943 // Returns -1 if (Time1 < Time2), +1 if (Time1 > Time2), or 0 if
944 // (Time1 == Time2). Precision is only to the nearest second; since
945 // this is used for sorting boot loader entries, differences smaller
946 // than this are likely to be meaningless (and unlikely!).
947 INTN TimeComp(IN EFI_TIME *Time1, IN EFI_TIME *Time2) {
948 INT64 Time1InSeconds, Time2InSeconds;
949
950 // Following values are overestimates; I'm assuming 31 days in every month.
951 // This is fine for the purpose of this function, which is limited
952 Time1InSeconds = Time1->Second + (Time1->Minute * 60) + (Time1->Hour * 3600) + (Time1->Day * 86400) +
953 (Time1->Month * 2678400) + ((Time1->Year - 1998) * 32140800);
954 Time2InSeconds = Time2->Second + (Time2->Minute * 60) + (Time2->Hour * 3600) + (Time2->Day * 86400) +
955 (Time2->Month * 2678400) + ((Time2->Year - 1998) * 32140800);
956 if (Time1InSeconds < Time2InSeconds)
957 return (-1);
958 else if (Time1InSeconds > Time2InSeconds)
959 return (1);
960
961 return 0;
962 } // INTN TimeComp()
963
964 // Adds a loader list element, keeping it sorted by date. Returns the new
965 // first element (the one with the most recent date).
966 static struct LOADER_LIST * AddLoaderListEntry(struct LOADER_LIST *LoaderList, struct LOADER_LIST *NewEntry) {
967 struct LOADER_LIST *LatestEntry, *CurrentEntry, *PrevEntry = NULL;
968
969 LatestEntry = CurrentEntry = LoaderList;
970 if (LoaderList == NULL) {
971 LatestEntry = NewEntry;
972 } else {
973 while ((CurrentEntry != NULL) && (TimeComp(&(NewEntry->TimeStamp), &(CurrentEntry->TimeStamp)) < 0)) {
974 PrevEntry = CurrentEntry;
975 CurrentEntry = CurrentEntry->NextEntry;
976 } // while
977 NewEntry->NextEntry = CurrentEntry;
978 if (PrevEntry == NULL) {
979 LatestEntry = NewEntry;
980 } else {
981 PrevEntry->NextEntry = NewEntry;
982 } // if/else
983 } // if/else
984 return (LatestEntry);
985 } // static VOID AddLoaderListEntry()
986
987 // Delete the LOADER_LIST linked list
988 static VOID CleanUpLoaderList(struct LOADER_LIST *LoaderList) {
989 struct LOADER_LIST *Temp;
990
991 while (LoaderList != NULL) {
992 Temp = LoaderList;
993 LoaderList = LoaderList->NextEntry;
994 MyFreePool(Temp->FileName);
995 MyFreePool(Temp);
996 } // while
997 } // static VOID CleanUpLoaderList()
998
999 // Returns FALSE if the specified file/volume matches the GlobalConfig.DontScanDirs
1000 // or GlobalConfig.DontScanVolumes specification, or if Path points to a volume
1001 // other than the one specified by Volume, or if the specified path is SelfDir.
1002 // Returns TRUE if none of these conditions is met -- that is, if the path is
1003 // eligible for scanning.
1004 static BOOLEAN ShouldScan(REFIT_VOLUME *Volume, CHAR16 *Path) {
1005 CHAR16 *VolName = NULL, *DontScanDir;
1006 UINTN i = 0, VolNum;
1007 BOOLEAN ScanIt = TRUE;
1008
1009 if (IsIn(Volume->VolName, GlobalConfig.DontScanVolumes))
1010 return FALSE;
1011
1012 if ((StriCmp(Path, SelfDirPath) == 0) && (Volume->DeviceHandle == SelfVolume->DeviceHandle))
1013 return FALSE;
1014
1015 while ((DontScanDir = FindCommaDelimited(GlobalConfig.DontScanDirs, i++)) && ScanIt) {
1016 SplitVolumeAndFilename(&DontScanDir, &VolName);
1017 CleanUpPathNameSlashes(DontScanDir);
1018 if (VolName != NULL) {
1019 if ((StriCmp(VolName, Volume->VolName) == 0) && (StriCmp(DontScanDir, Path) == 0))
1020 ScanIt = FALSE;
1021 if ((StrLen(VolName) > 2) && (VolName[0] == L'f') && (VolName[1] == L's') && (VolName[2] >= L'0') && (VolName[2] <= L'9')) {
1022 VolNum = Atoi(VolName + 2);
1023 if ((VolNum == Volume->VolNumber) && (StriCmp(DontScanDir, Path) == 0))
1024 ScanIt = FALSE;
1025 }
1026 } else {
1027 if (StriCmp(DontScanDir, Path) == 0)
1028 ScanIt = FALSE;
1029 }
1030 MyFreePool(DontScanDir);
1031 DontScanDir = NULL;
1032 }
1033 return ScanIt;
1034 } // BOOLEAN ShouldScan()
1035
1036 // Returns TRUE if the file is byte-for-byte identical with the fallback file
1037 // on the volume AND if the file is not itself the fallback file; returns
1038 // FALSE if the file is not identical to the fallback file OR if the file
1039 // IS the fallback file. Intended for use in excluding the fallback boot
1040 // loader when it's a duplicate of another boot loader.
1041 static BOOLEAN DuplicatesFallback(IN REFIT_VOLUME *Volume, IN CHAR16 *FileName) {
1042 CHAR8 *FileContents, *FallbackContents;
1043 EFI_FILE_HANDLE FileHandle, FallbackHandle;
1044 EFI_FILE_INFO *FileInfo, *FallbackInfo;
1045 UINTN FileSize = 0, FallbackSize = 0;
1046 EFI_STATUS Status;
1047 BOOLEAN AreIdentical = FALSE;
1048
1049 if (!FileExists(Volume->RootDir, FileName) || !FileExists(Volume->RootDir, FALLBACK_FULLNAME))
1050 return FALSE;
1051
1052 CleanUpPathNameSlashes(FileName);
1053
1054 if (StriCmp(FileName, FALLBACK_FULLNAME) == 0)
1055 return FALSE; // identical filenames, so not a duplicate....
1056
1057 Status = refit_call5_wrapper(Volume->RootDir->Open, Volume->RootDir, &FileHandle, FileName, EFI_FILE_MODE_READ, 0);
1058 if (Status == EFI_SUCCESS) {
1059 FileInfo = LibFileInfo(FileHandle);
1060 FileSize = FileInfo->FileSize;
1061 } else {
1062 return FALSE;
1063 }
1064
1065 Status = refit_call5_wrapper(Volume->RootDir->Open, Volume->RootDir, &FallbackHandle, FALLBACK_FULLNAME, EFI_FILE_MODE_READ, 0);
1066 if (Status == EFI_SUCCESS) {
1067 FallbackInfo = LibFileInfo(FallbackHandle);
1068 FallbackSize = FallbackInfo->FileSize;
1069 } else {
1070 refit_call1_wrapper(FileHandle->Close, FileHandle);
1071 return FALSE;
1072 }
1073
1074 if (FallbackSize != FileSize) { // not same size, so can't be identical
1075 AreIdentical = FALSE;
1076 } else { // could be identical; do full check....
1077 FileContents = AllocatePool(FileSize);
1078 FallbackContents = AllocatePool(FallbackSize);
1079 if (FileContents && FallbackContents) {
1080 Status = refit_call3_wrapper(FileHandle->Read, FileHandle, &FileSize, FileContents);
1081 if (Status == EFI_SUCCESS) {
1082 Status = refit_call3_wrapper(FallbackHandle->Read, FallbackHandle, &FallbackSize, FallbackContents);
1083 }
1084 if (Status == EFI_SUCCESS) {
1085 AreIdentical = (CompareMem(FileContents, FallbackContents, FileSize) == 0);
1086 } // if
1087 } // if
1088 MyFreePool(FileContents);
1089 MyFreePool(FallbackContents);
1090 } // if/else
1091
1092 // BUG ALERT: Some systems (e.g., DUET, some Macs with large displays) crash if the
1093 // following two calls are reversed. Go figure....
1094 refit_call1_wrapper(FileHandle->Close, FallbackHandle);
1095 refit_call1_wrapper(FileHandle->Close, FileHandle);
1096 return AreIdentical;
1097 } // BOOLEAN DuplicatesFallback()
1098
1099 // Scan an individual directory for EFI boot loader files and, if found,
1100 // add them to the list. Exception: Ignores FALLBACK_FULLNAME, which is picked
1101 // up in ScanEfiFiles(). Sorts the entries within the loader directory so that
1102 // the most recent one appears first in the list.
1103 // Returns TRUE if a duplicate for FALLBACK_FILENAME was found, FALSE if not.
1104 static BOOLEAN ScanLoaderDir(IN REFIT_VOLUME *Volume, IN CHAR16 *Path, IN CHAR16 *Pattern)
1105 {
1106 EFI_STATUS Status;
1107 REFIT_DIR_ITER DirIter;
1108 EFI_FILE_INFO *DirEntry;
1109 CHAR16 FileName[256], *Extension;
1110 struct LOADER_LIST *LoaderList = NULL, *NewLoader;
1111 BOOLEAN FoundFallbackDuplicate = FALSE;
1112
1113 if ((!SelfDirPath || !Path || ((StriCmp(Path, SelfDirPath) == 0) && (Volume->DeviceHandle != SelfVolume->DeviceHandle)) ||
1114 (StriCmp(Path, SelfDirPath) != 0)) &&
1115 (ShouldScan(Volume, Path))) {
1116 // look through contents of the directory
1117 DirIterOpen(Volume->RootDir, Path, &DirIter);
1118 while (DirIterNext(&DirIter, 2, Pattern, &DirEntry)) {
1119 Extension = FindExtension(DirEntry->FileName);
1120 if (DirEntry->FileName[0] == '.' ||
1121 StriCmp(Extension, L".icns") == 0 ||
1122 StriCmp(Extension, L".png") == 0 ||
1123 (StriCmp(DirEntry->FileName, FALLBACK_BASENAME) == 0 && (StriCmp(Path, L"EFI\\BOOT") == 0)) ||
1124 StriSubCmp(L"shell", DirEntry->FileName) ||
1125 IsIn(DirEntry->FileName, GlobalConfig.DontScanFiles))
1126 continue; // skip this
1127
1128 if (Path)
1129 SPrint(FileName, 255, L"\\%s\\%s", Path, DirEntry->FileName);
1130 else
1131 SPrint(FileName, 255, L"\\%s", DirEntry->FileName);
1132 CleanUpPathNameSlashes(FileName);
1133 NewLoader = AllocateZeroPool(sizeof(struct LOADER_LIST));
1134 if (NewLoader != NULL) {
1135 NewLoader->FileName = StrDuplicate(FileName);
1136 NewLoader->TimeStamp = DirEntry->ModificationTime;
1137 LoaderList = AddLoaderListEntry(LoaderList, NewLoader);
1138 if (DuplicatesFallback(Volume, FileName))
1139 FoundFallbackDuplicate = TRUE;
1140 } // if
1141 MyFreePool(Extension);
1142 } // while
1143
1144 NewLoader = LoaderList;
1145 while (NewLoader != NULL) {
1146 AddLoaderEntry(NewLoader->FileName, NULL, Volume);
1147 NewLoader = NewLoader->NextEntry;
1148 } // while
1149
1150 CleanUpLoaderList(LoaderList);
1151 Status = DirIterClose(&DirIter);
1152 // NOTE: EFI_INVALID_PARAMETER really is an error that should be reported;
1153 // but I've gotten reports from users who are getting this error occasionally
1154 // and I can't find anything wrong or reproduce the problem, so I'm putting
1155 // it down to buggy EFI implementations and ignoring that particular error....
1156 if ((Status != EFI_NOT_FOUND) && (Status != EFI_INVALID_PARAMETER)) {
1157 if (Path)
1158 SPrint(FileName, 255, L"while scanning the %s directory", Path);
1159 else
1160 StrCpy(FileName, L"while scanning the root directory");
1161 CheckError(Status, FileName);
1162 } // if (Status != EFI_NOT_FOUND)
1163 } // if not scanning a blacklisted directory
1164
1165 return FoundFallbackDuplicate;
1166 } /* static VOID ScanLoaderDir() */
1167
1168 static VOID ScanEfiFiles(REFIT_VOLUME *Volume) {
1169 EFI_STATUS Status;
1170 REFIT_DIR_ITER EfiDirIter;
1171 EFI_FILE_INFO *EfiDirEntry;
1172 CHAR16 FileName[256], *Directory, *MatchPatterns, *VolName = NULL, *SelfPath;
1173 UINTN i, Length;
1174 BOOLEAN ScanFallbackLoader = TRUE;
1175
1176 MatchPatterns = StrDuplicate(LOADER_MATCH_PATTERNS);
1177 if (GlobalConfig.ScanAllLinux)
1178 MergeStrings(&MatchPatterns, LINUX_MATCH_PATTERNS, L',');
1179
1180 if ((Volume->RootDir != NULL) && (Volume->VolName != NULL)) {
1181 // check for Mac OS X boot loader
1182 if (ShouldScan(Volume, L"System\\Library\\CoreServices")) {
1183 StrCpy(FileName, MACOSX_LOADER_PATH);
1184 if (FileExists(Volume->RootDir, FileName) && !IsIn(L"boot.efi", GlobalConfig.DontScanFiles)) {
1185 AddLoaderEntry(FileName, L"Mac OS X", Volume);
1186 if (DuplicatesFallback(Volume, FileName))
1187 ScanFallbackLoader = FALSE;
1188 }
1189
1190 // check for XOM
1191 StrCpy(FileName, L"System\\Library\\CoreServices\\xom.efi");
1192 if (FileExists(Volume->RootDir, FileName) && !IsIn(L"boot.efi", GlobalConfig.DontScanFiles)) {
1193 AddLoaderEntry(FileName, L"Windows XP (XoM)", Volume);
1194 if (DuplicatesFallback(Volume, FileName))
1195 ScanFallbackLoader = FALSE;
1196 }
1197 } // if should scan Mac directory
1198
1199 // check for Microsoft boot loader/menu
1200 StrCpy(FileName, L"EFI\\Microsoft\\Boot\\Bootmgfw.efi");
1201 if (FileExists(Volume->RootDir, FileName) && ShouldScan(Volume, L"EFI\\Microsoft\\Boot") &&
1202 !IsIn(L"bootmgfw.efi", GlobalConfig.DontScanFiles)) {
1203 AddLoaderEntry(FileName, L"Microsoft EFI boot", Volume);
1204 if (DuplicatesFallback(Volume, FileName))
1205 ScanFallbackLoader = FALSE;
1206 }
1207
1208 // scan the root directory for EFI executables
1209 if (ScanLoaderDir(Volume, L"\\", MatchPatterns))
1210 ScanFallbackLoader = FALSE;
1211
1212 // scan subdirectories of the EFI directory (as per the standard)
1213 DirIterOpen(Volume->RootDir, L"EFI", &EfiDirIter);
1214 while (DirIterNext(&EfiDirIter, 1, NULL, &EfiDirEntry)) {
1215 if (StriCmp(EfiDirEntry->FileName, L"tools") == 0 || EfiDirEntry->FileName[0] == '.')
1216 continue; // skip this, doesn't contain boot loaders or is scanned later
1217 SPrint(FileName, 255, L"EFI\\%s", EfiDirEntry->FileName);
1218 if (ScanLoaderDir(Volume, FileName, MatchPatterns))
1219 ScanFallbackLoader = FALSE;
1220 } // while()
1221 Status = DirIterClose(&EfiDirIter);
1222 if (Status != EFI_NOT_FOUND)
1223 CheckError(Status, L"while scanning the EFI directory");
1224
1225 // Scan user-specified (or additional default) directories....
1226 i = 0;
1227 while ((Directory = FindCommaDelimited(GlobalConfig.AlsoScan, i++)) != NULL) {
1228 SplitVolumeAndFilename(&Directory, &VolName);
1229 CleanUpPathNameSlashes(Directory);
1230 Length = StrLen(Directory);
1231 if ((Length > 0) && ScanLoaderDir(Volume, Directory, MatchPatterns))
1232 ScanFallbackLoader = FALSE;
1233 MyFreePool(Directory);
1234 MyFreePool(VolName);
1235 } // while
1236
1237 // Don't scan the fallback loader if it's on the same volume and a duplicate of rEFInd itself....
1238 SelfPath = DevicePathToStr(SelfLoadedImage->FilePath);
1239 CleanUpPathNameSlashes(SelfPath);
1240 if ((Volume->DeviceHandle == SelfLoadedImage->DeviceHandle) && DuplicatesFallback(Volume, SelfPath))
1241 ScanFallbackLoader = FALSE;
1242
1243 // If not a duplicate & if it exists & if it's not us, create an entry
1244 // for the fallback boot loader
1245 if (ScanFallbackLoader && FileExists(Volume->RootDir, FALLBACK_FULLNAME) && ShouldScan(Volume, L"EFI\\BOOT"))
1246 AddLoaderEntry(FALLBACK_FULLNAME, L"Fallback boot loader", Volume);
1247 } // if
1248 } // static VOID ScanEfiFiles()
1249
1250 // Scan internal disks for valid EFI boot loaders....
1251 static VOID ScanInternal(VOID) {
1252 UINTN VolumeIndex;
1253
1254 for (VolumeIndex = 0; VolumeIndex < VolumesCount; VolumeIndex++) {
1255 if (Volumes[VolumeIndex]->DiskKind == DISK_KIND_INTERNAL) {
1256 ScanEfiFiles(Volumes[VolumeIndex]);
1257 }
1258 } // for
1259 } // static VOID ScanInternal()
1260
1261 // Scan external disks for valid EFI boot loaders....
1262 static VOID ScanExternal(VOID) {
1263 UINTN VolumeIndex;
1264
1265 for (VolumeIndex = 0; VolumeIndex < VolumesCount; VolumeIndex++) {
1266 if (Volumes[VolumeIndex]->DiskKind == DISK_KIND_EXTERNAL) {
1267 ScanEfiFiles(Volumes[VolumeIndex]);
1268 }
1269 } // for
1270 } // static VOID ScanExternal()
1271
1272 // Scan internal disks for valid EFI boot loaders....
1273 static VOID ScanOptical(VOID) {
1274 UINTN VolumeIndex;
1275
1276 for (VolumeIndex = 0; VolumeIndex < VolumesCount; VolumeIndex++) {
1277 if (Volumes[VolumeIndex]->DiskKind == DISK_KIND_OPTICAL) {
1278 ScanEfiFiles(Volumes[VolumeIndex]);
1279 }
1280 } // for
1281 } // static VOID ScanOptical()
1282
1283 //
1284 // legacy boot functions
1285 //
1286
1287 static EFI_STATUS ActivateMbrPartition(IN EFI_BLOCK_IO *BlockIO, IN UINTN PartitionIndex)
1288 {
1289 EFI_STATUS Status;
1290 UINT8 SectorBuffer[512];
1291 MBR_PARTITION_INFO *MbrTable, *EMbrTable;
1292 UINT32 ExtBase, ExtCurrent, NextExtCurrent;
1293 UINTN LogicalPartitionIndex = 4;
1294 UINTN i;
1295 BOOLEAN HaveBootCode;
1296
1297 // read MBR
1298 Status = refit_call5_wrapper(BlockIO->ReadBlocks, BlockIO, BlockIO->Media->MediaId, 0, 512, SectorBuffer);
1299 if (EFI_ERROR(Status))
1300 return Status;
1301 if (*((UINT16 *)(SectorBuffer + 510)) != 0xaa55)
1302 return EFI_NOT_FOUND; // safety measure #1
1303
1304 // add boot code if necessary
1305 HaveBootCode = FALSE;
1306 for (i = 0; i < MBR_BOOTCODE_SIZE; i++) {
1307 if (SectorBuffer[i] != 0) {
1308 HaveBootCode = TRUE;
1309 break;
1310 }
1311 }
1312 if (!HaveBootCode) {
1313 // no boot code found in the MBR, add the syslinux MBR code
1314 SetMem(SectorBuffer, MBR_BOOTCODE_SIZE, 0);
1315 CopyMem(SectorBuffer, syslinux_mbr, SYSLINUX_MBR_SIZE);
1316 }
1317
1318 // set the partition active
1319 MbrTable = (MBR_PARTITION_INFO *)(SectorBuffer + 446);
1320 ExtBase = 0;
1321 for (i = 0; i < 4; i++) {
1322 if (MbrTable[i].Flags != 0x00 && MbrTable[i].Flags != 0x80)
1323 return EFI_NOT_FOUND; // safety measure #2
1324 if (i == PartitionIndex)
1325 MbrTable[i].Flags = 0x80;
1326 else if (PartitionIndex >= 4 && IS_EXTENDED_PART_TYPE(MbrTable[i].Type)) {
1327 MbrTable[i].Flags = 0x80;
1328 ExtBase = MbrTable[i].StartLBA;
1329 } else
1330 MbrTable[i].Flags = 0x00;
1331 }
1332
1333 // write MBR
1334 Status = refit_call5_wrapper(BlockIO->WriteBlocks, BlockIO, BlockIO->Media->MediaId, 0, 512, SectorBuffer);
1335 if (EFI_ERROR(Status))
1336 return Status;
1337
1338 if (PartitionIndex >= 4) {
1339 // we have to activate a logical partition, so walk the EMBR chain
1340
1341 // NOTE: ExtBase was set above while looking at the MBR table
1342 for (ExtCurrent = ExtBase; ExtCurrent; ExtCurrent = NextExtCurrent) {
1343 // read current EMBR
1344 Status = refit_call5_wrapper(BlockIO->ReadBlocks, BlockIO, BlockIO->Media->MediaId, ExtCurrent, 512, SectorBuffer);
1345 if (EFI_ERROR(Status))
1346 return Status;
1347 if (*((UINT16 *)(SectorBuffer + 510)) != 0xaa55)
1348 return EFI_NOT_FOUND; // safety measure #3
1349
1350 // scan EMBR, set appropriate partition active
1351 EMbrTable = (MBR_PARTITION_INFO *)(SectorBuffer + 446);
1352 NextExtCurrent = 0;
1353 for (i = 0; i < 4; i++) {
1354 if (EMbrTable[i].Flags != 0x00 && EMbrTable[i].Flags != 0x80)
1355 return EFI_NOT_FOUND; // safety measure #4
1356 if (EMbrTable[i].StartLBA == 0 || EMbrTable[i].Size == 0)
1357 break;
1358 if (IS_EXTENDED_PART_TYPE(EMbrTable[i].Type)) {
1359 // link to next EMBR
1360 NextExtCurrent = ExtBase + EMbrTable[i].StartLBA;
1361 EMbrTable[i].Flags = (PartitionIndex >= LogicalPartitionIndex) ? 0x80 : 0x00;
1362 break;
1363 } else {
1364 // logical partition
1365 EMbrTable[i].Flags = (PartitionIndex == LogicalPartitionIndex) ? 0x80 : 0x00;
1366 LogicalPartitionIndex++;
1367 }
1368 }
1369
1370 // write current EMBR
1371 Status = refit_call5_wrapper(BlockIO->WriteBlocks, BlockIO, BlockIO->Media->MediaId, ExtCurrent, 512, SectorBuffer);
1372 if (EFI_ERROR(Status))
1373 return Status;
1374
1375 if (PartitionIndex < LogicalPartitionIndex)
1376 break; // stop the loop, no need to touch further EMBRs
1377 }
1378
1379 }
1380
1381 return EFI_SUCCESS;
1382 } /* static EFI_STATUS ActivateMbrPartition() */
1383
1384 // early 2006 Core Duo / Core Solo models
1385 static UINT8 LegacyLoaderDevicePath1Data[] = {
1386 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1387 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1388 0xFF, 0xFF, 0xF9, 0xFF, 0x00, 0x00, 0x00, 0x00,
1389 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1390 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1391 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1392 };
1393 // mid-2006 Mac Pro (and probably other Core 2 models)
1394 static UINT8 LegacyLoaderDevicePath2Data[] = {
1395 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1396 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1397 0xFF, 0xFF, 0xF7, 0xFF, 0x00, 0x00, 0x00, 0x00,
1398 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1399 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1400 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1401 };
1402 // mid-2007 MBP ("Santa Rosa" based models)
1403 static UINT8 LegacyLoaderDevicePath3Data[] = {
1404 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1405 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1406 0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
1407 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1408 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1409 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1410 };
1411 // early-2008 MBA
1412 static UINT8 LegacyLoaderDevicePath4Data[] = {
1413 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1414 0x00, 0x00, 0xC0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1415 0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
1416 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1417 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1418 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1419 };
1420 // late-2008 MB/MBP (NVidia chipset)
1421 static UINT8 LegacyLoaderDevicePath5Data[] = {
1422 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1423 0x00, 0x40, 0xCB, 0xFF, 0x00, 0x00, 0x00, 0x00,
1424 0xFF, 0xBF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
1425 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1426 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1427 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1428 };
1429
1430 static EFI_DEVICE_PATH *LegacyLoaderList[] = {
1431 (EFI_DEVICE_PATH *)LegacyLoaderDevicePath1Data,
1432 (EFI_DEVICE_PATH *)LegacyLoaderDevicePath2Data,
1433 (EFI_DEVICE_PATH *)LegacyLoaderDevicePath3Data,
1434 (EFI_DEVICE_PATH *)LegacyLoaderDevicePath4Data,
1435 (EFI_DEVICE_PATH *)LegacyLoaderDevicePath5Data,
1436 NULL
1437 };
1438
1439 #define MAX_DISCOVERED_PATHS (16)
1440
1441 static VOID StartLegacy(IN LEGACY_ENTRY *Entry)
1442 {
1443 EFI_STATUS Status;
1444 EG_IMAGE *BootLogoImage;
1445 UINTN ErrorInStep = 0;
1446 EFI_DEVICE_PATH *DiscoveredPathList[MAX_DISCOVERED_PATHS];
1447
1448 BeginExternalScreen(TRUE, L"Booting Legacy OS (Mac mode)");
1449
1450 BootLogoImage = LoadOSIcon(Entry->Volume->OSIconName, L"legacy", TRUE);
1451 if (BootLogoImage != NULL)
1452 BltImageAlpha(BootLogoImage,
1453 (UGAWidth - BootLogoImage->Width ) >> 1,
1454 (UGAHeight - BootLogoImage->Height) >> 1,
1455 &StdBackgroundPixel);
1456
1457 if (Entry->Volume->IsMbrPartition) {
1458 ActivateMbrPartition(Entry->Volume->WholeDiskBlockIO, Entry->Volume->MbrPartitionIndex);
1459 }
1460
1461 ExtractLegacyLoaderPaths(DiscoveredPathList, MAX_DISCOVERED_PATHS, LegacyLoaderList);
1462
1463 Status = StartEFIImageList(DiscoveredPathList, Entry->LoadOptions, NULL, L"legacy loader", 0, &ErrorInStep, TRUE);
1464 if (Status == EFI_NOT_FOUND) {
1465 if (ErrorInStep == 1) {
1466 Print(L"\nPlease make sure that you have the latest firmware update installed.\n");
1467 } else if (ErrorInStep == 3) {
1468 Print(L"\nThe firmware refused to boot from the selected volume. Note that external\n"
1469 L"hard drives are not well-supported by Apple's firmware for legacy OS booting.\n");
1470 }
1471 }
1472 FinishExternalScreen();
1473 } /* static VOID StartLegacy() */
1474
1475 // Start a device on a non-Mac using the EFI_LEGACY_BIOS_PROTOCOL
1476 #ifdef __MAKEWITH_TIANO
1477 static VOID StartLegacyUEFI(IN LEGACY_ENTRY *Entry)
1478 {
1479 BeginExternalScreen(TRUE, L"Booting Legacy OS (UEFI mode)");
1480
1481 BdsLibConnectDevicePath (Entry->BdsOption->DevicePath);
1482 BdsLibDoLegacyBoot(Entry->BdsOption);
1483
1484 // If we get here, it means that there was a failure....
1485 Print(L"Failure booting legacy (BIOS) OS.");
1486 PauseForKey();
1487 FinishExternalScreen();
1488 } // static VOID StartLegacyUEFI()
1489 #endif // __MAKEWITH_TIANO
1490
1491 static LEGACY_ENTRY * AddLegacyEntry(IN CHAR16 *LoaderTitle, IN REFIT_VOLUME *Volume)
1492 {
1493 LEGACY_ENTRY *Entry, *SubEntry;
1494 REFIT_MENU_SCREEN *SubScreen;
1495 CHAR16 *VolDesc;
1496 CHAR16 ShortcutLetter = 0;
1497
1498 if (LoaderTitle == NULL) {
1499 if (Volume->OSName != NULL) {
1500 LoaderTitle = Volume->OSName;
1501 if (LoaderTitle[0] == 'W' || LoaderTitle[0] == 'L')
1502 ShortcutLetter = LoaderTitle[0];
1503 } else
1504 LoaderTitle = L"Legacy OS";
1505 }
1506 if (Volume->VolName != NULL)
1507 VolDesc = Volume->VolName;
1508 else
1509 VolDesc = (Volume->DiskKind == DISK_KIND_OPTICAL) ? L"CD" : L"HD";
1510
1511 // prepare the menu entry
1512 Entry = AllocateZeroPool(sizeof(LEGACY_ENTRY));
1513 Entry->me.Title = AllocateZeroPool(256 * sizeof(CHAR16));
1514 SPrint(Entry->me.Title, 255, L"Boot %s from %s", LoaderTitle, VolDesc);
1515 Entry->me.Tag = TAG_LEGACY;
1516 Entry->me.Row = 0;
1517 Entry->me.ShortcutLetter = ShortcutLetter;
1518 Entry->me.Image = LoadOSIcon(Volume->OSIconName, L"legacy", FALSE);
1519 Entry->me.BadgeImage = Volume->VolBadgeImage;
1520 Entry->Volume = Volume;
1521 Entry->LoadOptions = (Volume->DiskKind == DISK_KIND_OPTICAL) ? L"CD" :
1522 ((Volume->DiskKind == DISK_KIND_EXTERNAL) ? L"USB" : L"HD");
1523 Entry->Enabled = TRUE;
1524
1525 // create the submenu
1526 SubScreen = AllocateZeroPool(sizeof(REFIT_MENU_SCREEN));
1527 SubScreen->Title = AllocateZeroPool(256 * sizeof(CHAR16));
1528 SPrint(SubScreen->Title, 255, L"Boot Options for %s on %s", LoaderTitle, VolDesc);
1529 SubScreen->TitleImage = Entry->me.Image;
1530 SubScreen->Hint1 = StrDuplicate(SUBSCREEN_HINT1);
1531 if (GlobalConfig.HideUIFlags & HIDEUI_FLAG_EDITOR) {
1532 SubScreen->Hint2 = StrDuplicate(SUBSCREEN_HINT2_NO_EDITOR);
1533 } else {
1534 SubScreen->Hint2 = StrDuplicate(SUBSCREEN_HINT2);
1535 } // if/else
1536
1537 // default entry
1538 SubEntry = AllocateZeroPool(sizeof(LEGACY_ENTRY));
1539 SubEntry->me.Title = AllocateZeroPool(256 * sizeof(CHAR16));
1540 SPrint(SubEntry->me.Title, 255, L"Boot %s", LoaderTitle);
1541 SubEntry->me.Tag = TAG_LEGACY;
1542 SubEntry->Volume = Entry->Volume;
1543 SubEntry->LoadOptions = Entry->LoadOptions;
1544 AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry);
1545
1546 AddMenuEntry(SubScreen, &MenuEntryReturn);
1547 Entry->me.SubScreen = SubScreen;
1548 AddMenuEntry(&MainMenu, (REFIT_MENU_ENTRY *)Entry);
1549 return Entry;
1550 } /* static LEGACY_ENTRY * AddLegacyEntry() */
1551
1552
1553 #ifdef __MAKEWITH_GNUEFI
1554 static VOID ScanLegacyUEFI(IN UINTN DiskType){}
1555 #else
1556 // default volume badge icon based on disk kind
1557 static EG_IMAGE * GetDiskBadge(IN UINTN DiskType) {
1558 EG_IMAGE * Badge = NULL;
1559
1560 switch (DiskType) {
1561 case BBS_HARDDISK:
1562 Badge = BuiltinIcon(BUILTIN_ICON_VOL_INTERNAL);
1563 break;
1564 case BBS_USB:
1565 Badge = BuiltinIcon(BUILTIN_ICON_VOL_EXTERNAL);
1566 break;
1567 case BBS_CDROM:
1568 Badge = BuiltinIcon(BUILTIN_ICON_VOL_OPTICAL);
1569 break;
1570 } // switch()
1571 return Badge;
1572 } // static EG_IMAGE * GetDiskBadge()
1573
1574 /**
1575 Create a rEFInd boot option from a Legacy BIOS protocol option.
1576 */
1577 static LEGACY_ENTRY * AddLegacyEntryUEFI(BDS_COMMON_OPTION *BdsOption, IN UINT16 DiskType)
1578 {
1579 LEGACY_ENTRY *Entry, *SubEntry;
1580 REFIT_MENU_SCREEN *SubScreen;
1581 CHAR16 ShortcutLetter = 0;
1582 CHAR16 *LegacyDescription = BdsOption->Description;
1583
1584 // prepare the menu entry
1585 Entry = AllocateZeroPool(sizeof(LEGACY_ENTRY));
1586 Entry->me.Title = AllocateZeroPool(256 * sizeof(CHAR16));
1587 SPrint(Entry->me.Title, 255, L"Boot legacy target %s", LegacyDescription);
1588 Entry->me.Tag = TAG_LEGACY_UEFI;
1589 Entry->me.Row = 0;
1590 Entry->me.ShortcutLetter = ShortcutLetter;
1591 Entry->me.Image = LoadOSIcon(L"legacy", L"legacy", TRUE);
1592 Entry->LoadOptions = (DiskType == BBS_CDROM) ? L"CD" :
1593 ((DiskType == BBS_USB) ? L"USB" : L"HD");
1594 Entry->me.BadgeImage = GetDiskBadge(DiskType);
1595 Entry->BdsOption = BdsOption;
1596 Entry->Enabled = TRUE;
1597
1598 // create the submenu
1599 SubScreen = AllocateZeroPool(sizeof(REFIT_MENU_SCREEN));
1600 SubScreen->Title = AllocateZeroPool(256 * sizeof(CHAR16));
1601 SPrint(SubScreen->Title, 255, L"No boot options for legacy target");
1602 SubScreen->TitleImage = Entry->me.Image;
1603 SubScreen->Hint1 = StrDuplicate(SUBSCREEN_HINT1);
1604 if (GlobalConfig.HideUIFlags & HIDEUI_FLAG_EDITOR) {
1605 SubScreen->Hint2 = StrDuplicate(SUBSCREEN_HINT2_NO_EDITOR);
1606 } else {
1607 SubScreen->Hint2 = StrDuplicate(SUBSCREEN_HINT2);
1608 } // if/else
1609
1610 // default entry
1611 SubEntry = AllocateZeroPool(sizeof(LEGACY_ENTRY));
1612 SubEntry->me.Title = AllocateZeroPool(256 * sizeof(CHAR16));
1613 SPrint(SubEntry->me.Title, 255, L"Boot %s", LegacyDescription);
1614 SubEntry->me.Tag = TAG_LEGACY_UEFI;
1615 Entry->BdsOption = BdsOption;
1616 AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry);
1617
1618 AddMenuEntry(SubScreen, &MenuEntryReturn);
1619 Entry->me.SubScreen = SubScreen;
1620 AddMenuEntry(&MainMenu, (REFIT_MENU_ENTRY *)Entry);
1621 return Entry;
1622 } /* static LEGACY_ENTRY * AddLegacyEntryUEFI() */
1623
1624 /**
1625 Scan for legacy BIOS targets on machines that implement EFI_LEGACY_BIOS_PROTOCOL.
1626 In testing, protocol has not been implemented on Macs but has been
1627 implemented on several Dell PCs and an ASUS motherboard.
1628 Restricts output to disks of the specified DiskType.
1629 */
1630 static VOID ScanLegacyUEFI(IN UINTN DiskType)
1631 {
1632 EFI_STATUS Status;
1633 EFI_LEGACY_BIOS_PROTOCOL *LegacyBios;
1634 UINT16 *BootOrder = NULL;
1635 UINTN Index = 0;
1636 CHAR16 BootOption[10];
1637 UINTN BootOrderSize = 0;
1638 CHAR16 Buffer[20];
1639 BDS_COMMON_OPTION *BdsOption;
1640 LIST_ENTRY TempList;
1641 BBS_BBS_DEVICE_PATH * BbsDevicePath = NULL;
1642
1643 InitializeListHead (&TempList);
1644 ZeroMem (Buffer, sizeof (Buffer));
1645
1646 // If LegacyBios protocol is not implemented on this platform, then
1647 //we do not support this type of legacy boot on this machine.
1648 Status = gBS->LocateProtocol(&gEfiLegacyBootProtocolGuid, NULL, (VOID **) &LegacyBios);
1649 if (EFI_ERROR (Status))
1650 return;
1651
1652 // Grab the boot order
1653 BootOrder = BdsLibGetVariableAndSize(L"BootOrder", &gEfiGlobalVariableGuid, &BootOrderSize);
1654 if (BootOrder == NULL) {
1655 BootOrderSize = 0;
1656 }
1657
1658 Index = 0;
1659 while (Index < BootOrderSize / sizeof (UINT16))
1660 {
1661 // Grab each boot option variable from the boot order, and convert
1662 // the variable into a BDS boot option
1663 UnicodeSPrint (BootOption, sizeof (BootOption), L"Boot%04x", BootOrder[Index]);
1664 BdsOption = BdsLibVariableToOption (&TempList, BootOption);
1665
1666 if (BdsOption != NULL) {
1667 BbsDevicePath = (BBS_BBS_DEVICE_PATH *)BdsOption->DevicePath;
1668
1669 // Only add the entry if it is of a requested type (e.g. USB, HD)
1670
1671 // Two checks necessary because some systems return EFI boot loaders
1672 // with a DeviceType value that would inappropriately include them
1673 // as legacy loaders....
1674 if ((BbsDevicePath->DeviceType == DiskType) && (BdsOption->DevicePath->Type == DEVICE_TYPE_BIOS)) {
1675 AddLegacyEntryUEFI(BdsOption, BbsDevicePath->DeviceType);
1676 }
1677 }
1678 Index++;
1679 }
1680 } /* static VOID ScanLegacyUEFI() */
1681 #endif // __MAKEWITH_GNUEFI
1682
1683 static VOID ScanLegacyVolume(REFIT_VOLUME *Volume, UINTN VolumeIndex) {
1684 UINTN VolumeIndex2;
1685 BOOLEAN ShowVolume, HideIfOthersFound;
1686
1687 ShowVolume = FALSE;
1688 HideIfOthersFound = FALSE;
1689 if (Volume->IsAppleLegacy) {
1690 ShowVolume = TRUE;
1691 HideIfOthersFound = TRUE;
1692 } else if (Volume->HasBootCode) {
1693 ShowVolume = TRUE;
1694 if (Volume->BlockIO == Volume->WholeDiskBlockIO &&
1695 Volume->BlockIOOffset == 0 &&
1696 Volume->OSName == NULL)
1697 // this is a whole disk (MBR) entry; hide if we have entries for partitions
1698 HideIfOthersFound = TRUE;
1699 }
1700 if (HideIfOthersFound) {
1701 // check for other bootable entries on the same disk
1702 for (VolumeIndex2 = 0; VolumeIndex2 < VolumesCount; VolumeIndex2++) {
1703 if (VolumeIndex2 != VolumeIndex && Volumes[VolumeIndex2]->HasBootCode &&
1704 Volumes[VolumeIndex2]->WholeDiskBlockIO == Volume->WholeDiskBlockIO)
1705 ShowVolume = FALSE;
1706 }
1707 }
1708
1709 if (ShowVolume)
1710 AddLegacyEntry(NULL, Volume);
1711 } // static VOID ScanLegacyVolume()
1712
1713 // Scan attached optical discs for legacy (BIOS) boot code
1714 // and add anything found to the list....
1715 static VOID ScanLegacyDisc(VOID)
1716 {
1717 UINTN VolumeIndex;
1718 REFIT_VOLUME *Volume;
1719
1720 if (GlobalConfig.LegacyType == LEGACY_TYPE_MAC) {
1721 for (VolumeIndex = 0; VolumeIndex < VolumesCount; VolumeIndex++) {
1722 Volume = Volumes[VolumeIndex];
1723 if (Volume->DiskKind == DISK_KIND_OPTICAL)
1724 ScanLegacyVolume(Volume, VolumeIndex);
1725 } // for
1726 } else if (GlobalConfig.LegacyType == LEGACY_TYPE_UEFI) {
1727 ScanLegacyUEFI(BBS_CDROM);
1728 }
1729 } /* static VOID ScanLegacyDisc() */
1730
1731 // Scan internal hard disks for legacy (BIOS) boot code
1732 // and add anything found to the list....
1733 static VOID ScanLegacyInternal(VOID)
1734 {
1735 UINTN VolumeIndex;
1736 REFIT_VOLUME *Volume;
1737
1738 if (GlobalConfig.LegacyType == LEGACY_TYPE_MAC) {
1739 for (VolumeIndex = 0; VolumeIndex < VolumesCount; VolumeIndex++) {
1740 Volume = Volumes[VolumeIndex];
1741 if (Volume->DiskKind == DISK_KIND_INTERNAL)
1742 ScanLegacyVolume(Volume, VolumeIndex);
1743 } // for
1744 } else if (GlobalConfig.LegacyType == LEGACY_TYPE_UEFI) {
1745 ScanLegacyUEFI(BBS_HARDDISK);
1746 }
1747 } /* static VOID ScanLegacyInternal() */
1748
1749 // Scan external disks for legacy (BIOS) boot code
1750 // and add anything found to the list....
1751 static VOID ScanLegacyExternal(VOID)
1752 {
1753 UINTN VolumeIndex;
1754 REFIT_VOLUME *Volume;
1755
1756 if (GlobalConfig.LegacyType == LEGACY_TYPE_MAC) {
1757 for (VolumeIndex = 0; VolumeIndex < VolumesCount; VolumeIndex++) {
1758 Volume = Volumes[VolumeIndex];
1759 if (Volume->DiskKind == DISK_KIND_EXTERNAL)
1760 ScanLegacyVolume(Volume, VolumeIndex);
1761 } // for
1762 } else if (GlobalConfig.LegacyType == LEGACY_TYPE_UEFI) {
1763 ScanLegacyUEFI(BBS_USB);
1764 }
1765 } /* static VOID ScanLegacyExternal() */
1766
1767 //
1768 // pre-boot tool functions
1769 //
1770
1771 static VOID StartTool(IN LOADER_ENTRY *Entry)
1772 {
1773 BeginExternalScreen(Entry->UseGraphicsMode, Entry->me.Title + 6); // assumes "Start <title>" as assigned below
1774 StartEFIImage(Entry->DevicePath, Entry->LoadOptions, Basename(Entry->LoaderPath),
1775 Basename(Entry->LoaderPath), Entry->OSType, NULL, TRUE);
1776 FinishExternalScreen();
1777 } /* static VOID StartTool() */
1778
1779 static LOADER_ENTRY * AddToolEntry(EFI_HANDLE DeviceHandle, IN CHAR16 *LoaderPath, IN CHAR16 *LoaderTitle, IN EG_IMAGE *Image,
1780 IN CHAR16 ShortcutLetter, IN BOOLEAN UseGraphicsMode)
1781 {
1782 LOADER_ENTRY *Entry;
1783 CHAR16 *TitleStr = NULL;
1784
1785 Entry = AllocateZeroPool(sizeof(LOADER_ENTRY));
1786
1787 MergeStrings(&TitleStr, L"Start ", 0);
1788 MergeStrings(&TitleStr, LoaderTitle, 0);
1789 Entry->me.Title = TitleStr;
1790 Entry->me.Tag = TAG_TOOL;
1791 Entry->me.Row = 1;
1792 Entry->me.ShortcutLetter = ShortcutLetter;
1793 Entry->me.Image = Image;
1794 Entry->LoaderPath = (LoaderPath) ? StrDuplicate(LoaderPath) : NULL;
1795 Entry->DevicePath = FileDevicePath(DeviceHandle, Entry->LoaderPath);
1796 Entry->UseGraphicsMode = UseGraphicsMode;
1797
1798 AddMenuEntry(&MainMenu, (REFIT_MENU_ENTRY *)Entry);
1799 return Entry;
1800 } /* static LOADER_ENTRY * AddToolEntry() */
1801
1802 //
1803 // pre-boot driver functions
1804 //
1805
1806 static UINTN ScanDriverDir(IN CHAR16 *Path)
1807 {
1808 EFI_STATUS Status;
1809 REFIT_DIR_ITER DirIter;
1810 UINTN NumFound = 0;
1811 EFI_FILE_INFO *DirEntry;
1812 CHAR16 FileName[256];
1813
1814 CleanUpPathNameSlashes(Path);
1815 // look through contents of the directory
1816 DirIterOpen(SelfRootDir, Path, &DirIter);
1817 while (DirIterNext(&DirIter, 2, LOADER_MATCH_PATTERNS, &DirEntry)) {
1818 if (DirEntry->FileName[0] == '.')
1819 continue; // skip this
1820
1821 SPrint(FileName, 255, L"%s\\%s", Path, DirEntry->FileName);
1822 NumFound++;
1823 Status = StartEFIImage(FileDevicePath(SelfLoadedImage->DeviceHandle, FileName),
1824 L"", DirEntry->FileName, DirEntry->FileName, 0, NULL, FALSE);
1825 }
1826 Status = DirIterClose(&DirIter);
1827 if (Status != EFI_NOT_FOUND) {
1828 SPrint(FileName, 255, L"while scanning the %s directory", Path);
1829 CheckError(Status, FileName);
1830 }
1831 return (NumFound);
1832 }
1833
1834 #ifdef __MAKEWITH_GNUEFI
1835 static EFI_STATUS ConnectAllDriversToAllControllers(VOID)
1836 {
1837 EFI_STATUS Status;
1838 UINTN AllHandleCount;
1839 EFI_HANDLE *AllHandleBuffer;
1840 UINTN Index;
1841 UINTN HandleCount;
1842 EFI_HANDLE *HandleBuffer;
1843 UINT32 *HandleType;
1844 UINTN HandleIndex;
1845 BOOLEAN Parent;
1846 BOOLEAN Device;
1847
1848 Status = LibLocateHandle(AllHandles,
1849 NULL,
1850 NULL,
1851 &AllHandleCount,
1852 &AllHandleBuffer);
1853 if (EFI_ERROR(Status))
1854 return Status;
1855
1856 for (Index = 0; Index < AllHandleCount; Index++) {
1857 //
1858 // Scan the handle database
1859 //
1860 Status = LibScanHandleDatabase(NULL,
1861 NULL,
1862 AllHandleBuffer[Index],
1863 NULL,
1864 &HandleCount,
1865 &HandleBuffer,
1866 &HandleType);
1867 if (EFI_ERROR (Status))
1868 goto Done;
1869
1870 Device = TRUE;
1871 if (HandleType[Index] & EFI_HANDLE_TYPE_DRIVER_BINDING_HANDLE)
1872 Device = FALSE;
1873 if (HandleType[Index] & EFI_HANDLE_TYPE_IMAGE_HANDLE)
1874 Device = FALSE;
1875
1876 if (Device) {
1877 Parent = FALSE;
1878 for (HandleIndex = 0; HandleIndex < HandleCount; HandleIndex++) {
1879 if (HandleType[HandleIndex] & EFI_HANDLE_TYPE_PARENT_HANDLE)
1880 Parent = TRUE;
1881 } // for
1882
1883 if (!Parent) {
1884 if (HandleType[Index] & EFI_HANDLE_TYPE_DEVICE_HANDLE) {
1885 Status = refit_call4_wrapper(BS->ConnectController,
1886 AllHandleBuffer[Index],
1887 NULL,
1888 NULL,
1889 TRUE);
1890 }
1891 }
1892 }
1893
1894 MyFreePool (HandleBuffer);
1895 MyFreePool (HandleType);
1896 }
1897
1898 Done:
1899 MyFreePool (AllHandleBuffer);
1900 return Status;
1901 } /* EFI_STATUS ConnectAllDriversToAllControllers() */
1902 #else
1903 static EFI_STATUS ConnectAllDriversToAllControllers(VOID) {
1904 BdsLibConnectAllDriversToAllControllers();
1905 return 0;
1906 }
1907 #endif
1908
1909 // Load all EFI drivers from rEFInd's "drivers" subdirectory and from the
1910 // directories specified by the user in the "scan_driver_dirs" configuration
1911 // file line.
1912 static VOID LoadDrivers(VOID)
1913 {
1914 CHAR16 *Directory, *SelfDirectory;
1915 UINTN i = 0, Length, NumFound = 0;
1916
1917 // load drivers from the subdirectories of rEFInd's home directory specified
1918 // in the DRIVER_DIRS constant.
1919 while ((Directory = FindCommaDelimited(DRIVER_DIRS, i++)) != NULL) {
1920 SelfDirectory = SelfDirPath ? StrDuplicate(SelfDirPath) : NULL;
1921 CleanUpPathNameSlashes(SelfDirectory);
1922 MergeStrings(&SelfDirectory, Directory, L'\\');
1923 NumFound += ScanDriverDir(SelfDirectory);
1924 MyFreePool(Directory);
1925 MyFreePool(SelfDirectory);
1926 }
1927
1928 // Scan additional user-specified driver directories....
1929 i = 0;
1930 while ((Directory = FindCommaDelimited(GlobalConfig.DriverDirs, i++)) != NULL) {
1931 CleanUpPathNameSlashes(Directory);
1932 Length = StrLen(Directory);
1933 if (Length > 0) {
1934 NumFound += ScanDriverDir(Directory);
1935 } // if
1936 MyFreePool(Directory);
1937 } // while
1938
1939 // connect all devices
1940 if (NumFound > 0)
1941 ConnectAllDriversToAllControllers();
1942 } /* static VOID LoadDrivers() */
1943
1944 // Determine what (if any) type of legacy (BIOS) boot support is available
1945 static VOID FindLegacyBootType(VOID) {
1946 #ifdef __MAKEWITH_TIANO
1947 EFI_STATUS Status;
1948 EFI_LEGACY_BIOS_PROTOCOL *LegacyBios;
1949 #endif
1950
1951 GlobalConfig.LegacyType = LEGACY_TYPE_NONE;
1952
1953 // UEFI-style legacy BIOS support is available only with the TianoCore EDK2
1954 // build environment, and then only with some EFI implementations....
1955 #ifdef __MAKEWITH_TIANO
1956 Status = gBS->LocateProtocol (&gEfiLegacyBootProtocolGuid, NULL, (VOID **) &LegacyBios);
1957 if (!EFI_ERROR (Status))
1958 GlobalConfig.LegacyType = LEGACY_TYPE_UEFI;
1959 #endif
1960
1961 // Macs have their own system. If the firmware vendor code contains the
1962 // string "Apple", assume it's available. Note that this overrides the
1963 // UEFI type, and might yield false positives if the vendor string
1964 // contains "Apple" as part of something bigger, so this isn't 100%
1965 // perfect.
1966 if (StriSubCmp(L"Apple", ST->FirmwareVendor))
1967 GlobalConfig.LegacyType = LEGACY_TYPE_MAC;
1968 } // static VOID FindLegacyBootType
1969
1970 // Warn the user if legacy OS scans are enabled but the firmware or this
1971 // application can't support them....
1972 static VOID WarnIfLegacyProblems() {
1973 BOOLEAN found = FALSE;
1974 UINTN i = 0;
1975
1976 if (GlobalConfig.LegacyType == LEGACY_TYPE_NONE) {
1977 do {
1978 if (GlobalConfig.ScanFor[i] == 'h' || GlobalConfig.ScanFor[i] == 'b' || GlobalConfig.ScanFor[i] == 'c')
1979 found = TRUE;
1980 i++;
1981 } while ((i < NUM_SCAN_OPTIONS) && (!found));
1982 if (found) {
1983 Print(L"NOTE: refind.conf's 'scanfor' line specifies scanning for one or more legacy\n");
1984 Print(L"(BIOS) boot options; however, this is not possible because ");
1985 #ifdef __MAKEWITH_TIANO
1986 Print(L"your computer lacks\n");
1987 Print(L"the necessary Compatibility Support Module (CSM) support.\n");
1988 #else
1989 Print(L"this program was\n");
1990 Print(L"compiled without the necessary support. Please visit\n");
1991 Print(L"http://www.rodsbooks.com/refind/getting.html and download and install a rEFInd\n");
1992 Print(L"binary built with the TianoCore EDK2 to enable legacy boot support.\n");
1993 #endif
1994 PauseForKey();
1995 } // if (found)
1996 } // if no legacy support
1997 } // static VOID WarnIfLegacyProblems()
1998
1999 // Locates boot loaders. NOTE: This assumes that GlobalConfig.LegacyType is set correctly.
2000 static VOID ScanForBootloaders(VOID) {
2001 UINTN i;
2002
2003 ScanVolumes();
2004
2005 // scan for loaders and tools, add them to the menu
2006 for (i = 0; i < NUM_SCAN_OPTIONS; i++) {
2007 switch(GlobalConfig.ScanFor[i]) {
2008 case 'c': case 'C':
2009 ScanLegacyDisc();
2010 break;
2011 case 'h': case 'H':
2012 ScanLegacyInternal();
2013 break;
2014 case 'b': case 'B':
2015 ScanLegacyExternal();
2016 break;
2017 case 'm': case 'M':
2018 ScanUserConfigured(CONFIG_FILE_NAME);
2019 break;
2020 case 'e': case 'E':
2021 ScanExternal();
2022 break;
2023 case 'i': case 'I':
2024 ScanInternal();
2025 break;
2026 case 'o': case 'O':
2027 ScanOptical();
2028 break;
2029 } // switch()
2030 } // for
2031
2032 // assign shortcut keys
2033 for (i = 0; i < MainMenu.EntryCount && MainMenu.Entries[i]->Row == 0 && i < 9; i++)
2034 MainMenu.Entries[i]->ShortcutDigit = (CHAR16)('1' + i);
2035
2036 // wait for user ACK when there were errors
2037 FinishTextScreen(FALSE);
2038 } // static VOID ScanForBootloaders()
2039
2040 // Add the second-row tags containing built-in and external tools (EFI shell,
2041 // reboot, etc.)
2042 static VOID ScanForTools(VOID) {
2043 CHAR16 *FileName = NULL, *MokLocations, *MokName, *PathName, Description[256];
2044 REFIT_MENU_ENTRY *TempMenuEntry;
2045 UINTN i, j, k, VolumeIndex;
2046 UINT64 osind;
2047 CHAR8 *b = 0;
2048
2049 MokLocations = StrDuplicate(MOK_LOCATIONS);
2050 if (MokLocations != NULL)
2051 MergeStrings(&MokLocations, SelfDirPath, L',');
2052
2053 for (i = 0; i < NUM_TOOLS; i++) {
2054 switch(GlobalConfig.ShowTools[i]) {
2055 // NOTE: Be sure that FileName is NULL at the end of each case.
2056 case TAG_SHUTDOWN:
2057 TempMenuEntry = CopyMenuEntry(&MenuEntryShutdown);
2058 TempMenuEntry->Image = BuiltinIcon(BUILTIN_ICON_FUNC_SHUTDOWN);
2059 AddMenuEntry(&MainMenu, TempMenuEntry);
2060 break;
2061 case TAG_REBOOT:
2062 TempMenuEntry = CopyMenuEntry(&MenuEntryReset);
2063 TempMenuEntry->Image = BuiltinIcon(BUILTIN_ICON_FUNC_RESET);
2064 AddMenuEntry(&MainMenu, TempMenuEntry);
2065 break;
2066 case TAG_ABOUT:
2067 TempMenuEntry = CopyMenuEntry(&MenuEntryAbout);
2068 TempMenuEntry->Image = BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT);
2069 AddMenuEntry(&MainMenu, TempMenuEntry);
2070 break;
2071 case TAG_EXIT:
2072 TempMenuEntry = CopyMenuEntry(&MenuEntryExit);
2073 TempMenuEntry->Image = BuiltinIcon(BUILTIN_ICON_FUNC_EXIT);
2074 AddMenuEntry(&MainMenu, TempMenuEntry);
2075 break;
2076 case TAG_FIRMWARE:
2077 if (EfivarGetRaw(&GlobalGuid, L"OsIndicationsSupported", &b, &j) == EFI_SUCCESS) {
2078 osind = (UINT64)*b;
2079 if (osind & EFI_OS_INDICATIONS_BOOT_TO_FW_UI) {
2080 TempMenuEntry = CopyMenuEntry(&MenuEntryFirmware);
2081 TempMenuEntry->Image = BuiltinIcon(BUILTIN_ICON_FUNC_FIRMWARE);
2082 AddMenuEntry(&MainMenu, TempMenuEntry);
2083 } // if
2084 } // if
2085 break;
2086 case TAG_SHELL:
2087 j = 0;
2088 while ((FileName = FindCommaDelimited(SHELL_NAMES, j++)) != NULL) {
2089 if (FileExists(SelfRootDir, FileName)) {
2090 AddToolEntry(SelfLoadedImage->DeviceHandle, FileName, L"EFI Shell", BuiltinIcon(BUILTIN_ICON_TOOL_SHELL),
2091 'S', FALSE);
2092 }
2093 MyFreePool(FileName);
2094 } // while
2095 break;
2096 case TAG_GPTSYNC:
2097 j = 0;
2098 while ((FileName = FindCommaDelimited(GPTSYNC_NAMES, j++)) != NULL) {
2099 if (FileExists(SelfRootDir, FileName)) {
2100 AddToolEntry(SelfLoadedImage->DeviceHandle, FileName, L"Hybrid MBR tool", BuiltinIcon(BUILTIN_ICON_TOOL_PART),
2101 'P', FALSE);
2102 } // if
2103 MyFreePool(FileName);
2104 } // while
2105 FileName = NULL;
2106 break;
2107 case TAG_APPLE_RECOVERY:
2108 FileName = StrDuplicate(L"\\com.apple.recovery.boot\\boot.efi");
2109 for (VolumeIndex = 0; VolumeIndex < VolumesCount; VolumeIndex++) {
2110 if ((Volumes[VolumeIndex]->RootDir != NULL) && (FileExists(Volumes[VolumeIndex]->RootDir, FileName))) {
2111 SPrint(Description, 255, L"Apple Recovery on %s", Volumes[VolumeIndex]->VolName);
2112 AddToolEntry(Volumes[VolumeIndex]->DeviceHandle, FileName, Description,
2113 BuiltinIcon(BUILTIN_ICON_TOOL_APPLE_RESCUE), 'R', TRUE);
2114 }
2115 } // for
2116 MyFreePool(FileName);
2117 FileName = NULL;
2118 break;
2119 case TAG_MOK_TOOL:
2120 j = 0;
2121 while ((FileName = FindCommaDelimited(MokLocations, j++)) != NULL) {
2122 k = 0;
2123 while ((MokName = FindCommaDelimited(MOK_NAMES, k++)) != NULL) {
2124 PathName = StrDuplicate(FileName);
2125 MergeStrings(&PathName, MokName, (StriCmp(PathName, L"\\") == 0) ? 0 : L'\\');
2126 for (VolumeIndex = 0; VolumeIndex < VolumesCount; VolumeIndex++) {
2127 if ((Volumes[VolumeIndex]->RootDir != NULL) && (FileExists(Volumes[VolumeIndex]->RootDir, PathName))) {
2128 SPrint(Description, 255, L"MOK utility at %s on %s", PathName, Volumes[VolumeIndex]->VolName);
2129 AddToolEntry(Volumes[VolumeIndex]->DeviceHandle, PathName, Description,
2130 BuiltinIcon(BUILTIN_ICON_TOOL_MOK_TOOL), 'S', FALSE);
2131 } // if
2132 } // for
2133 MyFreePool(PathName);
2134 MyFreePool(MokName);
2135 } // while MOK_NAMES
2136 MyFreePool(FileName);
2137 } // while MokLocations
2138
2139 break;
2140 } // switch()
2141 } // for
2142 } // static VOID ScanForTools
2143
2144 // Rescan for boot loaders
2145 VOID RescanAll(VOID) {
2146 EG_PIXEL BGColor;
2147
2148 BGColor.b = 255;
2149 BGColor.g = 175;
2150 BGColor.r = 100;
2151 BGColor.a = 0;
2152 egDisplayMessage(L"Scanning for new boot loaders; please wait....", &BGColor);
2153 FreeList((VOID ***) &(MainMenu.Entries), &MainMenu.EntryCount);
2154 MainMenu.Entries = NULL;
2155 MainMenu.EntryCount = 0;
2156 ReadConfig(CONFIG_FILE_NAME);
2157 ConnectAllDriversToAllControllers();
2158 ScanVolumes();
2159 ScanForBootloaders();
2160 ScanForTools();
2161 SetupScreen();
2162 } // VOID RescanAll()
2163
2164 #ifdef __MAKEWITH_TIANO
2165
2166 // Minimal initialization function
2167 static VOID InitializeLib(IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable) {
2168 gST = SystemTable;
2169 // gImageHandle = ImageHandle;
2170 gBS = SystemTable->BootServices;
2171 // gRS = SystemTable->RuntimeServices;
2172 gRT = SystemTable->RuntimeServices; // Some BDS functions need gRT to be set
2173 EfiGetSystemConfigurationTable (&gEfiDxeServicesTableGuid, (VOID **) &gDS);
2174
2175 InitializeConsoleSim();
2176 }
2177
2178 #endif
2179
2180 // Set up our own Secure Boot extensions....
2181 // Returns TRUE on success, FALSE otherwise
2182 static BOOLEAN SecureBootSetup(VOID) {
2183 EFI_STATUS Status;
2184 BOOLEAN Success = FALSE;
2185
2186 if (secure_mode() && ShimLoaded()) {
2187 Status = security_policy_install();
2188 if (Status == EFI_SUCCESS) {
2189 Success = TRUE;
2190 } else {
2191 Print(L"Failed to install MOK Secure Boot extensions");
2192 }
2193 }
2194 return Success;
2195 } // VOID SecureBootSetup()
2196
2197 // Remove our own Secure Boot extensions....
2198 // Returns TRUE on success, FALSE otherwise
2199 static BOOLEAN SecureBootUninstall(VOID) {
2200 EFI_STATUS Status;
2201 BOOLEAN Success = TRUE;
2202
2203 if (secure_mode()) {
2204 Status = security_policy_uninstall();
2205 if (Status != EFI_SUCCESS) {
2206 Success = FALSE;
2207 BeginTextScreen(L"Secure Boot Policy Failure");
2208 Print(L"Failed to uninstall MOK Secure Boot extensions; forcing a reboot.");
2209 PauseForKey();
2210 refit_call4_wrapper(RT->ResetSystem, EfiResetCold, EFI_SUCCESS, 0, NULL);
2211 }
2212 }
2213 return Success;
2214 } // VOID SecureBootUninstall
2215
2216 //
2217 // main entry point
2218 //
2219 EFI_STATUS
2220 EFIAPI
2221 efi_main (EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable)
2222 {
2223 EFI_STATUS Status;
2224 BOOLEAN MainLoopRunning = TRUE;
2225 BOOLEAN MokProtocol;
2226 REFIT_MENU_ENTRY *ChosenEntry;
2227 UINTN MenuExit, i;
2228 CHAR16 *Selection = NULL;
2229 EG_PIXEL BGColor;
2230
2231 // bootstrap
2232 InitializeLib(ImageHandle, SystemTable);
2233 Status = InitRefitLib(ImageHandle);
2234 if (EFI_ERROR(Status))
2235 return Status;
2236
2237 // read configuration
2238 CopyMem(GlobalConfig.ScanFor, "ieom ", NUM_SCAN_OPTIONS);
2239 FindLegacyBootType();
2240 if (GlobalConfig.LegacyType == LEGACY_TYPE_MAC)
2241 CopyMem(GlobalConfig.ScanFor, "ihebocm ", NUM_SCAN_OPTIONS);
2242 ScanVolumes();
2243 ReadConfig(CONFIG_FILE_NAME);
2244
2245 InitScreen();
2246 WarnIfLegacyProblems();
2247 MainMenu.TimeoutSeconds = GlobalConfig.Timeout;
2248
2249 // disable EFI watchdog timer
2250 refit_call4_wrapper(BS->SetWatchdogTimer, 0x0000, 0x0000, 0x0000, NULL);
2251
2252 // further bootstrap (now with config available)
2253 MokProtocol = SecureBootSetup();
2254 LoadDrivers();
2255 ScanForBootloaders();
2256 ScanForTools();
2257 SetupScreen();
2258
2259 if (GlobalConfig.ScanDelay > 0) {
2260 BGColor.b = 255;
2261 BGColor.g = 175;
2262 BGColor.r = 100;
2263 BGColor.a = 0;
2264 egDisplayMessage(L"Pausing before disk scan; please wait....", &BGColor);
2265 for (i = 0; i < GlobalConfig.ScanDelay; i++)
2266 refit_call1_wrapper(BS->Stall, 1000000);
2267 RescanAll();
2268 } // if
2269
2270 if (GlobalConfig.DefaultSelection)
2271 Selection = StrDuplicate(GlobalConfig.DefaultSelection);
2272
2273 while (MainLoopRunning) {
2274 MenuExit = RunMainMenu(&MainMenu, Selection, &ChosenEntry);
2275
2276 // The Escape key triggers a re-scan operation....
2277 if (MenuExit == MENU_EXIT_ESCAPE) {
2278 RescanAll();
2279 continue;
2280 }
2281
2282 switch (ChosenEntry->Tag) {
2283
2284 case TAG_REBOOT: // Reboot
2285 TerminateScreen();
2286 refit_call4_wrapper(RT->ResetSystem, EfiResetCold, EFI_SUCCESS, 0, NULL);
2287 MainLoopRunning = FALSE; // just in case we get this far
2288 break;
2289
2290 case TAG_SHUTDOWN: // Shut Down
2291 TerminateScreen();
2292 refit_call4_wrapper(RT->ResetSystem, EfiResetShutdown, EFI_SUCCESS, 0, NULL);
2293 MainLoopRunning = FALSE; // just in case we get this far
2294 break;
2295
2296 case TAG_ABOUT: // About rEFInd
2297 AboutrEFInd();
2298 break;
2299
2300 case TAG_LOADER: // Boot OS via .EFI loader
2301 StartLoader((LOADER_ENTRY *)ChosenEntry);
2302 break;
2303
2304 case TAG_LEGACY: // Boot legacy OS
2305 StartLegacy((LEGACY_ENTRY *)ChosenEntry);
2306 break;
2307
2308 #ifdef __MAKEWITH_TIANO
2309 case TAG_LEGACY_UEFI: // Boot a legacy OS on a non-Mac
2310 StartLegacyUEFI((LEGACY_ENTRY *)ChosenEntry);
2311 break;
2312 #endif
2313
2314 case TAG_TOOL: // Start a EFI tool
2315 StartTool((LOADER_ENTRY *)ChosenEntry);
2316 break;
2317
2318 case TAG_EXIT: // Terminate rEFInd
2319 if ((MokProtocol) && !SecureBootUninstall()) {
2320 MainLoopRunning = FALSE; // just in case we get this far
2321 } else {
2322 BeginTextScreen(L" ");
2323 return EFI_SUCCESS;
2324 }
2325 break;
2326
2327 case TAG_FIRMWARE: // Reboot into firmware's user interface
2328 RebootIntoFirmware();
2329 break;
2330
2331 } // switch()
2332 MyFreePool(Selection);
2333 Selection = (ChosenEntry->Title) ? StrDuplicate(ChosenEntry->Title) : NULL;
2334 } // while()
2335
2336 // If we end up here, things have gone wrong. Try to reboot, and if that
2337 // fails, go into an endless loop.
2338 refit_call4_wrapper(RT->ResetSystem, EfiResetCold, EFI_SUCCESS, 0, NULL);
2339 EndlessIdleLoop();
2340
2341 return EFI_SUCCESS;
2342 } /* efi_main() */